Fixes to Opportunist and Mirror Herb adjustments (#4928)
* Replace Opportunist todo tests with proper tests * add failing test * desc * Fixes to Opportunist and Mirror Herb adjustments * more tests * some fixes * first turn events switch * simple enum + revert test desc
This commit is contained in:
parent
93ee3a9197
commit
552e2768da
11 changed files with 532 additions and 186 deletions
|
@ -1639,6 +1639,12 @@
|
|||
.4byte \failInstr
|
||||
.endm
|
||||
|
||||
.macro copyfoesstatincrease battler:req, failInstr:req
|
||||
callnative BS_CopyFoesStatIncrease
|
||||
.byte \battler
|
||||
.4byte \failInstr
|
||||
.endm
|
||||
|
||||
@ various command changed to more readable macros
|
||||
.macro cancelmultiturnmoves battler:req
|
||||
various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES
|
||||
|
|
|
@ -9100,7 +9100,6 @@ BattleScript_TotemFlaredToLife::
|
|||
call BattleScript_ApplyTotemVarBoost
|
||||
end2
|
||||
|
||||
@ remove the mirror herb, do totem loop
|
||||
BattleScript_MirrorHerbCopyStatChangeEnd2::
|
||||
call BattleScript_MirrorHerbCopyStatChange
|
||||
end2
|
||||
|
@ -9110,16 +9109,24 @@ BattleScript_MirrorHerbCopyStatChange::
|
|||
printstring STRINGID_MIRRORHERBCOPIED
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
removeitem BS_SCRIPTING
|
||||
call BattleScript_TotemVar_Ret
|
||||
copybyte gBattlerAttacker, sSAVED_BATTLER @ restore the original attacker just to be safe
|
||||
playanimation BS_SCRIPTING, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1
|
||||
BattleScript_MirrorHerbStartCopyStats:
|
||||
copyfoesstatincrease BS_SCRIPTING, BattleScript_MirrorHerbStartReturn
|
||||
statbuffchange STAT_CHANGE_ALLOW_PTR, BattleScript_MirrorHerbStartReturn
|
||||
goto BattleScript_MirrorHerbStartCopyStats
|
||||
BattleScript_MirrorHerbStartReturn:
|
||||
return
|
||||
|
||||
BattleScript_OpportunistCopyStatChange::
|
||||
call BattleScript_AbilityPopUp
|
||||
printstring STRINGID_OPPORTUNISTCOPIED
|
||||
call BattleScript_AbilityPopUpScripting
|
||||
playanimation BS_SCRIPTING, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1
|
||||
BattleScript_OpportunistStartCopyStats:
|
||||
copyfoesstatincrease BS_SCRIPTING, BattleScript_OpportunistCopyStatChangeEnd
|
||||
statbuffchange STAT_CHANGE_ALLOW_PTR, BattleScript_OpportunistCopyStatChangeEnd
|
||||
printfromtable gStatUpStringIds
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
call BattleScript_TotemVar_Ret
|
||||
copybyte gBattlerAttacker, sSAVED_BATTLER @ restore the original attacker just to be safe
|
||||
goto BattleScript_OpportunistStartCopyStats
|
||||
BattleScript_OpportunistCopyStatChangeEnd:
|
||||
end3
|
||||
|
||||
BattleScript_TotemVar::
|
||||
|
|
|
@ -628,7 +628,7 @@ struct BattleStruct
|
|||
u8 moneyMultiplierItem:1;
|
||||
u8 moneyMultiplierMove:1;
|
||||
u8 savedTurnActionNumber;
|
||||
u8 switchInAbilitiesCounter;
|
||||
u8 eventsBeforeFirstTurnState;
|
||||
u8 faintedActionsState;
|
||||
u8 faintedActionsBattlerId;
|
||||
u8 scriptPartyIdx; // for printing the nickname
|
||||
|
@ -672,7 +672,7 @@ struct BattleStruct
|
|||
u16 chosenItem[MAX_BATTLERS_COUNT];
|
||||
u16 choicedMove[MAX_BATTLERS_COUNT];
|
||||
u16 changedItems[MAX_BATTLERS_COUNT];
|
||||
u8 switchInItemsCounter;
|
||||
u8 switchInBattlerCounter;
|
||||
u8 arenaTurnCounter;
|
||||
u8 turnSideTracker;
|
||||
u16 lastTakenMoveFrom[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT]; // a 2-D array [target][attacker]
|
||||
|
|
|
@ -23,6 +23,20 @@ struct MultiPartnerMenuPokemon
|
|||
#define BOUNCE_MON 0x0
|
||||
#define BOUNCE_HEALTHBOX 0x1
|
||||
|
||||
enum {
|
||||
FIRST_TURN_EVENTS_START,
|
||||
FIRST_TURN_EVENTS_OVERWORLD_WEATHER,
|
||||
FIRST_TURN_EVENTS_TERRAIN,
|
||||
FIRST_TURN_EVENTS_STARTING_STATUS,
|
||||
FIRST_TURN_EVENTS_TOTEM_BOOST,
|
||||
FIRST_TURN_EVENTS_NEUTRALIZING_GAS,
|
||||
FIRST_TURN_EVENTS_SWITCH_IN_ABILITIES,
|
||||
FIRST_TURN_EVENTS_OPPORTUNIST_1,
|
||||
FIRST_TURN_EVENTS_ITEM_EFFECTS,
|
||||
FIRST_TURN_EVENTS_OPPORTUNIST_2,
|
||||
FIRST_TURN_EVENTS_END,
|
||||
};
|
||||
|
||||
void CB2_InitBattle(void);
|
||||
void BattleMainCB2(void);
|
||||
void CB2_QuitRecordedBattle(void);
|
||||
|
|
|
@ -670,45 +670,44 @@
|
|||
#define STRINGID_CURRENTMOVECANTSELECT 668
|
||||
#define STRINGID_TARGETISBEINGSALTCURED 669
|
||||
#define STRINGID_TARGETISHURTBYSALTCURE 670
|
||||
#define STRINGID_OPPORTUNISTCOPIED 671
|
||||
#define STRINGID_TARGETCOVEREDINSTICKYCANDYSYRUP 672
|
||||
#define STRINGID_SHARPSTEELFLOATS 673
|
||||
#define STRINGID_SHARPSTEELDMG 674
|
||||
#define STRINGID_PKMNBLEWAWAYSHARPSTEEL 675
|
||||
#define STRINGID_SHARPSTEELDISAPPEAREDFROMTEAM 676
|
||||
#define STRINGID_TEAMTRAPPEDWITHVINES 677
|
||||
#define STRINGID_PKMNHURTBYVINES 678
|
||||
#define STRINGID_TEAMCAUGHTINVORTEX 679
|
||||
#define STRINGID_PKMNHURTBYVORTEX 680
|
||||
#define STRINGID_TEAMSURROUNDEDBYFIRE 681
|
||||
#define STRINGID_PKMNBURNINGUP 682
|
||||
#define STRINGID_TEAMSURROUNDEDBYROCKS 683
|
||||
#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 STRINGID_PKMNTELLCHILLINGRECEPTIONJOKE 696
|
||||
#define STRINGID_HOSPITALITYRESTORATION 697
|
||||
#define STRINGID_ELECTROSHOTCHARGING 698
|
||||
#define STRINGID_ITEMWASUSEDUP 699
|
||||
#define STRINGID_ATTACKERLOSTITSTYPE 700
|
||||
#define STRINGID_SHEDITSTAIL 701
|
||||
#define STRINGID_CLOAKEDINAHARSHLIGHT 702
|
||||
#define STRINGID_SUPERSWEETAROMAWAFTS 703
|
||||
#define STRINGID_DIMENSIONSWERETWISTED 704
|
||||
#define STRINGID_BIZARREARENACREATED 705
|
||||
#define STRINGID_BIZARREAREACREATED 706
|
||||
#define STRINGID_TIDYINGUPCOMPLETE 707
|
||||
#define STRINGID_TARGETCOVEREDINSTICKYCANDYSYRUP 671
|
||||
#define STRINGID_SHARPSTEELFLOATS 672
|
||||
#define STRINGID_SHARPSTEELDMG 673
|
||||
#define STRINGID_PKMNBLEWAWAYSHARPSTEEL 674
|
||||
#define STRINGID_SHARPSTEELDISAPPEAREDFROMTEAM 675
|
||||
#define STRINGID_TEAMTRAPPEDWITHVINES 676
|
||||
#define STRINGID_PKMNHURTBYVINES 677
|
||||
#define STRINGID_TEAMCAUGHTINVORTEX 678
|
||||
#define STRINGID_PKMNHURTBYVORTEX 679
|
||||
#define STRINGID_TEAMSURROUNDEDBYFIRE 680
|
||||
#define STRINGID_PKMNBURNINGUP 681
|
||||
#define STRINGID_TEAMSURROUNDEDBYROCKS 682
|
||||
#define STRINGID_PKMNHURTBYROCKSTHROWN 683
|
||||
#define STRINGID_MOVEBLOCKEDBYDYNAMAX 684
|
||||
#define STRINGID_ZEROTOHEROTRANSFORMATION 685
|
||||
#define STRINGID_THETWOMOVESBECOMEONE 686
|
||||
#define STRINGID_ARAINBOWAPPEAREDONSIDE 687
|
||||
#define STRINGID_THERAINBOWDISAPPEARED 688
|
||||
#define STRINGID_WAITINGFORPARTNERSMOVE 689
|
||||
#define STRINGID_SEAOFFIREENVELOPEDSIDE 690
|
||||
#define STRINGID_HURTBYTHESEAOFFIRE 691
|
||||
#define STRINGID_THESEAOFFIREDISAPPEARED 692
|
||||
#define STRINGID_SWAMPENVELOPEDSIDE 693
|
||||
#define STRINGID_THESWAMPDISAPPEARED 694
|
||||
#define STRINGID_PKMNTELLCHILLINGRECEPTIONJOKE 695
|
||||
#define STRINGID_HOSPITALITYRESTORATION 696
|
||||
#define STRINGID_ELECTROSHOTCHARGING 697
|
||||
#define STRINGID_ITEMWASUSEDUP 698
|
||||
#define STRINGID_ATTACKERLOSTITSTYPE 699
|
||||
#define STRINGID_SHEDITSTAIL 700
|
||||
#define STRINGID_CLOAKEDINAHARSHLIGHT 701
|
||||
#define STRINGID_SUPERSWEETAROMAWAFTS 702
|
||||
#define STRINGID_DIMENSIONSWERETWISTED 703
|
||||
#define STRINGID_BIZARREARENACREATED 704
|
||||
#define STRINGID_BIZARREAREACREATED 705
|
||||
#define STRINGID_TIDYINGUPCOMPLETE 706
|
||||
|
||||
#define BATTLESTRINGS_COUNT 708
|
||||
#define BATTLESTRINGS_COUNT 707
|
||||
|
||||
// This is the string id that gBattleStringsTable starts with.
|
||||
// String ids before this (e.g. STRINGID_INTROMSG) are not in the table,
|
||||
|
|
|
@ -3995,8 +3995,8 @@ static void DoBattleIntro(void)
|
|||
}
|
||||
}
|
||||
|
||||
gBattleStruct->switchInAbilitiesCounter = 0;
|
||||
gBattleStruct->switchInItemsCounter = 0;
|
||||
gBattleStruct->eventsBeforeFirstTurnState = 0;
|
||||
gBattleStruct->switchInBattlerCounter = 0;
|
||||
gBattleStruct->overworldWeatherDone = FALSE;
|
||||
SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers
|
||||
Ai_InitPartyStruct(); // Save mons party counts, and first 2/4 mons on the battlefield.
|
||||
|
@ -4031,34 +4031,35 @@ static void TryDoEventsBeforeFirstTurn(void)
|
|||
if (gBattleControllerExecFlags)
|
||||
return;
|
||||
|
||||
// Set invalid mons as absent(for example when starting a double battle with only one pokemon).
|
||||
if (!(gBattleTypeFlags & BATTLE_TYPE_SAFARI))
|
||||
switch (gBattleStruct->eventsBeforeFirstTurnState)
|
||||
{
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
case FIRST_TURN_EVENTS_START:
|
||||
// Set invalid mons as absent(for example when starting a double battle with only one pokemon).
|
||||
if (!(gBattleTypeFlags & BATTLE_TYPE_SAFARI))
|
||||
{
|
||||
struct Pokemon *party = GetBattlerParty(i);
|
||||
struct Pokemon *mon = &party[gBattlerPartyIndexes[i]];
|
||||
if (gBattleMons[i].hp == 0 || gBattleMons[i].species == SPECIES_NONE || GetMonData(mon, MON_DATA_IS_EGG))
|
||||
gAbsentBattlerFlags |= gBitTable[i];
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
struct Pokemon *party = GetBattlerParty(i);
|
||||
struct Pokemon *mon = &party[gBattlerPartyIndexes[i]];
|
||||
if (gBattleMons[i].hp == 0 || gBattleMons[i].species == SPECIES_NONE || GetMonData(mon, MON_DATA_IS_EGG))
|
||||
gAbsentBattlerFlags |= gBitTable[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Allow for illegal abilities within tests.
|
||||
#if TESTING
|
||||
if (gTestRunnerEnabled && gBattleStruct->switchInAbilitiesCounter == 0)
|
||||
{
|
||||
for (i = 0; i < gBattlersCount; ++i)
|
||||
// Allow for illegal abilities within tests.
|
||||
#if TESTING
|
||||
if (gTestRunnerEnabled)
|
||||
{
|
||||
u32 side = GetBattlerSide(i);
|
||||
u32 partyIndex = gBattlerPartyIndexes[i];
|
||||
if (TestRunner_Battle_GetForcedAbility(side, partyIndex))
|
||||
gBattleMons[i].ability = gBattleStruct->overwrittenAbilities[i] = TestRunner_Battle_GetForcedAbility(side, partyIndex);
|
||||
for (i = 0; i < gBattlersCount; ++i)
|
||||
{
|
||||
u32 side = GetBattlerSide(i);
|
||||
u32 partyIndex = gBattlerPartyIndexes[i];
|
||||
if (TestRunner_Battle_GetForcedAbility(side, partyIndex))
|
||||
gBattleMons[i].ability = gBattleStruct->overwrittenAbilities[i] = TestRunner_Battle_GetForcedAbility(side, partyIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // TESTING
|
||||
#endif // TESTING
|
||||
|
||||
if (gBattleStruct->switchInAbilitiesCounter == 0)
|
||||
{
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
gBattlerByTurnOrder[i] = i;
|
||||
for (i = 0; i < gBattlersCount - 1; i++)
|
||||
|
@ -4069,109 +4070,135 @@ static void TryDoEventsBeforeFirstTurn(void)
|
|||
SwapTurnOrder(i, j);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!gBattleStruct->overworldWeatherDone
|
||||
&& AbilityBattleEffects(ABILITYEFFECT_SWITCH_IN_WEATHER, 0, 0, ABILITYEFFECT_SWITCH_IN_WEATHER, 0) != 0)
|
||||
{
|
||||
gBattleStruct->overworldWeatherDone = TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gBattleStruct->terrainDone && AbilityBattleEffects(ABILITYEFFECT_SWITCH_IN_TERRAIN, 0, 0, ABILITYEFFECT_SWITCH_IN_TERRAIN, 0) != 0)
|
||||
{
|
||||
gBattleStruct->terrainDone = TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gBattleStruct->startingStatusDone
|
||||
&& gBattleStruct->startingStatus
|
||||
&& AbilityBattleEffects(ABILITYEFFECT_SWITCH_IN_STATUSES, 0, 0, ABILITYEFFECT_SWITCH_IN_STATUSES, 0) != 0)
|
||||
{
|
||||
gBattleStruct->startingStatusDone = TRUE;
|
||||
return;
|
||||
}
|
||||
|
||||
// Totem boosts
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
if (gQueuedStatBoosts[i].stats != 0 && !gProtectStructs[i].eatMirrorHerb && gProtectStructs[i].activateOpportunist == 0)
|
||||
gBattleStruct->eventsBeforeFirstTurnState++;
|
||||
break;
|
||||
case FIRST_TURN_EVENTS_OVERWORLD_WEATHER:
|
||||
if (!gBattleStruct->overworldWeatherDone
|
||||
&& AbilityBattleEffects(ABILITYEFFECT_SWITCH_IN_WEATHER, 0, 0, ABILITYEFFECT_SWITCH_IN_WEATHER, 0) != 0)
|
||||
{
|
||||
gBattlerAttacker = i;
|
||||
BattleScriptExecute(BattleScript_TotemVar);
|
||||
gBattleStruct->overworldWeatherDone = TRUE;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check neutralizing gas
|
||||
if (AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, 0, 0, 0, 0) != 0)
|
||||
return;
|
||||
|
||||
// Check all switch in abilities happening from the fastest mon to slowest.
|
||||
while (gBattleStruct->switchInAbilitiesCounter < gBattlersCount)
|
||||
{
|
||||
gBattlerAttacker = gBattlerByTurnOrder[gBattleStruct->switchInAbilitiesCounter++];
|
||||
|
||||
if (TryPrimalReversion(gBattlerAttacker))
|
||||
gBattleStruct->eventsBeforeFirstTurnState++;
|
||||
break;
|
||||
case FIRST_TURN_EVENTS_TERRAIN:
|
||||
if (!gBattleStruct->terrainDone
|
||||
&& AbilityBattleEffects(ABILITYEFFECT_SWITCH_IN_TERRAIN, 0, 0, ABILITYEFFECT_SWITCH_IN_TERRAIN, 0) != 0)
|
||||
{
|
||||
gBattleStruct->terrainDone = TRUE;
|
||||
return;
|
||||
if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, gBattlerAttacker, 0, 0, 0) != 0)
|
||||
}
|
||||
gBattleStruct->eventsBeforeFirstTurnState++;
|
||||
break;
|
||||
case FIRST_TURN_EVENTS_STARTING_STATUS:
|
||||
if (!gBattleStruct->startingStatusDone
|
||||
&& gBattleStruct->startingStatus
|
||||
&& AbilityBattleEffects(ABILITYEFFECT_SWITCH_IN_STATUSES, 0, 0, ABILITYEFFECT_SWITCH_IN_STATUSES, 0) != 0)
|
||||
{
|
||||
gBattleStruct->startingStatusDone = TRUE;
|
||||
return;
|
||||
}
|
||||
// Check all switch in items having effect from the fastest mon to slowest.
|
||||
while (gBattleStruct->switchInItemsCounter < gBattlersCount)
|
||||
{
|
||||
if (ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, gBattlerByTurnOrder[gBattleStruct->switchInItemsCounter++], FALSE))
|
||||
}
|
||||
gBattleStruct->eventsBeforeFirstTurnState++;
|
||||
break;
|
||||
case FIRST_TURN_EVENTS_TOTEM_BOOST:
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
if (gQueuedStatBoosts[i].stats != 0 && !gProtectStructs[i].eatMirrorHerb && gProtectStructs[i].activateOpportunist == 0)
|
||||
{
|
||||
gBattlerAttacker = i;
|
||||
BattleScriptExecute(BattleScript_TotemVar);
|
||||
return;
|
||||
}
|
||||
}
|
||||
memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts)); // erase all totem boosts for Mirror Herb and Opportunist
|
||||
gBattleStruct->eventsBeforeFirstTurnState++;
|
||||
break;
|
||||
case FIRST_TURN_EVENTS_NEUTRALIZING_GAS:
|
||||
if (AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, 0, 0, 0, 0) != 0)
|
||||
return;
|
||||
gBattleStruct->eventsBeforeFirstTurnState++;
|
||||
break;
|
||||
case FIRST_TURN_EVENTS_SWITCH_IN_ABILITIES:
|
||||
while (gBattleStruct->switchInBattlerCounter < gBattlersCount) // From fastest to slowest
|
||||
{
|
||||
gBattlerAttacker = gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++];
|
||||
|
||||
if (TryPrimalReversion(gBattlerAttacker))
|
||||
return;
|
||||
if (AbilityBattleEffects(ABILITYEFFECT_ON_SWITCHIN, gBattlerAttacker, 0, 0, 0) != 0)
|
||||
return;
|
||||
}
|
||||
gBattleStruct->switchInBattlerCounter = 0;
|
||||
gBattleStruct->eventsBeforeFirstTurnState++;
|
||||
break;
|
||||
case FIRST_TURN_EVENTS_OPPORTUNIST_1:
|
||||
if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, 0, 0, 0, 0))
|
||||
return;
|
||||
gBattleStruct->eventsBeforeFirstTurnState++;
|
||||
break;
|
||||
case FIRST_TURN_EVENTS_ITEM_EFFECTS:
|
||||
while (gBattleStruct->switchInBattlerCounter < gBattlersCount) // From fastest to slowest
|
||||
{
|
||||
if (ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, gBattlerByTurnOrder[gBattleStruct->switchInBattlerCounter++], FALSE))
|
||||
return;
|
||||
}
|
||||
gBattleStruct->switchInBattlerCounter = 0;
|
||||
gBattleStruct->eventsBeforeFirstTurnState++;
|
||||
break;
|
||||
case FIRST_TURN_EVENTS_OPPORTUNIST_2:
|
||||
if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, 0, 0, 0, 0))
|
||||
return;
|
||||
gBattleStruct->eventsBeforeFirstTurnState++;
|
||||
break;
|
||||
case FIRST_TURN_EVENTS_END:
|
||||
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
|
||||
{
|
||||
*(gBattleStruct->monToSwitchIntoId + i) = PARTY_SIZE;
|
||||
gChosenActionByBattler[i] = B_ACTION_NONE;
|
||||
gChosenMoveByBattler[i] = MOVE_NONE;
|
||||
}
|
||||
TurnValuesCleanUp(FALSE);
|
||||
SpecialStatusesClear();
|
||||
*(&gBattleStruct->absentBattlerFlags) = gAbsentBattlerFlags;
|
||||
BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG);
|
||||
gBattleMainFunc = HandleTurnActionSelectionState;
|
||||
ResetSentPokesToOpponentValue();
|
||||
|
||||
for (i = 0; i < BATTLE_COMMUNICATION_ENTRIES_COUNT; i++)
|
||||
gBattleCommunication[i] = 0;
|
||||
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
gBattleMons[i].status2 &= ~STATUS2_FLINCHED;
|
||||
// Record party slots of player's mons that appeared in battle
|
||||
if (!BattlerHasAi(i))
|
||||
gBattleStruct->appearedInBattle |= gBitTable[gBattlerPartyIndexes[i]];
|
||||
}
|
||||
|
||||
*(&gBattleStruct->turnEffectsTracker) = 0;
|
||||
*(&gBattleStruct->turnEffectsBattlerId) = 0;
|
||||
*(&gBattleStruct->wishPerishSongState) = 0;
|
||||
*(&gBattleStruct->wishPerishSongBattlerId) = 0;
|
||||
gBattleScripting.moveendState = 0;
|
||||
gBattleStruct->faintedActionsState = 0;
|
||||
gBattleStruct->turnCountersTracker = 0;
|
||||
gMoveResultFlags = 0;
|
||||
|
||||
memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts));
|
||||
SetShellSideArmCategory();
|
||||
SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
|
||||
{
|
||||
StopCryAndClearCrySongs();
|
||||
BattleScriptExecute(BattleScript_ArenaTurnBeginning);
|
||||
}
|
||||
|
||||
if ((i = ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), TRAINER_SLIDE_BEFORE_FIRST_TURN)))
|
||||
BattleScriptExecute(i == 1 ? BattleScript_TrainerASlideMsgEnd2 : BattleScript_TrainerBSlideMsgEnd2);
|
||||
gBattleStruct->eventsBeforeFirstTurnState = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, 0, 0, 0, 0))
|
||||
return;
|
||||
|
||||
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
|
||||
{
|
||||
*(gBattleStruct->monToSwitchIntoId + i) = PARTY_SIZE;
|
||||
gChosenActionByBattler[i] = B_ACTION_NONE;
|
||||
gChosenMoveByBattler[i] = MOVE_NONE;
|
||||
}
|
||||
TurnValuesCleanUp(FALSE);
|
||||
SpecialStatusesClear();
|
||||
*(&gBattleStruct->absentBattlerFlags) = gAbsentBattlerFlags;
|
||||
BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG);
|
||||
gBattleMainFunc = HandleTurnActionSelectionState;
|
||||
ResetSentPokesToOpponentValue();
|
||||
|
||||
for (i = 0; i < BATTLE_COMMUNICATION_ENTRIES_COUNT; i++)
|
||||
gBattleCommunication[i] = 0;
|
||||
|
||||
for (i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
gBattleMons[i].status2 &= ~STATUS2_FLINCHED;
|
||||
// Record party slots of player's mons that appeared in battle
|
||||
if (!BattlerHasAi(i))
|
||||
gBattleStruct->appearedInBattle |= gBitTable[gBattlerPartyIndexes[i]];
|
||||
}
|
||||
|
||||
*(&gBattleStruct->turnEffectsTracker) = 0;
|
||||
*(&gBattleStruct->turnEffectsBattlerId) = 0;
|
||||
*(&gBattleStruct->wishPerishSongState) = 0;
|
||||
*(&gBattleStruct->wishPerishSongBattlerId) = 0;
|
||||
gBattleScripting.moveendState = 0;
|
||||
gBattleStruct->faintedActionsState = 0;
|
||||
gBattleStruct->turnCountersTracker = 0;
|
||||
gMoveResultFlags = 0;
|
||||
|
||||
memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts)); // erase all totem boosts just to be safe
|
||||
|
||||
SetShellSideArmCategory();
|
||||
SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
|
||||
{
|
||||
StopCryAndClearCrySongs();
|
||||
BattleScriptExecute(BattleScript_ArenaTurnBeginning);
|
||||
}
|
||||
|
||||
if ((i = ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), TRAINER_SLIDE_BEFORE_FIRST_TURN)))
|
||||
BattleScriptExecute(i == 1 ? BattleScript_TrainerASlideMsgEnd2 : BattleScript_TrainerBSlideMsgEnd2);
|
||||
}
|
||||
|
||||
static void HandleEndTurn_ContinueBattle(void)
|
||||
|
|
|
@ -824,7 +824,6 @@ static const u8 sText_TeamGainedEXP[] = _("The rest of your team gained EXP.\nPo
|
|||
static const u8 sText_CurrentMoveCantSelect[] = _("{B_BUFF1} cannot be used!\p");
|
||||
static const u8 sText_TargetIsBeingSaltCured[] = _("{B_DEF_NAME_WITH_PREFIX} is being salt cured!");
|
||||
static const u8 sText_TargetIsHurtBySaltCure[] = _("{B_DEF_NAME_WITH_PREFIX} is hurt by {B_BUFF1}!");
|
||||
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_PkmnTellChillingReceptionJoke[] = _("{B_ATK_NAME_WITH_PREFIX} is preparing to tell a\nchillingly bad joke!");
|
||||
static const u8 sText_ZeroToHeroTransformation[] = _("{B_ATK_NAME_WITH_PREFIX} underwent a heroic\ntransformation!");
|
||||
|
@ -864,7 +863,6 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
|
|||
[STRINGID_ZEROTOHEROTRANSFORMATION - BATTLESTRINGS_TABLE_START] = sText_ZeroToHeroTransformation,
|
||||
[STRINGID_PKMNTELLCHILLINGRECEPTIONJOKE - BATTLESTRINGS_TABLE_START] = sText_PkmnTellChillingReceptionJoke,
|
||||
[STRINGID_MOVEBLOCKEDBYDYNAMAX - BATTLESTRINGS_TABLE_START] = sText_MoveBlockedByDynamax,
|
||||
[STRINGID_OPPORTUNISTCOPIED - BATTLESTRINGS_TABLE_START] = sText_OpportunistCopied,
|
||||
[STRINGID_TARGETISHURTBYSALTCURE - BATTLESTRINGS_TABLE_START] = sText_TargetIsHurtBySaltCure,
|
||||
[STRINGID_TARGETISBEINGSALTCURED - BATTLESTRINGS_TABLE_START] = sText_TargetIsBeingSaltCured,
|
||||
[STRINGID_CURRENTMOVECANTSELECT - BATTLESTRINGS_TABLE_START] = sText_CurrentMoveCantSelect,
|
||||
|
|
|
@ -6412,6 +6412,7 @@ static void Cmd_moveend(void)
|
|||
gBattleStruct->bouncedMoveIsUsed = FALSE;
|
||||
gBattleStruct->enduredDamage = 0;
|
||||
gBattleStruct->additionalEffectsCounter = 0;
|
||||
memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts));
|
||||
gBattleScripting.moveendState++;
|
||||
break;
|
||||
case MOVEEND_COUNT:
|
||||
|
@ -11786,27 +11787,35 @@ static u32 ChangeStatBuffs(s8 statValue, u32 statId, u32 flags, const u8 *BS_ptr
|
|||
}
|
||||
else
|
||||
{
|
||||
u32 statIncrease;
|
||||
if ((statValue + gBattleMons[battler].statStages[statId]) > MAX_STAT_STAGE)
|
||||
statIncrease = MAX_STAT_STAGE - gBattleMons[battler].statStages[statId];
|
||||
else
|
||||
statIncrease = statValue;
|
||||
|
||||
gBattleCommunication[MULTISTRING_CHOOSER] = (gBattlerTarget == battler);
|
||||
gProtectStructs[battler].statRaised = TRUE;
|
||||
|
||||
// check mirror herb
|
||||
// Check Mirror Herb / Opportunist
|
||||
for (index = 0; index < gBattlersCount; index++)
|
||||
{
|
||||
if (GetBattlerSide(index) == GetBattlerSide(battler))
|
||||
continue; // Only triggers on opposing side
|
||||
|
||||
if (GetBattlerAbility(index) == ABILITY_OPPORTUNIST
|
||||
&& gProtectStructs[battler].activateOpportunist == 0) // don't activate opportunist on other mon's opportunist raises
|
||||
&& gProtectStructs[battler].activateOpportunist == 0) // don't activate opportunist on other mon's opportunist raises
|
||||
{
|
||||
gProtectStructs[index].activateOpportunist = 2; // set stats to copy
|
||||
gQueuedStatBoosts[index].stats |= (1 << (statId - 1)); // -1 to start at atk
|
||||
gQueuedStatBoosts[index].statChanges[statId - 1] += statValue; // cumulative in case of multiple opponent boosts
|
||||
}
|
||||
else if (GetBattlerHoldEffect(index, TRUE) == HOLD_EFFECT_MIRROR_HERB
|
||||
&& gBattleMons[index].statStages[statId] < MAX_STAT_STAGE)
|
||||
if (GetBattlerHoldEffect(index, TRUE) == HOLD_EFFECT_MIRROR_HERB)
|
||||
{
|
||||
gProtectStructs[index].eatMirrorHerb = 1;
|
||||
}
|
||||
|
||||
if (gProtectStructs[index].activateOpportunist == 2 || gProtectStructs[index].eatMirrorHerb == 1)
|
||||
{
|
||||
gQueuedStatBoosts[index].stats |= (1 << (statId - 1)); // -1 to start at atk
|
||||
gQueuedStatBoosts[index].statChanges[statId - 1] = statValue;
|
||||
gQueuedStatBoosts[index].statChanges[statId - 1] += statIncrease;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17005,3 +17014,38 @@ void BS_TryQuash(void)
|
|||
}
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
|
||||
void BS_CopyFoesStatIncrease(void)
|
||||
{
|
||||
NATIVE_ARGS(u8 battler, const u8 *jumpInstr);
|
||||
u32 stat = 0;
|
||||
u32 battler = GetBattlerForBattleScript(cmd->battler);
|
||||
|
||||
if (gQueuedStatBoosts[battler].stats == 0)
|
||||
{
|
||||
for (stat = 0; stat < (NUM_BATTLE_STATS - 1); stat++)
|
||||
{
|
||||
if (gQueuedStatBoosts[battler].statChanges[stat] != 0)
|
||||
gQueuedStatBoosts[battler].stats |= (1 << stat);
|
||||
}
|
||||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||||
return;
|
||||
}
|
||||
|
||||
for (stat = 0; stat < (NUM_BATTLE_STATS - 1); stat++)
|
||||
{
|
||||
if (gQueuedStatBoosts[battler].stats & (1 << stat))
|
||||
{
|
||||
if (gQueuedStatBoosts[battler].statChanges[stat] <= -1)
|
||||
SET_STATCHANGER(stat + 1, abs(gQueuedStatBoosts[battler].statChanges[stat]), TRUE);
|
||||
else
|
||||
SET_STATCHANGER(stat + 1, gQueuedStatBoosts[battler].statChanges[stat], FALSE);
|
||||
|
||||
gQueuedStatBoosts[battler].stats &= ~(1 << stat);
|
||||
gBattlerTarget = battler;
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||||
}
|
||||
|
|
|
@ -5874,9 +5874,40 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
|
|||
case ABILITY_OPPORTUNIST:
|
||||
if (gProtectStructs[battler].activateOpportunist == 2)
|
||||
{
|
||||
gBattleScripting.savedBattler = gBattlerAttacker;
|
||||
gBattleScripting.battler = gBattlerAttacker = gBattlerAbility = battler;
|
||||
bool32 statBuffMoreThan1 = FALSE;
|
||||
bool32 handleSpeedAnimLater = FALSE;
|
||||
gBattleScripting.animArg1 = 0;
|
||||
gBattleScripting.battler = battler;
|
||||
gProtectStructs[battler].activateOpportunist--;
|
||||
|
||||
for (i = 0; i < (NUM_BATTLE_STATS - 1); i++)
|
||||
{
|
||||
if ((gQueuedStatBoosts[battler].stats & (1 << i)) == 0)
|
||||
continue;
|
||||
|
||||
if (i == STAT_SPEED)
|
||||
{
|
||||
handleSpeedAnimLater = TRUE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!statBuffMoreThan1)
|
||||
statBuffMoreThan1 = ((gQueuedStatBoosts[battler].stats & (1 << i)) > 1);
|
||||
|
||||
if (gBattleScripting.animArg1 != 0) //Already set in a different stat so now boosting multiple stats
|
||||
gBattleScripting.animArg1 = (!statBuffMoreThan1 ? STAT_ANIM_MULTIPLE_PLUS1 : STAT_ANIM_MULTIPLE_PLUS2);
|
||||
else
|
||||
gBattleScripting.animArg1 = GET_STAT_BUFF_ID((i + 1)) + (!statBuffMoreThan1 ? STAT_ANIM_PLUS1 : STAT_ANIM_PLUS2);
|
||||
|
||||
}
|
||||
if (handleSpeedAnimLater)
|
||||
{
|
||||
if (gBattleScripting.animArg1 != 0) //Already set in a different stat so now boosting multiple stats
|
||||
gBattleScripting.animArg1 = (!statBuffMoreThan1 ? STAT_ANIM_MULTIPLE_PLUS1 : STAT_ANIM_MULTIPLE_PLUS2);
|
||||
else
|
||||
gBattleScripting.animArg1 = GET_STAT_BUFF_ID((STAT_SPEED + 1)) + (!statBuffMoreThan1 ? STAT_ANIM_PLUS1 : STAT_ANIM_PLUS2);
|
||||
}
|
||||
|
||||
BattleScriptPushCursorAndCallback(BattleScript_OpportunistCopyStatChange);
|
||||
effect = 1;
|
||||
}
|
||||
|
@ -6815,10 +6846,43 @@ static u8 TryConsumeMirrorHerb(u32 battler, bool32 execute)
|
|||
|
||||
if (gProtectStructs[battler].eatMirrorHerb)
|
||||
{
|
||||
u32 i;
|
||||
bool32 statBuffMoreThan1 = FALSE;
|
||||
bool32 handleSpeedAnimLater = FALSE;
|
||||
gBattleScripting.animArg1 = 0;
|
||||
|
||||
gLastUsedItem = gBattleMons[battler].item;
|
||||
gBattleScripting.savedBattler = gBattlerAttacker;
|
||||
gBattleScripting.battler = gBattlerAttacker = battler;
|
||||
gBattleScripting.battler = battler;
|
||||
gProtectStructs[battler].eatMirrorHerb = 0;
|
||||
|
||||
for (i = 0; i < (NUM_BATTLE_STATS - 1); i++)
|
||||
{
|
||||
if ((gQueuedStatBoosts[battler].stats & (1 << i)) == 0)
|
||||
continue;
|
||||
|
||||
if (i == STAT_SPEED)
|
||||
{
|
||||
handleSpeedAnimLater = TRUE;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!statBuffMoreThan1)
|
||||
statBuffMoreThan1 = ((gQueuedStatBoosts[battler].stats & (1 << i)) > 1);
|
||||
|
||||
if (gBattleScripting.animArg1 != 0) //Already set in a different stat so now boosting multiple stats
|
||||
gBattleScripting.animArg1 = (!statBuffMoreThan1 ? STAT_ANIM_MULTIPLE_PLUS1 : STAT_ANIM_MULTIPLE_PLUS2);
|
||||
else
|
||||
gBattleScripting.animArg1 = GET_STAT_BUFF_ID((i + 1)) + (!statBuffMoreThan1 ? STAT_ANIM_PLUS1 : STAT_ANIM_PLUS2);
|
||||
|
||||
}
|
||||
if (handleSpeedAnimLater)
|
||||
{
|
||||
if (gBattleScripting.animArg1 != 0) //Already set in a different stat so now boosting multiple stats
|
||||
gBattleScripting.animArg1 = (!statBuffMoreThan1 ? STAT_ANIM_MULTIPLE_PLUS1 : STAT_ANIM_MULTIPLE_PLUS2);
|
||||
else
|
||||
gBattleScripting.animArg1 = GET_STAT_BUFF_ID((STAT_SPEED + 1)) + (!statBuffMoreThan1 ? STAT_ANIM_PLUS1 : STAT_ANIM_PLUS2);
|
||||
}
|
||||
|
||||
if (execute)
|
||||
{
|
||||
BattleScriptExecute(BattleScript_MirrorHerbCopyStatChangeEnd2);
|
||||
|
@ -7345,6 +7409,9 @@ u8 ItemBattleEffects(u8 caseID, u32 battler, bool32 moveTurn)
|
|||
BattleScriptPushCursorAndCallback(BattleScript_BerserkGeneRet);
|
||||
effect = ITEM_STATS_CHANGE;
|
||||
break;
|
||||
case HOLD_EFFECT_MIRROR_HERB:
|
||||
effect = TryConsumeMirrorHerb(battler, TRUE);
|
||||
break;
|
||||
}
|
||||
if (effect != 0)
|
||||
{
|
||||
|
|
|
@ -16,16 +16,16 @@ SINGLE_BATTLE_TEST("Opportunist only copies foe's positive stat changes in a tur
|
|||
OPPONENT(SPECIES_ESPATHRA) { Speed(5); Ability(ability); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SHELL_SMASH); }
|
||||
TURN { MOVE(player, MOVE_TACKLE); MOVE(opponent, MOVE_TACKLE); }
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); }
|
||||
} SCENE {
|
||||
if (ability == ABILITY_FRISK) {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SMASH, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
HP_BAR(player, captureDamage: &results[i].damage);
|
||||
} else {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SMASH, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
HP_BAR(player, captureDamage: &results[i].damage);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
}
|
||||
} FINALLY {
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(2.0), results[1].damage);
|
||||
|
@ -78,12 +78,10 @@ DOUBLE_BATTLE_TEST("Opportunist raises Attack only once when partner has Intimid
|
|||
if ((abilityLeft == ABILITY_CONTRARY && abilityRight != ABILITY_CONTRARY)
|
||||
|| (abilityLeft != ABILITY_CONTRARY && abilityRight == ABILITY_CONTRARY)) {
|
||||
ABILITY_POPUP(playerRight, ABILITY_OPPORTUNIST);
|
||||
MESSAGE("Espathra copied its opponent's stat changes!");
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
|
||||
MESSAGE("Espathra's Attack rose!");
|
||||
} else if (abilityLeft == ABILITY_CONTRARY && abilityRight == ABILITY_CONTRARY) {
|
||||
ABILITY_POPUP(playerRight, ABILITY_OPPORTUNIST);
|
||||
MESSAGE("Espathra copied its opponent's stat changes!");
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
|
||||
MESSAGE("Espathra's Attack sharply rose!");
|
||||
}
|
||||
|
@ -124,6 +122,177 @@ SINGLE_BATTLE_TEST("Opportunist does not accumulate opposing mon's stat changes"
|
|||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Opportunist doesn't copy ally stat increases");
|
||||
TO_DO_BATTLE_TEST("Opportunist doesn't copy foe stat increases gained via Opportunist");
|
||||
TO_DO_BATTLE_TEST("Opportunist copies foe stat increased gained via Swagger and Flatter");
|
||||
SINGLE_BATTLE_TEST("Opportunist copies each stat increase individually from ability and move")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); }
|
||||
OPPONENT(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); }
|
||||
} WHEN {
|
||||
TURN { SWITCH(player, 1); }
|
||||
TURN { MOVE(player, MOVE_SWORDS_DANCE); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(player, ABILITY_INTREPID_SWORD);
|
||||
ABILITY_POPUP(opponent, ABILITY_OPPORTUNIST);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, player);
|
||||
ABILITY_POPUP(opponent, ABILITY_OPPORTUNIST);
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 3);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Opportunist doesn't copy foe stat increases gained via Opportunist")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); }
|
||||
OPPONENT(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SWORDS_DANCE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, player);
|
||||
ABILITY_POPUP(opponent, ABILITY_OPPORTUNIST);
|
||||
NOT ABILITY_POPUP(player, ABILITY_OPPORTUNIST);
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->statStages[STAT_ATK], player->statStages[STAT_ATK]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Opportunist copies foe stat increase gained via Swagger and Flatter")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); }
|
||||
OPPONENT(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_FLATTER); }
|
||||
TURN { MOVE(opponent, MOVE_SWAGGER); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLATTER, opponent);
|
||||
ABILITY_POPUP(opponent, ABILITY_OPPORTUNIST);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SWAGGER, opponent);
|
||||
ABILITY_POPUP(opponent, ABILITY_OPPORTUNIST);
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
|
||||
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Opportunist doesn't copy ally stat increases")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_SWORDS_DANCE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, playerLeft);
|
||||
NOT ABILITY_POPUP(playerRight, ABILITY_OPPORTUNIST);
|
||||
} THEN {
|
||||
EXPECT_EQ(playerRight->statStages[STAT_SPATK], DEFAULT_STAT_STAGE );
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Opportunist copies the stat increase of each opposing mon")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponentRight, MOVE_SWORDS_DANCE); MOVE(opponentLeft, MOVE_SWORDS_DANCE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, opponentRight);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, opponentLeft);
|
||||
ABILITY_POPUP(playerRight, ABILITY_OPPORTUNIST);
|
||||
} THEN {
|
||||
EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 4);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DOUBLE_BATTLE_TEST("Opportunist copies the stat of each pokemon that were raised at the same time")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); }
|
||||
OPPONENT(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); }
|
||||
OPPONENT(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); }
|
||||
} WHEN {
|
||||
TURN { }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(opponentLeft, ABILITY_INTREPID_SWORD);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
|
||||
ABILITY_POPUP(opponentRight, ABILITY_INTREPID_SWORD);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
|
||||
ABILITY_POPUP(playerRight, ABILITY_OPPORTUNIST);
|
||||
MESSAGE("Espathra's Attack sharply rose!");
|
||||
} THEN {
|
||||
EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Opportunist copies the increase not the stages")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CHARM); MOVE(opponent, MOVE_CHARM); }
|
||||
TURN { MOVE(player, MOVE_CHARM); MOVE(opponent, MOVE_CHARM); }
|
||||
TURN { MOVE(player, MOVE_CHARM); MOVE(opponent, MOVE_GROWL); }
|
||||
TURN { MOVE(player, MOVE_BELLY_DRUM); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARM, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARM, opponent);
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARM, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARM, opponent);
|
||||
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CHARM, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_GROWL, opponent);
|
||||
|
||||
ABILITY_POPUP(opponent, ABILITY_OPPORTUNIST);
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 5); // + 11
|
||||
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 6); // + 11
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Opportunist copies the stat increase from the incoming mon")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { HP(1); }
|
||||
OPPONENT(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_TACKLE); SEND_OUT(opponent, 1); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
|
||||
ABILITY_POPUP(opponent, ABILITY_INTREPID_SWORD);
|
||||
ABILITY_POPUP(player, ABILITY_OPPORTUNIST);
|
||||
} THEN {
|
||||
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Opportunist and Mirror Herb stack stat increases")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); }
|
||||
OPPONENT(SPECIES_ESPATHRA) { Ability(ABILITY_OPPORTUNIST); Item(ITEM_MIRROR_HERB); }
|
||||
} WHEN {
|
||||
TURN { }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(player, ABILITY_INTREPID_SWORD);
|
||||
ABILITY_POPUP(opponent, ABILITY_OPPORTUNIST);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 2);
|
||||
EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
|
||||
EXPECT_EQ(opponent->statStages[STAT_SPEED], DEFAULT_STAT_STAGE);
|
||||
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
|
||||
EXPECT_EQ(opponent->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,3 +73,18 @@ DOUBLE_BATTLE_TEST("Mirror Herb does not trigger for Ally's Soul Heart's stat ra
|
|||
EXPECT_EQ(playerLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Mirror Herb copies the boost gained by an ability")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_MIRROR_HERB); }
|
||||
} WHEN {
|
||||
TURN { }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(player, ABILITY_INTREPID_SWORD);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
|
||||
} THEN {
|
||||
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue