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 <iasperbassoonian@gmail.com>
This commit is contained in:
Alex 2023-11-21 16:23:22 +01:00 committed by GitHub
parent be42d4eafb
commit b9edbb429b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 872 additions and 22 deletions

View file

@ -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

View file

@ -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

View file

@ -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

Binary file not shown.

View file

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View file

@ -235,6 +235,9 @@ struct SideTimer
u8 retaliateTimer;
u8 damageNonTypesTimer;
u8 damageNonTypesType;
u8 rainbowTimer;
u8 seaOfFireTimer;
u8 swampTimer;
};
struct FieldTimer
@ -726,6 +729,7 @@ struct BattleStruct
u32 aiDelayTimer; // Counts number of frames AI takes to choose an action.
u32 aiDelayFrames; // Number of frames it took to choose an action.
bool8 transformZeroToHero[PARTY_SIZE][NUM_BATTLE_SIDES];
u8 pledgeMove:1;
};
// The palaceFlags member of struct BattleStruct contains 1 flag per move to indicate which moves the AI should consider,

View file

@ -483,6 +483,13 @@ extern const u8 BattleScript_SelectingNotAllowedCurrentMoveInPalace[];
extern const u8 BattleScript_SaltCureExtraDamage[];
extern const u8 BattleScript_SyrupBombEndTurn[];
extern const u8 BattleScript_SyrupBombActivates[];
extern const u8 BattleScript_EffectCombinedPledge_Water[];
extern const u8 BattleScript_EffectCombinedPledge_Fire[];
extern const u8 BattleScript_EffectCombinedPledge_Grass[];
extern const u8 BattleScript_TheRainbowDisappeared[];
extern const u8 BattleScript_HurtByTheSeaOfFire[];
extern const u8 BattleScript_TheSeaOfFireDisappeared[];
extern const u8 BattleScript_TheSwampDisappeared[];
// zmoves
extern const u8 BattleScript_ZMoveActivateDamaging[];

View file

@ -247,7 +247,7 @@ void RemoveConfusionStatus(u32 battler);
u8 GetBattlerGender(u32 battler);
bool32 AreBattlersOfOppositeGender(u32 battler1, u32 battler2);
bool32 AreBattlersOfSameGender(u32 battler1, u32 battler2);
u32 CalcSecondaryEffectChance(u32 battler, u8 secondaryEffectChance);
u32 CalcSecondaryEffectChance(u32 battler, u8 secondaryEffectChance, u16 moveEffect);
u8 GetBattlerType(u32 battler, u8 typeIndex);
#endif // GUARD_BATTLE_UTIL_H

View file

@ -56,7 +56,7 @@
#define B_TAUNT_TURNS GEN_LATEST // In Gen5+, Taunt lasts 3 turns if the user acts before the target, or 4 turns if the target acted before the user. In Gen3, taunt lasts 2 turns and in Gen 4, 3-5 turns.
#define B_SPORT_TURNS GEN_LATEST // In Gen6+, Water/Mud Sport last 5 turns, even if the user switches out.
#define B_MEGA_EVO_TURN_ORDER GEN_LATEST // In Gen7, a Pokémon's Speed after Mega Evolution is used to determine turn order, not its Speed before.
#define B_RECALC_TURN_AFTER_ACTIONS GEN_LATEST // In Gen8, switching/using a move affects the current turn's order of actions.
#define B_RECALC_TURN_AFTER_ACTIONS GEN_LATEST // In Gen8, switching/using a move affects the current turn's order of actions, better known as dynamic speed.
#define B_FAINT_SWITCH_IN GEN_LATEST // In Gen4+, sending out a new Pokémon after the previous one fainted happens at the end of the turn. Before, it would happen after each action.
// Move data settings

View file

@ -238,9 +238,13 @@
#define SIDE_STATUS_MAT_BLOCK (1 << 21)
#define SIDE_STATUS_STEELSURGE (1 << 22)
#define SIDE_STATUS_DAMAGE_NON_TYPES (1 << 23)
#define SIDE_STATUS_RAINBOW (1 << 24)
#define SIDE_STATUS_SEA_OF_FIRE (1 << 25)
#define SIDE_STATUS_SWAMP (1 << 26)
#define SIDE_STATUS_HAZARDS_ANY (SIDE_STATUS_SPIKES | SIDE_STATUS_STICKY_WEB | SIDE_STATUS_TOXIC_SPIKES | SIDE_STATUS_STEALTH_ROCK | SIDE_STATUS_STEELSURGE)
#define SIDE_STATUS_SCREEN_ANY (SIDE_STATUS_REFLECT | SIDE_STATUS_LIGHTSCREEN | SIDE_STATUS_AURORA_VEIL)
#define SIDE_STATUS_PLEDGE_ANY (SIDE_STATUS_RAINBOW | SIDE_STATUS_SEA_OF_FIRE | SIDE_STATUS_SWAMP)
// Field affecting statuses.
#define STATUS_FIELD_MAGIC_ROOM (1 << 0)

View file

@ -515,6 +515,7 @@
#define BG_STEEL_BEAM_OPPONENT 78
#define BG_STEEL_BEAM_PLAYER 79
#define BG_CHLOROBLAST 80
#define BG_RAINBOW 81
// table ids for general animations (gBattleAnims_General)
#define B_ANIM_STATS_CHANGE 0
@ -559,6 +560,9 @@
#define B_ANIM_DYNAMAX_GROWTH 39
#define B_ANIM_MAX_SET_WEATHER 40
#define B_ANIM_SYRUP_BOMB_SPEED_DROP 41
#define B_ANIM_RAINBOW 42
#define B_ANIM_SEA_OF_FIRE 43
#define B_ANIM_SWAMP 44
// special animations table (gBattleAnims_Special)
#define B_ANIM_LVL_UP 0

View file

@ -686,8 +686,17 @@
#define STRINGID_PKMNHURTBYROCKSTHROWN 684
#define STRINGID_MOVEBLOCKEDBYDYNAMAX 685
#define STRINGID_ZEROTOHEROTRANSFORMATION 686
#define STRINGID_THETWOMOVESBECOMEONE 687
#define STRINGID_ARAINBOWAPPEAREDONSIDE 688
#define STRINGID_THERAINBOWDISAPPEARED 689
#define STRINGID_WAITINGFORPARTNERSMOVE 690
#define STRINGID_SEAOFFIREENVELOPEDSIDE 691
#define STRINGID_HURTBYTHESEAOFFIRE 692
#define STRINGID_THESEAOFFIREDISAPPEARED 693
#define STRINGID_SWAMPENVELOPEDSIDE 694
#define STRINGID_THESWAMPDISAPPEARED 695
#define BATTLESTRINGS_COUNT 687
#define BATTLESTRINGS_COUNT 696
// This is the string id that gBattleStringsTable starts with.
// String ids before this (e.g. STRINGID_INTROMSG) are not in the table,

View file

@ -11510,6 +11510,11 @@ extern const u16 gSlotMachineReelTimePikachu_Pal[];
extern const u32 gBattleAnimBgTilemap_Sandstorm[];
extern const u32 gBattleAnimBgImage_Sandstorm[];
// Pledge Effect field status - Rainbow
extern const u32 gBattleAnimBgImage_Rainbow[];
extern const u32 gBattleAnimBGPalette_Rainbow[];
extern const u32 gBattleAnimBgTilemap_Rainbow[];
// Pokedex Area Screen
extern const u32 gPokedexAreaScreenAreaUnknown_Gfx[];
extern const u16 gPokedexAreaScreenAreaUnknown_Pal[];

View file

@ -578,6 +578,7 @@ static u32 ChooseMoveOrAction_Doubles(u32 battlerAi)
AI_THINKING_STRUCT->aiLogicId = 0;
AI_THINKING_STRUCT->movesetIndex = 0;
flags = AI_THINKING_STRUCT->aiFlags;
while (flags != 0)
{
if (flags & 1)

View file

@ -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:

View file

@ -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);

View file

@ -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;
}

View file

@ -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,

View file

@ -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;
}

View file

@ -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];
}

View file

@ -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},
};

View file

@ -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");

View file

@ -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); }

View file

@ -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);
}
}

View file

@ -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);
}
}