From b9edbb429b7821923e8699463e5b97bd5afd4672 Mon Sep 17 00:00:00 2001 From: Alex <93446519+AlexOn1ine@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:23:22 +0100 Subject: [PATCH] Adds combined pledge move effects (#3336) * Adds combined pledge move effects * added pledge status and various other fixes * leftover * fix triple arrow test tag * pledge moves can not be redirected by absorbing abilities * more pledge changes * remove duplicate test * Stab boost, Rainbow anim and new SeaOfFire anim * leftover --------- Co-authored-by: Bassoonian --- asm/macros/battle_script.inc | 11 + data/battle_anim_scripts.s | 78 +++- data/battle_scripts_1.s | 86 ++++- graphics/battle_anims/backgrounds/rainbow.bin | Bin 0 -> 2048 bytes graphics/battle_anims/backgrounds/rainbow.pal | 19 + graphics/battle_anims/backgrounds/rainbow.png | Bin 0 -> 20960 bytes include/battle.h | 4 + include/battle_scripts.h | 7 + include/battle_util.h | 2 +- include/config/battle.h | 2 +- include/constants/battle.h | 4 + include/constants/battle_anim.h | 4 + include/constants/battle_string_ids.h | 11 +- include/graphics.h | 5 + src/battle_ai_main.c | 1 + src/battle_anim.c | 3 + src/battle_anim_fire.c | 12 + src/battle_main.c | 3 + src/battle_message.c | 18 + src/battle_script_commands.c | 128 ++++++- src/battle_util.c | 129 ++++++- src/data/battle_anim.h | 1 + src/graphics.c | 5 + test/battle/ai.c | 1 - test/battle/damage_formula.c | 8 +- test/battle/move_effect/pledge.c | 352 ++++++++++++++++++ 26 files changed, 872 insertions(+), 22 deletions(-) create mode 100644 graphics/battle_anims/backgrounds/rainbow.bin create mode 100644 graphics/battle_anims/backgrounds/rainbow.pal create mode 100644 graphics/battle_anims/backgrounds/rainbow.png create mode 100644 test/battle/move_effect/pledge.c diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index cc06b4bf41..a279ba7569 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1380,6 +1380,17 @@ callnative BS_TryRelicSong .endm + .macro setpledge jumpInstr:req + callnative BS_SetPledge + .4byte \jumpInstr + .endm + + .macro setpledgestatus battler:req sidestatus:req + callnative BS_SetPledgeStatus + .byte \battler + .4byte \sidestatus + .endm + .macro setzeffect callnative BS_SetZEffect .endm diff --git a/data/battle_anim_scripts.s b/data/battle_anim_scripts.s index be0ae0adbb..3a1cd720c3 100644 --- a/data/battle_anim_scripts.s +++ b/data/battle_anim_scripts.s @@ -1010,6 +1010,9 @@ gBattleAnims_General:: .4byte General_DynamaxGrowth @ B_ANIM_DYNAMAX_GROWTH .4byte General_SetWeather @ B_ANIM_MAX_SET_WEATHER .4byte General_SyrupBombSpeedDrop @ B_ANIM_SYRUP_BOMB_SPEED_DROP + .4byte General_Rainbow @ B_ANIM_RAINBOW + .4byte General_SeaOfFire @ B_ANIM_SEA_OF_FIRE + .4byte General_Swamp @ B_ANIM_SWAMP .align 2 gBattleAnims_Special:: @@ -27060,6 +27063,10 @@ General_HangedOn: end General_Rain: + call RainDrops + end + +RainDrops: loadspritegfx ANIM_TAG_RAIN_DROPS playsewithpan SE_M_RAIN_DANCE, SOUND_PAN_ATTACKER createvisualtask AnimTask_BlendBattleAnimPal, 10, (F_PAL_BG | F_PAL_BATTLERS), 2, 0, 4, RGB_BLACK @@ -27070,7 +27077,7 @@ General_Rain: waitforvisualfinish createvisualtask AnimTask_BlendBattleAnimPal, 10, (F_PAL_BG | F_PAL_BATTLERS), 2, 4, 0, RGB_BLACK waitforvisualfinish - end + return General_Sun: goto Move_SUNNY_DAY @@ -27515,6 +27522,75 @@ General_AffectionHangedOn_3Hearts: General_SaltCureDamage:: goto Status_Freeze +General_Rainbow:: + call RainDrops + delay 30 + loadspritegfx ANIM_TAG_SUNLIGHT + createvisualtask AnimTask_BlendBattleAnimPal, 10, (F_PAL_BG | F_PAL_BATTLERS), 1, 0, 6, RGB_WHITE + waitforvisualfinish + panse_adjustnone SE_M_PETAL_DANCE, SOUND_PAN_ATTACKER, SOUND_PAN_TARGET, +1, 0 + call SunnyDayLightRay + call SunnyDayLightRay + call SunnyDayLightRay + waitforvisualfinish + createvisualtask AnimTask_BlendBattleAnimPal, 10, (F_PAL_BG | F_PAL_BATTLERS), 1, 6, 0, RGB_WHITE + waitforvisualfinish + delay 30 + fadetobg BG_RAINBOW + panse_adjustnone SE_M_ABSORB_2, SOUND_PAN_ATTACKER, SOUND_PAN_TARGET, +1, 0 + delay 90 + blendoff + restorebg + waitbgfadein + clearmonbg ANIM_ATK_PARTNER + end + +General_SeaOfFire:: + loadspritegfx ANIM_TAG_SMALL_EMBER + monbg ANIM_DEF_PARTNER + splitbgprio ANIM_TARGET + playsewithpan SE_M_SACRED_FIRE2, SOUND_PAN_TARGET + call SeaOfFireTwisterDos + delay 3 + call SeaOfFireTwisterTres + waitforvisualfinish + clearmonbg ANIM_DEF_PARTNER + blendoff + end + +SeaOfFireTwisterDos: + createsprite gTwisterEmberSpriteTemplate, ANIM_TARGET, 2, 45, 90, 5, 70, 30 + delay 2 + createsprite gTwisterEmberSpriteTemplate, ANIM_TARGET, 2, 50, 85, 6, 60, 30 + delay 1 + createsprite gTwisterEmberSpriteTemplate, ANIM_TARGET, 2, 47, 77, 7, 60, 30 + delay 2 + createsprite gTwisterEmberSpriteTemplate, ANIM_TARGET, 2, 40, 86, 8, 50, 30 + delay 3 + createsprite gTwisterEmberSpriteTemplate, ANIM_TARGET, 2, 42, 82, 7, 45, 30 + delay 1 + createsprite gTwisterEmberSpriteTemplate, ANIM_TARGET, 2, 47, 83, 5, 38, 30 + delay 2 + return + +SeaOfFireTwisterTres: + createsprite gTwisterEmberSpriteTemplate, ANIM_TARGET, 2, 45, 90, 3, 45, 30 + delay 2 + createsprite gTwisterEmberSpriteTemplate, ANIM_TARGET, 2, 50, 85, 4, 39, 30 + delay 1 + createsprite gTwisterEmberSpriteTemplate, ANIM_TARGET, 2, 47, 77, 5, 39, 30 + delay 2 + createsprite gTwisterEmberSpriteTemplate, ANIM_TARGET, 2, 40, 86, 6, 32, 30 + delay 3 + createsprite gTwisterEmberSpriteTemplate, ANIM_TARGET, 2, 42, 82, 5, 27, 30 + delay 1 + createsprite gTwisterEmberSpriteTemplate, ANIM_TARGET, 2, 47, 83, 3, 24, 30 + delay 2 + return + +General_Swamp:: @ To do + goto Move_HAZE + SnatchMoveTrySwapFromSubstitute: createvisualtask AnimTask_IsAttackerBehindSubstitute, 2 jumprettrue SnatchMoveSwapSubstituteForMon diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index 6581f5ca38..2e5494a03a 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -231,7 +231,7 @@ gBattleScriptsForMoveEffects:: .4byte BattleScript_EffectCalmMind @ EFFECT_CALM_MIND .4byte BattleScript_EffectDragonDance @ EFFECT_DRAGON_DANCE .4byte BattleScript_EffectCamouflage @ EFFECT_CAMOUFLAGE - .4byte BattleScript_EffectHit @ EFFECT_PLEDGE + .4byte BattleScript_EffectPledge @ EFFECT_PLEDGE .4byte BattleScript_EffectFling @ EFFECT_FLING .4byte BattleScript_EffectNaturalGift @ EFFECT_NATURAL_GIFT .4byte BattleScript_EffectWakeUpSlap @ EFFECT_WAKE_UP_SLAP @@ -472,6 +472,90 @@ BattleScript_EffectMatchaGotcha:: setmoveeffect MOVE_EFFECT_BURN goto BattleScript_EffectAbsorb +BattleScript_EffectPledge:: + attackcanceler + setpledge BattleScript_HitFromAccCheck + attackstring + pause B_WAIT_TIME_MED + ppreduce + printstring STRINGID_WAITINGFORPARTNERSMOVE + waitmessage B_WAIT_TIME_LONG + goto BattleScript_MoveEnd + +BattleScript_EffectCombinedPledge_Water:: + call BattleScript_EffectHit_Pledge + setpledgestatus BS_ATTACKER, SIDE_STATUS_RAINBOW + pause B_WAIT_TIME_SHORTEST + printstring STRINGID_ARAINBOWAPPEAREDONSIDE + waitmessage B_WAIT_TIME_LONG + playanimation BS_ATTACKER, B_ANIM_RAINBOW + waitanimation + goto BattleScript_MoveEnd + +BattleScript_TheRainbowDisappeared:: + printstring STRINGID_THERAINBOWDISAPPEARED + waitmessage B_WAIT_TIME_LONG + end2 + +BattleScript_EffectCombinedPledge_Fire:: + call BattleScript_EffectHit_Pledge + setpledgestatus BS_TARGET, SIDE_STATUS_SEA_OF_FIRE + pause B_WAIT_TIME_SHORTEST + printstring STRINGID_SEAOFFIREENVELOPEDSIDE + waitmessage B_WAIT_TIME_LONG + playanimation BS_TARGET, B_ANIM_SEA_OF_FIRE + waitanimation + goto BattleScript_MoveEnd + +BattleScript_HurtByTheSeaOfFire:: + printstring STRINGID_HURTBYTHESEAOFFIRE + waitmessage B_WAIT_TIME_LONG + goto BattleScript_DoTurnDmg + +BattleScript_TheSeaOfFireDisappeared:: + printstring STRINGID_THESEAOFFIREDISAPPEARED + waitmessage B_WAIT_TIME_LONG + end2 + +BattleScript_EffectCombinedPledge_Grass:: + call BattleScript_EffectHit_Pledge + setpledgestatus BS_TARGET, SIDE_STATUS_SWAMP + pause B_WAIT_TIME_SHORTEST + printstring STRINGID_SWAMPENVELOPEDSIDE + waitmessage B_WAIT_TIME_LONG + playanimation BS_TARGET, B_ANIM_SWAMP + waitanimation + goto BattleScript_MoveEnd + +BattleScript_TheSwampDisappeared:: + printstring STRINGID_THESWAMPDISAPPEARED + waitmessage B_WAIT_TIME_LONG + end2 + +BattleScript_EffectHit_Pledge:: + pause B_WAIT_TIME_MED + printstring STRINGID_THETWOMOVESBECOMEONE + waitmessage B_WAIT_TIME_LONG + accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE + ppreduce + critcalc + damagecalc + adjustdamage + attackanimation + waitanimation + effectivenesssound + hitanimation BS_TARGET + waitstate + healthbarupdate BS_TARGET + datahpupdate BS_TARGET + critmessage + waitmessage B_WAIT_TIME_LONG + resultmessage + waitmessage B_WAIT_TIME_LONG + seteffectwithchance + tryfaintmon BS_TARGET + return + BattleScript_EffectSaltCure: call BattleScript_EffectHit_Ret tryfaintmon BS_TARGET diff --git a/graphics/battle_anims/backgrounds/rainbow.bin b/graphics/battle_anims/backgrounds/rainbow.bin new file mode 100644 index 0000000000000000000000000000000000000000..770389abf4de20a60a8f56de39e119fd115b4124 GIT binary patch literal 2048 zcmeH_RZx{t7)4Q3L`6|luwZRaxMC1u3s{6L*m>>l?(XhxvAesw8@s!^^WJ}~8OCQ{ znDuaGzJ1ornf)Cb;t&_xe-e-QB*2b@*vGhImRX5NOcESON-`Wt9)lD(k&;xT#yLi2 z^<79qTGEl847lP3=r7#yAS0RZ{A>0nxB6a~$c#5x$VxV{y5`9`$KJLmJVTCWhaXW;CY-Eont-+R&DEw5J0d=|pF`(3Ng< zrw2XhWuZ5H=u1EPGk}4HKZwB$VJO2G&Im>_iqVW=EaMo@1ST?x$xLA?)0oZ-W-^P} z%waB3%rpG?EMOsvSj-ZZvW(@dU?rM z&jTLvh{rtPDbIM$3tsYy*Sz5^?|9D#KJtmrd@=m5eB(Pdilex)RXoL40@*2{?3GA~ zl|&9os$_Cha;1=yQYw{F%O7XCD2-+NW~Eg+rB?>I%FXhzBzJizqcX`;UNR}Oyp=^+ zl}*{@BVYN+UpW+@oXVx#hMz}yl~4IqK+&c^1z8AIK^0PA6;V-zC{$qzS1}b=36)eS cl~x&*RfHl{&hV{oh$Xgx*al)7_#X}Y05)U$ga7~l literal 0 HcmV?d00001 diff --git a/graphics/battle_anims/backgrounds/rainbow.pal b/graphics/battle_anims/backgrounds/rainbow.pal new file mode 100644 index 0000000000..9b62b7b25b --- /dev/null +++ b/graphics/battle_anims/backgrounds/rainbow.pal @@ -0,0 +1,19 @@ +JASC-PAL +0100 +16 +109 92 75 +255 255 255 +255 107 122 +255 200 102 +255 255 107 +143 255 160 +107 255 255 +107 129 255 +220 114 255 +199 255 250 +232 240 248 +224 232 240 +208 224 240 +191 202 224 +183 189 202 +157 166 181 diff --git a/graphics/battle_anims/backgrounds/rainbow.png b/graphics/battle_anims/backgrounds/rainbow.png new file mode 100644 index 0000000000000000000000000000000000000000..bd41645b3571cc8bcea41533b053625e25d964e8 GIT binary patch literal 20960 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 hO-%qQ00008000000002eQaiLogicId = 0; AI_THINKING_STRUCT->movesetIndex = 0; flags = AI_THINKING_STRUCT->aiFlags; + while (flags != 0) { if (flags & 1) diff --git a/src/battle_anim.c b/src/battle_anim.c index c46c5cee4c..d2216e5155 100644 --- a/src/battle_anim.c +++ b/src/battle_anim.c @@ -278,6 +278,9 @@ void LaunchBattleAnimation(u32 animType, u32 animId) case B_ANIM_PRIMAL_REVERSION: case B_ANIM_ULTRA_BURST: case B_ANIM_GULP_MISSILE: + case B_ANIM_RAINBOW: + case B_ANIM_SEA_OF_FIRE: + case B_ANIM_SWAMP: sAnimHideHpBoxes = TRUE; break; default: diff --git a/src/battle_anim_fire.c b/src/battle_anim_fire.c index 9a68f929bb..3a770a7c8c 100644 --- a/src/battle_anim_fire.c +++ b/src/battle_anim_fire.c @@ -527,6 +527,18 @@ const struct SpriteTemplate gSpacialRendBladesTemplate2 = .callback = AnimFireSpread }; +// Sea of Fire +const struct SpriteTemplate gTwisterEmberSpriteTemplate = +{ + .tileTag = ANIM_TAG_SMALL_EMBER, + .paletteTag = ANIM_TAG_SMALL_EMBER, + .oam = &gOamData_AffineOff_ObjNormal_32x32, + .anims = gAnims_BasicFire, + .images = NULL, + .affineAnims = gDummySpriteAffineAnimTable, + .callback = AnimMoveTwisterParticle, +}; + static void AnimLavaPlumeOrbitScatter(struct Sprite *sprite) { sprite->x = GetBattlerSpriteCoord(gBattleAnimAttacker, 2); diff --git a/src/battle_main.c b/src/battle_main.c index a9d1f91add..28484a7061 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4651,6 +4651,9 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, u32 holdEffect) if (gBattleMons[battler].status1 & STATUS1_PARALYSIS && ability != ABILITY_QUICK_FEET) speed /= B_PARALYSIS_SPEED >= GEN_7 ? 2 : 4; + if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SWAMP) + speed /= 4; + return speed; } diff --git a/src/battle_message.c b/src/battle_message.c index f2052e6919..6763fe32ba 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -823,9 +823,27 @@ static const u8 sText_TargetIsHurtBySaltCure[] = _("{B_DEF_NAME_WITH_PREFIX} is static const u8 sText_OpportunistCopied[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} copied its\nopponent's stat changes!"); static const u8 sText_TargetCoveredInStickyCandySyrup[] = _("{B_DEF_NAME_WITH_PREFIX} got covered\nin sticky syrup!"); static const u8 sText_ZeroToHeroTransformation[] = _("{B_ATK_NAME_WITH_PREFIX} underwent a heroic\ntransformation!"); +static const u8 sText_TheTwoMovesBecomeOne[] = _("The two moves become one!\nIt's a combined move!{PAUSE 16}"); +static const u8 sText_ARainbowAppearedOnSide[] = _("A rainbow appeared in the sky\non {B_ATK_TEAM2} team's side!"); +static const u8 sText_TheRainbowDisappeared[] = _("The rainbow on {B_ATK_TEAM2}\nside disappeared!"); +static const u8 sText_WaitingForPartnersMove[] = _("{B_ATK_NAME_WITH_PREFIX} is waiting\nfor {B_ATK_PARTNER_NAME}'s move…{PAUSE 16}"); +static const u8 sText_SeaOfFireEnvelopedSide[] = _("A sea of fire enveloped\n{B_DEF_TEAM2} team!"); +static const u8 sText_HurtByTheSeaOfFire[] = _("{B_ATK_TEAM1} {B_ATK_NAME_WITH_PREFIX} was hurt\nby the sea of fire!"); +static const u8 sText_TheSeaOfFireDisappeared[] = _("The sea of fire around {B_ATK_TEAM2}\nteam disappeared!"); +static const u8 sText_SwampEnvelopedSide[] = _("A swamp enveloped\n{B_DEF_TEAM2} team!"); +static const u8 sText_TheSwampDisappeared[] = _("The swamp around {B_ATK_TEAM2}\nteam disappeared!"); const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] = { + [STRINGID_THESWAMPDISAPPEARED - BATTLESTRINGS_TABLE_START] = sText_TheSwampDisappeared, + [STRINGID_SWAMPENVELOPEDSIDE - BATTLESTRINGS_TABLE_START] = sText_SwampEnvelopedSide, + [STRINGID_THESEAOFFIREDISAPPEARED - BATTLESTRINGS_TABLE_START] = sText_TheSeaOfFireDisappeared, + [STRINGID_HURTBYTHESEAOFFIRE - BATTLESTRINGS_TABLE_START] = sText_HurtByTheSeaOfFire, + [STRINGID_SEAOFFIREENVELOPEDSIDE - BATTLESTRINGS_TABLE_START] = sText_SeaOfFireEnvelopedSide, + [STRINGID_WAITINGFORPARTNERSMOVE - BATTLESTRINGS_TABLE_START] = sText_WaitingForPartnersMove, + [STRINGID_THERAINBOWDISAPPEARED - BATTLESTRINGS_TABLE_START] = sText_TheRainbowDisappeared, + [STRINGID_ARAINBOWAPPEAREDONSIDE - BATTLESTRINGS_TABLE_START] = sText_ARainbowAppearedOnSide, + [STRINGID_THETWOMOVESBECOMEONE - BATTLESTRINGS_TABLE_START] = sText_TheTwoMovesBecomeOne, [STRINGID_ZEROTOHEROTRANSFORMATION - BATTLESTRINGS_TABLE_START] = sText_ZeroToHeroTransformation, [STRINGID_MOVEBLOCKEDBYDYNAMAX - BATTLESTRINGS_TABLE_START] = sText_MoveBlockedByDynamax, [STRINGID_OPPORTUNISTCOPIED - BATTLESTRINGS_TABLE_START] = sText_OpportunistCopied, diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 9268e99ca2..bf63d0c1b2 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -2076,7 +2076,9 @@ END: } if (gSpecialStatuses[gBattlerAttacker].gemBoost && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) - && gBattleMons[gBattlerAttacker].item) + && gBattleMons[gBattlerAttacker].item + && gBattleMoves[gCurrentMove].effect != EFFECT_PLEDGE + && gCurrentMove != MOVE_STRUGGLE) { BattleScriptPushCursor(); gBattlescriptCurrInstr = BattleScript_GemActivates; @@ -3598,8 +3600,8 @@ void SetMoveEffect(bool32 primary, u32 certain) break; case MOVE_EFFECT_TRIPLE_ARROWS: { - u8 randomLowerDefenseChance = RandomPercentage(RNG_TRIPLE_ARROWS_DEFENSE_DOWN, CalcSecondaryEffectChance(gBattlerAttacker, 50)); - u8 randomFlinchChance = RandomPercentage(RNG_TRIPLE_ARROWS_FLINCH, CalcSecondaryEffectChance(gBattlerAttacker, 30)); + u8 randomLowerDefenseChance = RandomPercentage(RNG_TRIPLE_ARROWS_DEFENSE_DOWN, CalcSecondaryEffectChance(gBattlerAttacker, 50, EFFECT_DEFENSE_DOWN_HIT)); + u8 randomFlinchChance = RandomPercentage(RNG_TRIPLE_ARROWS_FLINCH, CalcSecondaryEffectChance(gBattlerAttacker, 30, EFFECT_FLINCH_HIT)); if (randomFlinchChance && battlerAbility != ABILITY_INNER_FOCUS && GetBattlerTurnOrderNum(gEffectBattler) > gCurrentTurnActionNumber) gBattleMons[gEffectBattler].status2 |= sStatusFlagsForMoveEffects[MOVE_EFFECT_FLINCH]; @@ -3639,7 +3641,7 @@ static void Cmd_seteffectwithchance(void) { CMD_ARGS(); - u32 percentChance = CalcSecondaryEffectChance(gBattlerAttacker, gBattleMoves[gCurrentMove].secondaryEffectChance); + u32 percentChance = CalcSecondaryEffectChance(gBattlerAttacker, gBattleMoves[gCurrentMove].secondaryEffectChance, gBattleMoves[gCurrentMove].effect); if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && gBattleScripting.moveEffect) @@ -8322,8 +8324,6 @@ static void CourtChangeSwapSideStatuses(void) struct SideTimer *sideTimerOpp = &gSideTimers[B_SIDE_OPPONENT]; u32 temp; - // TODO: add Pledge-related effects - // Swap timers and statuses COURTCHANGE_SWAP(SIDE_STATUS_REFLECT, reflectTimer, temp) COURTCHANGE_SWAP(SIDE_STATUS_LIGHTSCREEN, lightscreenTimer, temp) @@ -8339,6 +8339,10 @@ static void CourtChangeSwapSideStatuses(void) COURTCHANGE_SWAP(SIDE_STATUS_STICKY_WEB, stickyWebAmount, temp); COURTCHANGE_SWAP(SIDE_STATUS_STEELSURGE, steelsurgeAmount, temp); COURTCHANGE_SWAP(SIDE_STATUS_DAMAGE_NON_TYPES, damageNonTypesTimer, temp); + // Track Pledge effect side + COURTCHANGE_SWAP(SIDE_STATUS_RAINBOW, rainbowTimer, temp); + COURTCHANGE_SWAP(SIDE_STATUS_SEA_OF_FIRE, seaOfFireTimer, temp); + COURTCHANGE_SWAP(SIDE_STATUS_SWAMP, swampTimer, temp); // Change battler IDs of swapped effects. Needed for the correct string when they expire // E.g. "Foe's Reflect wore off!" @@ -16285,3 +16289,115 @@ void BS_TryRelicSong(void) else gBattlescriptCurrInstr = cmd->nextInstr; } + + +void BS_SetPledge(void) +{ + NATIVE_ARGS(const u8 *jumpInstr); + + u32 partner = BATTLE_PARTNER(gBattlerAttacker); + u32 partnerMove = gBattleMons[partner].moves[gBattleStruct->chosenMovePositions[partner]]; + u32 i = 0; + u32 k = 0; + + if (gBattleStruct->pledgeMove) + { + PrepareStringBattle(STRINGID_USEDMOVE, gBattlerAttacker); + gHitMarker |= HITMARKER_ATTACKSTRING_PRINTED; + + if ((gCurrentMove == MOVE_GRASS_PLEDGE && partnerMove == MOVE_WATER_PLEDGE) + || (gCurrentMove == MOVE_WATER_PLEDGE && partnerMove == MOVE_GRASS_PLEDGE)) + { + gCurrentMove = MOVE_GRASS_PLEDGE; + gBattlescriptCurrInstr = BattleScript_EffectCombinedPledge_Grass; + } + else if ((gCurrentMove == MOVE_FIRE_PLEDGE && partnerMove == MOVE_GRASS_PLEDGE) + || (gCurrentMove == MOVE_GRASS_PLEDGE && partnerMove == MOVE_FIRE_PLEDGE)) + { + gCurrentMove = MOVE_FIRE_PLEDGE; + gBattlescriptCurrInstr = BattleScript_EffectCombinedPledge_Fire; + } + else if ((gCurrentMove == MOVE_WATER_PLEDGE && partnerMove == MOVE_FIRE_PLEDGE) + || (gCurrentMove == MOVE_FIRE_PLEDGE && partnerMove == MOVE_WATER_PLEDGE)) + { + gCurrentMove = MOVE_WATER_PLEDGE; + gBattlescriptCurrInstr = BattleScript_EffectCombinedPledge_Water; + } + + gBattleCommunication[MSG_DISPLAY] = 0; + } + else if ((gChosenActionByBattler[partner] == B_ACTION_USE_MOVE) + && gBattleTypeFlags & BATTLE_TYPE_DOUBLE + && IsBattlerAlive(partner) + && gCurrentMove != partnerMove + && gBattleMoves[partnerMove].effect == EFFECT_PLEDGE) + { + u32 currPledgeUser = 0; + u32 newTurnOrder[] = {0xFF, 0xFF}; + + for (i = 0; i < gBattlersCount; i++) + { + if (gBattlerByTurnOrder[i] == gBattlerAttacker) + { + currPledgeUser = i + 1; // Current battler going after attacker + break; + } + } + for (i = currPledgeUser; i < gBattlersCount; i++) + { + if (gBattlerByTurnOrder[i] != partner) + { + newTurnOrder[k] = gBattlerByTurnOrder[i]; + k++; + } + } + + gBattlerByTurnOrder[currPledgeUser] = partner; + currPledgeUser++; + + for (i = 0; newTurnOrder[i] != 0xFF && i < 2; i++) + { + gBattlerByTurnOrder[currPledgeUser] = newTurnOrder[i]; + currPledgeUser++; + } + + gBattleStruct->pledgeMove = TRUE; + gBattleScripting.battler = partner; + gBattlescriptCurrInstr = cmd->nextInstr; + } + else + { + gBattlescriptCurrInstr = cmd->jumpInstr; + + } +} + +void BS_SetPledgeStatus(void) +{ + NATIVE_ARGS(u8 battler, u32 sideStatus); + + u32 battler = GetBattlerForBattleScript(cmd->battler); + u32 side = GetBattlerSide(battler); + + gBattleStruct->pledgeMove = FALSE; + if (!(gSideStatuses[side] & cmd->sideStatus)) + { + gSideStatuses[side] |= cmd->sideStatus; + + switch (cmd->sideStatus) + { + case SIDE_STATUS_RAINBOW: + gSideTimers[side].rainbowTimer = 4; + break; + case SIDE_STATUS_SEA_OF_FIRE: + gSideTimers[side].seaOfFireTimer = 4; + break; + case SIDE_STATUS_SWAMP: + gSideTimers[side].swampTimer = 4; + } + + gBattlescriptCurrInstr = cmd->nextInstr; + } + else + gBattlescriptCurrInstr = BattleScript_MoveEnd; +} diff --git a/src/battle_util.c b/src/battle_util.c index fcd6de48b7..c4fb6da0a0 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -373,6 +373,7 @@ void HandleAction_UseMove(void) || (GetBattlerAbility(battler) == ABILITY_STORM_DRAIN && moveType == TYPE_WATER)) && GetBattlerTurnOrderNum(battler) < var && gBattleMoves[gCurrentMove].effect != EFFECT_SNIPE_SHOT + && gBattleMoves[gCurrentMove].effect != EFFECT_PLEDGE && (GetBattlerAbility(gBattlerAttacker) != ABILITY_PROPELLER_TAIL || GetBattlerAbility(gBattlerAttacker) != ABILITY_STALWART)) { @@ -865,7 +866,7 @@ void HandleAction_ActionFinished(void) gBattleResources->battleScriptsStack->size = 0; gBattleStruct->dynamax.usingMaxMove[gBattlerAttacker] = 0; - if (B_RECALC_TURN_AFTER_ACTIONS >= GEN_8 && !afterYouActive) + if (B_RECALC_TURN_AFTER_ACTIONS >= GEN_8 && !afterYouActive && !gBattleStruct->pledgeMove) { // i starts at `gCurrentTurnActionNumber` because we don't want to recalculate turn order for mon that have already // taken action. It's been previously increased, which we want in order to not recalculate the turn of the mon that just finished its action @@ -1926,6 +1927,9 @@ enum ENDTURN_RETALIATE, ENDTURN_WEATHER_FORM, ENDTURN_STATUS_HEAL, + ENDTURN_RAINBOW, + ENDTURN_SEA_OF_FIRE, + ENDTURN_SWAMP, ENDTURN_FIELD_COUNT, }; @@ -2433,6 +2437,95 @@ u8 DoFieldEndTurnEffects(void) } gBattleStruct->turnCountersTracker++; break; + case ENDTURN_RAINBOW: + while (gBattleStruct->turnSideTracker < 2) + { + side = gBattleStruct->turnSideTracker; + if (gSideStatuses[side] & SIDE_STATUS_RAINBOW) + { + for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++) + { + if (GetBattlerSide(gBattlerAttacker) == side) + break; + } + + if (gSideTimers[side].rainbowTimer > 0 && --gSideTimers[side].rainbowTimer == 0) + { + gSideStatuses[side] &= ~SIDE_STATUS_RAINBOW; + BattleScriptExecute(BattleScript_TheRainbowDisappeared); + effect++; + } + } + gBattleStruct->turnSideTracker++; + if (effect != 0) + break; + } + if (!effect) + { + gBattleStruct->turnCountersTracker++; + gBattleStruct->turnSideTracker = 0; + } + break; + case ENDTURN_SEA_OF_FIRE: + while (gBattleStruct->turnSideTracker < 2) + { + side = gBattleStruct->turnSideTracker; + + if (gSideStatuses[side] & SIDE_STATUS_SEA_OF_FIRE) + { + for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++) + { + if (GetBattlerSide(gBattlerAttacker) == side) + break; + } + + if (gSideTimers[side].seaOfFireTimer > 0 && --gSideTimers[side].seaOfFireTimer == 0) + { + gSideStatuses[side] &= ~SIDE_STATUS_SEA_OF_FIRE; + BattleScriptExecute(BattleScript_TheSeaOfFireDisappeared); + effect++; + } + } + gBattleStruct->turnSideTracker++; + if (effect != 0) + break; + } + if (!effect) + { + gBattleStruct->turnCountersTracker++; + gBattleStruct->turnSideTracker = 0; + } + break; + case ENDTURN_SWAMP: + while (gBattleStruct->turnSideTracker < 2) + { + side = gBattleStruct->turnSideTracker; + + if (gSideStatuses[side] & SIDE_STATUS_SWAMP) + { + for (gBattlerAttacker = 0; gBattlerAttacker < gBattlersCount; gBattlerAttacker++) + { + if (GetBattlerSide(gBattlerAttacker) == side) + break; + } + + if (gSideTimers[side].swampTimer > 0 && --gSideTimers[side].swampTimer == 0) + { + gSideStatuses[side] &= ~SIDE_STATUS_SWAMP; + BattleScriptExecute(BattleScript_TheSwampDisappeared); + effect++; + } + } + gBattleStruct->turnSideTracker++; + if (effect != 0) + break; + } + if (!effect) + { + gBattleStruct->turnCountersTracker++; + gBattleStruct->turnSideTracker = 0; + } + break; case ENDTURN_FIELD_COUNT: effect++; break; @@ -2483,6 +2576,7 @@ enum ENDTURN_SALT_CURE, ENDTURN_SYRUP_BOMB, ENDTURN_DYNAMAX, + ENDTURN_SEA_OF_FIRE_DAMAGE, ENDTURN_BATTLER_COUNT }; @@ -3069,6 +3163,17 @@ u8 DoBattlerEndTurnEffects(void) } gBattleStruct->turnEffectsTracker++; break; + case ENDTURN_SEA_OF_FIRE_DAMAGE: + if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SEA_OF_FIRE) + { + gBattleMoveDamage = gBattleMons[battler].maxHP / 8; + BtlController_EmitStatusAnimation(battler, BUFFER_A, FALSE, STATUS1_BURN); + MarkBattlerForControllerExec(battler); + BattleScriptExecute(BattleScript_HurtByTheSeaOfFire); + effect++; + } + gBattleStruct->turnEffectsTracker++; + break; case ENDTURN_BATTLER_COUNT: // done gBattleStruct->turnEffectsTracker = 0; gBattleStruct->turnEffectsBattlerId++; @@ -7610,6 +7715,8 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn) u16 ability = GetBattlerAbility(gBattlerAttacker); if (B_SERENE_GRACE_BOOST >= GEN_5 && ability == ABILITY_SERENE_GRACE) atkHoldEffectParam *= 2; + if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_RAINBOW && gCurrentMove != MOVE_SECRET_POWER) + atkHoldEffectParam *= 2; if (gBattleMoveDamage != 0 // Need to have done damage && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT) && TARGET_TURN_DAMAGED @@ -8525,7 +8632,8 @@ static inline u32 CalcMoveBasePower(u32 move, u32 battlerAtk, u32 battlerDef, u3 switch (gBattleMoves[move].effect) { case EFFECT_PLEDGE: - // todo + if (gBattleStruct->pledgeMove) + basePower = 150; break; case EFFECT_FLING: basePower = GetFlingPowerFromItemId(gBattleMons[battlerAtk].item); @@ -9504,7 +9612,9 @@ static inline uq4_12_t GetParentalBondModifier(u32 battlerAtk) static inline uq4_12_t GetSameTypeAttackBonusModifier(u32 battlerAtk, u32 moveType, u32 move, u32 abilityAtk) { - if (!IS_BATTLER_OF_TYPE(battlerAtk, moveType) || move == MOVE_STRUGGLE || move == MOVE_NONE) + if (gBattleStruct->pledgeMove && IS_BATTLER_OF_TYPE(BATTLE_PARTNER(battlerAtk), moveType)) + return (abilityAtk == ABILITY_ADAPTABILITY) ? UQ_4_12(2.0) : UQ_4_12(1.5); + else if (!IS_BATTLER_OF_TYPE(battlerAtk, moveType) || move == MOVE_STRUGGLE || move == MOVE_NONE) return UQ_4_12(1.0); return (abilityAtk == ABILITY_ADAPTABILITY) ? UQ_4_12(2.0) : UQ_4_12(1.5); } @@ -11122,9 +11232,17 @@ bool32 AreBattlersOfSameGender(u32 battler1, u32 battler2) return (gender1 != MON_GENDERLESS && gender2 != MON_GENDERLESS && gender1 == gender2); } -u32 CalcSecondaryEffectChance(u32 battler, u8 secondaryEffectChance) +u32 CalcSecondaryEffectChance(u32 battler, u8 secondaryEffectChance, u16 moveEffect) { - if (GetBattlerAbility(battler) == ABILITY_SERENE_GRACE) + bool8 hasSereneGrace = (GetBattlerAbility(battler) == ABILITY_SERENE_GRACE); + bool8 hasRainbow = (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_RAINBOW) != 0; + + if (hasRainbow && hasSereneGrace && moveEffect == EFFECT_FLINCH_HIT) + return secondaryEffectChance *= 2; + + if (hasSereneGrace) + secondaryEffectChance *= 2; + if (hasRainbow && moveEffect != EFFECT_SECRET_POWER) secondaryEffectChance *= 2; return secondaryEffectChance; @@ -11166,4 +11284,3 @@ u8 GetBattlerType(u32 battler, u8 typeIndex) return types[typeIndex]; } - diff --git a/src/data/battle_anim.h b/src/data/battle_anim.h index 880b98efce..239e655918 100644 --- a/src/data/battle_anim.h +++ b/src/data/battle_anim.h @@ -1996,4 +1996,5 @@ const struct BattleAnimBackground gBattleAnimBackgroundTable[] = [BG_STEEL_BEAM_OPPONENT] = {gBattleAnimBgImage_Highspeed, gBattleAnimBgPalette_SteelBeam, gBattleAnimBgTilemap_HighspeedOpponent}, [BG_STEEL_BEAM_PLAYER] = {gBattleAnimBgImage_Highspeed, gBattleAnimBgPalette_SteelBeam, gBattleAnimBgTilemap_HighspeedPlayer}, [BG_CHLOROBLAST] = {gBattleAnimBgImage_HydroCannon, gBattleAnimBgPalette_Chloroblast, gBattleAnimBgTilemap_HydroCannon}, + [BG_RAINBOW] = {gBattleAnimBgImage_Rainbow, gBattleAnimBGPalette_Rainbow, gBattleAnimBgTilemap_Rainbow}, }; diff --git a/src/graphics.c b/src/graphics.c index 45c066ad26..c1049d63a1 100644 --- a/src/graphics.c +++ b/src/graphics.c @@ -1606,6 +1606,11 @@ const u32 gBattleAnimSpritePal_Slash2[] = INCBIN_U32("graphics/battle_anims/spri const u32 gBattleAnimSpriteGfx_WhiteShadow[] = INCBIN_U32("graphics/battle_anims/sprites/white_shadow.4bpp.lz"); const u32 gBattleAnimSpritePal_WhiteShadow[] = INCBIN_U32("graphics/battle_anims/sprites/white_shadow.gbapal.lz"); +// Pledge Effect field status - Rainbow +const u32 gBattleAnimBgImage_Rainbow[] = INCBIN_U32("graphics/battle_anims/backgrounds/rainbow.4bpp.lz"); +const u32 gBattleAnimBGPalette_Rainbow[] = INCBIN_U32("graphics/battle_anims/backgrounds/rainbow.gbapal.lz"); +const u32 gBattleAnimBgTilemap_Rainbow[] = INCBIN_U32("graphics/battle_anims/backgrounds/rainbow.bin.lz"); + const u32 gPartyMenuBg_Gfx[] = INCBIN_U32("graphics/party_menu/bg.4bpp.lz"); const u32 gPartyMenuBg_Pal[] = INCBIN_U32("graphics/party_menu/bg.gbapal.lz"); const u32 gPartyMenuBg_Tilemap[] = INCBIN_U32("graphics/party_menu/bg.bin.lz"); diff --git a/test/battle/ai.c b/test/battle/ai.c index b3f336d15e..42fa32760f 100644 --- a/test/battle/ai.c +++ b/test/battle/ai.c @@ -53,7 +53,6 @@ AI_SINGLE_BATTLE_TEST("AI prefers Water Gun over Bubble if it knows that foe has PARAMETRIZE { abilityAI = ABILITY_MOXIE; } PARAMETRIZE { abilityAI = ABILITY_MOLD_BREAKER; } // Mold Breaker ignores Contrary. - GIVEN { AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT); PLAYER(SPECIES_SHUCKLE) { Ability(ABILITY_CONTRARY); } diff --git a/test/battle/damage_formula.c b/test/battle/damage_formula.c index a4b0a03f58..3aeeeb9f83 100644 --- a/test/battle/damage_formula.c +++ b/test/battle/damage_formula.c @@ -31,11 +31,11 @@ SINGLE_BATTLE_TEST("Damage calculation matches Gen5+") MOVE(player, MOVE_ICE_FANG, WITH_RNG(RNG_DAMAGE_MODIFIER, i)); } } - SCENE{ + SCENE { MESSAGE("Glaceon used Ice Fang!"); HP_BAR(opponent, captureDamage: &dmg); } - THEN{ + THEN { EXPECT_EQ(expectedDamage, dmg); } } @@ -68,11 +68,11 @@ SINGLE_BATTLE_TEST("Damage calculation matches Gen5+ (Muscle Band, crit)") MOVE(player, MOVE_ICE_FANG, WITH_RNG(RNG_DAMAGE_MODIFIER, i), criticalHit: TRUE); } } - SCENE{ + SCENE { MESSAGE("Glaceon used Ice Fang!"); HP_BAR(opponent, captureDamage: &dmg); } - THEN{ + THEN { EXPECT_EQ(expectedDamage, dmg); } } diff --git a/test/battle/move_effect/pledge.c b/test/battle/move_effect/pledge.c new file mode 100644 index 0000000000..a7843be619 --- /dev/null +++ b/test/battle/move_effect/pledge.c @@ -0,0 +1,352 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gBattleMoves[MOVE_WATER_PLEDGE].effect == EFFECT_PLEDGE); + ASSUME(gBattleMoves[MOVE_FIRE_PLEDGE].effect == EFFECT_PLEDGE); + ASSUME(gBattleMoves[MOVE_GRASS_PLEDGE].effect == EFFECT_PLEDGE); +} + +DOUBLE_BATTLE_TEST("Water and Fire Pledge create a rainbow on the user's side of the field for four turns") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(4); } + PLAYER(SPECIES_WYNAUT) { Speed(3); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(8); } + OPPONENT(SPECIES_WYNAUT) { Speed(5); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_WATER_PLEDGE, target: opponentLeft); + MOVE(playerRight, MOVE_FIRE_PLEDGE, target: opponentRight); + } + TURN {} + TURN {} + TURN {} + } SCENE { + MESSAGE("Wobbuffet used Water Pledge!"); + MESSAGE("Wobbuffet is waiting for Wynaut's move…{PAUSE 16}"); + MESSAGE("Wynaut used Fire Pledge!"); + MESSAGE("The two moves become one! It's a combined move!{PAUSE 16}"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_PLEDGE, playerRight); + HP_BAR(opponentRight); + MESSAGE("A rainbow appeared in the sky on your team's side!"); + MESSAGE("The rainbow on your side disappeared!"); + } +} + +DOUBLE_BATTLE_TEST("Rainbow doubles the chance of secondary move effects") +{ + PASSES_RANDOMLY(20, 100, RNG_SECONDARY_EFFECT); + GIVEN { + ASSUME(gBattleMoves[MOVE_EMBER].effect == EFFECT_BURN_HIT); + PLAYER(SPECIES_WOBBUFFET) { Speed(4); } + PLAYER(SPECIES_WYNAUT) { Speed(3); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(8); } + OPPONENT(SPECIES_WYNAUT) { Speed(5); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_WATER_PLEDGE, target: opponentLeft); + MOVE(playerRight, MOVE_FIRE_PLEDGE, target: opponentRight); + } + TURN { MOVE(playerLeft, MOVE_EMBER, target: opponentRight); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_PLEDGE, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_EMBER, playerLeft); + MESSAGE("Foe Wynaut was burned!"); + } +} + +DOUBLE_BATTLE_TEST("Rainbow flinch chance does not stack with Serene Grace") +{ + PASSES_RANDOMLY(60, 100, RNG_SECONDARY_EFFECT); + GIVEN { + ASSUME(gBattleMoves[MOVE_BITE].effect == EFFECT_FLINCH_HIT); + PLAYER(SPECIES_TOGEPI) { Speed(8); Ability(ABILITY_SERENE_GRACE); } + PLAYER(SPECIES_WOBBUFFET) { Speed(5); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(4); } + OPPONENT(SPECIES_WYNAUT) { Speed(3); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_WATER_PLEDGE, target: opponentLeft); + MOVE(playerRight, MOVE_FIRE_PLEDGE, target: opponentRight); + } + TURN { MOVE(playerLeft, MOVE_BITE, target: opponentRight); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_PLEDGE, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_BITE, playerLeft); + MESSAGE("Foe Wynaut flinched!"); + } +} + +DOUBLE_BATTLE_TEST("Rainbow flinch chance does not stack with Serene Grace if mvoe Triple Arrows is used") +{ + PASSES_RANDOMLY(60, 100, RNG_TRIPLE_ARROWS_FLINCH); + GIVEN { + ASSUME(gBattleMoves[MOVE_TRIPLE_ARROWS].effect == EFFECT_TRIPLE_ARROWS); + PLAYER(SPECIES_TOGEPI) { Speed(8); Ability(ABILITY_SERENE_GRACE); } + PLAYER(SPECIES_WOBBUFFET) { Speed(5); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(4); } + OPPONENT(SPECIES_WYNAUT) { Speed(3); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_WATER_PLEDGE, target: opponentLeft); + MOVE(playerRight, MOVE_FIRE_PLEDGE, target: opponentRight); + } + TURN { MOVE(playerLeft, MOVE_TRIPLE_ARROWS, target: opponentRight); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_PLEDGE, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_TRIPLE_ARROWS, playerLeft); + MESSAGE("Foe Wynaut flinched!"); + } +} + +DOUBLE_BATTLE_TEST("Fire and Grass Pledge summons Sea Of Fire for four turns that damages the opponent") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(4); } + PLAYER(SPECIES_WYNAUT) { Speed(3); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(8); } + OPPONENT(SPECIES_WYNAUT) { Speed(5); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_FIRE_PLEDGE, target: opponentLeft); + MOVE(playerRight, MOVE_GRASS_PLEDGE, target: opponentRight); + } + TURN {} + TURN {} + TURN {} + } SCENE { + MESSAGE("Wobbuffet used Fire Pledge!"); + MESSAGE("Wobbuffet is waiting for Wynaut's move…{PAUSE 16}"); + MESSAGE("Wynaut used Grass Pledge!"); + MESSAGE("The two moves become one! It's a combined move!{PAUSE 16}"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_FIRE_PLEDGE, playerRight); + HP_BAR(opponentRight); + MESSAGE("A sea of fire enveloped the opposing team!"); + ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_SEA_OF_FIRE, opponentRight); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponentLeft); + MESSAGE("The opposing Foe Wobbuffet was hurt by the sea of fire!"); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponentRight); + MESSAGE("The opposing Foe Wynaut was hurt by the sea of fire!"); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponentLeft); + MESSAGE("The opposing Foe Wobbuffet was hurt by the sea of fire!"); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponentRight); + MESSAGE("The opposing Foe Wynaut was hurt by the sea of fire!"); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponentLeft); + MESSAGE("The opposing Foe Wobbuffet was hurt by the sea of fire!"); + ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponentRight); + MESSAGE("The opposing Foe Wynaut was hurt by the sea of fire!"); + MESSAGE("The sea of fire around the opposing team disappeared!"); + } +} + +DOUBLE_BATTLE_TEST("Sea Of Fire deals 1/8th damage per turn") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(4); } + PLAYER(SPECIES_WYNAUT) { Speed(3); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(8); } + OPPONENT(SPECIES_WYNAUT) { Speed(5); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_FIRE_PLEDGE, target: opponentLeft); + MOVE(playerRight, MOVE_GRASS_PLEDGE, target: opponentRight); + } + } SCENE { + s32 maxHPopponentLeft = GetMonData(&OPPONENT_PARTY[0], MON_DATA_MAX_HP); + s32 maxHPopponentRight = GetMonData(&OPPONENT_PARTY[1], MON_DATA_MAX_HP); + HP_BAR(opponentLeft, damage: maxHPopponentLeft / 8); + HP_BAR(opponentRight, damage: maxHPopponentRight / 8); + } +} + +DOUBLE_BATTLE_TEST("Grass and Water Pledge create a swamp on the user's side of the field for four turns") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(4); } + PLAYER(SPECIES_WYNAUT) { Speed(3); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(8); } + OPPONENT(SPECIES_WYNAUT) { Speed(5); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_GRASS_PLEDGE, target: opponentLeft); + MOVE(playerRight, MOVE_WATER_PLEDGE, target: opponentRight); + } + TURN {} + TURN {} + TURN {} + } SCENE { + MESSAGE("Wobbuffet used Grass Pledge!"); + MESSAGE("Wobbuffet is waiting for Wynaut's move…{PAUSE 16}"); + MESSAGE("Wynaut used Water Pledge!"); + MESSAGE("The two moves become one! It's a combined move!{PAUSE 16}"); + ANIMATION(ANIM_TYPE_MOVE, MOVE_GRASS_PLEDGE, playerRight); + HP_BAR(opponentRight); + MESSAGE("A swamp enveloped the opposing team!"); + MESSAGE("The swamp around the opposing team disappeared!"); + } +} + +DOUBLE_BATTLE_TEST("Swamp reduces the speed of the effected side by 1/4th") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(5); } + PLAYER(SPECIES_WYNAUT) { Speed(4); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(12); } + OPPONENT(SPECIES_WYNAUT) { Speed(8); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_GRASS_PLEDGE, target: opponentLeft); + MOVE(playerRight, MOVE_WATER_PLEDGE, target: opponentRight); + } + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_GRASS_PLEDGE, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, playerRight); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentLeft); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponentRight); + } +} + +DOUBLE_BATTLE_TEST("The base power of a combined pledge move effect is 150") +{ + s16 hyperBeamDamage; + s16 combinedPledgeDamage; + + GIVEN { + ASSUME(gBattleMoves[MOVE_HYPER_BEAM].power == 150); + PLAYER(SPECIES_WOBBUFFET) { Speed(4); } + PLAYER(SPECIES_WYNAUT) { Speed(3); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(8); } + OPPONENT(SPECIES_WYNAUT) { Speed(5); } + } WHEN { + TURN { MOVE(opponentRight, MOVE_HYPER_BEAM, target: playerRight); + MOVE(playerLeft, MOVE_WATER_PLEDGE, target: opponentLeft); + MOVE(playerRight, MOVE_FIRE_PLEDGE, target: opponentRight); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPER_BEAM, opponentRight); + HP_BAR(playerRight, captureDamage: &hyperBeamDamage); + ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_PLEDGE, playerRight); + HP_BAR(opponentRight, captureDamage: &combinedPledgeDamage); + } THEN { + EXPECT_EQ(hyperBeamDamage, combinedPledgeDamage); + } +} + +DOUBLE_BATTLE_TEST("Pledge moves can not be redirected by absorbing abilities") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_LILEEP) { Ability(ABILITY_STORM_DRAIN); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, MOVE_WATER_PLEDGE, target: opponentRight);} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_PLEDGE, playerLeft); + HP_BAR(opponentRight); + } +} + +DOUBLE_BATTLE_TEST("Pledge status timer does not reset if combined move is used again") +{ + u16 pledgeMove1, pledgeMove2; + + PARAMETRIZE { pledgeMove1 = MOVE_WATER_PLEDGE; pledgeMove2 = MOVE_FIRE_PLEDGE; } + PARAMETRIZE { pledgeMove1 = MOVE_FIRE_PLEDGE; pledgeMove2 = MOVE_GRASS_PLEDGE; } + PARAMETRIZE { pledgeMove1 = MOVE_GRASS_PLEDGE; pledgeMove2 = MOVE_WATER_PLEDGE; } + + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(4); } + PLAYER(SPECIES_WYNAUT) { Speed(3); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(8); } + OPPONENT(SPECIES_WYNAUT) { Speed(5); } + } WHEN { + TURN { MOVE(playerLeft, pledgeMove1, target: opponentLeft); + MOVE(playerRight, pledgeMove2, target: opponentRight); + } + TURN { MOVE(playerLeft, pledgeMove1, target: opponentLeft); + MOVE(playerRight, pledgeMove2, target: opponentRight); + } + TURN {} + TURN {} + TURN {} + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, pledgeMove1, playerRight); + ANIMATION(ANIM_TYPE_MOVE, pledgeMove1, playerRight); + if (pledgeMove1 == MOVE_WATER_PLEDGE && pledgeMove2 == MOVE_FIRE_PLEDGE) + { + NOT MESSAGE("A rainbow appeared in the sky on your team's side!"); + MESSAGE("The rainbow on your side disappeared!"); + } + if (pledgeMove1 == MOVE_FIRE_PLEDGE && pledgeMove2 == MOVE_GRASS_PLEDGE) + { + NOT MESSAGE("A sea of fire enveloped the opposing team!"); + MESSAGE("The sea of fire around the opposing team disappeared!"); + } + if (pledgeMove1 == MOVE_GRASS_PLEDGE && pledgeMove2 == MOVE_WATER_PLEDGE) + { + NOT MESSAGE("A swamp enveloped the opposing team!"); + MESSAGE("The swamp around the opposing team disappeared!"); + } + } +} + +DOUBLE_BATTLE_TEST("Pledge moves get same attack type bonus from partner", s16 damage) +{ + u32 species; + + PARAMETRIZE { species = SPECIES_WOBBUFFET; } + PARAMETRIZE { species = SPECIES_CHARMANDER; } + + GIVEN { + PLAYER(species) { Speed(4); } + PLAYER(SPECIES_WYNAUT) { Speed(3); } + OPPONENT(SPECIES_WOBBUFFET) { Speed(8); } + OPPONENT(SPECIES_WYNAUT) { Speed(5); } + } WHEN { + TURN { MOVE(playerLeft, MOVE_FIRE_PLEDGE, target: opponentLeft); + MOVE(playerRight, MOVE_GRASS_PLEDGE, target: opponentRight); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_FIRE_PLEDGE, playerRight); + HP_BAR(opponentRight, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage); + } +} + +DOUBLE_BATTLE_TEST("Damage calculation: Combined pledge move") +{ + s16 dmg; + s16 expectedDamage; + PARAMETRIZE { expectedDamage = 159; } + PARAMETRIZE { expectedDamage = 156; } + PARAMETRIZE { expectedDamage = 154; } + PARAMETRIZE { expectedDamage = 153; } + PARAMETRIZE { expectedDamage = 151; } + PARAMETRIZE { expectedDamage = 150; } + PARAMETRIZE { expectedDamage = 148; } + PARAMETRIZE { expectedDamage = 147; } + PARAMETRIZE { expectedDamage = 145; } + PARAMETRIZE { expectedDamage = 144; } + PARAMETRIZE { expectedDamage = 142; } + PARAMETRIZE { expectedDamage = 141; } + PARAMETRIZE { expectedDamage = 139; } + PARAMETRIZE { expectedDamage = 138; } + PARAMETRIZE { expectedDamage = 136; } + PARAMETRIZE { expectedDamage = 135; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Speed(4); } + PLAYER(SPECIES_WOBBUFFET) { HP(521); SpDefense(152); Speed(3); } + OPPONENT(SPECIES_CHARIZARD) { Speed(8); } + OPPONENT(SPECIES_EEVEE) { SpAttack(126); Speed(5); } + } WHEN { + TURN { MOVE(opponentLeft, MOVE_FIRE_PLEDGE, target: playerLeft, WITH_RNG(RNG_DAMAGE_MODIFIER, i)); + MOVE(opponentRight, MOVE_GRASS_PLEDGE, target: playerRight, WITH_RNG(RNG_DAMAGE_MODIFIER, i)); + } + } + SCENE { + HP_BAR(playerRight, captureDamage: &dmg); + } + THEN { + EXPECT_EQ(expectedDamage, dmg); + } +}