From 7eb1b122245bc06448cf7a0ed669a3e3dda5de06 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Wed, 1 Nov 2023 14:47:31 +0100 Subject: [PATCH 01/11] Fix Knock Off healing berries (#3509) --- include/constants/battle_script_commands.h | 20 ++++---- test/battle/move_effect/knock_off.c | 53 ++++++++++++++++++++++ 2 files changed, 63 insertions(+), 10 deletions(-) create mode 100644 test/battle/move_effect/knock_off.c diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index ee98bccec7..ea74a4d1e9 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -302,16 +302,16 @@ #define MOVEEND_ATTACKER_VISIBLE 10 #define MOVEEND_TARGET_VISIBLE 11 #define MOVEEND_ITEM_EFFECTS_TARGET 12 -#define MOVEEND_ITEM_EFFECTS_ALL 13 -#define MOVEEND_KINGSROCK 14 // These item effects will occur each strike of a multi-hit move -#define MOVEEND_SUBSTITUTE 15 -#define MOVEEND_SKY_DROP_CONFUSE 16 -#define MOVEEND_UPDATE_LAST_MOVES 17 -#define MOVEEND_MIRROR_MOVE 18 -#define MOVEEND_NEXT_TARGET 19 // Everything up until here is handled for each strike of a multi-hit move -#define MOVEEND_MULTIHIT_MOVE 20 -#define MOVEEND_DEFROST 21 -#define MOVEEND_MOVE_EFFECTS2 22 +#define MOVEEND_MOVE_EFFECTS2 13 +#define MOVEEND_ITEM_EFFECTS_ALL 14 +#define MOVEEND_KINGSROCK 15 // These item effects will occur each strike of a multi-hit move +#define MOVEEND_SUBSTITUTE 16 +#define MOVEEND_SKY_DROP_CONFUSE 17 +#define MOVEEND_UPDATE_LAST_MOVES 18 +#define MOVEEND_MIRROR_MOVE 19 +#define MOVEEND_NEXT_TARGET 20 // Everything up until here is handled for each strike of a multi-hit move +#define MOVEEND_MULTIHIT_MOVE 21 +#define MOVEEND_DEFROST 22 #define MOVEEND_RECOIL 23 #define MOVEEND_MAGICIAN 24 // Occurs after final multi-hit strike, and after other items/abilities would activate #define MOVEEND_EJECT_BUTTON 25 diff --git a/test/battle/move_effect/knock_off.c b/test/battle/move_effect/knock_off.c new file mode 100644 index 0000000000..3022a19633 --- /dev/null +++ b/test/battle/move_effect/knock_off.c @@ -0,0 +1,53 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_KNOCK_OFF].effect == EFFECT_KNOCK_OFF); +} + +SINGLE_BATTLE_TEST("Knock Off knocks a healing berry before it has the chance to activate") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); MaxHP(500); HP(255); } + } WHEN { + TURN { MOVE(player, MOVE_KNOCK_OFF); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, player); + NONE_OF { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + MESSAGE("Foe Wobbuffet's Sitrus Berry restored health!"); + } + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF); + MESSAGE("Wobbuffet knocked off Foe Wobbuffet's Sitrus Berry!"); + } +} + +SINGLE_BATTLE_TEST("Knock Off activates after Rocky Helmet and Weakness Policy") +{ + u16 item = 0; + + PARAMETRIZE { item = ITEM_WEAKNESS_POLICY; } + PARAMETRIZE { item = ITEM_ROCKY_HELMET; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET) { Item(item); } + } WHEN { + TURN { MOVE(player, MOVE_KNOCK_OFF); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_KNOCK_OFF, player); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent); + if (item == ITEM_WEAKNESS_POLICY) { + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE); + MESSAGE("Using WeaknssPolicy, the Attack of Foe Wobbuffet sharply rose!"); + MESSAGE("Using WeaknssPolicy, the Sp. Atk of Foe Wobbuffet sharply rose!"); + } else if (item == ITEM_ROCKY_HELMET) { + HP_BAR(player); + MESSAGE("Wobbuffet was hurt by Foe Wobbuffet's Rocky Helmet!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ITEM_KNOCKOFF); + MESSAGE("Wobbuffet knocked off Foe Wobbuffet's Rocky Helmet!"); + } + } +} From 47341f3f305bd26a1a84525ecfb122fa3881862c Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Wed, 1 Nov 2023 14:55:50 +0100 Subject: [PATCH 02/11] Fix Anger Shell activation (#3508) --- include/battle.h | 2 +- src/battle_util.c | 14 +++-- test/battle/ability/anger_shell.c | 95 +++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 5 deletions(-) create mode 100644 test/battle/ability/anger_shell.c diff --git a/include/battle.h b/include/battle.h index 95886b7e13..5b2ed5c6de 100644 --- a/include/battle.h +++ b/include/battle.h @@ -645,7 +645,7 @@ struct BattleStruct u8 lastMoveFailed; // as bits for each battler, for the sake of Stomping Tantrum u8 lastMoveTarget[MAX_BATTLERS_COUNT]; // The last target on which each mon used a move, for the sake of Instruct u16 tracedAbility[MAX_BATTLERS_COUNT]; - u16 hpBefore[MAX_BATTLERS_COUNT]; // Hp of battlers before using a move. For Berserk + u16 hpBefore[MAX_BATTLERS_COUNT]; // Hp of battlers before using a move. For Berserk and Anger Shell. bool8 spriteIgnore0Hp; struct Illusion illusion[MAX_BATTLERS_COUNT]; s32 aiFinalScore[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // AI, target, moves to make debugging easier diff --git a/src/battle_util.c b/src/battle_util.c index c9ef0b08bd..f37bd901cc 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -4161,6 +4161,13 @@ static uq4_12_t GetSupremeOverlordModifier(u32 battler) return modifier; } +static bool32 HadMoreThanHalfHpNowHasLess(u32 battler) +{ + // Had more than half of hp before, now has less + return (gBattleStruct->hpBefore[battler] >= gBattleMons[battler].maxHP / 2 + && gBattleMons[battler].hp < gBattleMons[battler].maxHP / 2); +} + u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 moveArg) { u32 effect = 0; @@ -5192,9 +5199,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED && IsBattlerAlive(battler) - // Had more than half of hp before, now has less - && gBattleStruct->hpBefore[battler] >= gBattleMons[battler].maxHP / 2 - && gBattleMons[battler].hp < gBattleMons[battler].maxHP / 2 + && HadMoreThanHalfHpNowHasLess(battler) && (gMultiHitCounter == 0 || gMultiHitCounter == 1) && !(TestSheerForceFlag(gBattlerAttacker, gCurrentMove)) && CompareStat(battler, STAT_SPATK, MAX_STAT_STAGE, CMP_LESS_THAN)) @@ -5673,8 +5678,9 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && !gProtectStructs[gBattlerAttacker].confusionSelfDmg && TARGET_TURN_DAMAGED + && (gMultiHitCounter == 0 || gMultiHitCounter == 1) // Activates after all hits from a multi-hit move. && IsBattlerAlive(gBattlerTarget) - && (gBattleMons[gBattlerTarget].hp <= gBattleMons[gBattlerTarget].maxHP / 2) + && HadMoreThanHalfHpNowHasLess(gBattlerTarget) && !(TestSheerForceFlag(gBattlerAttacker, gCurrentMove))) { gBattlerAttacker = gBattlerTarget; diff --git a/test/battle/ability/anger_shell.c b/test/battle/ability/anger_shell.c new file mode 100644 index 0000000000..6b916e088f --- /dev/null +++ b/test/battle/ability/anger_shell.c @@ -0,0 +1,95 @@ +#include "global.h" +#include "test/battle.h" + +SINGLE_BATTLE_TEST("Anger Shell activates only if the target had more than 50% of its hp") +{ + bool32 activates = FALSE; + u16 maxHp = 500, hp = 0; + + PARAMETRIZE { hp = 249; activates = FALSE; } + PARAMETRIZE { hp = 100; activates = FALSE; } + PARAMETRIZE { hp = 50; activates = FALSE; } + PARAMETRIZE { hp = 251; activates = TRUE; } + PARAMETRIZE { hp = 255; activates = TRUE; } + + GIVEN { + ASSUME(gBattleMoves[MOVE_TACKLE].power != 0); + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_ANGER_SHELL); MaxHP(maxHp); HP(hp); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + if (activates) { + ABILITY_POPUP(player, ABILITY_ANGER_SHELL); + } else { + NOT ABILITY_POPUP(player, ABILITY_ANGER_SHELL); + } + } THEN { + if (activates) { + EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 1); + } + } +} + +SINGLE_BATTLE_TEST("Anger Shell lowers Def/Sp.Def by 1 and raises Atk/Sp.Atk/Spd by 1") +{ + u16 maxHp = 500; + GIVEN { + ASSUME(gBattleMoves[MOVE_TACKLE].power != 0); + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_ANGER_SHELL); MaxHP(maxHp); HP(maxHp / 2 + 1); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_TACKLE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent); + ABILITY_POPUP(player, ABILITY_ANGER_SHELL); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Defense fell!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Sp. Def fell!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Attack rose!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Sp. Atk rose!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Wobbuffet's Speed rose!"); + } THEN { + EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 1); + } +} + +SINGLE_BATTLE_TEST("Anger Shell activates after all hits from a multi-hit move") +{ + u32 j; + u16 maxHp = 500; + GIVEN { + ASSUME(gBattleMoves[MOVE_DOUBLE_SLAP].effect == EFFECT_MULTI_HIT); + PLAYER(SPECIES_WOBBUFFET) { Ability(ABILITY_ANGER_SHELL); MaxHP(maxHp); HP(maxHp / 2 + 1); } + OPPONENT(SPECIES_SHELLDER) { Ability(ABILITY_SKILL_LINK); } // Always hits 5 times. + } WHEN { + TURN { MOVE(opponent, MOVE_DOUBLE_SLAP); } + } SCENE { + for (j = 0; j < 4; j++) + { + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SLAP, opponent); + NOT ABILITY_POPUP(player, ABILITY_ANGER_SHELL); + } + ANIMATION(ANIM_TYPE_MOVE, MOVE_DOUBLE_SLAP, opponent); + ABILITY_POPUP(player, ABILITY_ANGER_SHELL); + } THEN { + EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE - 1); + EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1); + EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 1); + } +} From 6cce1b1873a79b6aa0426cb6279b0c37aca434c9 Mon Sep 17 00:00:00 2001 From: Eduardo Quezada D'Ottone Date: Wed, 1 Nov 2023 16:45:03 -0300 Subject: [PATCH 03/11] Fixed Dynamax's indicator not disappearing after Dynamax ends (#3510) --- src/battle_interface.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/battle_interface.c b/src/battle_interface.c index ac33177a77..d3751f702f 100644 --- a/src/battle_interface.c +++ b/src/battle_interface.c @@ -1057,6 +1057,7 @@ static void UpdateLvlInHealthbox(u8 healthboxSpriteId, u8 lvl) objVram = ConvertIntToDecimalStringN(text + 2, lvl, STR_CONV_MODE_LEFT_ALIGN, 3); xPos = 5 * (3 - (objVram - (text + 2))); + MegaIndicator_SetVisibilities(healthboxSpriteId, TRUE); } windowTileData = AddTextPrinterAndCreateWindowOnHealthbox(text, xPos, 3, 2, &windowId); From 244ca0be95b730fd1bba87b20009372db58f5f8a Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Wed, 1 Nov 2023 16:49:44 -0500 Subject: [PATCH 04/11] Add custom female icon sprites for Pikachu and Wobbuffet (#3506) Co-authored-by: Eduardo Quezada D'Ottone --- graphics/pokemon/pikachu/iconf.png | Bin 0 -> 347 bytes graphics/pokemon/wobbuffet/iconf.png | Bin 0 -> 336 bytes include/config/pokemon.h | 2 +- include/graphics.h | 8 +++++++- src/data/graphics/pokemon.h | 8 +++++++- src/pokemon_icon.c | 8 +++++++- 6 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 graphics/pokemon/pikachu/iconf.png create mode 100644 graphics/pokemon/wobbuffet/iconf.png diff --git a/graphics/pokemon/pikachu/iconf.png b/graphics/pokemon/pikachu/iconf.png new file mode 100644 index 0000000000000000000000000000000000000000..22eeed0a464364550c32857d8b830593516671c2 GIT binary patch literal 347 zcmV-h0i^zkP)Px#Fi=cXMPi(TdwYAmy|n-T|8sNAq^$O|gIeyzi^Z%d_Vy|EVp5cQ&Bdh6y(yex zVp>5#LFq)aUjP6A*-1n}R7i>Kl)(ywFbqTs3zB%4`TxJ&Hm$Vcq_;f?n}ewHOhdp| z%D;Y4*Mwh$ZsF(YeM+$d*aaLo#Gct^t2>BBl88+rkOMMrfMblQD-LNVqyS^pX@d@7 zpO*sc^MAdhmOow!Wa=Ri$PEwzS^SasVfsS?i$A0Q7Juk``eP%Q?vF$d(;t#p{Q)VE t)gK#yoY7(<5DbHC1TtDA|L=SK@CB9~6u^r!S+M{B002ovPDHLkV1nB0l)V4| literal 0 HcmV?d00001 diff --git a/graphics/pokemon/wobbuffet/iconf.png b/graphics/pokemon/wobbuffet/iconf.png new file mode 100644 index 0000000000000000000000000000000000000000..1f7f86991b24b0a41f33636a69da67f260133f24 GIT binary patch literal 336 zcmV-W0k8gvP)Px#Fi=cXMPi(TgM)Luy}kec|GlI^_Vy{&VnOzlDT{mmVoJ^KbDaOQq@>0FYOI`M zVp>5#L0PGgWB>pF&PhZ;R7i>Kl)(-IAqYgx9t;>W{Qut;&}}y9=w8}O+qv=_g~*ih z*C%A5UAentiDWql;e_=Y!mtNs)eH_W!aC4eM>8IP5=S0 zE`!hn%mFs>094(;YjyxHgdg&{4g=sI7SllCb@op4E5arS-i8j@s*5~tx)?*gau_YX z!_{aDl-;NN>uvgl^{;p9Uu)g3e-O29*T0gD85(#0000 Date: Thu, 2 Nov 2023 08:44:23 +0100 Subject: [PATCH 05/11] Remove powerfulMoveEffects array (#3515) * Remove powerfulMoveEffects array * Solar Beam test --- include/battle_ai_util.h | 2 +- src/battle_ai_main.c | 58 +++++++++--------------------------- src/battle_ai_util.c | 63 ++++++++++++++++------------------------ test/battle/ai.c | 48 ++++++++++++++++++++++++++++-- 4 files changed, 84 insertions(+), 87 deletions(-) diff --git a/include/battle_ai_util.h b/include/battle_ai_util.h index 3d37a185c7..2e0629b0cd 100644 --- a/include/battle_ai_util.h +++ b/include/battle_ai_util.h @@ -91,7 +91,6 @@ bool32 AI_IsDamagedByRecoil(u32 battler); u32 GetNoOfHitsToKO(u32 dmg, s32 hp); u32 GetNoOfHitsToKOBattlerDmg(u32 dmg, u32 battlerDef); u32 GetNoOfHitsToKOBattler(u32 battlerAtk, u32 battlerDef, u32 moveIndex); -bool32 IsInIgnoredPowerfulMoveEffects(u32 effect); void SetMovesDamageResults(u32 battlerAtk, u16 *moves); u32 GetMoveDamageResult(u32 battlerAtk, u32 battlerDef, u32 moveIndex); u32 GetCurrDamageHpPercent(u32 battlerAtk, u32 battlerDef); @@ -113,6 +112,7 @@ bool32 IsMoveRedirectionPrevented(u32 move, u32 atkAbility); bool32 IsMoveEncouragedToHit(u32 battlerAtk, u32 battlerDef, u32 move); bool32 IsHazardMoveEffect(u32 moveEffect); bool32 IsEncoreEncouragedEffect(u32 moveEffect); +bool32 IsChargingMove(u32 battlerAtk, u32 effect); void ProtectChecks(u32 battlerAtk, u32 battlerDef, u32 move, u32 predictedMove, s32 *score); bool32 ShouldSetSandstorm(u32 battler, u32 ability, u32 holdEffect); bool32 ShouldSetHail(u32 battler, u32 ability, u32 holdEffect); diff --git a/src/battle_ai_main.c b/src/battle_ai_main.c index 8339195ea7..33282b955c 100644 --- a/src/battle_ai_main.c +++ b/src/battle_ai_main.c @@ -760,6 +760,9 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) if (IsSemiInvulnerable(battlerDef, move) && moveEffect != EFFECT_SEMI_INVULNERABLE && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) RETURN_SCORE_MINUS(20); // if target off screen and we go first, don't use move + if (IsChargingMove(battlerAtk, moveEffect) && CanTargetFaintAi(battlerDef, battlerAtk)) + RETURN_SCORE_MINUS(10); + // check if negates type switch (effectiveness) { @@ -1356,22 +1359,13 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) } break; //case EFFECT_BIDE: - //case EFFECT_SUPER_FANG: //case EFFECT_RECHARGE: - case EFFECT_LEVEL_DAMAGE: - case EFFECT_PSYWAVE: //case EFFECT_COUNTER: - //case EFFECT_FLAIL: - case EFFECT_RETURN: case EFFECT_PRESENT: - case EFFECT_FRUSTRATION: case EFFECT_SONICBOOM: //case EFFECT_MIRROR_COAT: - case EFFECT_SKULL_BASH: case EFFECT_FOCUS_PUNCH: - case EFFECT_SUPERPOWER: //case EFFECT_ENDEAVOR: - case EFFECT_LOW_KICK: // AI_CBM_HighRiskForDamage if (aiData->abilities[battlerDef] == ABILITY_WONDER_GUARD && effectiveness < AI_EFFECTIVENESS_x2) ADJUST_SCORE(-10); @@ -1906,17 +1900,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) || (gBattleMons[battlerDef].status2 & (STATUS2_TRANSFORMED | STATUS2_SUBSTITUTE))) //Leave out Illusion b/c AI is supposed to be fooled ADJUST_SCORE(-10); break; - case EFFECT_TWO_TURNS_ATTACK: - if (aiData->holdEffects[battlerAtk] != HOLD_EFFECT_POWER_HERB && CanTargetFaintAi(battlerDef, battlerAtk)) - ADJUST_SCORE(-6); - break; - case EFFECT_RECHARGE: - if (aiData->abilities[battlerDef] == ABILITY_WONDER_GUARD && effectiveness < AI_EFFECTIVENESS_x2) - ADJUST_SCORE(-10); - else if (aiData->abilities[battlerAtk] != ABILITY_TRUANT - && !CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, 0)) - ADJUST_SCORE(-2); - break; case EFFECT_SPITE: case EFFECT_MIMIC: if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) // Attacker should go first @@ -2109,13 +2092,6 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) break; case EFFECT_SPECTRAL_THIEF: break; - case EFFECT_SOLAR_BEAM: - if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB - || ((AI_GetWeather(aiData) & B_WEATHER_SUN) && aiData->holdEffects[battlerAtk] != HOLD_EFFECT_UTILITY_UMBRELLA)) - break; - if (CanTargetFaintAi(battlerDef, battlerAtk)) //Attacker can be knocked out - ADJUST_SCORE(-4); - break; case EFFECT_SEMI_INVULNERABLE: if (predictedMove != MOVE_NONE && AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER @@ -2674,6 +2650,10 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) && !BattlerStatCanRise(battlerAtk, aiData->abilities[battlerAtk], STAT_SPDEF)) ADJUST_SCORE(-10); break; + case EFFECT_LOW_KICK: + if (IsDynamaxed(battlerDef)) + ADJUST_SCORE(-10); + break; case EFFECT_PLACEHOLDER: return 0; // cannot even select } // move effect checks @@ -3151,7 +3131,7 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId) s32 score = 0; s32 leastHits = 1000; u16 *moves = GetMovesArray(battlerAtk); - bool8 isPowerfulIgnoredEffect[MAX_MON_MOVES]; + bool8 isChargingMoveEffect[MAX_MON_MOVES]; for (i = 0; i < MAX_MON_MOVES; i++) { @@ -3163,13 +3143,13 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId) leastHits = noOfHits[i]; } viableMoveScores[i] = AI_SCORE_DEFAULT; - isPowerfulIgnoredEffect[i] = IsInIgnoredPowerfulMoveEffects(gBattleMoves[moves[i]].effect); + isChargingMoveEffect[i] = IsChargingMove(battlerAtk, gBattleMoves[moves[i]].effect); } else { noOfHits[i] = -1; viableMoveScores[i] = 0; - isPowerfulIgnoredEffect[i] = FALSE; + isChargingMoveEffect[i] = FALSE; } /* MgbaPrintf_("%S: required hits: %d Dmg: %d", gMoveNames[moves[i]], noOfHits[i], AI_DATA->simulatedDmg[battlerAtk][battlerDef][i]); @@ -3178,7 +3158,7 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId) // Priority list: // 1. Less no of hits to ko - // 2. Not in the powerful but ignored move effects table + // 2. Not charging // 3. More accuracy // 4. Better effect @@ -3193,9 +3173,9 @@ static s32 AI_CompareDamagingMoves(u32 battlerAtk, u32 battlerDef, u32 currId) { multipleBestMoves = TRUE; // We need to make sure it's the current move which is objectively better. - if (isPowerfulIgnoredEffect[i] && !isPowerfulIgnoredEffect[currId]) + if (isChargingMoveEffect[i] && !isChargingMoveEffect[currId]) viableMoveScores[i] -= 3; - else if (!isPowerfulIgnoredEffect[i] && isPowerfulIgnoredEffect[currId]) + else if (!isChargingMoveEffect[i] && isChargingMoveEffect[currId]) viableMoveScores[currId] -= 3; switch (CompareMoveAccuracies(battlerAtk, battlerDef, currId, i)) @@ -4847,15 +4827,6 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score IncreasePoisonScore(battlerAtk, battlerDef, move, &score); IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPEED, &score); break; - case EFFECT_SOLAR_BEAM: - if (GetNoOfHitsToKOBattler(battlerAtk, battlerDef, movesetIndex) >= 2 - && HasMoveEffect(battlerAtk, EFFECT_SUNNY_DAY) && (AI_GetWeather(aiData) & B_WEATHER_SUN)) // Use Sunny Day to boost damage. - ADJUST_SCORE(-3); - case EFFECT_TWO_TURNS_ATTACK: - case EFFECT_SKULL_BASH: - if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB) - ADJUST_SCORE(2); - break; case EFFECT_COUNTER: if (!IsBattlerIncapacitated(battlerDef, aiData->abilities[battlerDef]) && predictedMove != MOVE_NONE) { @@ -5246,9 +5217,6 @@ static s32 AI_HPAware(u32 battlerAtk, u32 battlerDef, u32 move, s32 score) case EFFECT_BELLY_DRUM: case EFFECT_PSYCH_UP: case EFFECT_MIRROR_COAT: - case EFFECT_SOLAR_BEAM: - case EFFECT_TWO_TURNS_ATTACK: - case EFFECT_ERUPTION: case EFFECT_TICKLE: case EFFECT_SUNNY_DAY: case EFFECT_SANDSTORM: diff --git a/src/battle_ai_util.c b/src/battle_ai_util.c index dd06a69eaf..68450e4e59 100644 --- a/src/battle_ai_util.c +++ b/src/battle_ai_util.c @@ -363,25 +363,6 @@ static const u16 sEncouragedEncoreEffects[] = EFFECT_CAMOUFLAGE, }; -// For the purposes of determining the most powerful move in a moveset, these -// moves are treated the same as having a power of 0 or 1 -#define IGNORED_MOVES_END 0xFFFF -static const u16 sIgnoredPowerfulMoveEffects[] = -{ - EFFECT_EXPLOSION, - EFFECT_DREAM_EATER, - EFFECT_RECHARGE, - EFFECT_SKULL_BASH, - EFFECT_SOLAR_BEAM, - EFFECT_FOCUS_PUNCH, - EFFECT_SUPERPOWER, - EFFECT_ERUPTION, - EFFECT_OVERHEAT, - EFFECT_MIND_BLOWN, - EFFECT_MAKE_IT_RAIN, - IGNORED_MOVES_END -}; - // Functions u32 GetAIChosenMove(u32 battlerId) { @@ -980,6 +961,11 @@ static bool32 AI_IsMoveEffectInMinus(u32 battlerAtk, u32 battlerDef, u32 move, s switch (gBattleMoves[move].effect) { case EFFECT_RECHARGE: + case EFFECT_SUPERPOWER: + case EFFECT_OVERHEAT: + case EFFECT_MAKE_IT_RAIN: + case EFFECT_MIND_BLOWN: + case EFFECT_STEEL_BEAM: return TRUE; case EFFECT_RECOIL_25: case EFFECT_RECOIL_IF_MISS: @@ -1054,22 +1040,6 @@ u32 GetNoOfHitsToKOBattler(u32 battlerAtk, u32 battlerDef, u32 moveIndex) return GetNoOfHitsToKOBattlerDmg(AI_DATA->simulatedDmg[battlerAtk][battlerDef][moveIndex], battlerDef); } -bool32 IsInIgnoredPowerfulMoveEffects(u32 effect) -{ - u32 i; - for (i = 0; sIgnoredPowerfulMoveEffects[i] != IGNORED_MOVES_END; i++) - { - if (effect == sIgnoredPowerfulMoveEffects[i]) - { - // Don't ingore Solar Beam if doesn't have a charging turn. - if (effect == EFFECT_SOLAR_BEAM && (AI_GetWeather(AI_DATA) & B_WEATHER_SUN)) - break; - return TRUE; - } - } - return FALSE; -} - void SetMovesDamageResults(u32 battlerAtk, u16 *moves) { s32 i, j, battlerDef, bestId, currId, hp, result; @@ -1079,7 +1049,7 @@ void SetMovesDamageResults(u32 battlerAtk, u16 *moves) for (i = 0; i < MAX_MON_MOVES; i++) { u32 move = moves[i]; - if (move == MOVE_NONE || move == MOVE_UNAVAILABLE || gBattleMoves[move].power == 0 || IsInIgnoredPowerfulMoveEffects(gBattleMoves[move].effect)) + if (move == MOVE_NONE || move == MOVE_UNAVAILABLE || gBattleMoves[move].power == 0) isNotConsidered[i] = TRUE; else isNotConsidered[i] = FALSE; @@ -1094,11 +1064,10 @@ void SetMovesDamageResults(u32 battlerAtk, u16 *moves) if (isNotConsidered[i]) { - result = MOVE_POWER_OTHER; // Move has a power of 0/1, or is in the group sIgnoredPowerfulMoveEffects + result = MOVE_POWER_OTHER; // Move has a power of 0/1 } else { - // Considered move has power and is not in sIgnoredPowerfulMoveEffects // Check all other moves and calculate their power for (j = 0; j < MAX_MON_MOVES; j++) { @@ -2394,6 +2363,24 @@ bool32 IsEncoreEncouragedEffect(u32 moveEffect) return FALSE; } +bool32 IsChargingMove(u32 battlerAtk, u32 effect) +{ + switch (effect) + { + case EFFECT_SOLAR_BEAM: + if (AI_GetWeather(AI_DATA) & B_WEATHER_SUN) + return FALSE; + case EFFECT_SKULL_BASH: + case EFFECT_METEOR_BEAM: + case EFFECT_TWO_TURNS_ATTACK: + if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_POWER_HERB) + return FALSE; + return TRUE; + default: + return FALSE; + } +} + static u32 GetLeechSeedDamage(u32 battlerId) { u32 damage = 0; diff --git a/test/battle/ai.c b/test/battle/ai.c index 6f863e3f31..91a9bc663a 100644 --- a/test/battle/ai.c +++ b/test/battle/ai.c @@ -260,8 +260,8 @@ AI_SINGLE_BATTLE_TEST("AI can choose a status move that boosts the attack by two AI_SINGLE_BATTLE_TEST("AI chooses the safest option to faint the target, taking into account accuracy and move effect") { u16 move1 = MOVE_NONE, move2 = MOVE_NONE, move3 = MOVE_NONE, move4 = MOVE_NONE; - u16 expectedMove, abilityAtk = ABILITY_NONE; - u16 expectedMove2 = MOVE_NONE; + u16 expectedMove, expectedMove2 = MOVE_NONE; + u16 abilityAtk = ABILITY_NONE, holdItemAtk = ITEM_NONE; // Psychic is not very effective, but always hits. Solarbeam requires a charging turn, Double Edge has recoil and Focus Blast can miss; PARAMETRIZE { abilityAtk = ABILITY_STURDY; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SOLAR_BEAM; move3 = MOVE_PSYCHIC; move4 = MOVE_DOUBLE_EDGE; expectedMove = MOVE_PSYCHIC; } @@ -272,12 +272,24 @@ AI_SINGLE_BATTLE_TEST("AI chooses the safest option to faint the target, taking // This time it's Solarbeam + Psychic, because the weather is sunny. PARAMETRIZE { abilityAtk = ABILITY_DROUGHT; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SOLAR_BEAM; move3 = MOVE_PSYCHIC; move4 = MOVE_DOUBLE_EDGE; expectedMove = MOVE_PSYCHIC; expectedMove2 = MOVE_SOLAR_BEAM; } + // Psychic and Solar Beam are chosen because user is holding Power Herb + PARAMETRIZE { abilityAtk = ABILITY_STURDY; holdItemAtk = ITEM_POWER_HERB; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SOLAR_BEAM; move3 = MOVE_PSYCHIC; move4 = MOVE_DOUBLE_EDGE; + expectedMove = MOVE_PSYCHIC; expectedMove2 = MOVE_SOLAR_BEAM; } + // Psychic and Skull Bash are chosen because user is holding Power Herb + PARAMETRIZE { abilityAtk = ABILITY_STURDY; holdItemAtk = ITEM_POWER_HERB; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SKULL_BASH; move3 = MOVE_PSYCHIC; move4 = MOVE_DOUBLE_EDGE; + expectedMove = MOVE_PSYCHIC; expectedMove2 = MOVE_SKULL_BASH; } + // Skull Bash is chosen because it's the most accurate and is holding Power Herb + PARAMETRIZE { abilityAtk = ABILITY_STURDY; holdItemAtk = ITEM_POWER_HERB; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SKULL_BASH; move3 = MOVE_SLAM; move4 = MOVE_DOUBLE_EDGE; + expectedMove = MOVE_SKULL_BASH; } + // Crabhammer is chosen even if Skull Bash is more accurate, the user has no Power Herb + PARAMETRIZE { abilityAtk = ABILITY_STURDY; move1 = MOVE_FOCUS_BLAST; move2 = MOVE_SKULL_BASH; move3 = MOVE_SLAM; move4 = MOVE_CRABHAMMER; + expectedMove = MOVE_CRABHAMMER; } GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); PLAYER(SPECIES_WOBBUFFET) { HP(5); } PLAYER(SPECIES_WOBBUFFET); - OPPONENT(SPECIES_GEODUDE) { Moves(move1, move2, move3, move4); Ability(abilityAtk); } + OPPONENT(SPECIES_GEODUDE) { Moves(move1, move2, move3, move4); Ability(abilityAtk); Item(holdItemAtk); } } WHEN { TURN { if (expectedMove2 == MOVE_NONE) { EXPECT_MOVE(opponent, expectedMove); SEND_OUT(player, 1); } else {EXPECT_MOVES(opponent, expectedMove, expectedMove2); SCORE_EQ(opponent, expectedMove, expectedMove2); SEND_OUT(player, 1);} @@ -288,6 +300,36 @@ AI_SINGLE_BATTLE_TEST("AI chooses the safest option to faint the target, taking } } +AI_SINGLE_BATTLE_TEST("AI won't use Solar Beam if there is no Sun up or the user is not holding Power Herb") +{ + u16 abilityAtk = ABILITY_NONE; + u16 holdItemAtk = ITEM_NONE; + + PARAMETRIZE { abilityAtk = ABILITY_DROUGHT; } + PARAMETRIZE { holdItemAtk = ITEM_POWER_HERB; } + PARAMETRIZE { } + + GIVEN { + AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); + PLAYER(SPECIES_WOBBUFFET) { HP(211); } + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_TYPHLOSION) { Moves(MOVE_SOLAR_BEAM, MOVE_GRASS_PLEDGE); Ability(abilityAtk); Item(holdItemAtk); } + } WHEN { + if (abilityAtk == ABILITY_DROUGHT) { + TURN { EXPECT_MOVES(opponent, MOVE_SOLAR_BEAM, MOVE_GRASS_PLEDGE); } + TURN { EXPECT_MOVES(opponent, MOVE_SOLAR_BEAM, MOVE_GRASS_PLEDGE); SEND_OUT(player, 1); } + } else if (holdItemAtk == ITEM_POWER_HERB) { + TURN { EXPECT_MOVES(opponent, MOVE_SOLAR_BEAM, MOVE_GRASS_PLEDGE); MOVE(player, MOVE_KNOCK_OFF); } + TURN { EXPECT_MOVE(opponent, MOVE_GRASS_PLEDGE); SEND_OUT(player, 1); } + } else { + TURN { EXPECT_MOVE(opponent, MOVE_GRASS_PLEDGE); } + TURN { EXPECT_MOVE(opponent, MOVE_GRASS_PLEDGE); SEND_OUT(player, 1); } + } + } SCENE { + MESSAGE("Wobbuffet fainted!"); + } +} + AI_SINGLE_BATTLE_TEST("AI won't use ground type attacks against flying type Pokemon unless Gravity is in effect") { GIVEN { From 90d9334f0b7b3859595ed5657f6950539bed9304 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Thu, 2 Nov 2023 11:43:06 +0100 Subject: [PATCH 06/11] Fix Sap Sipper not blocking Bullet Seed (#3516) * Fix Sap Sipper not blocking bullet seed * add defiant sticky web test --- src/battle_util.c | 3 +++ test/battle/ability/defiant.c | 23 +++++++++++++++++++++++ test/battle/ability/sap_sipper.c | 21 +++++++++++++++++++++ 3 files changed, 47 insertions(+) diff --git a/src/battle_util.c b/src/battle_util.c index f37bd901cc..a4dd07caf0 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -5135,6 +5135,9 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 #endif } } + + if (effect) + gMultiHitCounter = 0; // Prevent multi-hit moves from hitting more than once after move has been absorbed. } break; case ABILITYEFFECT_MOVE_END: // Think contact abilities. diff --git a/test/battle/ability/defiant.c b/test/battle/ability/defiant.c index 4b9cfa42b5..e8443ecc5f 100644 --- a/test/battle/ability/defiant.c +++ b/test/battle/ability/defiant.c @@ -115,3 +115,26 @@ DOUBLE_BATTLE_TEST("Defiant sharply raises opponent's Attack after Intimidate") EXPECT_EQ(opponentRight->statStages[STAT_ATK], (abilityRight == ABILITY_DEFIANT) ? DEFAULT_STAT_STAGE + 2 : DEFAULT_STAT_STAGE - 2); } } + +SINGLE_BATTLE_TEST("Defiant activates after Sticky Web lowers Speed") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_MANKEY) { Ability(ABILITY_DEFIANT); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(opponent, MOVE_STICKY_WEB); } + TURN { SWITCH(player, 1); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, opponent); + // Switch-in - Sticky Web activates + MESSAGE("Go! Mankey!"); + MESSAGE("Mankey was caught in a Sticky Web!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Mankey's Speed fell!"); + // Defiant activates + ABILITY_POPUP(player, ABILITY_DEFIANT); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Mankey's Attack sharply rose!"); + } +} diff --git a/test/battle/ability/sap_sipper.c b/test/battle/ability/sap_sipper.c index 918e553a3a..903427fcc5 100644 --- a/test/battle/ability/sap_sipper.c +++ b/test/battle/ability/sap_sipper.c @@ -57,3 +57,24 @@ SINGLE_BATTLE_TEST("Sap Sipper does not increase Attack if already maxed") } } } + +SINGLE_BATTLE_TEST("Sap Sipper blocks multi-hit grass type moves") +{ + GIVEN { + ASSUME(gBattleMoves[MOVE_BULLET_SEED].effect == EFFECT_MULTI_HIT); + PLAYER(SPECIES_MARILL) { Ability(ABILITY_SAP_SIPPER); } + OPPONENT(SPECIES_SHELLDER) { Ability(ABILITY_SKILL_LINK); } + } WHEN { + TURN { MOVE(opponent, MOVE_BULLET_SEED); } + } SCENE { + MESSAGE("Foe Shellder used Bullet Seed!"); + ABILITY_POPUP(player, ABILITY_SAP_SIPPER); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player); + MESSAGE("Marill's Attack rose!"); + NONE_OF { + ANIMATION(ANIM_TYPE_MOVE, MOVE_BULLET_SEED, opponent); + HP_BAR(player); + MESSAGE("Hit 5 time(s)!"); + } + } +} From 90471741e9e0630456f974827884f42a9a4598ce Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Fri, 3 Nov 2023 14:32:12 -0500 Subject: [PATCH 07/11] Add Fairy Feather sprite (#3520) --- .../items/icon_palettes/fairy_feather.pal | 19 ++++++++++++++++++ graphics/items/icons/fairy_feather.png | Bin 0 -> 266 bytes include/graphics.h | 3 +++ src/data/graphics/items.h | 3 +++ src/data/item_icon_table.h | 2 +- 5 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 graphics/items/icon_palettes/fairy_feather.pal create mode 100644 graphics/items/icons/fairy_feather.png diff --git a/graphics/items/icon_palettes/fairy_feather.pal b/graphics/items/icon_palettes/fairy_feather.pal new file mode 100644 index 0000000000..a20a702b74 --- /dev/null +++ b/graphics/items/icon_palettes/fairy_feather.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +180 180 180 +11 15 16 +33 19 27 +59 38 38 +102 90 92 +200 124 124 +182 116 141 +255 124 189 +227 140 140 +253 155 155 +222 173 189 +255 175 175 +195 191 192 +252 161 206 +225 221 223 +245 245 245 diff --git a/graphics/items/icons/fairy_feather.png b/graphics/items/icons/fairy_feather.png new file mode 100644 index 0000000000000000000000000000000000000000..26446886a8423de50788413bffe233e615cce661 GIT binary patch literal 266 zcmV+l0rmcgP)Px#Fi=cXMYOcE3l9(>6B|1wCT3b($b5XZbdCRfz2l6G{hOQKt-b%RufxB<{Gra_ z-QV@~^=kN}p#T5?h)G02R49?H(MuA9APhxO5`vKCll|{jf?5KdK@a*3&lRys=YAjd znObW-_B3?t4PH4Ko;}9+9emE}Pl3rfUaKMk?~9oE#>Gbf08(zN2k1s5txT?=dw(7p z&3=bb7T1793N80OcO#ORfqDI)E2I!oYOp(g4zF|j@GySNIAHt#4)kK`3625=%}zB0 Qq5uE@07*qoM6N<$f~>}H00000 literal 0 HcmV?d00001 diff --git a/include/graphics.h b/include/graphics.h index 97fb8811f8..ba10753fa6 100644 --- a/include/graphics.h +++ b/include/graphics.h @@ -9288,6 +9288,9 @@ extern const u32 gItemIconPalette_LustrousGlobe[]; extern const u32 gItemIcon_BerserkGene[]; extern const u32 gItemIconPalette_BerserkGene[]; +extern const u32 gItemIcon_FairyFeather[]; +extern const u32 gItemIconPalette_FairyFeather[]; + extern const u32 gItemIcon_ReturnToFieldArrow[]; extern const u32 gItemIconPalette_ReturnToFieldArrow[]; diff --git a/src/data/graphics/items.h b/src/data/graphics/items.h index 60cd7d5187..2ef587d994 100644 --- a/src/data/graphics/items.h +++ b/src/data/graphics/items.h @@ -2023,3 +2023,6 @@ const u32 gItemIconPalette_LustrousGlobe[] = INCBIN_U32("graphics/items/icon_pal const u32 gItemIcon_BerserkGene[] = INCBIN_U32("graphics/items/icons/berserk_gene.4bpp.lz"); const u32 gItemIconPalette_BerserkGene[] = INCBIN_U32("graphics/items/icon_palettes/berserk_gene.gbapal.lz"); + +const u32 gItemIcon_FairyFeather[] = INCBIN_U32("graphics/items/icons/fairy_feather.4bpp.lz"); +const u32 gItemIconPalette_FairyFeather[] = INCBIN_U32("graphics/items/icon_palettes/fairy_feather.gbapal.lz"); diff --git a/src/data/item_icon_table.h b/src/data/item_icon_table.h index 30ae4ba19f..de6e92371b 100644 --- a/src/data/item_icon_table.h +++ b/src/data/item_icon_table.h @@ -844,7 +844,7 @@ const u32 *const gItemIconTable[ITEMS_COUNT + 1][2] = [ITEM_LINKING_CORD] = {gItemIcon_LinkingCord, gItemIconPalette_LinkingCord}, [ITEM_PEAT_BLOCK] = {gItemIcon_PeatBlock, gItemIconPalette_PeatBlock}, [ITEM_BERSERK_GENE] = {gItemIcon_BerserkGene, gItemIconPalette_BerserkGene}, - [ITEM_FAIRY_FEATHER] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_FairyFeather, gItemIconPalette_FairyFeather}, + [ITEM_FAIRY_FEATHER] = {gItemIcon_FairyFeather, gItemIconPalette_FairyFeather}, [ITEM_SYRUPY_APPLE] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_SyrupyApple, gItemIconPalette_SyrupyApple}, [ITEM_UNREMARKABLE_TEACUP] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_UnremarkableTeacup, gItemIconPalette_UnremarkableTeacup}, [ITEM_MASTERPIECE_TEACUP] = {gItemIcon_QuestionMark, gItemIconPalette_QuestionMark}, // {gItemIcon_MasterpieceTeacup, gItemIconPalette_MasterpieceTeacup}, From 8b359c2ca4a9e17931ef542637563ff7b4fde139 Mon Sep 17 00:00:00 2001 From: DizzyEggg Date: Fri, 3 Nov 2023 21:01:02 +0100 Subject: [PATCH 08/11] fix psychic terrain blocking moves it shouldnt block (#3521) --- src/battle_util.c | 2 ++ test/battle/terrain/psychic.c | 3 --- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/battle_util.c b/src/battle_util.c index a4dd07caf0..7eb4aa1920 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -3824,6 +3824,8 @@ u8 AtkCanceller_UnableToUseMove2(void) if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN && IsBattlerGrounded(gBattlerTarget) && GetChosenMovePriority(gBattlerAttacker) > 0 + && gBattleMoves[gCurrentMove].target != MOVE_TARGET_ALL_BATTLERS + && gBattleMoves[gCurrentMove].target != MOVE_TARGET_OPPONENTS_FIELD && GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget)) { CancelMultiTurnMoves(gBattlerAttacker); diff --git a/test/battle/terrain/psychic.c b/test/battle/terrain/psychic.c index c810af3401..45c2886e32 100644 --- a/test/battle/terrain/psychic.c +++ b/test/battle/terrain/psychic.c @@ -78,7 +78,6 @@ SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target the SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all battlers") { - KNOWN_FAILING; GIVEN { PLAYER(SPECIES_SABLEYE) { Ability(ABILITY_PRANKSTER); } OPPONENT(SPECIES_WOBBUFFET); @@ -93,7 +92,6 @@ SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all opponents") { - KNOWN_FAILING; GIVEN { PLAYER(SPECIES_SABLEYE) { Ability(ABILITY_PRANKSTER); } OPPONENT(SPECIES_WOBBUFFET); @@ -124,7 +122,6 @@ DOUBLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target all SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority field moves") { - KNOWN_FAILING; GIVEN { PLAYER(SPECIES_SABLEYE) { Ability(ABILITY_PRANKSTER); } OPPONENT(SPECIES_WOBBUFFET); From 3004fd588aa4afe17a3bfd297978c0b932b45fef Mon Sep 17 00:00:00 2001 From: Eduardo Quezada D'Ottone Date: Sun, 5 Nov 2023 18:46:22 -0300 Subject: [PATCH 09/11] CI fix test (#3530) --- .github/workflows/build.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 51c0a78e51..8c5bcb7ed8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,7 +28,9 @@ jobs: repository: pret/agbcc - name: Install binutils - run: sudo apt install -y build-essential libpng-dev libelf-dev + run: | + sudo apt update + sudo apt install -y build-essential libpng-dev libelf-dev # build-essential, git, and libpng-dev are already installed # gcc-arm-none-eabi is only needed for the modern build # as an alternative to dkP From 2b9f6b350e49b41803af7ac5084cbaa5fb2098cc Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Mon, 6 Nov 2023 12:31:43 +0100 Subject: [PATCH 10/11] Move animations. Credits to Captain-Ford (#3529) Co-authored-by: DizzyEggg <16259973+DizzyEggg@users.noreply.github.com> --- data/battle_anim_scripts.s | 356 ++++++++++++++++++++++++++++-------- src/battle_anim_effects_1.c | 87 ++++++--- src/battle_anim_water.c | 22 +++ 3 files changed, 365 insertions(+), 100 deletions(-) diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index 4943e887f2..8b64d3e0e1 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -4061,58 +4061,57 @@ Move_BUG_BITE: end Move_CHARGE_BEAM: - loadspritegfx ANIM_TAG_BLACK_BALL_2 + loadspritegfx ANIM_TAG_ELECTRIC_ORBS + loadspritegfx ANIM_TAG_CIRCLE_OF_LIGHT + loadspritegfx ANIM_TAG_ELECTRICITY loadspritegfx ANIM_TAG_SPARK_2 - delay 0 - createvisualtask AnimTask_BlendColorCycle, 2, (F_PAL_BG | F_PAL_ATTACKER), -31, 1, 5, 5, RGB(31, 31, 22) - playsewithpan SE_M_THUNDERBOLT2, SOUND_PAN_ATTACKER - createsprite gSparkElectricitySpriteTemplate, ANIM_ATTACKER, 0, 32, 24, 190, 12, 0, 1, 0 - delay 0 - createsprite gSparkElectricitySpriteTemplate, ANIM_ATTACKER, 0, 80, 24, 22, 12, 0, 1, 0 - createsprite gSparkElectricitySpriteTemplate, ANIM_ATTACKER, 0, 156, 24, 121, 13, 0, 1, 1 - delay 0 - createvisualtask AnimTask_BlendColorCycle, 2, (F_PAL_BG | F_PAL_ATTACKER), -31, 1, 0, 0, RGB(31, 31, 22) - delay 10 - createvisualtask AnimTask_BlendColorCycle, 2, (F_PAL_BG | F_PAL_ATTACKER), -31, 1, 5, 5, RGB(31, 31, 22) - playsewithpan SE_M_THUNDERBOLT2, SOUND_PAN_ATTACKER - createsprite gSparkElectricitySpriteTemplate, ANIM_ATTACKER, 0, 100, 24, 60, 10, 0, 1, 0 - createsprite gSparkElectricitySpriteTemplate, ANIM_ATTACKER, 0, 170, 24, 42, 11, 0, 1, 1 - delay 0 - createsprite gSparkElectricitySpriteTemplate, ANIM_ATTACKER, 0, 238, 24, 165, 10, 0, 1, 1 - delay 0 - createvisualtask AnimTask_BlendColorCycle, 2, (F_PAL_BG | F_PAL_ATTACKER), -31, 1, 0, 0, RGB(31, 31, 22) + setalpha 12, 8 + createsprite gSimplePaletteBlendSpriteTemplate, ANIM_ATTACKER, 2, F_PAL_BG, 2, 0, 4, RGB_BLACK + waitforvisualfinish + createvisualtask AnimTask_ElectricChargingParticles, 2, ANIM_ATTACKER, 20, 0, 2 + playsewithpan SE_M_CHARGE, SOUND_PAN_ATTACKER + delay 12 + createsprite gGrowingShockWaveOrbSpriteTemplate, ANIM_ATTACKER, 2 + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_ATTACKER, 2, 0, 11, RGB(31, 31, 22) + delay 50 + createsoundtask SoundTask_LoopSEAdjustPanning, SE_M_THUNDERBOLT2, SOUND_PAN_ATTACKER, SOUND_PAN_TARGET, 1, 16, 0, 5 + createvisualtask AnimTask_ShakeMon, 2, ANIM_ATTACKER, 0, 4, 50, 1 + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + createvisualtask AnimTask_ShakeMon2, 2, ANIM_TARGET, 4, 0, 50, 1 + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_ATTACKER, 2, 11, 0, RGB(31, 31, 22) + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_TARGET, 2, 0, 11, RGB(31, 31, 22) + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam + call SparkBeam delay 20 - createvisualtask AnimTask_BlendColorCycle, 2, (F_PAL_BG | F_PAL_ATTACKER), -31, 1, 7, 7, RGB(31, 31, 22) - playsewithpan SE_M_THUNDERBOLT2, SOUND_PAN_ATTACKER - createsprite gSparkElectricityFlashingSpriteTemplate, ANIM_ATTACKER, 4, 0, 0, 32, 12, 0, 20, 0, 0 - createsprite gSparkElectricityFlashingSpriteTemplate, ANIM_ATTACKER, 4, 0, 0, 32, 12, 64, 20, 1, 0 - createsprite gSparkElectricityFlashingSpriteTemplate, ANIM_ATTACKER, 4, 0, 0, 32, 12, 128, 20, 0, 0 - createsprite gSparkElectricityFlashingSpriteTemplate, ANIM_ATTACKER, 4, 0, 0, 32, 12, 192, 20, 2, 0 - createsprite gSparkElectricityFlashingSpriteTemplate, ANIM_ATTACKER, 4, 0, 0, 16, 12, 32, 20, 0, 0 - createsprite gSparkElectricityFlashingSpriteTemplate, ANIM_ATTACKER, 4, 0, 0, 16, 12, 96, 20, 1, 0 - createsprite gSparkElectricityFlashingSpriteTemplate, ANIM_ATTACKER, 4, 0, 0, 16, 12, 160, 20, 0, 0 - createsprite gSparkElectricityFlashingSpriteTemplate, ANIM_ATTACKER, 4, 0, 0, 16, 12, 224, 20, 2, 0 - delay 4 - waitforvisualfinish - createvisualtask AnimTask_BlendColorCycle, 2, (F_PAL_BG | F_PAL_ATTACKER), -31, 1, 0, 0, RGB(31, 31, 22) - playsewithpan SE_M_THUNDER_WAVE, SOUND_PAN_ATTACKER - createsprite gZapCannonBallSpriteTemplate, ANIM_TARGET, 3, 10, 0, 0, 0, 30, 0 - createsprite gZapCannonSparkSpriteTemplate, ANIM_TARGET, 4, 10, 0, 16, 30, 0, 40, 0 - createsprite gZapCannonSparkSpriteTemplate, ANIM_TARGET, 4, 10, 0, 16, 30, 64, 40, 1 - createsprite gZapCannonSparkSpriteTemplate, ANIM_TARGET, 4, 10, 0, 16, 30, 128, 40, 0 - createsprite gZapCannonSparkSpriteTemplate, ANIM_TARGET, 4, 10, 0, 16, 30, 192, 40, 2 - createsprite gZapCannonSparkSpriteTemplate, ANIM_TARGET, 4, 10, 0, 8, 30, 32, 40, 0 - createsprite gZapCannonSparkSpriteTemplate, ANIM_TARGET, 4, 10, 0, 8, 30, 96, 40, 1 - createsprite gZapCannonSparkSpriteTemplate, ANIM_TARGET, 4, 10, 0, 8, 30, 160, 40, 0 - createsprite gZapCannonSparkSpriteTemplate, ANIM_TARGET, 4, 10, 0, 8, 30, 224, 40, 2 - waitforvisualfinish - createvisualtask AnimTask_ShakeMon2, 2, ANIM_TARGET, 4, 0, 5, 1 - delay 15 - waitplaysewithpan SE_M_THUNDERBOLT2, SOUND_PAN_TARGET, 19 - call ElectricityEffect + createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_TARGET, 2, 11, 0, RGB(31, 31, 22) waitforvisualfinish + createsprite gSimplePaletteBlendSpriteTemplate, ANIM_ATTACKER, 2, F_PAL_BG, 4, 4, 0, RGB_BLACK + blendoff end +SparkBeam: + createsprite gSparkBeamSpriteTemplate, ANIM_ATTACKER, 3, 0, 0, 0, 0 + delay 1 + createsprite gSparkBeamSpriteTemplate, ANIM_ATTACKER, 3, 0, 0, 0, 0 + delay 1 + return + Move_WOOD_HAMMER: loadspritegfx ANIM_TAG_WOOD_HAMMER loadspritegfx ANIM_TAG_WOOD_HAMMER_HAMMER @@ -4297,22 +4296,22 @@ Move_HEAL_ORDER: Move_HEAD_SMASH: loadspritegfx ANIM_TAG_IMPACT - call SetImpactBackground - createsprite gBowMonSpriteTemplate, ANIM_ATTACKER, 2, 0 + loadspritegfx ANIM_TAG_ROCKS + createvisualtask AnimTask_SkullBashPosition, 2, 0 playsewithpan SE_M_TAKE_DOWN, SOUND_PAN_ATTACKER waitforvisualfinish - delay 2 - createsprite gBowMonSpriteTemplate, ANIM_ATTACKER, 2, 1 + playse SE_BANG + call SetImpactBackground + createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_ATTACKER, 2, 0, 40, 1 + createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_TARGET, 10, 0, 40, 1 + createsprite gFlashingHitSplatSpriteTemplate, ANIM_TARGET, 4, 0, 0, ANIM_TARGET, 0 + playsewithpan SE_M_ROCK_THROW, SOUND_PAN_TARGET + createsprite gRockScatterSpriteTemplate, ANIM_TARGET, 2, -12, 32, 3, 4 + createsprite gRockScatterSpriteTemplate, ANIM_TARGET, 2, 8, 31, 2, 2 + createsprite gRockScatterSpriteTemplate, ANIM_TARGET, 2, -4, 28, 2, 3 + createsprite gRockScatterSpriteTemplate, ANIM_TARGET, 2, 12, 30, 4, 3 waitforvisualfinish - createvisualtask AnimTask_ShakeMonInPlace, 2, ANIM_ATTACKER, 2, 0, 4, 1 - createvisualtask AnimTask_ShakeMon, 2, ANIM_TARGET, 5, 0, 6, 1 - createsprite gBowMonSpriteTemplate, ANIM_ATTACKER, 2, 2 - createsprite gFlashingHitSplatSpriteTemplate, ANIM_TARGET, 3, 0, 0, 1, 1 - loopsewithpan SE_M_MEGA_KICK2, SOUND_PAN_TARGET, 8, 3 - waitforvisualfinish - clearmonbg ANIM_TARGET - blendoff - delay 2 + createvisualtask AnimTask_SkullBashPosition, 2, 1 restorebg waitbgfadein end @@ -4861,19 +4860,82 @@ Move_WONDER_ROOM:: end Move_PSYSHOCK: + loadspritegfx ANIM_TAG_RED_ORB_2 + loadspritegfx ANIM_TAG_POISON_JAB + loadspritegfx ANIM_TAG_GRAY_SMOKE + loadspritegfx ANIM_TAG_WISP_FIRE monbg ANIM_TARGET setalpha 8, 8 + playsewithpan SE_M_SUPERSONIC, SOUND_PAN_ATTACKER createvisualtask AnimTask_ShakeMon2, 2, ANIM_ATTACKER, 1, 0, 10, 1 - createvisualtask AnimTask_BlendColorCycle, 2, F_PAL_ATTACKER, 0, 2, 0, 8, RGB(31, 23, 0) + createvisualtask AnimTask_BlendColorCycle, 2, F_PAL_ATTACKER, 0, 2, 0, 8, RGB_WHITE waitforvisualfinish - loopsewithpan SE_M_SUPERSONIC, SOUND_PAN_TARGET, 10, 3 - createvisualtask AnimTask_ShakeMon, 2, ANIM_TARGET, 5, 0, 15, 1 - createvisualtask AnimTask_ScaleMonAndRestore, 5, -6, -6, 15, ANIM_TARGET, 1 + delay 10 + call PsyshockConverge waitforvisualfinish clearmonbg ANIM_TARGET blendoff end +PsyshockConverge: + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, 40, 40, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, -40, -40, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, 0, 40, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, 0, -40, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, 40, -20, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, 40, 20, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, -40, -20, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, -40, 20, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, -20, 30, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, 20, -30, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, -20, -30, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, 20, 30, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, -40, 0, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockOrbSpriteTemplate, ANIM_TARGET, 2, 40, 0, 16 + playsewithpan SE_M_SWIFT, SOUND_PAN_TARGET + delay 6 + createvisualtask AnimTask_ShakeMon, 2, ANIM_TARGET, 5, 0, 15, 1 + createvisualtask AnimTask_BlendMonInAndOut, 5, ANIM_TARGET, RGB_WHITE, 12, 0, 1 + createsprite gPsyshockSmokeSpriteTemplate, ANIM_TARGET, 2, 8, 8, 1, 0 + playsewithpan SE_M_SWAGGER, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockSmokeSpriteTemplate, ANIM_TARGET, 2, -8, -8, 1, 0 + playsewithpan SE_M_SWAGGER, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockSmokeSpriteTemplate, ANIM_TARGET, 2, 8, -8, 1, 0 + playsewithpan SE_M_SWAGGER, SOUND_PAN_TARGET + delay 2 + createsprite gPsyshockSmokeSpriteTemplate, ANIM_TARGET, 2, -8, 8, 1, 0 + playsewithpan SE_M_SWAGGER, SOUND_PAN_TARGET + delay 2 + return + Move_VENOSHOCK: loadspritegfx ANIM_TAG_POISON_BUBBLE loadspritegfx ANIM_TAG_TOXIC_BUBBLE @@ -5802,23 +5864,79 @@ Move_QUASH: Move_ACROBATICS: loadspritegfx ANIM_TAG_ROUND_SHADOW + loadspritegfx ANIM_TAG_WHITE_STREAK loadspritegfx ANIM_TAG_IMPACT monbg ANIM_TARGET setalpha 12, 8 playsewithpan SE_M_FLY, SOUND_PAN_ATTACKER createsprite gFlyBallUpSpriteTemplate, ANIM_ATTACKER, 2, 0, 0, 13, 336 waitforvisualfinish - playsewithpan SE_M_SWAGGER, SOUND_PAN_TARGET - createsprite gBounceBallLandSpriteTemplate, ANIM_TARGET, 3 - delay 7 - playsewithpan SE_M_MEGA_KICK2, SOUND_PAN_TARGET - createsprite gBasicHitSplatSpriteTemplate, ANIM_TARGET, 2, 0, 0, 1, 0 - createvisualtask AnimTask_ShakeMon, 5, ANIM_TARGET, 0, 5, 11, 1 + call SetSkyBg + call AcrobaticsSlashes waitforvisualfinish clearmonbg ANIM_TARGET - blendoff + visible ANIM_ATTACKER + call UnsetSkyBg end +AcrobaticsSlashes: + createvisualtask AnimTask_ShakeMon2, 2, ANIM_TARGET, 4, 0, 40, 1 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, -10, 3 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 1 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, 24, -19 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 1 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, -28, -15 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 2 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, -6, -30 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 1 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, -20, 6 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 1 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, 28, 2 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 2 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, -14, -25 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 1 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, 9, -2 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 2 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, -1, 0 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 1 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, 21, 4 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 2 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, 28, 20 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 2 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, -7, 24 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 2 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, -11, 1 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 2 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, 12, -18 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 2 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, -21, -14 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 1 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, -29, 7 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 2 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, 15, 28 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 1 + createsprite gAcrobaticsSlashesSpriteTemplate, ANIM_TARGET, 2, -21, -16 + playsewithpan SE_M_CUT, SOUND_PAN_TARGET + delay 2 + return + Move_REFLECT_TYPE: loadspritegfx ANIM_TAG_GUARD_RING @ring around user loadspritegfx ANIM_TAG_ICE_CHUNK @blue green color @@ -8313,17 +8431,99 @@ Move_FELL_STINGER: end Move_PHANTOM_FORCE: - choosetwoturnanim PhantomForceSetUp, PhantomForceUnleash -PhantomForceEnd: + loadspritegfx ANIM_TAG_ROUND_SHADOW + loadspritegfx ANIM_TAG_IMPACT + choosetwoturnanim PhantomForcePrep PhantomForceAttack +PhantomForceWaitEnd: + waitforvisualfinish + restorebg + waitbgfadein end -PhantomForceSetUp: +PhantomForcePrep: + monbg ANIM_ATTACKER + fadetobg BG_GHOST + waitbgfadein + delay 0 + playsewithpan SE_M_FAINT_ATTACK, SOUND_PAN_ATTACKER + createvisualtask AnimTask_TranslateMonEllipticalRespectSide, 2, ANIM_ATTACKER, 18, 6, 1, 3 + createvisualtask AnimTask_AttackerFadeToInvisible, 2, 1 + waitforvisualfinish + clearmonbg ANIM_ATTACKER invisible ANIM_ATTACKER - goto PhantomForceEnd -PhantomForceUnleash: - visible ANIM_ATTACKER - goto PhantomForceEnd + delay 1 + goto PhantomForceWaitEnd +PhantomForceAttack: + loadspritegfx ANIM_TAG_PURPLE_FLAME + loadspritegfx ANIM_TAG_WHITE_SHADOW @Destiny Bond + monbg ANIM_ATTACKER + splitbgprio ANIM_ATTACKER + fadetobg BG_GHOST + waitbgfadein + delay 1 + createvisualtask AnimTask_BlendParticle, 5, ANIM_TAG_IMPACT, 0, 12, 12, RGB(0, 0, 23) + setalpha 12, 8 + waitforvisualfinish + delay 10 + playsewithpan SE_M_PSYBEAM, SOUND_PAN_ATTACKER + createvisualtask AnimTask_PurpleFlamesOnTarget, 0x3 + createvisualtask AnimTask_DestinyBondWhiteShadow, 0x5, 0x0, 0x30 + delay 30 + createvisualtask AnimTask_BlendParticle, 5, ANIM_TAG_IMPACT, 0, 12, 12, RGB(0, 0, 23) + waitforvisualfinish + createsprite gRandomPosHitSplatSpriteTemplate, ANIM_TARGET, 3, ANIM_TARGET, 2 + createsprite gRandomPosHitSplatSpriteTemplate, ANIM_TARGET, 3, ANIM_TARGET, 2 + createsprite gRandomPosHitSplatSpriteTemplate, ANIM_TARGET, 3, ANIM_TARGET, 2 + createvisualtask SoundTask_PlaySE1WithPanning, 5, 215, SOUND_PAN_TARGET + delay 3 + createsprite gRandomPosHitSplatSpriteTemplate, ANIM_TARGET, 3, ANIM_TARGET, 2 + createsprite gRandomPosHitSplatSpriteTemplate, ANIM_TARGET, 3, ANIM_DEF_PARTNER, 2 + createsprite gRandomPosHitSplatSpriteTemplate, ANIM_TARGET, 3, ANIM_ATK_PARTNER, 2 + createvisualtask SoundTask_PlaySE1WithPanning, 5, 215, SOUND_PAN_TARGET + delay 3 + createsprite gRandomPosHitSplatSpriteTemplate, ANIM_TARGET, 3, ANIM_TARGET, 2 + createsprite gRandomPosHitSplatSpriteTemplate, ANIM_TARGET, 3, ANIM_DEF_PARTNER, 2 + createsprite gRandomPosHitSplatSpriteTemplate, ANIM_TARGET, 3, ANIM_ATK_PARTNER, 2 + createvisualtask SoundTask_PlaySE1WithPanning, 5, 215, SOUND_PAN_TARGET + delay 3 + createvisualtask AnimTask_ShakeMon2, 2, ANIM_TARGET, 2, 0, 12, 1 + createvisualtask AnimTask_BlendColorCycle, 2, F_PAL_TARGET, 0, 2, 0, 13, RGB_PURPLE + waitforvisualfinish + delay 1 + playsewithpan SOUND_PAN_ATTACKER, 192 + createvisualtask AnimTask_NightShadeClone, 5, 10 + waitforvisualfinish + clearmonbg ANIM_ATTACKER + delay 1 + goto PhantomForceWaitEnd +PhantomForceBg: + fadetobg BG_DARK + waitbgfadeout + createvisualtask AnimTask_FadeScreenToWhite, 5 + waitbgfadein + return Move_TRICK_OR_TREAT: + loadspritegfx ANIM_TAG_EYE_SPARKLE + loadspritegfx ANIM_TAG_GHOSTLY_SPIRIT + fadetobg BG_NIGHTMARE + waitbgfadein + delay 10 + playsewithpan SE_M_PSYBEAM, SOUND_PAN_ATTACKER + createvisualtask AnimTask_ShakeMon2, 2, ANIM_ATTACKER, 1, 0, 10, 1 + createvisualtask AnimTask_BlendColorCycle, 2, F_PAL_ATTACKER, 0, 2, 0, 8, RGB(10, 2, 19) + waitforvisualfinish + playsewithpan SE_M_LEER, SOUND_PAN_ATTACKER + createvisualtask AnimTask_ScaryFace, 5 + delay 13 + waitforvisualfinish + createvisualtask AnimTask_BlendColorCycle, 2, F_PAL_TARGET, 2, 2, 0, 12, RGB(10, 2, 19) @;Deep purple + playsewithpan SE_M_NIGHTMARE, SOUND_PAN_TARGET + createsprite gCurseGhostSpriteTemplate, ANIM_TARGET, 2 + createvisualtask AnimTask_ShakeMon2, 2, ANIM_TARGET, 2, 0, 14, 1 + waitforvisualfinish + clearmonbg ANIM_TARGET + restorebg + waitbgfadein end Move_NOBLE_ROAR: diff --git a/src/battle_anim_effects_1.c b/src/battle_anim_effects_1.c index d8addec01c..b79c94a6a3 100644 --- a/src/battle_anim_effects_1.c +++ b/src/battle_anim_effects_1.c @@ -158,6 +158,7 @@ static void AnimRockPolishSparkle(struct Sprite *); static void AnimPoisonJabProjectile(struct Sprite *); static void AnimNightSlash(struct Sprite *); static void AnimPluck(struct Sprite *); +static void AnimAcrobaticsSlashes(struct Sprite *); const union AnimCmd gPowderParticlesAnimCmds[] = { @@ -3006,6 +3007,61 @@ const struct SpriteTemplate gSeedFlareGreenCirclesTemplate = .callback = AnimPowerAbsorptionOrb }; +const struct SpriteTemplate gSteelBeamBigOrbSpriteTemplate = +{ + .tileTag = ANIM_TAG_STEEL_BEAM, + .paletteTag = ANIM_TAG_STEEL_BEAM, + .oam = &gOamData_AffineOff_ObjNormal_8x8, + .anims = gSolarBeamBigOrbAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = AnimSolarBeamBigOrb, +}; + +const struct SpriteTemplate gSteelBeamSmallOrbSpriteTemplate = +{ + .tileTag = ANIM_TAG_STEEL_BEAM, + .paletteTag = ANIM_TAG_STEEL_BEAM, + .oam = &gOamData_AffineOff_ObjNormal_8x8, + .anims = gSolarBeamSmallOrbAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = AnimSolarBeamSmallOrb, +}; + +const struct SpriteTemplate gAcrobaticsSlashesSpriteTemplate = +{ + .tileTag = ANIM_TAG_WHITE_STREAK, + .paletteTag = ANIM_TAG_WHITE_STREAK, + .oam = &gOamData_AffineDouble_ObjBlend_32x8, + .anims = gRockPolishStreak_AnimCmds, + .images = NULL, + .affineAnims = gRockPolishStreak_AffineAnimCmds, + .callback = AnimAcrobaticsSlashes, +}; + +const struct SpriteTemplate gPsyshockOrbSpriteTemplate = +{ + .tileTag = ANIM_TAG_RED_ORB_2, + .paletteTag = ANIM_TAG_POISON_JAB, + .oam = &gOamData_AffineOff_ObjNormal_8x8, + .anims = gDummySpriteAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = AnimPoisonJabProjectile, +}; + +const struct SpriteTemplate gPsyshockSmokeSpriteTemplate = +{ + .tileTag = ANIM_TAG_GRAY_SMOKE, + .paletteTag = ANIM_TAG_WISP_FIRE, + .oam = &gOamData_AffineOff_ObjNormal_32x32, + .anims = gOctazookaAnimTable, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = AnimSpriteOnMonPos, +}; + // functions static void AnimGrassKnot(struct Sprite *sprite) { @@ -6971,28 +7027,6 @@ void AnimTask_CompressTargetHorizontally(u8 taskId) task->func = AnimTask_CompressTargetStep; } -const struct SpriteTemplate gSteelBeamBigOrbSpriteTemplate = -{ - .tileTag = ANIM_TAG_STEEL_BEAM, - .paletteTag = ANIM_TAG_STEEL_BEAM, - .oam = &gOamData_AffineOff_ObjNormal_8x8, - .anims = gSolarBeamBigOrbAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = AnimSolarBeamBigOrb, -}; - -const struct SpriteTemplate gSteelBeamSmallOrbSpriteTemplate = -{ - .tileTag = ANIM_TAG_STEEL_BEAM, - .paletteTag = ANIM_TAG_STEEL_BEAM, - .oam = &gOamData_AffineOff_ObjNormal_8x8, - .anims = gSolarBeamSmallOrbAnimTable, - .images = NULL, - .affineAnims = gDummySpriteAffineAnimTable, - .callback = AnimSolarBeamSmallOrb, -}; - void AnimTask_CreateSmallSteelBeamOrbs(u8 taskId) { if (--gTasks[taskId].data[0] == -1) @@ -7009,3 +7043,12 @@ void AnimTask_CreateSmallSteelBeamOrbs(u8 taskId) if (gTasks[taskId].data[1] == 15) DestroyAnimVisualTask(taskId); } + +static void AnimAcrobaticsSlashes(struct Sprite *sprite) +{ + int affineAnimNum = Random2() % ARRAY_COUNT(gRockPolishStreak_AffineAnimCmds); + InitSpritePosToAnimTarget(sprite, TRUE); + StartSpriteAffineAnim(sprite, affineAnimNum); + StoreSpriteCallbackInData6(sprite, DestroySpriteAndMatrix); + sprite->callback = RunStoredCallbackWhenAnimEnds; +} diff --git a/src/battle_anim_water.c b/src/battle_anim_water.c index e4044efe65..159b27dec9 100644 --- a/src/battle_anim_water.c +++ b/src/battle_anim_water.c @@ -584,6 +584,28 @@ const struct SpriteTemplate gAquaTailHitSpriteTemplate = .callback = AnimAquaTail, }; +static const union AnimCmd sAnimCmdAnimatedSpark2[] = { + ANIMCMD_FRAME((8 * 8) / (16 * 16) * 0, 8), + ANIMCMD_FRAME((8 * 8) / (16 * 16) * 1, 8), + ANIMCMD_FRAME((8 * 8) / (16 * 16) * 2, 8), + ANIMCMD_JUMP(0) +}; + +static const union AnimCmd *const sAnimCmdTable_AnimatedSpark2[] = { + sAnimCmdAnimatedSpark2, +}; + +const struct SpriteTemplate gSparkBeamSpriteTemplate = +{ + .tileTag = ANIM_TAG_SPARK_2, + .paletteTag = ANIM_TAG_SPARK_2, + .oam = &gOamData_AffineOff_ObjNormal_16x16, + .anims = sAnimCmdTable_AnimatedSpark2, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = AnimToTargetInSinWave, +}; + static void AnimAquaTail(struct Sprite *sprite) { StartSpriteAffineAnim(sprite, gBattleAnimArgs[3]); From 182d1f5b264223290dcf78fd8b3a8266f07421a1 Mon Sep 17 00:00:00 2001 From: Frank DeBlasio <35279583+fdeblasio@users.noreply.github.com> Date: Mon, 6 Nov 2023 10:57:42 -0500 Subject: [PATCH 11/11] Fixed GetNextBall (#3534) --- src/battle_controller_player.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index e1041582e8..48ab691dfa 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -233,7 +233,7 @@ static u16 GetNextBall(u16 ballId) { if (ballId == gBagPockets[BALLS_POCKET].itemSlots[i].itemId) { - ballNext = gBagPockets[BALLS_POCKET].itemSlots[i].itemId; + ballNext = gBagPockets[BALLS_POCKET].itemSlots[i+1].itemId; break; } }