From b4db3b6a813ea84bba39d748f3192514a7433a8d Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Thu, 7 Sep 2023 15:32:26 -0500 Subject: [PATCH 01/13] Add enigma berry effect --- src/battle_util.c | 9 +++++++++ src/data/items.h | 1 + 2 files changed, 10 insertions(+) diff --git a/src/battle_util.c b/src/battle_util.c index 1ff0a762e..cd73d4577 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -7761,6 +7761,15 @@ u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn) gBattleScripting.statChanger = SET_STATCHANGER(STAT_SPATK, 1, FALSE); } break; + case HOLD_EFFECT_ENIGMA_BERRY: // consume and heal if hit by super effective move + if (IsBattlerAlive(battlerId) + && TARGET_TURN_DAMAGED + && gMoveResultFlags & MOVE_RESULT_SUPER_EFFECTIVE) + { + effect = ItemHealHp(battlerId, gLastUsedItem, TRUE, TRUE); + } + break; + case HOLD_EFFECT_JABOCA_BERRY: // consume and damage attacker if used physical move if (IsBattlerAlive(battlerId) && TARGET_TURN_DAMAGED diff --git a/src/data/items.h b/src/data/items.h index 5e15b2734..98940277b 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -7630,6 +7630,7 @@ const struct Item gItems[] = .itemId = ITEM_ENIGMA_BERRY, .price = 20, .holdEffect = HOLD_EFFECT_ENIGMA_BERRY, + .holdEffectParam = 25, .description = sEnigmaBerryDesc, .pocket = POCKET_BERRIES, .type = ITEM_USE_BAG_MENU, From 8f8705929ac9dd94ed4d2a364a4b23a3745ab1be Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Thu, 7 Sep 2023 15:37:58 -0500 Subject: [PATCH 02/13] Update battle_util.c --- src/battle_util.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/battle_util.c b/src/battle_util.c index cd73d4577..87f93389d 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -7769,7 +7769,6 @@ u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn) effect = ItemHealHp(battlerId, gLastUsedItem, TRUE, TRUE); } break; - case HOLD_EFFECT_JABOCA_BERRY: // consume and damage attacker if used physical move if (IsBattlerAlive(battlerId) && TARGET_TURN_DAMAGED From 40ac21476433c9d8ca37df10d70f6c8ae81ddfd6 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Thu, 7 Sep 2023 17:10:25 -0500 Subject: [PATCH 03/13] Fix enigma berry --- src/battle_util.c | 23 +++++++++++++++-------- src/data/items.h | 1 - 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index 87f93389d..94437dca2 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6802,6 +6802,21 @@ static u8 ItemEffectMoveEnd(u32 battlerId, u16 holdEffect) case HOLD_EFFECT_SP_DEFENSE_UP: effect = StatRaiseBerry(battlerId, gLastUsedItem, STAT_SPDEF, FALSE); break; + case HOLD_EFFECT_ENIGMA_BERRY: // consume and heal if hit by super effective move + if (IsBattlerAlive(battlerId) + && TARGET_TURN_DAMAGED + && (gBattleScripting.overrideBerryRequirements + || (!DoesSubstituteBlockMove(gBattlerAttacker, battlerId, gCurrentMove) && (gMoveResultFlags & MOVE_RESULT_SUPER_EFFECTIVE)))) + { + gBattleMoveDamage = (gBattleMons[battlerId].maxHP * 25 / 100) * -1; + if (GetBattlerAbility(battlerId) == ABILITY_RIPEN) + gBattleMoveDamage *= 2; + + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_ItemHealHP_RemoveItemRet; + return ITEM_HP_CHANGE; + } + break; case HOLD_EFFECT_KEE_BERRY: // consume and boost defense if used physical move effect = DamagedStatBoostBerryEffect(battlerId, STAT_DEF, SPLIT_PHYSICAL); break; @@ -7761,14 +7776,6 @@ u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn) gBattleScripting.statChanger = SET_STATCHANGER(STAT_SPATK, 1, FALSE); } break; - case HOLD_EFFECT_ENIGMA_BERRY: // consume and heal if hit by super effective move - if (IsBattlerAlive(battlerId) - && TARGET_TURN_DAMAGED - && gMoveResultFlags & MOVE_RESULT_SUPER_EFFECTIVE) - { - effect = ItemHealHp(battlerId, gLastUsedItem, TRUE, TRUE); - } - break; case HOLD_EFFECT_JABOCA_BERRY: // consume and damage attacker if used physical move if (IsBattlerAlive(battlerId) && TARGET_TURN_DAMAGED diff --git a/src/data/items.h b/src/data/items.h index 98940277b..5e15b2734 100644 --- a/src/data/items.h +++ b/src/data/items.h @@ -7630,7 +7630,6 @@ const struct Item gItems[] = .itemId = ITEM_ENIGMA_BERRY, .price = 20, .holdEffect = HOLD_EFFECT_ENIGMA_BERRY, - .holdEffectParam = 25, .description = sEnigmaBerryDesc, .pocket = POCKET_BERRIES, .type = ITEM_USE_BAG_MENU, From 09f92719cbe33e0bb0b0045e4e8b13f01d547fda Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Sun, 10 Sep 2023 23:59:12 +0200 Subject: [PATCH 04/13] Tests for status curing berries (#3289) --- test/battle/hold_effect/cure_status.c | 215 ++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) create mode 100644 test/battle/hold_effect/cure_status.c diff --git a/test/battle/hold_effect/cure_status.c b/test/battle/hold_effect/cure_status.c new file mode 100644 index 000000000..c8c32ecf7 --- /dev/null +++ b/test/battle/hold_effect/cure_status.c @@ -0,0 +1,215 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gItems[ITEM_LUM_BERRY].holdEffect == HOLD_EFFECT_CURE_STATUS); +} + +SINGLE_BATTLE_TEST("Pecha and Lum Berries cure poison") +{ + u16 item; + + PARAMETRIZE { item = ITEM_PECHA_BERRY; } + PARAMETRIZE { item = ITEM_LUM_BERRY; } + + GIVEN { + ASSUME(gItems[ITEM_PECHA_BERRY].holdEffect == HOLD_EFFECT_CURE_PSN); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(item); } + } WHEN { + TURN { MOVE(player, MOVE_POISON_POWDER); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_POISON_POWDER, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + STATUS_ICON(opponent, poison: TRUE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + STATUS_ICON(opponent, poison: FALSE); + } +} + +SINGLE_BATTLE_TEST("Pecha and Lum Berries cure bad poison") +{ + u16 item; + + PARAMETRIZE { item = ITEM_PECHA_BERRY; } + PARAMETRIZE { item = ITEM_LUM_BERRY; } + + GIVEN { + ASSUME(gItems[ITEM_PECHA_BERRY].holdEffect == HOLD_EFFECT_CURE_PSN); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(item); } + } WHEN { + TURN { MOVE(player, MOVE_TOXIC); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + STATUS_ICON(opponent, badPoison: TRUE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + STATUS_ICON(opponent, badPoison: FALSE); + } +} + +SINGLE_BATTLE_TEST("Rawst and Lum Berries cure burn") +{ + u16 item; + + PARAMETRIZE { item = ITEM_RAWST_BERRY; } + PARAMETRIZE { item = ITEM_LUM_BERRY; } + + GIVEN { + ASSUME(gItems[ITEM_RAWST_BERRY].holdEffect == HOLD_EFFECT_CURE_BRN); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(item); } + } WHEN { + TURN { MOVE(player, MOVE_WILL_O_WISP); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WILL_O_WISP, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponent); + STATUS_ICON(opponent, burn: TRUE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + STATUS_ICON(opponent, burn: FALSE); + } +} + +SINGLE_BATTLE_TEST("Aspear and Lum Berries cure freeze") +{ + u16 item; + + PARAMETRIZE { item = ITEM_ASPEAR_BERRY; } + PARAMETRIZE { item = ITEM_LUM_BERRY; } + + GIVEN { + ASSUME(gItems[ITEM_ASPEAR_BERRY].holdEffect == HOLD_EFFECT_CURE_FRZ); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(item); } + } WHEN { + TURN { MOVE(player, MOVE_ICE_PUNCH); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ICE_PUNCH, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent); + STATUS_ICON(opponent, freeze: TRUE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + STATUS_ICON(opponent, freeze: FALSE); + } +} + +SINGLE_BATTLE_TEST("Chesto and Lum Berries cure sleep") +{ + u16 item; + + PARAMETRIZE { item = ITEM_CHESTO_BERRY; } + PARAMETRIZE { item = ITEM_LUM_BERRY; } + + GIVEN { + ASSUME(gItems[ITEM_CHESTO_BERRY].holdEffect == HOLD_EFFECT_CURE_SLP); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(item); } + } WHEN { + TURN { MOVE(player, MOVE_HYPNOSIS); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPNOSIS, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_SLP, opponent); + STATUS_ICON(opponent, sleep: TRUE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + STATUS_ICON(opponent, sleep: FALSE); + } +} + +SINGLE_BATTLE_TEST("Cheri and Lum Berries cure paralysis") +{ + u16 item; + + PARAMETRIZE { item = ITEM_CHERI_BERRY; } + PARAMETRIZE { item = ITEM_LUM_BERRY; } + + GIVEN { + ASSUME(gItems[ITEM_CHERI_BERRY].holdEffect == HOLD_EFFECT_CURE_PAR); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(item); } + } WHEN { + TURN { MOVE(player, MOVE_THUNDER_WAVE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_THUNDER_WAVE, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PRZ, opponent); + STATUS_ICON(opponent, paralysis: TRUE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + STATUS_ICON(opponent, paralysis: FALSE); + } +} + +SINGLE_BATTLE_TEST("Perism and Lum Berries cure confusion") +{ + u16 item; + + PARAMETRIZE { item = ITEM_PERSIM_BERRY; } + PARAMETRIZE { item = ITEM_LUM_BERRY; } + + GIVEN { + ASSUME(gItems[ITEM_PERSIM_BERRY].holdEffect == HOLD_EFFECT_CURE_CONFUSION); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(item); } + } WHEN { + TURN { MOVE(player, MOVE_CONFUSE_RAY); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CONFUSE_RAY, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + } +} + +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; } + + GIVEN { + ASSUME(gItems[ITEM_RAWST_BERRY].holdEffect == HOLD_EFFECT_CURE_BRN); + ASSUME(gItems[ITEM_ASPEAR_BERRY].holdEffect == HOLD_EFFECT_CURE_FRZ); + ASSUME(gItems[ITEM_CHERI_BERRY].holdEffect == HOLD_EFFECT_CURE_PAR); + ASSUME(gItems[ITEM_PECHA_BERRY].holdEffect == HOLD_EFFECT_CURE_PSN); + ASSUME(gItems[ITEM_CHESTO_BERRY].holdEffect == HOLD_EFFECT_CURE_SLP); + PLAYER(SPECIES_WOBBUFFET) { Status1(status); Item(ITEM_LUM_BERRY); } + OPPONENT(SPECIES_WOBBUFFET) { Status1(status); Item(item); } + } WHEN { + TURN { } + } SCENE { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + } +} + +SINGLE_BATTLE_TEST("Pokemon can be further poisoned with Toxic spikes after a status healing hold effect was previously used") +{ + u16 item; + + PARAMETRIZE { item = ITEM_PECHA_BERRY; } + PARAMETRIZE { item = ITEM_LUM_BERRY; } + + KNOWN_FAILING; + GIVEN { + ASSUME(gItems[ITEM_PECHA_BERRY].holdEffect == HOLD_EFFECT_CURE_PSN); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT) { Item(item); } + } WHEN { + TURN { MOVE(player, MOVE_TOXIC_SPIKES); } + TURN { SWITCH(opponent, 1); } + TURN { SWITCH(opponent, 0); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TOXIC_SPIKES, player); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + STATUS_ICON(opponent, poison: TRUE); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + STATUS_ICON(opponent, poison: FALSE); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_PSN, opponent); + STATUS_ICON(opponent, poison: TRUE); + } +} From 13dedfb5116ac9d61e65ddd905092308bcb03e56 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada D'Ottone Date: Mon, 11 Sep 2023 10:37:17 -0300 Subject: [PATCH 05/13] Revert "Implement Enigma Berry's hold effect" (#3298) --- src/battle_util.c | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index 94437dca2..1ff0a762e 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6802,21 +6802,6 @@ static u8 ItemEffectMoveEnd(u32 battlerId, u16 holdEffect) case HOLD_EFFECT_SP_DEFENSE_UP: effect = StatRaiseBerry(battlerId, gLastUsedItem, STAT_SPDEF, FALSE); break; - case HOLD_EFFECT_ENIGMA_BERRY: // consume and heal if hit by super effective move - if (IsBattlerAlive(battlerId) - && TARGET_TURN_DAMAGED - && (gBattleScripting.overrideBerryRequirements - || (!DoesSubstituteBlockMove(gBattlerAttacker, battlerId, gCurrentMove) && (gMoveResultFlags & MOVE_RESULT_SUPER_EFFECTIVE)))) - { - gBattleMoveDamage = (gBattleMons[battlerId].maxHP * 25 / 100) * -1; - if (GetBattlerAbility(battlerId) == ABILITY_RIPEN) - gBattleMoveDamage *= 2; - - BattleScriptPushCursor(); - gBattlescriptCurrInstr = BattleScript_ItemHealHP_RemoveItemRet; - return ITEM_HP_CHANGE; - } - break; case HOLD_EFFECT_KEE_BERRY: // consume and boost defense if used physical move effect = DamagedStatBoostBerryEffect(battlerId, STAT_DEF, SPLIT_PHYSICAL); break; From ca9e78410269c66a0980b80387f4f5fa11f9dbc9 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Wed, 13 Sep 2023 10:31:20 +0200 Subject: [PATCH 06/13] Fixes Toxic Debris + CanTargetFaintAi index issue (#3306) * Fixes Toxic Debris + CanTargetFaintAi index issue * swap macro --- data/battle_scripts_1.s | 3 + src/battle_ai_util.c | 2 +- src/battle_util.c | 4 +- test/battle/ability/toxic_debris.c | 96 ++++++++++++++++++++++++++++++ 4 files changed, 102 insertions(+), 3 deletions(-) create mode 100644 test/battle/ability/toxic_debris.c diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 9ef768530..405ad3d10 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -7333,6 +7333,9 @@ BattleScript_ToxicDebrisActivates:: printstring STRINGID_POISONSPIKESSCATTERED waitmessage B_WAIT_TIME_LONG BattleScript_ToxicDebrisRet: + copybyte sBATTLER, gBattlerTarget + copybyte gBattlerTarget, gBattlerAttacker + copybyte gBattlerAttacker, sBATTLER return BattleScript_EarthEaterActivates:: diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index d7e32a452..aff8ba91a 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -1154,7 +1154,7 @@ bool32 CanTargetFaintAi(u8 battlerDef, u8 battlerAtk) for (i = 0; i < MAX_MON_MOVES; i++) { if (moves[i] != MOVE_NONE && moves[i] != 0xFFFF && !(unusable & gBitTable[i]) - && AI_DATA->simulatedDmg[battlerDef][battlerAtk][moves[i]] >= gBattleMons[battlerAtk].hp) + && AI_DATA->simulatedDmg[battlerDef][battlerAtk][i] >= gBattleMons[battlerAtk].hp) { return TRUE; } diff --git a/src/battle_util.c b/src/battle_util.c index 1ff0a762e..32bbce974 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -5710,10 +5710,10 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && IS_MOVE_PHYSICAL(gCurrentMove) && TARGET_TURN_DAMAGED - && !(gSideStatuses[GetBattlerSide(gBattlerAttacker)] & SIDE_STATUS_TOXIC_SPIKES) + && (gSideTimers[gBattlerAttacker].toxicSpikesAmount != 2) && IsBattlerAlive(gBattlerTarget)) { - gBattlerTarget = gBattlerAttacker; + SWAP(gBattlerAttacker, gBattlerTarget, i); BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_ToxicDebrisActivates; effect++; diff --git a/test/battle/ability/toxic_debris.c b/test/battle/ability/toxic_debris.c new file mode 100644 index 000000000..95749358a --- /dev/null +++ b/test/battle/ability/toxic_debris.c @@ -0,0 +1,96 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Toxic Debris sets Toxic Spikes on the opposing side if hit by a physical attack") +{ + u32 move; + + PARAMETRIZE { move = MOVE_TACKLE;} + PARAMETRIZE { move = MOVE_SWIFT;} + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TOXIC_DEBRIS); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, move); } + } SCENE { + if (move == MOVE_TACKLE) { + ABILITY_POPUP(player, ABILITY_TOXIC_DEBRIS); + MESSAGE("Poison Spikes were scattered all around the opposing team's feet!"); + } + else { + NOT ABILITY_POPUP(player, ABILITY_TOXIC_DEBRIS); + NOT MESSAGE("Poison Spikes were scattered all around the opposing team's feet!"); + } + } +} + +SINGLE_BATTLE_TEST("Toxic Debris does not activate if two layers of Toxic Spikes are already up") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TOXIC_DEBRIS); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + TURN { MOVE(opponent, MOVE_TACKLE); } + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + ABILITY_POPUP(player, ABILITY_TOXIC_DEBRIS); + MESSAGE("Poison Spikes were scattered all around the opposing team's feet!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + ABILITY_POPUP(player, ABILITY_TOXIC_DEBRIS); + MESSAGE("Poison Spikes were scattered all around the opposing team's feet!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + NOT ABILITY_POPUP(player, ABILITY_TOXIC_DEBRIS); + NOT MESSAGE("Poison Spikes were scattered all around the opposing team's feet!"); + } +} + +SINGLE_BATTLE_TEST("If a Substitute is hit, Toxic Debris does not set Toxic Spikes") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TOXIC_DEBRIS); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SUBSTITUTE); } + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + NOT ABILITY_POPUP(player, ABILITY_TOXIC_DEBRIS); + NOT MESSAGE("Poison Spikes were scattered all around the opposing team's feet!"); + } +} + +SINGLE_BATTLE_TEST("Each hit of a Multi Hit move activates Toxic Debris") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TOXIC_DEBRIS); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_FURY_SWIPES); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FURY_SWIPES, opponent); + ABILITY_POPUP(player, ABILITY_TOXIC_DEBRIS); + MESSAGE("Poison Spikes were scattered all around the opposing team's feet!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FURY_SWIPES, opponent); + ABILITY_POPUP(player, ABILITY_TOXIC_DEBRIS); + MESSAGE("Poison Spikes were scattered all around the opposing team's feet!"); + } +} + +SINGLE_BATTLE_TEST("Air Balloon is popped after Toxic Debris activates") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_TOXIC_DEBRIS); Item(ITEM_AIR_BALLOON); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + ABILITY_POPUP(player, ABILITY_TOXIC_DEBRIS); + MESSAGE("Poison Spikes were scattered all around the opposing team's feet!"); + MESSAGE("Wobbuffet's Air Balloon popped!"); + } +} From db039ee4e2796098b598ea8f2e077a0868ca41d6 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Thu, 14 Sep 2023 12:38:45 +0200 Subject: [PATCH 07/13] Tera Shards Icons (#3307) --- .../items/icon_palettes/bug_tera_shard.pal | 19 +++++++++ .../items/icon_palettes/dark_tera_shard.pal | 19 +++++++++ .../items/icon_palettes/dragon_tera_shard.pal | 19 +++++++++ .../icon_palettes/electric_tera_shard.pal | 19 +++++++++ .../items/icon_palettes/fairy_tera_shard.pal | 19 +++++++++ .../icon_palettes/fighting_tera_shard.pal | 19 +++++++++ .../items/icon_palettes/fire_tera_shard.pal | 19 +++++++++ .../items/icon_palettes/flying_tera_shard.pal | 19 +++++++++ .../items/icon_palettes/ghost_tera_shard.pal | 19 +++++++++ .../items/icon_palettes/grass_tera_shard.pal | 19 +++++++++ .../items/icon_palettes/ground_tera_shard.pal | 19 +++++++++ .../items/icon_palettes/ice_tera_shard.pal | 19 +++++++++ .../items/icon_palettes/normal_tera_shard.pal | 19 +++++++++ .../items/icon_palettes/poison_tera_shard.pal | 19 +++++++++ .../icon_palettes/psychic_tera_shard.pal | 19 +++++++++ .../items/icon_palettes/rock_tera_shard.pal | 19 +++++++++ .../items/icon_palettes/steel_tera_shard.pal | 19 +++++++++ .../items/icon_palettes/water_tera_shard.pal | 13 +++++++ graphics/items/icons/tera_shard.png | Bin 0 -> 285 bytes include/graphics.h | 21 ++++++++++ src/data/graphics/items.h | 24 +++++++++++- src/data/item_icon_table.h | 36 +++++++++--------- 22 files changed, 398 insertions(+), 19 deletions(-) create mode 100644 graphics/items/icon_palettes/bug_tera_shard.pal create mode 100644 graphics/items/icon_palettes/dark_tera_shard.pal create mode 100644 graphics/items/icon_palettes/dragon_tera_shard.pal create mode 100644 graphics/items/icon_palettes/electric_tera_shard.pal create mode 100644 graphics/items/icon_palettes/fairy_tera_shard.pal create mode 100644 graphics/items/icon_palettes/fighting_tera_shard.pal create mode 100644 graphics/items/icon_palettes/fire_tera_shard.pal create mode 100644 graphics/items/icon_palettes/flying_tera_shard.pal create mode 100644 graphics/items/icon_palettes/ghost_tera_shard.pal create mode 100644 graphics/items/icon_palettes/grass_tera_shard.pal create mode 100644 graphics/items/icon_palettes/ground_tera_shard.pal create mode 100644 graphics/items/icon_palettes/ice_tera_shard.pal create mode 100644 graphics/items/icon_palettes/normal_tera_shard.pal create mode 100644 graphics/items/icon_palettes/poison_tera_shard.pal create mode 100644 graphics/items/icon_palettes/psychic_tera_shard.pal create mode 100644 graphics/items/icon_palettes/rock_tera_shard.pal create mode 100644 graphics/items/icon_palettes/steel_tera_shard.pal create mode 100644 graphics/items/icon_palettes/water_tera_shard.pal create mode 100644 graphics/items/icons/tera_shard.png diff --git a/graphics/items/icon_palettes/bug_tera_shard.pal b/graphics/items/icon_palettes/bug_tera_shard.pal new file mode 100644 index 000000000..548020fdf --- /dev/null +++ b/graphics/items/icon_palettes/bug_tera_shard.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 255 92 +0 0 0 +255 255 255 +241 247 180 +185 237 206 +153 229 190 +208 225 193 +107 164 67 +155 224 232 +110 219 159 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/dark_tera_shard.pal b/graphics/items/icon_palettes/dark_tera_shard.pal new file mode 100644 index 000000000..b855250a4 --- /dev/null +++ b/graphics/items/icon_palettes/dark_tera_shard.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 255 92 +0 0 0 +255 255 255 +177 181 211 +233 170 159 +168 164 170 +153 149 153 +45 44 49 +200 72 57 +100 108 167 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/dragon_tera_shard.pal b/graphics/items/icon_palettes/dragon_tera_shard.pal new file mode 100644 index 000000000..f86711a33 --- /dev/null +++ b/graphics/items/icon_palettes/dragon_tera_shard.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 255 92 +0 0 0 +255 255 255 +199 221 235 +225 185 216 +202 138 188 +146 188 216 +32 75 132 +215 142 134 +173 57 140 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/electric_tera_shard.pal b/graphics/items/icon_palettes/electric_tera_shard.pal new file mode 100644 index 000000000..15b393c85 --- /dev/null +++ b/graphics/items/icon_palettes/electric_tera_shard.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 255 92 +0 0 0 +255 255 255 +220 238 231 +249 247 189 +243 243 174 +242 227 174 +209 155 7 +173 214 99 +147 87 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/fairy_tera_shard.pal b/graphics/items/icon_palettes/fairy_tera_shard.pal new file mode 100644 index 000000000..58d6cae33 --- /dev/null +++ b/graphics/items/icon_palettes/fairy_tera_shard.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 255 92 +0 0 0 +255 255 255 +213 224 243 +243 199 212 +139 131 164 +188 205 234 +187 54 111 +225 165 149 +226 147 172 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/fighting_tera_shard.pal b/graphics/items/icon_palettes/fighting_tera_shard.pal new file mode 100644 index 000000000..5ce865690 --- /dev/null +++ b/graphics/items/icon_palettes/fighting_tera_shard.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 255 92 +0 0 0 +255 255 255 +248 191 188 +244 207 147 +241 188 105 +246 112 0 +178 63 0 +251 235 141 +233 157 39 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/fire_tera_shard.pal b/graphics/items/icon_palettes/fire_tera_shard.pal new file mode 100644 index 000000000..406b90e98 --- /dev/null +++ b/graphics/items/icon_palettes/fire_tera_shard.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 255 92 +0 0 0 +255 255 255 +233 199 215 +243 181 145 +232 185 185 +186 38 40 +105 20 26 +253 245 121 +239 148 99 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/flying_tera_shard.pal b/graphics/items/icon_palettes/flying_tera_shard.pal new file mode 100644 index 000000000..461d9e5b8 --- /dev/null +++ b/graphics/items/icon_palettes/flying_tera_shard.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 255 92 +0 0 0 +255 255 255 +212 242 239 +198 191 239 +171 194 230 +157 239 231 +40 84 165 +215 175 232 +66 128 226 +171 202 223 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/ghost_tera_shard.pal b/graphics/items/icon_palettes/ghost_tera_shard.pal new file mode 100644 index 000000000..bba3a968f --- /dev/null +++ b/graphics/items/icon_palettes/ghost_tera_shard.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 255 92 +0 0 0 +255 255 255 +214 227 249 +208 175 239 +172 164 226 +188 183 231 +46 27 178 +234 76 104 +160 101 225 +171 202 223 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/grass_tera_shard.pal b/graphics/items/icon_palettes/grass_tera_shard.pal new file mode 100644 index 000000000..ce10ab10c --- /dev/null +++ b/graphics/items/icon_palettes/grass_tera_shard.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 255 92 +0 0 0 +255 255 255 +211 233 194 +202 233 173 +171 225 199 +0 161 79 +255 239 132 +167 219 139 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/ground_tera_shard.pal b/graphics/items/icon_palettes/ground_tera_shard.pal new file mode 100644 index 000000000..b2168640b --- /dev/null +++ b/graphics/items/icon_palettes/ground_tera_shard.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 255 92 +0 0 0 +255 255 255 +232 214 212 +209 205 177 +216 198 174 +225 207 175 +159 104 0 +204 210 214 +188 182 142 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/ice_tera_shard.pal b/graphics/items/icon_palettes/ice_tera_shard.pal new file mode 100644 index 000000000..61ac07f67 --- /dev/null +++ b/graphics/items/icon_palettes/ice_tera_shard.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 255 92 +0 0 0 +255 255 255 +237 235 231 +183 224 242 +76 171 182 +58 121 135 +137 157 224 +183 221 224 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/normal_tera_shard.pal b/graphics/items/icon_palettes/normal_tera_shard.pal new file mode 100644 index 000000000..5792b0034 --- /dev/null +++ b/graphics/items/icon_palettes/normal_tera_shard.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 255 92 +0 0 0 +255 255 255 +241 241 229 +236 218 200 +221 219 215 +224 222 218 +159 146 133 +175 204 224 +223 197 166 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/poison_tera_shard.pal b/graphics/items/icon_palettes/poison_tera_shard.pal new file mode 100644 index 000000000..92eb2adaf --- /dev/null +++ b/graphics/items/icon_palettes/poison_tera_shard.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 255 92 +0 0 0 +255 255 255 +188 233 208 +213 161 233 +202 138 188 +195 179 220 +74 23 153 +227 101 191 +165 72 209 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/psychic_tera_shard.pal b/graphics/items/icon_palettes/psychic_tera_shard.pal new file mode 100644 index 000000000..e4c7e5860 --- /dev/null +++ b/graphics/items/icon_palettes/psychic_tera_shard.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 255 92 +0 0 0 +255 255 255 +201 199 238 +225 181 237 +202 138 188 +228 181 216 +167 25 135 +217 120 165 +189 109 220 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/rock_tera_shard.pal b/graphics/items/icon_palettes/rock_tera_shard.pal new file mode 100644 index 000000000..8575ff4d7 --- /dev/null +++ b/graphics/items/icon_palettes/rock_tera_shard.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 255 92 +0 0 0 +255 255 255 +227 210 173 +192 187 225 +171 164 209 +209 172 159 +159 57 18 +171 139 179 +123 123 198 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/steel_tera_shard.pal b/graphics/items/icon_palettes/steel_tera_shard.pal new file mode 100644 index 000000000..9dd861037 --- /dev/null +++ b/graphics/items/icon_palettes/steel_tera_shard.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +0 255 92 +0 0 0 +255 255 255 +220 227 225 +196 209 205 +168 183 189 +202 208 215 +57 67 89 +182 154 134 +121 147 155 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 +0 0 0 diff --git a/graphics/items/icon_palettes/water_tera_shard.pal b/graphics/items/icon_palettes/water_tera_shard.pal new file mode 100644 index 000000000..db62ce1e3 --- /dev/null +++ b/graphics/items/icon_palettes/water_tera_shard.pal @@ -0,0 +1,13 @@ +JASC-PAL +0100 +10 +0 255 92 +0 0 0 +255 255 255 +221 246 229 +194 223 242 +166 174 226 +192 196 234 +57 67 189 +145 233 225 +137 195 227 diff --git a/graphics/items/icons/tera_shard.png b/graphics/items/icons/tera_shard.png new file mode 100644 index 0000000000000000000000000000000000000000..c4e9854865ca094992c85898af071afceea56fd0 GIT binary patch literal 285 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaN3?zjj6;1;w#^NA%Cx&(BWL^R}Ea{HEjtmSN z`?>!lvI6;X0X`wF4F6+*%>V!YfBg9L&8-u6Z(o0K@7DZDt?SP`xPNrnYVG&ufzq4> z9+AZi419+{nDKc2iWHz=ho_5Uh{pNk16&fU6K*(l6>M8DfwM{Z$(u(!;p(q)a@Zy( zg@hX#8#j4zZWWhQcTSZsJ$-h{6qbcDr%!r5b2SK^nl@{)y3gTe~DWM4f3UX+u literal 0 HcmV?d00001 diff --git a/include/graphics.h b/include/graphics.h index 87635d43a..00727abac 100644 --- a/include/graphics.h +++ b/include/graphics.h @@ -9028,6 +9028,27 @@ extern const u32 gItemIconPalette_TeraOrb[]; extern const u32 gItemIcon_TinyBambooShoot[]; extern const u32 gItemIconPalette_TinyBambooShoot[]; +// Tera Shards +extern const u32 gItemIcon_TeraShard[]; +extern const u32 gItemIconPalette_NormalTeraShard[]; +extern const u32 gItemIconPalette_FireTeraShard[]; +extern const u32 gItemIconPalette_WaterTeraShard[]; +extern const u32 gItemIconPalette_GrassTeraShard[]; +extern const u32 gItemIconPalette_ElectricTeraShard[]; +extern const u32 gItemIconPalette_IceTeraShard[]; +extern const u32 gItemIconPalette_FightingTeraShard[]; +extern const u32 gItemIconPalette_PoisonTeraShard[]; +extern const u32 gItemIconPalette_GroundTeraShard[]; +extern const u32 gItemIconPalette_FlyingTeraShard[]; +extern const u32 gItemIconPalette_PsychicTeraShard[]; +extern const u32 gItemIconPalette_BugTeraShard[]; +extern const u32 gItemIconPalette_RockTeraShard[]; +extern const u32 gItemIconPalette_GhostTeraShard[]; +extern const u32 gItemIconPalette_DarkTeraShard[]; +extern const u32 gItemIconPalette_DragonTeraShard[]; +extern const u32 gItemIconPalette_SteelTeraShard[]; +extern const u32 gItemIconPalette_FairyTeraShard[]; + extern const u32 gItemIcon_AdamantCrystal[]; extern const u32 gItemIconPalette_AdamantCrystal[]; extern const u32 gItemIcon_GriseousCore[]; diff --git a/src/data/graphics/items.h b/src/data/graphics/items.h index 327fab037..60cd7d518 100644 --- a/src/data/graphics/items.h +++ b/src/data/graphics/items.h @@ -1988,7 +1988,29 @@ const u32 gItemIconPalette_TeraOrb[] = INCBIN_U32("graphics/items/icon_palettes/ const u32 gItemIcon_TinyBambooShoot[] = INCBIN_U32("graphics/items/icons/tiny_bamboo_shoot.4bpp.lz"); const u32 gItemIconPalette_TinyBambooShoot[] = INCBIN_U32("graphics/items/icon_palettes/tiny_bamboo_shoot.gbapal.lz"); -// Tera Shards here +// Tera Shards + +const u32 gItemIcon_TeraShard[] = INCBIN_U32("graphics/items/icons/tera_shard.4bpp.lz"); +const u32 gItemIconPalette_NormalTeraShard[] = INCBIN_U32("graphics/items/icon_palettes/normal_tera_shard.gbapal.lz"); +const u32 gItemIconPalette_FireTeraShard[] = INCBIN_U32("graphics/items/icon_palettes/fire_tera_shard.gbapal.lz"); +const u32 gItemIconPalette_WaterTeraShard[] = INCBIN_U32("graphics/items/icon_palettes/water_tera_shard.gbapal.lz"); +const u32 gItemIconPalette_GrassTeraShard[] = INCBIN_U32("graphics/items/icon_palettes/grass_tera_shard.gbapal.lz"); +const u32 gItemIconPalette_ElectricTeraShard[] = INCBIN_U32("graphics/items/icon_palettes/electric_tera_shard.gbapal.lz"); +const u32 gItemIconPalette_IceTeraShard[] = INCBIN_U32("graphics/items/icon_palettes/ice_tera_shard.gbapal.lz"); +const u32 gItemIconPalette_FightingTeraShard[] = INCBIN_U32("graphics/items/icon_palettes/fighting_tera_shard.gbapal.lz"); +const u32 gItemIconPalette_PoisonTeraShard[] = INCBIN_U32("graphics/items/icon_palettes/poison_tera_shard.gbapal.lz"); +const u32 gItemIconPalette_GroundTeraShard[] = INCBIN_U32("graphics/items/icon_palettes/ground_tera_shard.gbapal.lz"); +const u32 gItemIconPalette_FlyingTeraShard[] = INCBIN_U32("graphics/items/icon_palettes/flying_tera_shard.gbapal.lz"); +const u32 gItemIconPalette_PsychicTeraShard[] = INCBIN_U32("graphics/items/icon_palettes/psychic_tera_shard.gbapal.lz"); +const u32 gItemIconPalette_BugTeraShard[] = INCBIN_U32("graphics/items/icon_palettes/bug_tera_shard.gbapal.lz"); +const u32 gItemIconPalette_RockTeraShard[] = INCBIN_U32("graphics/items/icon_palettes/rock_tera_shard.gbapal.lz"); +const u32 gItemIconPalette_GhostTeraShard[] = INCBIN_U32("graphics/items/icon_palettes/ghost_tera_shard.gbapal.lz"); +const u32 gItemIconPalette_DarkTeraShard[] = INCBIN_U32("graphics/items/icon_palettes/dark_tera_shard.gbapal.lz"); +const u32 gItemIconPalette_DragonTeraShard[] = INCBIN_U32("graphics/items/icon_palettes/dragon_tera_shard.gbapal.lz"); +const u32 gItemIconPalette_SteelTeraShard[] = INCBIN_U32("graphics/items/icon_palettes/steel_tera_shard.gbapal.lz"); +const u32 gItemIconPalette_FairyTeraShard[] = INCBIN_U32("graphics/items/icon_palettes/fairy_tera_shard.gbapal.lz"); + +// Misc. Items const u32 gItemIcon_AdamantCrystal[] = INCBIN_U32("graphics/items/icons/adamant_crystal.4bpp.lz"); const u32 gItemIconPalette_AdamantCrystal[] = INCBIN_U32("graphics/items/icon_palettes/adamant_crystal.gbapal.lz"); diff --git a/src/data/item_icon_table.h b/src/data/item_icon_table.h index 0e8112260..a0a207be2 100644 --- a/src/data/item_icon_table.h +++ b/src/data/item_icon_table.h @@ -819,24 +819,24 @@ const u32 *const gItemIconTable[ITEMS_COUNT + 1][2] = [ITEM_SCROLL_OF_WATERS] = {gItemIcon_ScrollOfWaters, gItemIconPalette_ScrollOfWaters}, [ITEM_TERA_ORB] = {gItemIcon_TeraOrb, gItemIconPalette_TeraOrb}, [ITEM_TINY_BAMBOO_SHOOT] = {gItemIcon_TinyBambooShoot, gItemIconPalette_TinyBambooShoot}, - [ITEM_BUG_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_BugTeraShard, gItemIconPalette_BugTeraShard}, - [ITEM_DARK_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_DarkTeraShard, gItemIconPalette_DarkTeraShard}, - [ITEM_DRAGON_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_DragonTeraShard, gItemIconPalette_DragonTeraShard}, - [ITEM_ELECTRIC_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_ElectricTeraShard, gItemIconPalette_ElectricTeraShard}, - [ITEM_FAIRY_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_FairyTeraShard, gItemIconPalette_FairyTeraShard}, - [ITEM_FIGHTING_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_FightingTeraShard, gItemIconPalette_FightingTeraShard}, - [ITEM_FIRE_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_FireTeraShard, gItemIconPalette_FireTeraShard}, - [ITEM_FLYING_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_FlyingTeraShard, gItemIconPalette_FlyingTeraShard}, - [ITEM_GHOST_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_GhostTeraShard, gItemIconPalette_GhostTeraShard}, - [ITEM_GRASS_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_GrassTeraShard, gItemIconPalette_GrassTeraShard}, - [ITEM_GROUND_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_GroundTeraShard, gItemIconPalette_GroundTeraShard}, - [ITEM_ICE_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_IceTeraShard, gItemIconPalette_IceTeraShard}, - [ITEM_NORMAL_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_NormalTeraShard, gItemIconPalette_NormalTeraShard}, - [ITEM_POISON_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_PoisonTeraShard, gItemIconPalette_PoisonTeraShard}, - [ITEM_PSYCHIC_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_PsychicTeraShard, gItemIconPalette_PsychicTeraShard}, - [ITEM_ROCK_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_RockTeraShard, gItemIconPalette_RockTeraShard}, - [ITEM_STEEL_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_SteelTeraShard, gItemIconPalette_SteelTeraShard}, - [ITEM_WATER_TERA_SHARD] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_WaterTeraShard, gItemIconPalette_WaterTeraShard}, + [ITEM_BUG_TERA_SHARD] = {gItemIcon_TeraShard, gItemIconPalette_BugTeraShard}, + [ITEM_DARK_TERA_SHARD] = {gItemIcon_TeraShard, gItemIconPalette_DarkTeraShard}, + [ITEM_DRAGON_TERA_SHARD] = {gItemIcon_TeraShard, gItemIconPalette_DragonTeraShard}, + [ITEM_ELECTRIC_TERA_SHARD] = {gItemIcon_TeraShard, gItemIconPalette_ElectricTeraShard}, + [ITEM_FAIRY_TERA_SHARD] = {gItemIcon_TeraShard, gItemIconPalette_FairyTeraShard}, + [ITEM_FIGHTING_TERA_SHARD] = {gItemIcon_TeraShard, gItemIconPalette_FightingTeraShard}, + [ITEM_FIRE_TERA_SHARD] = {gItemIcon_TeraShard, gItemIconPalette_FireTeraShard}, + [ITEM_FLYING_TERA_SHARD] = {gItemIcon_TeraShard, gItemIconPalette_FlyingTeraShard}, + [ITEM_GHOST_TERA_SHARD] = {gItemIcon_TeraShard, gItemIconPalette_GhostTeraShard}, + [ITEM_GRASS_TERA_SHARD] = {gItemIcon_TeraShard, gItemIconPalette_GrassTeraShard}, + [ITEM_GROUND_TERA_SHARD] = {gItemIcon_TeraShard, gItemIconPalette_GroundTeraShard}, + [ITEM_ICE_TERA_SHARD] = {gItemIcon_TeraShard, gItemIconPalette_IceTeraShard}, + [ITEM_NORMAL_TERA_SHARD] = {gItemIcon_TeraShard, gItemIconPalette_NormalTeraShard}, + [ITEM_POISON_TERA_SHARD] = {gItemIcon_TeraShard, gItemIconPalette_PoisonTeraShard}, + [ITEM_PSYCHIC_TERA_SHARD] = {gItemIcon_TeraShard, gItemIconPalette_PsychicTeraShard}, + [ITEM_ROCK_TERA_SHARD] = {gItemIcon_TeraShard, gItemIconPalette_RockTeraShard}, + [ITEM_STEEL_TERA_SHARD] = {gItemIcon_TeraShard, gItemIconPalette_SteelTeraShard}, + [ITEM_WATER_TERA_SHARD] = {gItemIcon_TeraShard, gItemIconPalette_WaterTeraShard}, [ITEM_ADAMANT_CRYSTAL] = {gItemIcon_AdamantCrystal, gItemIconPalette_AdamantCrystal}, [ITEM_GRISEOUS_CORE] = {gItemIcon_GriseousCore, gItemIconPalette_GriseousCore}, [ITEM_LUSTROUS_GLOBE] = {gItemIcon_LustrousGlobe, gItemIconPalette_LustrousGlobe}, From 2ce3f4db7888cf0fea42e188148536c3b20c834c Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Thu, 14 Sep 2023 06:08:24 -0500 Subject: [PATCH 08/13] Implement Enigma Berry's hold effect + tests (#3300) * Fix Enigma Berry * Tests * Update enigma_berry.c --- src/battle_util.c | 29 +++++++++++++ test/battle/hold_effect/enigma_berry.c | 60 ++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 test/battle/hold_effect/enigma_berry.c diff --git a/src/battle_util.c b/src/battle_util.c index aabb6e6e8..0b0aadab3 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6554,6 +6554,29 @@ static u8 TrySetMicleBerry(u32 battler, u32 itemId, bool32 end2) return 0; } +static u8 TrySetEnigmaBerry(u32 battler) +{ + if (IsBattlerAlive(battler) + && TARGET_TURN_DAMAGED + && !DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) + && !(gBattleScripting.overrideBerryRequirements && gBattleMons[battler].hp == gBattleMons[battler].maxHP) +#if B_HEAL_BLOCKING >= GEN_5 + && !(gStatuses3[battler] & STATUS3_HEAL_BLOCK) +#endif + && gMoveResultFlags & MOVE_RESULT_SUPER_EFFECTIVE) + { + gBattleScripting.battler = battler; + gBattleMoveDamage = (gBattleMons[battler].maxHP * 25 / 100) * -1; + if (GetBattlerAbility(battler) == ABILITY_RIPEN) + gBattleMoveDamage *= 2; + + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_ItemHealHP_RemoveItemRet; + return ITEM_HP_CHANGE; + } + return 0; +} + static u8 DamagedStatBoostBerryEffect(u8 battler, u8 statId, u8 split) { if (IsBattlerAlive(battler) @@ -6767,6 +6790,9 @@ static u8 ItemEffectMoveEnd(u32 battler, u16 holdEffect) case HOLD_EFFECT_SP_DEFENSE_UP: effect = StatRaiseBerry(battler, gLastUsedItem, STAT_SPDEF, FALSE); break; + case HOLD_EFFECT_ENIGMA_BERRY: // consume and heal if hit by super effective move + effect = TrySetEnigmaBerry(battler); + break; case HOLD_EFFECT_KEE_BERRY: // consume and boost defense if used physical move effect = DamagedStatBoostBerryEffect(battler, STAT_DEF, SPLIT_PHYSICAL); break; @@ -7730,6 +7756,9 @@ u8 ItemBattleEffects(u8 caseID, u8 battler, bool8 moveTurn) gBattleScripting.statChanger = SET_STATCHANGER(STAT_SPATK, 1, FALSE); } break; + case HOLD_EFFECT_ENIGMA_BERRY: // consume and heal if hit by super effective move + effect = TrySetEnigmaBerry(battler); + break; case HOLD_EFFECT_JABOCA_BERRY: // consume and damage attacker if used physical move if (IsBattlerAlive(battler) && TARGET_TURN_DAMAGED diff --git a/test/battle/hold_effect/enigma_berry.c b/test/battle/hold_effect/enigma_berry.c new file mode 100644 index 000000000..6758ac402 --- /dev/null +++ b/test/battle/hold_effect/enigma_berry.c @@ -0,0 +1,60 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + gItems[ITEM_ENIGMA_BERRY].holdEffect == HOLD_EFFECT_ENIGMA_BERRY; +} + +SINGLE_BATTLE_TEST("Enigma Berry recovers 25% of HP if hit by super effective move") +{ + GIVEN { + PLAYER(SPECIES_WYNAUT) { MaxHP(100); HP(2); Item(ITEM_ENIGMA_BERRY); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ENDURE); MOVE(opponent, MOVE_BITE); } + } SCENE { + s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ENDURE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponent); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wynaut's Enigma Berry restored health!"); + HP_BAR(player, damage: -maxHP / 4); + } +} + +SINGLE_BATTLE_TEST("Enigma Berry does nothing if not hit by super effective move") +{ + GIVEN { + PLAYER(SPECIES_MIGHTYENA) { MaxHP(100); HP(2); Item(ITEM_ENIGMA_BERRY); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_ENDURE); MOVE(opponent, MOVE_BITE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_ENDURE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponent); + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Mightyena's Enigma Berry restored health!"); + } + } +} + +SINGLE_BATTLE_TEST("Enigma Berry does nothing if Heal Block applies") +{ + GIVEN { + PLAYER(SPECIES_WYNAUT) { MaxHP(100); HP(2); Item(ITEM_ENIGMA_BERRY); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_HEAL_BLOCK); } + TURN { MOVE(player, MOVE_ENDURE); MOVE(opponent, MOVE_BITE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_HEAL_BLOCK, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ENDURE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, opponent); + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player); + MESSAGE("Wynaut's Enigma Berry restored health!"); + } + } +} From 7936910f905e95c49bfb7c81f503f3b3be3268fb Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Thu, 14 Sep 2023 13:57:30 +0200 Subject: [PATCH 09/13] Bug Bite - Tests and small fix (#3282) Co-authored-by: Eduardo Quezada D'Ottone --- data/battle_scripts_1.s | 14 +-- include/battle_scripts.h | 1 + include/test/battle.h | 2 +- src/battle_util.c | 102 +++++++++++---------- test/battle/move_effect/bug_bite.c | 139 +++++++++++++++++++++++++++++ test/battle/move_effect/fling.c | 10 ++- 6 files changed, 213 insertions(+), 55 deletions(-) create mode 100644 test/battle/move_effect/bug_bite.c diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 405ad3d10..2b3803ab9 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -9633,16 +9633,20 @@ BattleScript_ItemHealHP_RemoveItemEnd2_Anim: removeitem BS_ATTACKER end2 -BattleScript_BerryPPHealEnd2:: - jumpifability BS_ATTACKER, ABILITY_RIPEN, BattleScript_BerryPPHealEnd2_AbilityPopup - goto BattleScript_BerryPPHealEnd2_Anim -BattleScript_BerryPPHealEnd2_AbilityPopup: +BattleScript_BerryPPHealRet:: + jumpifability BS_ATTACKER, ABILITY_RIPEN, BattleScript_BerryPPHeal_AbilityPopup + goto BattleScript_BerryPPHeal_Anim +BattleScript_BerryPPHeal_AbilityPopup: call BattleScript_AbilityPopUp -BattleScript_BerryPPHealEnd2_Anim: +BattleScript_BerryPPHeal_Anim: playanimation BS_ATTACKER, B_ANIM_HELD_ITEM_EFFECT printstring STRINGID_PKMNSITEMRESTOREDPP waitmessage B_WAIT_TIME_LONG removeitem BS_ATTACKER + return + +BattleScript_BerryPPHealEnd2:: + call BattleScript_BerryPPHealRet end2 BattleScript_ItemHealHP_End2:: diff --git a/include/battle_scripts.h b/include/battle_scripts.h index ecfbb9bec..e08032a7f 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -214,6 +214,7 @@ extern const u8 BattleScript_WhiteHerbRet[]; extern const u8 BattleScript_ItemHealHP_RemoveItemRet[]; extern const u8 BattleScript_ItemHealHP_RemoveItemEnd2[]; extern const u8 BattleScript_BerryPPHealEnd2[]; +extern const u8 BattleScript_BerryPPHealRet[]; extern const u8 BattleScript_ItemHealHP_End2[]; extern const u8 BattleScript_ItemHealHP_Ret[]; extern const u8 BattleScript_SelectingNotAllowedMoveChoiceItem[]; diff --git a/include/test/battle.h b/include/test/battle.h index dc40a8a20..57727b84f 100644 --- a/include/test/battle.h +++ b/include/test/battle.h @@ -871,7 +871,7 @@ struct HPEventContext struct StatusEventContext { - u8 status1; + u16 status1; bool8 none:1; bool8 sleep:1; bool8 poison:1; diff --git a/src/battle_util.c b/src/battle_util.c index 32bbce974..a6c7810ba 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6592,10 +6592,11 @@ static u8 TrySetMicleBerry(u32 battlerId, u32 itemId, bool32 end2) static u8 DamagedStatBoostBerryEffect(u8 battlerId, u8 statId, u8 split) { if (IsBattlerAlive(battlerId) - && TARGET_TURN_DAMAGED && CompareStat(battlerId, statId, MAX_STAT_STAGE, CMP_LESS_THAN) && (gBattleScripting.overrideBerryRequirements - || (!DoesSubstituteBlockMove(gBattlerAttacker, battlerId, gCurrentMove) && GetBattleMoveSplit(gCurrentMove) == split)) + || (!DoesSubstituteBlockMove(gBattlerAttacker, battlerId, gCurrentMove) + && GetBattleMoveSplit(gCurrentMove) == split + && TARGET_TURN_DAMAGED)) ) { BufferStatChange(battlerId, statId, STRINGID_STATROSE); @@ -6639,6 +6640,54 @@ u8 TryHandleSeed(u8 battler, u32 terrainFlag, u8 statId, u16 itemId, bool32 exec return 0; } +static u32 ItemRestorePp(u32 battler, u32 itemId, bool32 execute) +{ + struct Pokemon *party = GetBattlerParty(battler); + struct Pokemon *mon = &party[gBattlerPartyIndexes[battler]]; + u32 i, changedPP = 0; + + for (i = 0; i < MAX_MON_MOVES; i++) + { + u32 move = GetMonData(mon, MON_DATA_MOVE1 + i); + u32 currentPP = GetMonData(mon, MON_DATA_PP1 + i); + u32 ppBonuses = GetMonData(mon, MON_DATA_PP_BONUSES); + u32 maxPP = CalculatePPWithBonus(move, ppBonuses, i); + if (move && (currentPP == 0 || (gBattleScripting.overrideBerryRequirements && currentPP != maxPP))) + { + u32 ppRestored = GetBattlerItemHoldEffectParam(battler, itemId); + + if (GetBattlerAbility(battler) == ABILITY_RIPEN) + { + ppRestored *= 2; + gBattlerAbility = battler; + } + if (currentPP + ppRestored > maxPP) + changedPP = maxPP; + else + changedPP = currentPP + ppRestored; + + PREPARE_MOVE_BUFFER(gBattleTextBuff1, move); + + if (execute) + { + BattleScriptExecute(BattleScript_BerryPPHealEnd2); + } + else + { + BattleScriptPushCursor(); + gBattlescriptCurrInstr = BattleScript_BerryPPHealRet; + } + gActiveBattler = battler; + BtlController_EmitSetMonData(BUFFER_A, i + REQUEST_PPMOVE1_BATTLE, 0, 1, &changedPP); + MarkBattlerForControllerExec(gActiveBattler); + if (MOVE_IS_PERMANENT(battler, i)) + gBattleMons[battler].pp[i] = changedPP; + return ITEM_PP_CHANGE; + } + } + return 0; +} + static u8 ItemHealHp(u32 battlerId, u32 itemId, bool32 end2, bool32 percentHeal) { if (!(gBattleScripting.overrideBerryRequirements && gBattleMons[battlerId].hp == gBattleMons[battlerId].maxHP) @@ -6772,6 +6821,9 @@ static u8 ItemEffectMoveEnd(u32 battlerId, u16 holdEffect) case HOLD_EFFECT_RESTORE_PCT_HP: effect = ItemHealHp(battlerId, gLastUsedItem, FALSE, TRUE); break; + case HOLD_EFFECT_RESTORE_PP: + effect = ItemRestorePp(battlerId, gLastUsedItem, FALSE); + break; case HOLD_EFFECT_CONFUSE_SPICY: effect = HealConfuseBerry(battlerId, gLastUsedItem, FLAVOR_SPICY, FALSE); break; @@ -7236,10 +7288,6 @@ u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn) BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battlerId].status1); MarkBattlerForControllerExec(gActiveBattler); break; - case ITEM_PP_CHANGE: - if (MOVE_IS_PERMANENT(battlerId, i)) - gBattleMons[battlerId].pp[i] = changedPP; - break; } } } @@ -7259,43 +7307,7 @@ u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn) break; case HOLD_EFFECT_RESTORE_PP: if (!moveTurn) - { - struct Pokemon *party = GetBattlerParty(battlerId); - struct Pokemon *mon = &party[gBattlerPartyIndexes[battlerId]]; - u8 ppBonuses; - u16 move; - - for (i = 0; i < MAX_MON_MOVES; i++) - { - move = GetMonData(mon, MON_DATA_MOVE1 + i); - changedPP = GetMonData(mon, MON_DATA_PP1 + i); - ppBonuses = GetMonData(mon, MON_DATA_PP_BONUSES); - if (move && changedPP == 0) - break; - } - if (i != MAX_MON_MOVES) - { - u8 maxPP = CalculatePPWithBonus(move, ppBonuses, i); - u8 ppRestored = GetBattlerHoldEffectParam(battlerId); - - if (GetBattlerAbility(battlerId) == ABILITY_RIPEN) - { - ppRestored *= 2; - gBattlerAbility = battlerId; - } - if (changedPP + ppRestored > maxPP) - changedPP = maxPP; - else - changedPP = changedPP + ppRestored; - - PREPARE_MOVE_BUFFER(gBattleTextBuff1, move); - - BattleScriptExecute(BattleScript_BerryPPHealEnd2); - BtlController_EmitSetMonData(BUFFER_A, i + REQUEST_PPMOVE1_BATTLE, 0, 1, &changedPP); - MarkBattlerForControllerExec(gActiveBattler); - effect = ITEM_PP_CHANGE; - } - } + effect = ItemRestorePp(battlerId, gLastUsedItem, TRUE); break; case HOLD_EFFECT_RESTORE_STATS: for (i = 0; i < NUM_BATTLE_STATS; i++) @@ -7543,10 +7555,6 @@ u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn) BtlController_EmitSetMonData(BUFFER_A, REQUEST_STATUS_BATTLE, 0, 4, &gBattleMons[battlerId].status1); MarkBattlerForControllerExec(gActiveBattler); break; - case ITEM_PP_CHANGE: - if (MOVE_IS_PERMANENT(battlerId, i)) - gBattleMons[battlerId].pp[i] = changedPP; - break; } } } diff --git a/test/battle/move_effect/bug_bite.c b/test/battle/move_effect/bug_bite.c new file mode 100644 index 000000000..315d52109 --- /dev/null +++ b/test/battle/move_effect/bug_bite.c @@ -0,0 +1,139 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_BUG_BITE].effect == EFFECT_BUG_BITE); + ASSUME(gBattleMoves[MOVE_BUG_BITE].pp == 20); +} + +// Pretty much copy/paste of the Berry Fling Test. +SINGLE_BATTLE_TEST("Bug Bite eats the target's berry and immediately gains its effect") +{ + u16 item; + u32 status1 = STATUS1_NONE, effect, statId; + + PARAMETRIZE { item = ITEM_NONE; } + PARAMETRIZE { item = ITEM_ORAN_BERRY; effect = HOLD_EFFECT_RESTORE_HP; } + PARAMETRIZE { item = ITEM_SITRUS_BERRY; effect = HOLD_EFFECT_RESTORE_HP; } + // PARAMETRIZE { item = ITEM_ENIGMA_BERRY; effect = HOLD_EFFECT_RESTORE_HP; } To do once Enigma Berry's effect is done + PARAMETRIZE { item = ITEM_LEPPA_BERRY; effect = HOLD_EFFECT_RESTORE_PP; } + PARAMETRIZE { item = ITEM_CHESTO_BERRY; effect = HOLD_EFFECT_CURE_SLP; status1 = STATUS1_SLEEP; } + PARAMETRIZE { item = ITEM_CHERI_BERRY; effect = HOLD_EFFECT_CURE_PAR; status1 = STATUS1_PARALYSIS; } + PARAMETRIZE { item = ITEM_PECHA_BERRY; effect = HOLD_EFFECT_CURE_PSN; status1 = STATUS1_POISON; } + PARAMETRIZE { item = ITEM_PECHA_BERRY; effect = HOLD_EFFECT_CURE_PSN; status1 = STATUS1_TOXIC_POISON; } + PARAMETRIZE { item = ITEM_RAWST_BERRY; effect = HOLD_EFFECT_CURE_BRN; status1 = STATUS1_BURN; } + PARAMETRIZE { item = ITEM_ASPEAR_BERRY; effect = HOLD_EFFECT_CURE_FRZ; status1 = STATUS1_FROSTBITE; } + PARAMETRIZE { item = ITEM_APICOT_BERRY; effect = HOLD_EFFECT_SP_DEFENSE_UP; statId = STAT_SPDEF; } + PARAMETRIZE { item = ITEM_MARANGA_BERRY; effect = HOLD_EFFECT_MARANGA_BERRY; statId = STAT_SPDEF; } + PARAMETRIZE { item = ITEM_GANLON_BERRY; effect = HOLD_EFFECT_DEFENSE_UP; statId = STAT_DEF; } + PARAMETRIZE { item = ITEM_KEE_BERRY; effect = HOLD_EFFECT_KEE_BERRY; statId = STAT_DEF; } + PARAMETRIZE { item = ITEM_LIECHI_BERRY; effect = HOLD_EFFECT_ATTACK_UP; statId = STAT_ATK; } + PARAMETRIZE { item = ITEM_PETAYA_BERRY; effect = HOLD_EFFECT_SP_ATTACK_UP; statId = STAT_SPATK; } + PARAMETRIZE { item = ITEM_SALAC_BERRY; effect = HOLD_EFFECT_SPEED_UP; statId = STAT_SPEED; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { HP(399); MaxHP(400); Status1(status1); Moves(MOVE_SLEEP_TALK, MOVE_BUG_BITE); } + OPPONENT(SPECIES_WOBBUFFET) { Item(item); } + } WHEN { + // Chesto Berry can only be applied if the pokemon is asleep and uses Sleep Talk. + if (item == ITEM_CHESTO_BERRY) { + TURN { MOVE(player, MOVE_SLEEP_TALK); } + } else { + TURN { MOVE(player, MOVE_BUG_BITE); } + } + + } SCENE { + if (item == ITEM_CHESTO_BERRY) { + MESSAGE("Wobbuffet used Sleep Talk!"); + } + MESSAGE("Wobbuffet used Bug Bite!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BUG_BITE, player); + HP_BAR(opponent); + if (effect == HOLD_EFFECT_RESTORE_HP) { + if (item == ITEM_ORAN_BERRY) { + MESSAGE("Wobbuffet's Oran Berry restored health!"); + } else if (item == ITEM_SITRUS_BERRY) { + MESSAGE("Wobbuffet's Sitrus Berry restored health!"); + } else { + // MESSAGE("Wobbuffet's Enigma Berry restored health!"); + } + HP_BAR(player); + } + else if (effect == HOLD_EFFECT_RESTORE_PP) { + MESSAGE("Wobbuffet's Leppa Berry restored Bug Bite's PP!"); + } + else if (status1 != STATUS1_NONE) { + if (status1 == STATUS1_BURN) { + MESSAGE("Wobbuffet's Rawst Berry healed its burn!"); + } else if (status1 == STATUS1_SLEEP) { + MESSAGE("Wobbuffet's Chesto Berry woke it from its sleep!"); + } else if (status1 == STATUS1_PARALYSIS) { + MESSAGE("Wobbuffet's Cheri Berry cured paralysis!"); + } else if (status1 == STATUS1_TOXIC_POISON || status1 == STATUS1_POISON) { + MESSAGE("Wobbuffet's Pecha Berry cured poison!"); + } else if (status1 == STATUS1_FROSTBITE) { + MESSAGE("Wobbuffet's Aspear Berry healed its frostbite!"); + } + NOT STATUS_ICON(player, status1); + } + else if (statId != 0) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + if (statId == STAT_ATK) { + MESSAGE("Using Liechi Berry, the Attack of Wobbuffet rose!"); + } else if (statId == STAT_DEF) { + if (item == ITEM_GANLON_BERRY) { + MESSAGE("Using Ganlon Berry, the Defense of Wobbuffet rose!"); + } else { + MESSAGE("Using Kee Berry, the Defense of Wobbuffet rose!"); + } + } else if (statId == STAT_SPDEF) { + if (item == ITEM_APICOT_BERRY) { + MESSAGE("Using Apicot Berry, the Sp. Def of Wobbuffet rose!"); + } else { + MESSAGE("Using Maranga Berry, the Sp. Def of Wobbuffet rose!"); + } + } else if (statId == STAT_SPEED) { + MESSAGE("Using Salac Berry, the Speed of Wobbuffet rose!"); + } else if (statId == STAT_SPATK) { + MESSAGE("Using Petaya Berry, the Sp. Atk of Wobbuffet rose!"); + } + } + } THEN { + if (effect == HOLD_EFFECT_RESTORE_HP) { + EXPECT_EQ(player->hp, player->maxHP); + } else if (effect == HOLD_EFFECT_RESTORE_PP) { + EXPECT_EQ(player->pp[1], 20); + } else if (status1 != STATUS1_NONE) { + EXPECT_EQ(player->status1, STATUS1_NONE); + } + else if (statId != 0) { + EXPECT_EQ(player->statStages[statId], DEFAULT_STAT_STAGE + 1); + } + EXPECT_EQ(opponent->item, ITEM_NONE); // Opponent's Berry was eaten. + } +} + +// To verify in the actual games. +// Bulbapedia - The effect of a Jaboca Berry will activate before the Berry can be stolen. +// Showdown - Jaboca Berry is stolen and eaten and nothing happens. This is how it currently works on expansion. +TO_DO_BATTLE_TEST("Bug Bite interaction with Jaboca Berry."); + +SINGLE_BATTLE_TEST("Tanga Berry activates before Bug Bite") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) {Item(ITEM_TANGA_BERRY); } + } WHEN { + TURN { MOVE(player, MOVE_BUG_BITE); } + } SCENE { + MESSAGE("Wobbuffet used Bug Bite!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + MESSAGE("Foe Wobbuffet ate its Tanga Berry!"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BUG_BITE, player); + HP_BAR(opponent); + MESSAGE("Tanga Berry weakened the damage to Foe Wobbuffet!"); + } THEN { + EXPECT_EQ(player->item, ITEM_NONE); + } +} diff --git a/test/battle/move_effect/fling.c b/test/battle/move_effect/fling.c index 8016b2f22..5abb883e1 100644 --- a/test/battle/move_effect/fling.c +++ b/test/battle/move_effect/fling.c @@ -255,6 +255,7 @@ SINGLE_BATTLE_TEST("Fling - thrown berry's effect activates for the target even PARAMETRIZE { item = ITEM_ORAN_BERRY; effect = HOLD_EFFECT_RESTORE_HP; } PARAMETRIZE { item = ITEM_SITRUS_BERRY; effect = HOLD_EFFECT_RESTORE_HP; } + PARAMETRIZE { item = ITEM_LEPPA_BERRY; effect = HOLD_EFFECT_RESTORE_PP; } PARAMETRIZE { item = ITEM_CHESTO_BERRY; effect = HOLD_EFFECT_CURE_SLP; status1 = STATUS1_SLEEP; } PARAMETRIZE { item = ITEM_CHERI_BERRY; effect = HOLD_EFFECT_CURE_PAR; status1 = STATUS1_PARALYSIS; } PARAMETRIZE { item = ITEM_PECHA_BERRY; effect = HOLD_EFFECT_CURE_PSN; status1 = STATUS1_POISON; } @@ -271,7 +272,7 @@ SINGLE_BATTLE_TEST("Fling - thrown berry's effect activates for the target even GIVEN { PLAYER(SPECIES_WOBBUFFET) { Item(item); } - OPPONENT(SPECIES_WOBBUFFET) { Status1(status1); HP(399); MaxHP(400); } + OPPONENT(SPECIES_WOBBUFFET) { Status1(status1); HP(399); MaxHP(400); MovesWithPP({MOVE_CELEBRATE, 35}); } } WHEN { TURN { MOVE(player, MOVE_FLING); } } SCENE { @@ -286,6 +287,9 @@ SINGLE_BATTLE_TEST("Fling - thrown berry's effect activates for the target even } HP_BAR(opponent); } + else if (effect == HOLD_EFFECT_RESTORE_PP) { + MESSAGE("Foe Wobbuffet's Leppa Berry restored Celebrate's PP!"); + } else if (status1 != STATUS1_NONE) { if (status1 == STATUS1_BURN) { MESSAGE("Foe Wobbuffet's Rawst Berry healed its burn!"); @@ -325,7 +329,9 @@ SINGLE_BATTLE_TEST("Fling - thrown berry's effect activates for the target even } THEN { if (effect == HOLD_EFFECT_RESTORE_HP) { EXPECT_EQ(opponent->hp, opponent->maxHP); - } else if (status1 != STATUS1_NONE) { + } else if (effect == HOLD_EFFECT_RESTORE_PP) { + EXPECT_EQ(opponent->pp[0], 39); // Not 40, because Celebrate was used. + } else if (status1 != STATUS1_NONE) { EXPECT_EQ(opponent->status1, STATUS1_NONE); } else if (statId != 0) { From 4fb0b5566478a67b55657588cbda5472de83f825 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Thu, 14 Sep 2023 19:13:11 +0200 Subject: [PATCH 10/13] Bug bite/Fling enigma berry interaction & tests (#3311) --- src/battle_util.c | 7 +++---- test/battle/move_effect/bug_bite.c | 10 +++++----- test/battle/move_effect/fling.c | 8 +++++++- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index a9901d2c4..5ce61373d 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -6557,19 +6557,18 @@ static u8 TrySetMicleBerry(u32 battler, u32 itemId, bool32 end2) static u8 TrySetEnigmaBerry(u32 battler) { if (IsBattlerAlive(battler) - && TARGET_TURN_DAMAGED && !DoesSubstituteBlockMove(gBattlerAttacker, battler, gCurrentMove) + && ((TARGET_TURN_DAMAGED && gMoveResultFlags & MOVE_RESULT_SUPER_EFFECTIVE) || gBattleScripting.overrideBerryRequirements) && !(gBattleScripting.overrideBerryRequirements && gBattleMons[battler].hp == gBattleMons[battler].maxHP) #if B_HEAL_BLOCKING >= GEN_5 - && !(gStatuses3[battler] & STATUS3_HEAL_BLOCK) + && !(gStatuses3[battler] & STATUS3_HEAL_BLOCK)) #endif - && gMoveResultFlags & MOVE_RESULT_SUPER_EFFECTIVE) { gBattleScripting.battler = battler; gBattleMoveDamage = (gBattleMons[battler].maxHP * 25 / 100) * -1; if (GetBattlerAbility(battler) == ABILITY_RIPEN) gBattleMoveDamage *= 2; - + BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_ItemHealHP_RemoveItemRet; return ITEM_HP_CHANGE; diff --git a/test/battle/move_effect/bug_bite.c b/test/battle/move_effect/bug_bite.c index 315d52109..385b8a400 100644 --- a/test/battle/move_effect/bug_bite.c +++ b/test/battle/move_effect/bug_bite.c @@ -10,13 +10,13 @@ ASSUMPTIONS // Pretty much copy/paste of the Berry Fling Test. SINGLE_BATTLE_TEST("Bug Bite eats the target's berry and immediately gains its effect") { - u16 item; - u32 status1 = STATUS1_NONE, effect, statId; + u16 item = ITEM_NONE; + u32 status1 = STATUS1_NONE, effect = HOLD_EFFECT_NONE, statId = 0; PARAMETRIZE { item = ITEM_NONE; } PARAMETRIZE { item = ITEM_ORAN_BERRY; effect = HOLD_EFFECT_RESTORE_HP; } PARAMETRIZE { item = ITEM_SITRUS_BERRY; effect = HOLD_EFFECT_RESTORE_HP; } - // PARAMETRIZE { item = ITEM_ENIGMA_BERRY; effect = HOLD_EFFECT_RESTORE_HP; } To do once Enigma Berry's effect is done + PARAMETRIZE { item = ITEM_ENIGMA_BERRY; effect = HOLD_EFFECT_ENIGMA_BERRY; } PARAMETRIZE { item = ITEM_LEPPA_BERRY; effect = HOLD_EFFECT_RESTORE_PP; } PARAMETRIZE { item = ITEM_CHESTO_BERRY; effect = HOLD_EFFECT_CURE_SLP; status1 = STATUS1_SLEEP; } PARAMETRIZE { item = ITEM_CHERI_BERRY; effect = HOLD_EFFECT_CURE_PAR; status1 = STATUS1_PARALYSIS; } @@ -50,13 +50,13 @@ SINGLE_BATTLE_TEST("Bug Bite eats the target's berry and immediately gains its e MESSAGE("Wobbuffet used Bug Bite!"); ANIMATION(ANIM_TYPE_MOVE, MOVE_BUG_BITE, player); HP_BAR(opponent); - if (effect == HOLD_EFFECT_RESTORE_HP) { + if (effect == HOLD_EFFECT_RESTORE_HP || effect == HOLD_EFFECT_ENIGMA_BERRY) { if (item == ITEM_ORAN_BERRY) { MESSAGE("Wobbuffet's Oran Berry restored health!"); } else if (item == ITEM_SITRUS_BERRY) { MESSAGE("Wobbuffet's Sitrus Berry restored health!"); } else { - // MESSAGE("Wobbuffet's Enigma Berry restored health!"); + MESSAGE("Wobbuffet's Enigma Berry restored health!"); } HP_BAR(player); } diff --git a/test/battle/move_effect/fling.c b/test/battle/move_effect/fling.c index 5abb883e1..8d44d9b9f 100644 --- a/test/battle/move_effect/fling.c +++ b/test/battle/move_effect/fling.c @@ -255,6 +255,7 @@ SINGLE_BATTLE_TEST("Fling - thrown berry's effect activates for the target even PARAMETRIZE { item = ITEM_ORAN_BERRY; effect = HOLD_EFFECT_RESTORE_HP; } PARAMETRIZE { item = ITEM_SITRUS_BERRY; effect = HOLD_EFFECT_RESTORE_HP; } + PARAMETRIZE { item = ITEM_ENIGMA_BERRY; effect = HOLD_EFFECT_ENIGMA_BERRY; } PARAMETRIZE { item = ITEM_LEPPA_BERRY; effect = HOLD_EFFECT_RESTORE_PP; } PARAMETRIZE { item = ITEM_CHESTO_BERRY; effect = HOLD_EFFECT_CURE_SLP; status1 = STATUS1_SLEEP; } PARAMETRIZE { item = ITEM_CHERI_BERRY; effect = HOLD_EFFECT_CURE_PAR; status1 = STATUS1_PARALYSIS; } @@ -262,6 +263,7 @@ SINGLE_BATTLE_TEST("Fling - thrown berry's effect activates for the target even PARAMETRIZE { item = ITEM_PECHA_BERRY; effect = HOLD_EFFECT_CURE_PSN; status1 = STATUS1_TOXIC_POISON; } PARAMETRIZE { item = ITEM_RAWST_BERRY; effect = HOLD_EFFECT_CURE_BRN; status1 = STATUS1_BURN; } PARAMETRIZE { item = ITEM_ASPEAR_BERRY; effect = HOLD_EFFECT_CURE_FRZ; status1 = STATUS1_FREEZE; } + PARAMETRIZE { item = ITEM_ASPEAR_BERRY; effect = HOLD_EFFECT_CURE_FRZ; status1 = STATUS1_FROSTBITE; } PARAMETRIZE { item = ITEM_APICOT_BERRY; effect = HOLD_EFFECT_SP_DEFENSE_UP; statId = STAT_SPDEF; } PARAMETRIZE { item = ITEM_MARANGA_BERRY; effect = HOLD_EFFECT_MARANGA_BERRY; statId = STAT_SPDEF; } PARAMETRIZE { item = ITEM_GANLON_BERRY; effect = HOLD_EFFECT_DEFENSE_UP; statId = STAT_DEF; } @@ -282,8 +284,10 @@ SINGLE_BATTLE_TEST("Fling - thrown berry's effect activates for the target even if (effect == HOLD_EFFECT_RESTORE_HP) { if (item == ITEM_ORAN_BERRY) { MESSAGE("Foe Wobbuffet's Oran Berry restored health!"); - } else { + } else if (item == ITEM_SITRUS_BERRY) { MESSAGE("Foe Wobbuffet's Sitrus Berry restored health!"); + } else { + MESSAGE("Wobbuffet's Enigma Berry restored health!"); } HP_BAR(opponent); } @@ -297,6 +301,8 @@ SINGLE_BATTLE_TEST("Fling - thrown berry's effect activates for the target even MESSAGE("Foe Wobbuffet's Chesto Berry woke it from its sleep!"); } else if (status1 == STATUS1_FREEZE) { MESSAGE("Foe Wobbuffet's Aspear Berry defrosted it!"); + } else if (status1 == STATUS1_FROSTBITE) { + MESSAGE("Foe Wobbuffet's Aspear Berry healed its frostbite!"); } else if (status1 == STATUS1_PARALYSIS) { MESSAGE("Foe Wobbuffet's Cheri Berry cured paralysis!"); } else if (status1 == STATUS1_TOXIC_POISON || status1 == STATUS1_POISON) { From 80ec67482e590b438534ce33f2fdb5d12ca9d007 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Thu, 14 Sep 2023 20:31:18 +0200 Subject: [PATCH 11/13] Bitter Blade and Double Shock animations (#3310) --- data/battle_anim_scripts.s | 78 +++++++++++++++++++++++++++++++++++++- src/battle_anim_new.c | 42 ++++++++++++-------- 2 files changed, 103 insertions(+), 17 deletions(-) diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index e9b7c538f..4997f56d2 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -16382,6 +16382,82 @@ TakeHeartRings: delay 0x4 return +Move_BITTER_BLADE:: + loadspritegfx ANIM_TAG_FOCUS_ENERGY + loadspritegfx ANIM_TAG_CLAW_SLASH + loadspritegfx ANIM_TAG_POISON_BUBBLE + loadspritegfx ANIM_TAG_ORBS + loadspritegfx ANIM_TAG_BLUE_STAR + loadspritegfx ANIM_TAG_IMPACT + monbg ANIM_DEF_PARTNER + splitbgprio_foes ANIM_TARGET + createsprite gSimplePaletteBlendSpriteTemplate, ANIM_ATTACKER, 0, 1, 3, 0, 12, RGB(14, 6, 24) + playsewithpan SE_M_DRAGON_RAGE, SOUND_PAN_ATTACKER + waitforvisualfinish + call EndureEffect + delay 8 + call EndureEffect + delay 8 + call EndureEffect + waitforvisualfinish + delay 3 + playsewithpan SE_M_RAZOR_WIND, SOUND_PAN_TARGET + createsprite gBitterBladeImpactTemplate ANIM_TARGET, 1, 0x0, 0xa, 0x0, 0xFF00, 0xA + delay 2 + createvisualtask AnimTask_ShakeMon, 0x2, 0x1, 0x0, 0x3, 0xa, 0x1 + waitforvisualfinish + delay 3 + call GigaDrainAbsorbEffect + waitforvisualfinish + delay 15 + call HealingEffect + waitforvisualfinish + createsprite gSimplePaletteBlendSpriteTemplate, ANIM_ATTACKER, 0, 1, 3, 12, 0, RGB(14, 6, 24) + waitforvisualfinish + clearmonbg ANIM_DEF_PARTNER + blendoff + end + +Move_DOUBLE_SHOCK:: + loadspritegfx ANIM_TAG_ELECTRIC_ORBS + loadspritegfx ANIM_TAG_CIRCLE_OF_LIGHT + loadspritegfx ANIM_TAG_LIGHTNING + monbg ANIM_TARGET + setalpha 12, 8 + createsprite gSimplePaletteBlendSpriteTemplate, ANIM_ATTACKER, 0, 1, 3, 0, 12, RGB_BLACK + waitforvisualfinish + createvisualtask AnimTask_ElectricChargingParticles, 0x2, ANIM_ATTACKER, 30, 0, 3 @;Amount, Slowness, Slowness, Compaction + playsewithpan SE_M_CHARGE, SOUND_PAN_ATTACKER + delay 12 + createsprite gGrowingShockWaveOrbSpriteTemplate, ANIM_ATTACKER, 0, 0x0 + waitforvisualfinish + delay 1 + createsprite gSimplePaletteBlendSpriteTemplate, ANIM_ATTACKER, 2, F_PAL_BG, 2, 16, 0, RGB_BLACK + delay 1 + waitforvisualfinish + playsewithpan SE_M_TRI_ATTACK2, SOUND_PAN_TARGET + createvisualtask AnimTask_InvertScreenColor, 2, 0x1 | 0x2 | 0x4 + delay 1 + createsprite gLightningSpriteTemplate, ANIM_TARGET, 6, -16, -32 + createsprite gLightningSpriteTemplate, ANIM_TARGET, 2, 24, -32 + delay 1 + createsprite gLightningSpriteTemplate, ANIM_TARGET, 2, -16, -16 + createsprite gLightningSpriteTemplate, ANIM_TARGET, 2, 24, -16 + delay 1 + createsprite gLightningSpriteTemplate, ANIM_TARGET, 6, -16, 16 + createsprite gLightningSpriteTemplate, ANIM_TARGET, 2, 24, 16 + delay 1 + playsewithpan SE_M_TRI_ATTACK2, SOUND_PAN_TARGET + createvisualtask AnimTask_InvertScreenColor, 2, 0x1 | 0x2 | 0x4 + delay 2 + createvisualtask AnimTask_ShakeMon, 2, ANIM_TARGET, 0, 3, 15, 1 + createsprite gBasicHitSplatSpriteTemplate, ANIM_ATTACKER, 3, 0, 0, ANIM_TARGET, 2 + delay 1 + waitforvisualfinish + clearmonbg ANIM_TARGET + blendoff + end + Move_TERA_BLAST:: Move_AXE_KICK:: Move_LAST_RESPECTS:: @@ -16418,8 +16494,6 @@ Move_HYPER_DRILL:: Move_TWIN_BEAM:: Move_RAGE_FIST:: Move_ARMOR_CANNON:: -Move_BITTER_BLADE:: -Move_DOUBLE_SHOCK:: Move_GIGATON_HAMMER:: Move_COMEUPPANCE:: Move_AQUA_CUTTER:: diff --git a/src/battle_anim_new.c b/src/battle_anim_new.c index 966887b25..1f04e0169 100644 --- a/src/battle_anim_new.c +++ b/src/battle_anim_new.c @@ -4151,7 +4151,7 @@ const struct SpriteTemplate gSkitterSmackImpactTemplate = }; // triple axel -const struct SpriteTemplate gTripleAxelIceCrystalSpriteTemplate = +const struct SpriteTemplate gTripleAxelIceCrystalSpriteTemplate = { .tileTag = ANIM_TAG_ICE_CRYSTALS, .paletteTag = ANIM_TAG_ICE_CRYSTALS, @@ -4173,7 +4173,7 @@ static const union AffineAnimCmd* const sSpriteAffineAnimTable_WingAttackFeather { sSpriteAffineAnim_WingAttackFeather, }; -const struct SpriteTemplate gDualWingbeatFeatherSpriteTemplate = +const struct SpriteTemplate gDualWingbeatFeatherSpriteTemplate = { .tileTag = ANIM_TAG_WHITE_FEATHER, .paletteTag = ANIM_TAG_WHITE_FEATHER, @@ -4192,7 +4192,7 @@ const struct SpriteTemplate gSpriteTemplate_LargeCrossImpact = { .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = sSpriteAffineAnimTable_LargeHailRock, - .callback = AnimCrossImpact + .callback = AnimCrossImpact }; // expanding force @@ -4464,7 +4464,7 @@ const struct SpriteTemplate gSpriteTemplate_GlacialLance = { .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = sSpriteAffineAnimTable_GlacialLance, - .callback = SpriteCB_GlacialLance + .callback = SpriteCB_GlacialLance }; // eerie spell @@ -4611,7 +4611,7 @@ const struct SpriteTemplate gSpriteTemplate_HeadlongRushImpact = { .images = NULL, .affineAnims = sSpriteAffineAnimTable_DrainPunchFist, .callback = AnimHitSplatBasic -}; +}; const struct SpriteTemplate gSpriteTemplate_MaxQuakeDirtGeyser = { .tileTag = ANIM_TAG_MUD_SAND, .paletteTag = ANIM_TAG_MUD_SAND, @@ -4696,7 +4696,7 @@ const struct SpriteTemplate gSpriteTemplate_CeaselessEdgeSlash = { .anims = gSlashSliceAnimTable, .images = NULL, .affineAnims = gDummySpriteAffineAnimTable, - .callback = SpriteCB_FlippableSlash + .callback = SpriteCB_FlippableSlash }; // wildbolt storm @@ -4758,7 +4758,7 @@ const struct SpriteTemplate gSpriteTemplate_BitterMaliceRing = { .anims = gDummySpriteAnimTable, .images = NULL, .affineAnims = gAffineAnims_ShadowBall, - .callback = AnimParticleInVortex + .callback = AnimParticleInVortex }; //ultra burst @@ -7175,6 +7175,18 @@ const struct SpriteTemplate gSoulStealZStarSpriteTemplate = .callback = AnimCrossImpact }; +// Bitter Blade +const struct SpriteTemplate gBitterBladeImpactTemplate = +{ + .tileTag = ANIM_TAG_CLAW_SLASH, + .paletteTag = ANIM_TAG_POISON_BUBBLE, + .oam = &gOamData_AffineOff_ObjNormal_32x32, + .anims = gAnims_ClawSlash, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = AnimClawSlash +}; + // functions //general void AnimTask_IsTargetPartner(u8 taskId) @@ -8297,7 +8309,7 @@ void AnimTask_AllBattlersVisible(u8 taskId) { spriteId = gBattlerSpriteIds[i]; if (IsBattlerSpriteVisible(i) && spriteId != 0xFF) - gSprites[spriteId].invisible = FALSE; + gSprites[spriteId].invisible = FALSE; } DestroyAnimVisualTask(taskId); @@ -8693,7 +8705,7 @@ void SpriteCB_HorizontalSliceStep(struct Sprite *sprite) static void SpriteCB_LashOutStrike(struct Sprite* sprite) { bool8 flip = GetBattlerSide(gBattleAnimTarget) == B_SIDE_PLAYER; - + if (gBattleAnimArgs[2]) flip ^= 1; @@ -8871,7 +8883,7 @@ static void SpriteCB_SteelRoller(struct Sprite* sprite) sprite->x2 = gBattleAnimArgs[0]; sprite->y2 += gBattleAnimArgs[1]; - + sprite->data[4] = gBattleAnimArgs[3]; //Left/Right distance sprite->data[5] = gBattleAnimArgs[4]; //Left/Right speed @@ -8930,10 +8942,10 @@ static void SpriteCB_DragonEnergyShot(struct Sprite* sprite) else { y = 0; - + if (IsBattlerSpritePresent(def1)) y = GetBattlerSpriteCoord(def1, BATTLER_COORD_Y_PIC_OFFSET); - + if (IsBattlerSpritePresent(def2)) y += GetBattlerSpriteCoord(def2, BATTLER_COORD_Y_PIC_OFFSET); @@ -9018,13 +9030,13 @@ static void SpriteCB_GlacialLance(struct Sprite* sprite) { u8 def1 = gBattleAnimTarget; u8 def2 = BATTLE_PARTNER(def1); - + InitSpritePosToAnimAttacker(sprite, TRUE); sprite->data[5] = gBattleAnimArgs[4]; sprite->data[6] = gBattleAnimArgs[5]; - + sprite->data[0] = gBattleAnimArgs[6]; - + if (!IsDoubleBattle() || IsAlly(gBattleAnimAttacker, gBattleAnimTarget)) { sprite->data[2] = GetBattlerSpriteCoord(def1, BATTLER_COORD_X_2) + gBattleAnimArgs[2]; //Converge on target From 4a3ee0db7f4aa77084d3cf50a8f9e1bfae1f71ee Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Thu, 14 Sep 2023 13:37:57 -0500 Subject: [PATCH 12/13] Implement Exp Charm and unevolved Exp multipliers + Exp formula fixes (#3301) Co-authored-by: Eduardo Quezada D'Ottone --- include/config/battle.h | 1 + include/pokemon.h | 1 + src/battle_script_commands.c | 18 +++++++++--------- src/pokemon.c | 20 ++++++++++++++++++++ 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/include/config/battle.h b/include/config/battle.h index dd7e11f02..aee79daee 100644 --- a/include/config/battle.h +++ b/include/config/battle.h @@ -14,6 +14,7 @@ #define B_TRAINER_EXP_MULTIPLIER GEN_LATEST // In Gen7+, trainer battles no longer give a 1.5 multiplier to EXP gain. #define B_SPLIT_EXP GEN_LATEST // In Gen6+, all participating mon get full experience. #define B_SCALED_EXP GEN_LATEST // In Gen5 and Gen7+, experience is weighted by level difference. +#define B_UNEVOLVED_EXP_MULTIPLIER GEN_LATEST // In Gen6+, if the Pokémon is at or past the level where it would be able to evolve, but it has not, it gets a ~1.2 multiplier to EXP gain. Only applies to Pokémon with EVO_LEVEL method. // Stat settings #define B_BADGE_BOOST GEN_LATEST // In Gen4+, Gym Badges no longer boost a Pokémon's stats. diff --git a/include/pokemon.h b/include/pokemon.h index a37d5f010..7f70d8a52 100644 --- a/include/pokemon.h +++ b/include/pokemon.h @@ -543,6 +543,7 @@ u8 *UseStatIncreaseItem(u16 itemId); u8 GetNature(struct Pokemon *mon); u8 GetNatureFromPersonality(u32 personality); u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 type, u16 evolutionItem, struct Pokemon *tradePartner); +bool8 IsMonPastEvolutionLevel(struct Pokemon *mon); u16 HoennPokedexNumToSpecies(u16 hoennNum); u16 NationalPokedexNumToSpecies(u16 nationalNum); u16 NationalToHoennOrder(u16 nationalNum); diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index b89b7d844..5c779c3d1 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -4087,6 +4087,9 @@ static void Cmd_getexp(void) calculatedExp = gSpeciesInfo[gBattleMons[gBattlerFainted].species].expYield * gBattleMons[gBattlerFainted].level / 7; #endif + if (B_TRAINER_EXP_MULTIPLIER <= GEN_7 && gBattleTypeFlags & BATTLE_TYPE_TRAINER) + calculatedExp = (calculatedExp * 150) / 100; + #if B_SPLIT_EXP < GEN_6 if (viaExpShare) // at least one mon is getting exp via exp share { @@ -4179,14 +4182,9 @@ static void Cmd_getexp(void) { // check if the pokemon doesn't belong to the player if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && gBattleStruct->expGetterMonId >= 3) - { i = STRINGID_EMPTYSTRING4; - } else - { - gBattleMoveDamage = (gBattleMoveDamage * 150) / 100; i = STRINGID_ABOOSTED; - } } else { @@ -16029,13 +16027,15 @@ void ApplyExperienceMultipliers(s32 *expAmount, u8 expGetterMonId, u8 faintedBat else holdEffect = ItemId_GetHoldEffect(item); + if (IsTradedMon(&gPlayerParty[expGetterMonId])) + *expAmount = (*expAmount * 150) / 100; if (holdEffect == HOLD_EFFECT_LUCKY_EGG) *expAmount = (*expAmount * 150) / 100; - if (B_TRAINER_EXP_MULTIPLIER <= GEN_7 && gBattleTypeFlags & BATTLE_TYPE_TRAINER) - *expAmount = (*expAmount * 150) / 100; + if (B_UNEVOLVED_EXP_MULTIPLIER >= GEN_6 && IsMonPastEvolutionLevel(&gPlayerParty[expGetterMonId])) + *expAmount = (*expAmount * 4915) / 4096; if (B_AFFECTION_MECHANICS == TRUE && GetBattlerFriendshipScore(expGetterMonId) >= FRIENDSHIP_50_TO_99) - *expAmount = (*expAmount * 120) / 100; - if (IsTradedMon(&gPlayerParty[expGetterMonId])) + *expAmount = (*expAmount * 4915) / 4096; + if (CheckBagHasItem(ITEM_EXP_CHARM, 1)) //is also for other exp boosting Powers if/when implemented *expAmount = (*expAmount * 150) / 100; if (B_SCALED_EXP >= GEN_5 && B_SCALED_EXP != GEN_6) diff --git a/src/pokemon.c b/src/pokemon.c index 838d0068c..dc7054c04 100644 --- a/src/pokemon.c +++ b/src/pokemon.c @@ -6770,6 +6770,26 @@ u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 mode, u16 evolutionItem, s return targetSpecies; } +bool8 IsMonPastEvolutionLevel(struct Pokemon *mon) +{ + int i; + u16 species = GetMonData(mon, MON_DATA_SPECIES, 0); + u8 level = GetMonData(mon, MON_DATA_LEVEL, 0); + + for (i = 0; i < EVOS_PER_MON; i++) + { + switch (gEvolutionTable[species][i].method) + { + case EVO_LEVEL: + if (gEvolutionTable[species][i].param <= level) + return TRUE; + break; + } + } + + return FALSE; +} + u16 HoennPokedexNumToSpecies(u16 hoennNum) { u16 species; From a073e7bd5924419cec4f960febce41b0fad35a8a Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Fri, 15 Sep 2023 16:18:47 +0200 Subject: [PATCH 13/13] Adds Gigaton Hammer and Salt Cure (#3297) --- asm/macros/battle_script.inc | 15 ++++- data/battle_anim_scripts.s | 4 ++ data/battle_scripts_1.s | 46 ++++++++++++--- include/battle_scripts.h | 3 + include/battle_util.h | 1 + include/constants/battle.h | 1 + include/constants/battle_anim.h | 1 + include/constants/battle_move_effects.h | 4 +- include/constants/battle_string_ids.h | 5 +- src/battle_ai_main.c | 4 ++ src/battle_ai_util.c | 4 +- src/battle_message.c | 6 ++ src/battle_script_commands.c | 9 +++ src/battle_util.c | 35 ++++++++++++ src/data/battle_moves.h | 6 +- test/battle/move_effect/gigaton_hammer.c | 63 +++++++++++++++++++++ test/battle/move_effect/salt_cure.c | 72 ++++++++++++++++++++++++ 17 files changed, 262 insertions(+), 17 deletions(-) create mode 100644 test/battle/move_effect/gigaton_hammer.c create mode 100644 test/battle/move_effect/salt_cure.c diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 31a359a0e..f33537bbb 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1311,7 +1311,7 @@ callnative BS_CalcMetalBurstDmg .4byte \failInstr .endm - + .macro jumpifcantfling battler:req, jumpInstr:req callnative BS_JumpIfCantFling .byte \battler @@ -1361,6 +1361,11 @@ callnative BS_SetSnow .endm + .macro applysaltcure battler:req + callnative BS_ApplySaltCure + .byte \battler + .endm + .macro setzeffect callnative BS_SetZEffect .endm @@ -1381,7 +1386,7 @@ .byte \counter .4byte \ptr .endm - + .macro jumpifcantloseitem battler:req, ptr:req callnative BS_JumpIfCantLoseItem .byte \battler @@ -2322,3 +2327,9 @@ various 0, VARIOUS_TRY_REVIVAL_BLESSING .4byte \jumpInstr .endm + + @ Will jump to script pointer if the specified battler has or has not fainted. + .macro jumpiffainted battler:req, value:req, ptr:req + getbattlerfainted \battler + jumpifbyte CMP_EQUAL, gBattleCommunication, \value, \ptr + .endm diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 4997f56d2..0b72e1cf9 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -948,6 +948,7 @@ gBattleAnims_General:: .4byte General_AffectionHangedOn @ B_ANIM_AFFECTION_HANGED_ON .4byte General_Snow @ B_ANIM_SNOW_CONTINUES .4byte General_UltraBurst @ B_ANIM_ULTRA_BURST + .4byte General_SaltCureDamage @ B_ANIM_SALT_CURE_DAMAGE .align 2 gBattleAnims_Special:: @@ -27135,6 +27136,9 @@ General_AffectionHangedOn_3Hearts: waitforvisualfinish end +General_SaltCureDamage:: + goto Status_Freeze + SnatchMoveTrySwapFromSubstitute: createvisualtask AnimTask_IsAttackerBehindSubstitute, 2 jumprettrue SnatchMoveSwapSubstituteForMon diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index cc69d3f78..cf3c953f1 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -436,6 +436,32 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectCorrosiveGas @ EFFECT_CORROSIVE_GAS .4byte BattleScript_EffectHit @ EFFECT_POPULATION_BOMB .4byte BattleScript_EffectMortalSpin @ EFFECT_MORTAL_SPIN + .4byte BattleScript_EffectHit @ EFFECT_GIGATON_HAMMER + .4byte BattleScript_EffectSaltCure @ EFFECT_SALT_CURE + +BattleScript_EffectSaltCure: + call BattleScript_EffectHit_Ret + jumpiffainted BS_TARGET, TRUE, BattleScript_EffectSaltCure_End + applysaltcure BS_TARGET + printstring STRINGID_TARGETISBEINGSALTCURED + waitmessage B_WAIT_TIME_LONG +BattleScript_EffectSaltCure_End: + goto BattleScript_MoveEnd + +BattleScript_SaltCureExtraDamage:: + playanimation BS_TARGET, B_ANIM_SALT_CURE_DAMAGE, NULL + waitanimation + call BattleScript_HurtTarget_NoString + printstring STRINGID_TARGETISHURTBYSALTCURE + waitmessage B_WAIT_TIME_LONG + end2 + +BattleScript_HurtTarget_NoString: + orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE + healthbarupdate BS_TARGET + datahpupdate BS_TARGET + tryfaintmon BS_TARGET + return BattleScript_EffectMortalSpin: call BattleScript_EffectHit_Ret @@ -6617,8 +6643,7 @@ BattleScript_PursuitDmgOnSwitchOut:: waitmessage B_WAIT_TIME_LONG tryfaintmon BS_TARGET moveendfromto MOVEEND_ABILITIES, MOVEEND_CHOICE_MOVE - getbattlerfainted BS_TARGET - jumpifbyte CMP_EQUAL, gBattleCommunication, FALSE, BattleScript_PursuitDmgOnSwitchOutRet + jumpiffainted BS_TARGET, FALSE, BattleScript_PursuitDmgOnSwitchOutRet setbyte sGIVEEXP_STATE, 0 getexp BS_TARGET BattleScript_PursuitDmgOnSwitchOutRet: @@ -7131,8 +7156,7 @@ BattleScript_GulpMissileGorging:: healthbarupdate BS_ATTACKER datahpupdate BS_ATTACKER tryfaintmon BS_ATTACKER - getbattlerfainted BS_ATTACKER - jumpifbyte CMP_EQUAL, gBattleCommunication, TRUE, BattleScript_GulpMissileNoSecondEffectGorging + jumpiffainted BS_ATTACKER, TRUE, BattleScript_GulpMissileNoSecondEffectGorging BattleScript_GulpMissileNoDmgGorging: handleformchange BS_TARGET, 0 playanimation BS_TARGET, B_ANIM_FORM_CHANGE @@ -7160,8 +7184,7 @@ BattleScript_GulpMissileGulping:: healthbarupdate BS_ATTACKER datahpupdate BS_ATTACKER tryfaintmon BS_ATTACKER - getbattlerfainted BS_ATTACKER - jumpifbyte CMP_EQUAL, gBattleCommunication, TRUE, BattleScript_GulpMissileNoSecondEffectGulping + jumpiffainted BS_ATTACKER, TRUE, BattleScript_GulpMissileNoSecondEffectGulping jumpifholdeffect BS_ATTACKER, HOLD_EFFECT_CLEAR_AMULET, BattleScript_GulpMissileNoSecondEffectGulping jumpifability BS_ATTACKER, ABILITY_CLEAR_BODY, BattleScript_GulpMissileNoSecondEffectGulping jumpifability BS_ATTACKER, ABILITY_FULL_METAL_BODY, BattleScript_GulpMissileNoSecondEffectGulping @@ -7502,6 +7525,14 @@ BattleScript_SelectingNotAllowedMoveHealBlockInPalace:: printstring STRINGID_HEALBLOCKPREVENTSUSAGE goto BattleScript_SelectingUnusableMoveInPalace +BattleScript_SelectingNotAllowedCurrentMove:: + printselectionstring STRINGID_CURRENTMOVECANTSELECT + endselectionscript + +BattleScript_SelectingNotAllowedCurrentMoveInPalace:: + printstring STRINGID_CURRENTMOVECANTSELECT + goto BattleScript_SelectingUnusableMoveInPalace + BattleScript_WishComesTrue:: trywish 1, BattleScript_WishButFullHp playanimation BS_TARGET, B_ANIM_WISH_HEAL @@ -7875,8 +7906,7 @@ BattleScript_CottonDownActivates:: savetarget setbyte gBattlerTarget, 0 BattleScript_CottonDownLoop: - getbattlerfainted BS_TARGET - jumpifbyte CMP_EQUAL, gBattleCommunication, TRUE, BattleScript_CottonDownLoopIncrement + jumpiffainted BS_TARGET, TRUE, BattleScript_CottonDownLoopIncrement setstatchanger STAT_SPEED, 1, TRUE jumpifbyteequal gBattlerTarget, gEffectBattler, BattleScript_CottonDownLoopIncrement statbuffchange STAT_CHANGE_NOT_PROTECT_AFFECTED, BattleScript_CottonDownTargetSpeedCantGoLower diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 2a810e7ff..335aafb58 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -475,6 +475,9 @@ extern const u8 BattleScript_BerserkGeneRet[]; extern const u8 BattleScript_TargetFormChangeWithStringNoPopup[]; extern const u8 BattleScript_DefDown[]; extern const u8 BattleScript_UltraBurst[]; +extern const u8 BattleScript_SelectingNotAllowedCurrentMove[]; +extern const u8 BattleScript_SelectingNotAllowedCurrentMoveInPalace[]; +extern const u8 BattleScript_SaltCureExtraDamage[]; // zmoves extern const u8 BattleScript_ZMoveActivateDamaging[]; diff --git a/include/battle_util.h b/include/battle_util.h index 62394d85d..36fb7a6e5 100644 --- a/include/battle_util.h +++ b/include/battle_util.h @@ -15,6 +15,7 @@ #define MOVE_LIMITATION_BELCH (1 << 11) #define MOVE_LIMITATION_THROAT_CHOP (1 << 12) #define MOVE_LIMITATION_STUFF_CHEEKS (1 << 13) +#define MOVE_LIMITATION_GIGATON_HAMMER (1 << 14) #define MOVE_LIMITATION_PLACEHOLDER (1 << 15) #define MOVE_LIMITATIONS_ALL 0xFFFF diff --git a/include/constants/battle.h b/include/constants/battle.h index d99eae919..0f0d39db8 100644 --- a/include/constants/battle.h +++ b/include/constants/battle.h @@ -187,6 +187,7 @@ #define STATUS4_MUD_SPORT (1 << 2) // Only used if B_SPORT_TURNS < GEN_6 #define STATUS4_WATER_SPORT (1 << 3) // Only used if B_SPORT_TURNS < GEN_6 #define STATUS4_INFINITE_CONFUSION (1 << 4) // Used for Berserk Gene +#define STATUS4_SALT_CURE (1 << 5) #define HITMARKER_WAKE_UP_CLEAR (1 << 4) // Cleared when waking up. Never set or checked. #define HITMARKER_SKIP_DMG_TRACK (1 << 5) diff --git a/include/constants/battle_anim.h b/include/constants/battle_anim.h index e70eba0fd..9876652b6 100644 --- a/include/constants/battle_anim.h +++ b/include/constants/battle_anim.h @@ -549,6 +549,7 @@ #define B_ANIM_AFFECTION_HANGED_ON 35 #define B_ANIM_SNOW_CONTINUES 36 #define B_ANIM_ULTRA_BURST 37 +#define B_ANIM_SALT_CURE_DAMAGE 38 // special animations table (gBattleAnims_Special) #define B_ANIM_LVL_UP 0 diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index e91d45d3e..72f54c4ec 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -413,7 +413,9 @@ #define EFFECT_CORROSIVE_GAS 407 #define EFFECT_POPULATION_BOMB 408 #define EFFECT_MORTAL_SPIN 409 +#define EFFECT_GIGATON_HAMMER 410 +#define EFFECT_SALT_CURE 411 -#define NUM_BATTLE_MOVE_EFFECTS 410 +#define NUM_BATTLE_MOVE_EFFECTS 412 #endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index dabb7d337..f7845d80b 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -668,8 +668,11 @@ #define STRINGID_ULTRABURSTREACTING 666 #define STRINGID_ULTRABURSTCOMPLETED 667 #define STRINGID_TEAMGAINEDEXP 668 +#define STRINGID_CURRENTMOVECANTSELECT 669 +#define STRINGID_TARGETISBEINGSALTCURED 670 +#define STRINGID_TARGETISHURTBYSALTCURE 671 -#define BATTLESTRINGS_COUNT 669 +#define BATTLESTRINGS_COUNT 672 // This is the string id that gBattleStringsTable starts with. // String ids before this (e.g. STRINGID_INTROMSG) are not in the table, diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 39f5dbe08..1805b2d9d 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -4958,6 +4958,10 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score) || gBattleMons[BATTLE_PARTNER(battlerAtk)].status1 & STATUS1_ANY) score += 3; break; + case EFFECT_SALT_CURE: + if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_WATER) || IS_BATTLER_OF_TYPE(battlerDef, TYPE_STEEL)) + score += 2; + break; } // move effect checks return score; diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index 9bd9ef2f7..ed75fbad2 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -2973,7 +2973,7 @@ bool32 AI_CanBeInfatuated(u8 battlerAtk, u8 battlerDef, u16 defAbility) u32 ShouldTryToFlinch(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbility, u16 move) { if (((AI_DATA->abilities[battlerAtk] != ABILITY_MOLD_BREAKER && (defAbility == ABILITY_SHIELD_DUST || defAbility == ABILITY_INNER_FOCUS)) - || AI_GetHoldEffect(battlerDef) == HOLD_EFFECT_COVERT_CLOAK + || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_COVERT_CLOAK || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) || AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER)) // Opponent goes first { @@ -3010,7 +3010,7 @@ bool32 ShouldFakeOut(u8 battlerAtk, u8 battlerDef, u16 move) if (!gDisableStructs[battlerAtk].isFirstTurn || AI_DATA->abilities[battlerAtk] == ABILITY_GORILLA_TACTICS || AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_CHOICE_BAND - || AI_GetHoldEffect(battlerDef) == HOLD_EFFECT_COVERT_CLOAK + || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_COVERT_CLOAK || DoesSubstituteBlockMove(battlerAtk, battlerDef, move) || (AI_DATA->abilities[battlerAtk] != ABILITY_MOLD_BREAKER && (AI_DATA->abilities[battlerDef] == ABILITY_SHIELD_DUST || AI_DATA->abilities[battlerDef] == ABILITY_INNER_FOCUS))) diff --git a/src/battle_message.c b/src/battle_message.c index 397cc213e..a353c6b2d 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -804,9 +804,15 @@ static const u8 sText_PkmnItemMelted[] = _("{B_ATK_NAME_WITH_PREFIX} corroded\n{ static const u8 sText_UltraBurstReacting[] = _("Bright light is about to\nburst out of {B_ATK_NAME_WITH_PREFIX}!"); static const u8 sText_UltraBurstCompleted[] = _("{B_ATK_NAME_WITH_PREFIX} regained its\ntrue power through Ultra Burst!"); static const u8 sText_TeamGainedEXP[] = _("The rest of your team gained EXP.\nPoints thanks to the {B_LAST_ITEM}!\p"); +static const u8 sText_CurrentMoveCantSelect[] = _("{B_BUFF1} cannot be used!\p"); +static const u8 sText_TargetIsBeingSaltCured[] = _("{B_DEF_NAME_WITH_PREFIX} is being salt cured!"); +static const u8 sText_TargetIsHurtBySaltCure[] = _("{B_DEF_NAME_WITH_PREFIX} is hurt by {B_BUFF1}!"); const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { + [STRINGID_TARGETISHURTBYSALTCURE - BATTLESTRINGS_TABLE_START] = sText_TargetIsHurtBySaltCure, + [STRINGID_TARGETISBEINGSALTCURED - BATTLESTRINGS_TABLE_START] = sText_TargetIsBeingSaltCured, + [STRINGID_CURRENTMOVECANTSELECT - BATTLESTRINGS_TABLE_START] = sText_CurrentMoveCantSelect, [STRINGID_PKMNITEMMELTED - BATTLESTRINGS_TABLE_START] = sText_PkmnItemMelted, [STRINGID_MIRRORHERBCOPIED - BATTLESTRINGS_TABLE_START] = sText_MirrorHerbCopied, [STRINGID_THUNDERCAGETRAPPED - BATTLESTRINGS_TABLE_START] = sText_AtkTrappedDef, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 5c779c3d1..1183d4052 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -16311,3 +16311,12 @@ void BS_JumpIfMotor(void) else gBattlescriptCurrInstr = cmd->nextInstr; } + +void BS_ApplySaltCure(void) +{ + NATIVE_ARGS(u8 battler); + + u8 battler = GetBattlerForBattleScript(cmd->battler); + gStatuses4[battler] |= STATUS4_SALT_CURE; + gBattlescriptCurrInstr = cmd->nextInstr; +} diff --git a/src/battle_util.c b/src/battle_util.c index 5ce61373d..b13e3cb29 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -1749,6 +1749,22 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler) } } + if (gBattleMoves[move].effect == EFFECT_GIGATON_HAMMER && move == gLastResultingMoves[battler]) + { + gCurrentMove = move; + PREPARE_MOVE_BUFFER(gBattleTextBuff1, gCurrentMove); + if (gBattleTypeFlags & BATTLE_TYPE_PALACE) + { + gPalaceSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedCurrentMoveInPalace; + gProtectStructs[battler].palaceUnableToUseMove = TRUE; + } + else + { + gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedCurrentMove; + limitations++; + } + } + gPotentialItemEffectBattler = battler; if (HOLD_EFFECT_CHOICE(holdEffect) && *choicedMove != MOVE_NONE && *choicedMove != MOVE_UNAVAILABLE && *choicedMove != move) { @@ -1885,6 +1901,8 @@ u8 CheckMoveLimitations(u8 battler, u8 unusableMoves, u16 check) // Gorilla Tactics else if (check & MOVE_LIMITATION_CHOICE_ITEM && GetBattlerAbility(battler) == ABILITY_GORILLA_TACTICS && *choicedMove != MOVE_NONE && *choicedMove != MOVE_UNAVAILABLE && *choicedMove != gBattleMons[battler].moves[i]) unusableMoves |= gBitTable[i]; + else if (check & MOVE_LIMITATION_GIGATON_HAMMER && gBattleMoves[gBattleMons[battler].moves[i]].effect == EFFECT_GIGATON_HAMMER && gBattleMons[battler].moves[i] == gLastResultingMoves[battler]) + unusableMoves |= gBitTable[i]; } return unusableMoves; } @@ -2523,6 +2541,7 @@ enum ENDTURN_SLOW_START, ENDTURN_PLASMA_FISTS, ENDTURN_CUD_CHEW, + ENDTURN_SALT_CURE, ENDTURN_BATTLER_COUNT }; @@ -3086,6 +3105,22 @@ u8 DoBattlerEndTurnEffects(void) gDisableStructs[battler].cudChew = TRUE; gBattleStruct->turnEffectsTracker++; break; + case ENDTURN_SALT_CURE: + if (gStatuses4[battler] & STATUS4_SALT_CURE && gBattleMons[battler].hp != 0) + { + gBattlerTarget = battler; + if (IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_STEEL) || IS_BATTLER_OF_TYPE(gBattlerTarget, TYPE_WATER)) + gBattleMoveDamage = gBattleMons[gBattlerTarget].maxHP / 4; + else + gBattleMoveDamage = gBattleMons[gBattlerTarget].maxHP / 8; + if (gBattleMoveDamage == 0) + gBattleMoveDamage = 1; + PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_SALT_CURE); + BattleScriptExecute(BattleScript_SaltCureExtraDamage); + effect++; + } + gBattleStruct->turnEffectsTracker++; + break; case ENDTURN_BATTLER_COUNT: // done gBattleStruct->turnEffectsTracker = 0; gBattleStruct->turnEffectsBattlerId++; diff --git a/src/data/battle_moves.h b/src/data/battle_moves.h index 75b89dccc..00a8ee2c7 100644 --- a/src/data/battle_moves.h +++ b/src/data/battle_moves.h @@ -10687,7 +10687,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = .priority = 0, .split = SPLIT_SPECIAL, .zMoveEffect = Z_EFFECT_NONE, - .ballisticMove = B_UPDATED_MOVE_FLAGS < GEN_9, + .ballisticMove = TRUE, }, [MOVE_ANCHOR_SHOT] = @@ -13218,7 +13218,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = [MOVE_SALT_CURE] = { - .effect = EFFECT_PLACEHOLDER, // EFFECT_SALT_CURE + .effect = EFFECT_SALT_CURE, .power = 40, .type = TYPE_ROCK, .accuracy = 100, @@ -13639,7 +13639,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] = [MOVE_GIGATON_HAMMER] = { - .effect = EFFECT_PLACEHOLDER, // EFFECT_GIGATON_HAMMER + .effect = EFFECT_GIGATON_HAMMER, .power = 160, .type = TYPE_STEEL, .accuracy = 100, diff --git a/test/battle/move_effect/gigaton_hammer.c b/test/battle/move_effect/gigaton_hammer.c new file mode 100644 index 000000000..aaff50f75 --- /dev/null +++ b/test/battle/move_effect/gigaton_hammer.c @@ -0,0 +1,63 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_GIGATON_HAMMER].effect == EFFECT_GIGATON_HAMMER); +} + +SINGLE_BATTLE_TEST("Struggle will be used if slow Encore is used on Gigaton Hammer") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_ENCORE].effect == EFFECT_ENCORE); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_GIGATON_HAMMER); MOVE(opponent, MOVE_ENCORE); } + TURN { FORCED_MOVE(player); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_GIGATON_HAMMER, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ENCORE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player); + } +} + +SINGLE_BATTLE_TEST("Gigaton Hammer strikes again if fast encore is used") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_ENCORE].effect == EFFECT_ENCORE); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_GIGATON_HAMMER); } + TURN { MOVE(opponent, MOVE_ENCORE); FORCED_MOVE(player); } + TURN { FORCED_MOVE(player); } + TURN { FORCED_MOVE(player); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_GIGATON_HAMMER, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_ENCORE, opponent); + ANIMATION(ANIM_TYPE_MOVE, MOVE_GIGATON_HAMMER, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player); + } +} + +SINGLE_BATTLE_TEST("Gigaton Hammer alternates with Struggle if it is the only usable move left") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_GIGATON_HAMMER, MOVE_NONE, MOVE_NONE, MOVE_NONE); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_GIGATON_HAMMER); } + TURN { FORCED_MOVE(player); } + TURN { MOVE(player, MOVE_GIGATON_HAMMER); } + TURN { FORCED_MOVE(player); } + TURN { MOVE(player, MOVE_GIGATON_HAMMER); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_GIGATON_HAMMER, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_GIGATON_HAMMER, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_STRUGGLE, player); + ANIMATION(ANIM_TYPE_MOVE, MOVE_GIGATON_HAMMER, player); + } +} diff --git a/test/battle/move_effect/salt_cure.c b/test/battle/move_effect/salt_cure.c new file mode 100644 index 000000000..fcf240631 --- /dev/null +++ b/test/battle/move_effect/salt_cure.c @@ -0,0 +1,72 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_SALT_CURE].effect == EFFECT_SALT_CURE); +} + +SINGLE_BATTLE_TEST("Salt Cure inflicts 1/8 of the target's maximum HP as damage per turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_SALT_CURE); } + for (i = 0; i < 3; i++) + TURN {} + } SCENE { + s32 maxHP = GetMonData(&OPPONENT_PARTY[0], MON_DATA_MAX_HP); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SALT_CURE, player); + MESSAGE("Foe Wobbuffet is being salt cured!"); + for (i = 0; i < 4; i++) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SALT_CURE_DAMAGE, opponent); + HP_BAR(opponent, damage: maxHP / 8); + MESSAGE("Foe Wobbuffet is hurt by Salt Cure!"); + } + } +} + +SINGLE_BATTLE_TEST("Salt Cure inflicts 1/4 to Water/Steel types of their maximum HP as damage per turn") +{ + u32 species; + + PARAMETRIZE { species = SPECIES_LAPRAS; }; + PARAMETRIZE { species = SPECIES_JIRACHI; }; + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(species); + } WHEN { + TURN { MOVE(player, MOVE_SALT_CURE); } + TURN {} + } SCENE { + s32 maxHP = GetMonData(&OPPONENT_PARTY[0], MON_DATA_MAX_HP); + ANIMATION(ANIM_TYPE_MOVE, MOVE_SALT_CURE, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SALT_CURE_DAMAGE, opponent); + HP_BAR(opponent, damage: maxHP / 4); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SALT_CURE_DAMAGE, opponent); + HP_BAR(opponent, damage: maxHP / 4); + } +} + +SINGLE_BATTLE_TEST("Salt Cure is removed when the afflicted Pokémon is switched out") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WYNAUT); + } WHEN { + TURN { MOVE(player, MOVE_SALT_CURE); } + TURN { SWITCH(opponent, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_SALT_CURE, player); + MESSAGE("Foe Wobbuffet is being salt cured!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SALT_CURE_DAMAGE, opponent); + MESSAGE("Foe Wobbuffet is hurt by Salt Cure!"); + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SALT_CURE_DAMAGE, opponent); + MESSAGE("Foe Wobbuffet is hurt by Salt Cure!"); + } + } +}