Merge branch '_RHH/master' into _RHH/upcoming

# Conflicts:
#	asm/macros/battle_script.inc
#	include/constants/battle_string_ids.h
#	src/battle_ai_switch_items.c
#	src/battle_main.c
#	src/battle_script_commands.c
#	src/battle_util.c
#	src/frontier_util.c
#	test/battle/ai/ai.c
This commit is contained in:
Eduardo Quezada 2024-07-19 09:20:05 -04:00
commit 98eb4e5027
22 changed files with 658 additions and 263 deletions

View File

@ -484,7 +484,7 @@ $(DATA_SRC_SUBDIR)/pokemon/teachable_learnsets.h: $(DATA_ASM_BUILDDIR)/event_scr
define TEST_DEP
$1: $2 $$(shell $(SCANINC) -I include -I tools/agbcc/include -I gflib $2)
@echo "$$(CC1) <flags> -o $$@ $$<"
@$$(CPP) $$(CPPFLAGS) $$< | $$(PREPROC) $$< charmap.txt -i | $$(CC1) $$(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $$(AS) $$(ASFLAGS) -o $$@ -
@$$(CPP) $$(CPPFLAGS) $$< | $$(PREPROC) -i $$< charmap.txt | $$(CC1) $$(CFLAGS) -o - - | cat - <(echo -e ".text\n\t.align\t2, 0") | $$(AS) $$(ASFLAGS) -o $$@ -
endef
$(foreach src, $(TEST_SRCS), $(eval $(call TEST_DEP,$(patsubst $(TEST_SUBDIR)/%.c,$(TEST_BUILDDIR)/%.o,$(src)),$(src),$(patsubst $(TEST_SUBDIR)/%.c,%,$(src)))))

View File

@ -1652,6 +1652,12 @@
.4byte \failInstr
.endm
.macro copyfoesstatincrease battler:req, failInstr:req
callnative BS_CopyFoesStatIncrease
.byte \battler
.4byte \failInstr
.endm
.macro removeweather
callnative BS_RemoveWeather
.endm

View File

@ -853,13 +853,11 @@ BattleScript_OctolockEndTurn::
playstatchangeanimation BS_ATTACKER, BIT_DEF | BIT_SPDEF, STAT_CHANGE_NEGATIVE
setstatchanger STAT_DEF, 1, TRUE
statbuffchange STAT_CHANGE_ALLOW_PTR | STAT_CHANGE_NOT_PROTECT_AFFECTED, BattleScript_OctolockTryLowerSpDef
BattleScript_OctolockTryLowerDef:
printfromtable gStatDownStringIds
waitmessage B_WAIT_TIME_LONG
BattleScript_OctolockTryLowerSpDef:
setstatchanger STAT_SPDEF, 1, TRUE
statbuffchange STAT_CHANGE_ALLOW_PTR | STAT_CHANGE_NOT_PROTECT_AFFECTED, BattleScript_OctlockTurnDmgEnd
BattleScript_OctolockTurnDmgPrintMsg:
printfromtable gStatDownStringIds
waitmessage B_WAIT_TIME_LONG
BattleScript_OctlockTurnDmgEnd:
@ -999,7 +997,6 @@ BattleScript_EffectStuffCheeks::
jumpifnotberry BS_ATTACKER, BattleScript_ButItFailed
attackanimation
waitanimation
BattleScript_StuffCheeksEatBerry:
setbyte sBERRY_OVERRIDE, 1
orword gHitMarker, HITMARKER_DISABLE_ANIMATION
consumeberry BS_ATTACKER, TRUE
@ -1211,10 +1208,6 @@ BattleScript_StrengthSapLower:
waitmessage B_WAIT_TIME_LONG
goto BattleScript_StrengthSapHp
@ Drain HP without lowering a stat
BattleScript_StrengthSapTryHp:
jumpiffullhp BS_ATTACKER, BattleScript_ButItFailed
attackanimation
waitanimation
BattleScript_StrengthSapHp:
jumpifability BS_TARGET, ABILITY_LIQUID_OOZE, BattleScript_StrengthSapManipulateDmg
jumpifstatus3 BS_ATTACKER, STATUS3_HEAL_BLOCK, BattleScript_MoveEnd
@ -1345,8 +1338,6 @@ BattleScript_EffectPartingShotTrySpAtk:
BattleScript_EffectPartingShotSwitch:
moveendall
goto BattleScript_MoveSwitch
BattleScript_PartingShotEnd:
end
BattleScript_EffectPowder::
attackcanceler
@ -1549,7 +1540,6 @@ BattleScript_RototillerLoop:
jumpifstat BS_TARGET, CMP_EQUAL, STAT_SPATK, MAX_STAT_STAGE, BattleScript_RototillerCantRaiseMultipleStats
BattleScript_RototillerCheckAffected:
jumpifnotrototilleraffected BS_TARGET, BattleScript_RototillerNoEffect
BattleScript_RototillerAffected:
setbyte sSTAT_ANIM_PLAYED, FALSE
playstatchangeanimation BS_TARGET, BIT_ATK | BIT_SPATK, 0
setstatchanger STAT_ATK, 1, FALSE
@ -1780,7 +1770,6 @@ BattleScript_EffectAutotomize::
BattleScript_AutotomizeAttackAnim::
attackanimation
waitanimation
BattleScript_AutotomizeDoAnim::
setgraphicalstatchangevalues
playanimation BS_ATTACKER, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1
BattleScript_AutotomizePrintString::
@ -2691,7 +2680,6 @@ BattleScript_TryTailwindAbilitiesLoop_Iter:
BattleScript_TryTailwindAbilitiesLoop_Increment:
addbyte gBattlerTarget, 0x1
jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_TryTailwindAbilitiesLoop_Iter
BattleScript_TryTailwindAbilitiesLoop_Ret:
restoretarget
return
@ -2792,7 +2780,6 @@ BattleScript_EffectPlaceholder::
goto BattleScript_MoveEnd
BattleScript_EffectHit::
BattleScript_HitFromAtkCanceler::
attackcanceler
BattleScript_HitFromAccCheck::
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
@ -3285,14 +3272,6 @@ BattleScript_RoarBlockedByDynamax:
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_EffectMultiHit::
attackcanceler
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
attackstring
ppreduce
setmultihitcounter 0
initmultihitstring
sethword sMULTIHIT_EFFECT, 0
BattleScript_MultiHitLoop::
jumpifhasnohp BS_ATTACKER, BattleScript_MultiHitEnd
jumpifhasnohp BS_TARGET, BattleScript_MultiHitPrintStrings
@ -3705,7 +3684,6 @@ BattleScript_EffectParalyze::
jumpifshieldsdown BS_TARGET, BattleScript_AbilityProtectsDoesntAffect
jumpifsubstituteblocks BattleScript_ButItFailed
typecalc
BattleScript_BattleScript_EffectParalyzeNoTypeCalc:
jumpifmovehadnoeffect BattleScript_ButItFailed
jumpifstatus BS_TARGET, STATUS1_PARALYSIS, BattleScript_AlreadyParalyzed
jumpifelectricabilityaffected BS_TARGET, ABILITY_VOLT_ABSORB, BattleScript_VoltAbsorbHeal
@ -4632,7 +4610,6 @@ BattleScript_EffectTeleport::
.else
jumpifbattletype BATTLE_TYPE_TRAINER, BattleScript_FailedFromAtkCanceler
.endif
BattleScript_EffectTeleportTryToRunAway:
attackcanceler
attackstring
ppreduce
@ -6501,7 +6478,6 @@ BattleScript_WindPowerActivates::
setcharge BS_TARGET
printstring STRINGID_BEINGHITCHARGEDPKMNWITHPOWER
waitmessage B_WAIT_TIME_LONG
BattleScript_WindPowerActivates_Ret:
return
BattleScript_ToxicDebrisActivates::
@ -7203,7 +7179,6 @@ BattleScript_CottonDownTargetSpeedCantGoLower:
BattleScript_CottonDownLoopIncrement:
addbyte gBattlerTarget, 1
jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_CottonDownLoop
BattleScript_CottonDownReturn:
swapattackerwithtarget
copybyte gBattlerAttacker, sSAVED_BATTLER
return
@ -7822,7 +7797,6 @@ BattleScript_ActivateWeatherAbilities:
BattleScript_ActivateWeatherAbilities_Loop:
copybyte sBATTLER, gBattlerTarget
activateweatherchangeabilities BS_TARGET
BattleScript_ActivateWeatherAbilities_Increment:
addbyte gBattlerTarget, 1
jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_ActivateWeatherAbilities_Loop
restoretarget
@ -7881,7 +7855,6 @@ BattleScript_IntimidateEffect_WaitString:
BattleScript_IntimidateLoopIncrement:
addbyte gBattlerTarget, 1
jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_IntimidateLoop
BattleScript_IntimidateEnd:
copybyte sBATTLER, gBattlerAttacker
destroyabilitypopup
restoretarget
@ -7947,7 +7920,6 @@ BattleScript_SupersweetSyrupEffect_WaitString:
BattleScript_SupersweetSyrupLoopIncrement:
addbyte gBattlerTarget, 1
jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_SupersweetSyrupLoop
BattleScript_SupersweetSyrupEnd:
copybyte sBATTLER, gBattlerAttacker
destroyabilitypopup
restoretarget
@ -8106,7 +8078,6 @@ BattleScript_ActivateTerrainSeed:
removeitem BS_TARGET
BattleScript_ActivateTerrainAbility:
activateterrainchangeabilities BS_TARGET
BattleScript_ActivateTerrainEffects_Increment:
addbyte gBattlerTarget, 0x1
jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_ActivateTerrainSeed
restoretarget
@ -8362,14 +8333,12 @@ BattleScript_GrassyTerrainLoop:
checkgrassyterrainheal BS_ATTACKER, BattleScript_GrassyTerrainLoopIncrement
printstring STRINGID_GRASSYTERRAINHEALS
waitmessage B_WAIT_TIME_LONG
BattleScript_GrassyTerrainHpChange:
orword gHitMarker, HITMARKER_IGNORE_BIDE | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE
healthbarupdate BS_ATTACKER
datahpupdate BS_ATTACKER
BattleScript_GrassyTerrainLoopIncrement::
addbyte gBattleCommunication, 1
jumpifbytenotequal gBattleCommunication, gBattlersCount, BattleScript_GrassyTerrainLoop
BattleScript_GrassyTerrainLoopEnd::
bicword gHitMarker, HITMARKER_IGNORE_BIDE | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE
jumpifword CMP_COMMON_BITS, gFieldStatuses, STATUS_FIELD_TERRAIN_PERMANENT, BattleScript_GrassyTerrainHealEnd
BattleScript_GrassyTerrainHealEnd:
@ -8378,7 +8347,6 @@ BattleScript_GrassyTerrainHealEnd:
BattleScript_AbilityNoSpecificStatLoss::
pause B_WAIT_TIME_SHORT
call BattleScript_AbilityPopUp
BattleScript_AbilityNoSpecificStatLossPrint:
printstring STRINGID_PKMNSXPREVENTSYLOSS
waitmessage B_WAIT_TIME_LONG
setbyte cMULTISTRING_CHOOSER, B_MSG_STAT_FELL_EMPTY
@ -9165,16 +9133,6 @@ BattleScript_ArenaTurnBeginning::
volumeup
end2
@ Unused
BattleScript_ArenaNothingDecided::
playse SE_DING_DONG
arenadrawreftextbox
arenajudgmentstring B_MSG_REF_NOTHING_IS_DECIDED
arenawaitmessage B_MSG_REF_NOTHING_IS_DECIDED
pause B_WAIT_TIME_LONG
arenaerasereftextbox
end2
BattleScript_ArenaDoJudgment::
makevisible BS_PLAYER1
waitstate
@ -9274,7 +9232,6 @@ BattleScript_TotemFlaredToLife::
call BattleScript_ApplyTotemVarBoost
end2
@ remove the mirror herb, do totem loop
BattleScript_MirrorHerbCopyStatChangeEnd2::
call BattleScript_MirrorHerbCopyStatChange
end2
@ -9284,16 +9241,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::
@ -9467,7 +9432,6 @@ BattleScript_EffectExtremeEvoboost::
BattleScript_ExtremeEvoboostAnim:
attackanimation
waitanimation
BattleScript_ExtremeEvoboostAtk::
setbyte sSTAT_ANIM_PLAYED, FALSE
playstatchangeanimation BS_ATTACKER, BIT_ATK | BIT_DEF | BIT_SPEED | BIT_SPATK | BIT_SPDEF, 0x0
setstatchanger STAT_ATK, 2, FALSE
@ -10025,7 +9989,6 @@ BattleScript_CouldntFullyProtect::
return
BattleScript_BerserkGeneRet::
BattleScript_BerserkGeneRet_Anim:
savetarget
copybyte gBattlerTarget, sBATTLER
statbuffchange STAT_CHANGE_ALLOW_PTR, BattleScript_BerserkGeneRet_TryConfuse

View File

@ -159,7 +159,6 @@ BattleScript_SafariBallThrow::
BattleScript_SuccessBallThrow::
setbyte sMON_CAUGHT, TRUE
incrementgamestat GAME_STAT_POKEMON_CAPTURES
BattleScript_PrintCaughtMonInfo::
printstring STRINGID_GOTCHAPKMNCAUGHTPLAYER
jumpifbyte CMP_NOT_EQUAL, sEXP_CATCH, TRUE, BattleScript_TryPrintCaughtMonInfo
setbyte sGIVEEXP_STATE, 0

View File

@ -632,7 +632,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
@ -676,7 +676,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]

View File

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

View File

@ -670,51 +670,50 @@
#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_PKMNTERASTALLIZEDINTO 708
#define STRINGID_BOOSTERENERGYACTIVATES 709
#define STRINGID_FOGCREPTUP 710
#define STRINGID_FOGISDEEP 711
#define STRINGID_FOGLIFTED 712
#define STRINGID_PKMNMADESHELLGLEAM 713
#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 STRINGID_PKMNTERASTALLIZEDINTO 707
#define STRINGID_BOOSTERENERGYACTIVATES 708
#define STRINGID_FOGCREPTUP 709
#define STRINGID_FOGISDEEP 710
#define STRINGID_FOGLIFTED 711
#define STRINGID_PKMNMADESHELLGLEAM 712
#define BATTLESTRINGS_COUNT 714
#define BATTLESTRINGS_COUNT 713
// This is the string id that gBattleStringsTable starts with.
// String ids before this (e.g. STRINGID_INTROMSG) are not in the table,

View File

@ -995,6 +995,8 @@ void SendOut(u32 sourceLine, struct BattlePokemon *, u32 partyIndex);
// Static const is needed to make the modern compiler put the pattern variable in the .rodata section, instead of putting it on stack(which can break the game).
#define MESSAGE(pattern) do {static const u8 msg[] = _(pattern); QueueMessage(__LINE__, msg);} while (0)
#define STATUS_ICON(battler, status) QueueStatus(__LINE__, battler, (struct StatusEventContext) { status })
#define FREEZE_OR_FROSTBURN_STATUS(battler, isFrostbite) \
(B_USE_FROSTBITE ? STATUS_ICON(battler, frostbite: isFrostbite) : STATUS_ICON(battler, freeze: isFrostbite))
#define SWITCH_OUT_MESSAGE(name) ONE_OF { \
MESSAGE(name ", that's enough! Come back!"); \

View File

@ -1997,12 +1997,12 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
else if (typeMatchupId != PARTY_SIZE) return typeMatchupId;
else if (defensiveMonId != PARTY_SIZE) return defensiveMonId;
else if (batonPassId != PARTY_SIZE) return batonPassId;
// If ace mon is the last available Pokemon and U-Turn/Volt Switch was used - switch to the mon.
else if (aceMonId != PARTY_SIZE
&& (gMovesInfo[gLastUsedMove].effect == EFFECT_HIT_ESCAPE || gMovesInfo[gLastUsedMove].effect == EFFECT_PARTING_SHOT || gMovesInfo[gLastUsedMove].effect == EFFECT_BATON_PASS))
return aceMonId;
}
// If ace mon is the last available Pokemon and U-Turn/Volt Switch was used - switch to the mon.
if (aceMonId != PARTY_SIZE
&& (gMovesInfo[gLastUsedMove].effect == EFFECT_HIT_ESCAPE || gMovesInfo[gLastUsedMove].effect == EFFECT_PARTING_SHOT || gMovesInfo[gLastUsedMove].effect == EFFECT_BATON_PASS))
return aceMonId;
return PARTY_SIZE;
}

View File

@ -3733,8 +3733,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.
@ -3769,34 +3769,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 (!IsBattlerAlive(i) || 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 (!IsBattlerAlive(i) || 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++)
@ -3807,110 +3808,136 @@ 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);
AssignUsableGimmicks();
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);
AssignUsableGimmicks();
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)

View File

@ -829,7 +829,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!");
@ -871,7 +870,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,

View File

@ -6405,6 +6405,7 @@ static void Cmd_moveend(void)
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE)
SetActiveGimmick(gBattlerAttacker, GIMMICK_NONE);
gBattleStruct->distortedTypeMatchups = 0;
memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts));
gBattleScripting.moveendState++;
break;
case MOVEEND_COUNT:
@ -9981,7 +9982,7 @@ static void Cmd_various(void)
gBattleCommunication[MULTISTRING_CHOOSER] = 3;
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) && CanBeSlept(gBattlerTarget, targetAbility))
gBattleCommunication[MULTISTRING_CHOOSER] = 4;
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_FROSTBITE) && CanBeFrozen(gBattlerTarget))
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_FROSTBITE) && CanGetFrostbite(gBattlerTarget))
gBattleCommunication[MULTISTRING_CHOOSER] = 5;
else
{
@ -11829,27 +11830,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;
}
}
}
@ -17056,6 +17065,41 @@ 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;
}
void BS_RemoveWeather(void)
{
NATIVE_ARGS();

View File

@ -6025,9 +6025,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;
}
@ -6966,10 +6997,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);
@ -7496,6 +7560,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;
case HOLD_EFFECT_BOOSTER_ENERGY:
if (!(gBattleStruct->boosterEnergyActivates & gBitTable[battler])
&& (((GetBattlerAbility(battler) == ABILITY_PROTOSYNTHESIS) && !(gBattleWeather & B_WEATHER_SUN))

View File

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

View File

@ -67,6 +67,19 @@ AI_DOUBLE_BATTLE_TEST("AI will not try to switch for the same pokemon for 2 spot
}
}
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: U-Turn will send out Ace Mon if it's the only one remaining")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_U_TURN].effect == EFFECT_HIT_ESCAPE);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_ACE_POKEMON);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_U_TURN); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { EXPECT_MOVE(opponent, MOVE_U_TURN); EXPECT_SEND_OUT(opponent, 1); }
}
}
// General AI_FLAG_SMART_MON_CHOICES behaviour
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Number of hits to KO calculation checks whether incoming damage is less than recurring healing to avoid an infinite loop")
{

View File

@ -24,7 +24,7 @@ SINGLE_BATTLE_TEST("Shaymin-Sky reverts to Shaymin-Land when frozen or frostbitt
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
if (move == MOVE_POWDER_SNOW) {
STATUS_ICON(player, freeze: TRUE);
FREEZE_OR_FROSTBURN_STATUS(player, TRUE);
NOT HP_BAR(player); // Regression caused by Mimikyu form change
MESSAGE("Shaymin transformed!");
} else {

View File

@ -72,7 +72,7 @@ SINGLE_BATTLE_TEST("Rawst and Lum Berries cure burn")
}
}
SINGLE_BATTLE_TEST("Aspear and Lum Berries cure freeze")
SINGLE_BATTLE_TEST("Aspear and Lum Berries cure freeze or frostbite")
{
u16 item;
@ -88,9 +88,9 @@ SINGLE_BATTLE_TEST("Aspear and Lum Berries cure freeze")
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ICE_PUNCH, player);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent);
STATUS_ICON(opponent, freeze: TRUE);
FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
STATUS_ICON(opponent, freeze: FALSE);
FREEZE_OR_FROSTBURN_STATUS(opponent, FALSE);
}
}

View File

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

View File

@ -7,7 +7,11 @@ ASSUMPTIONS
ASSUME(gMovesInfo[MOVE_BLIZZARD].accuracy == 70);
}
#if B_USE_FROSTBITE == TRUE
SINGLE_BATTLE_TEST("Powder Snow inflicts frostbite")
#else
SINGLE_BATTLE_TEST("Powder Snow inflicts freeze")
#endif
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
@ -18,11 +22,15 @@ SINGLE_BATTLE_TEST("Powder Snow inflicts freeze")
ANIMATION(ANIM_TYPE_MOVE, MOVE_POWDER_SNOW, player);
HP_BAR(opponent);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent);
STATUS_ICON(opponent, freeze: TRUE);
FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE);
}
}
#if B_USE_FROSTBITE == TRUE
SINGLE_BATTLE_TEST("Powder Snow cannot frostbite an Ice-type Pokémon")
#else
SINGLE_BATTLE_TEST("Powder Snow cannot freeze an Ice-type Pokémon")
#endif
{
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_SNORUNT].types[0] == TYPE_ICE);
@ -35,7 +43,7 @@ SINGLE_BATTLE_TEST("Powder Snow cannot freeze an Ice-type Pokémon")
HP_BAR(opponent);
NONE_OF {
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent);
STATUS_ICON(opponent, freeze: TRUE);
FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE);
}
}
}
@ -68,10 +76,18 @@ SINGLE_BATTLE_TEST("Blizzard bypasses accuracy checks in Hail and Snow")
}
#if B_STATUS_TYPE_IMMUNITY > GEN_1
#if B_USE_FROSTBITE == TRUE
SINGLE_BATTLE_TEST("Freezing Glare should frostbite Psychic-types")
#else
SINGLE_BATTLE_TEST("Freezing Glare should freeze Psychic-types")
#endif
#else
#if B_USE_FROSTBITE == TRUE
SINGLE_BATTLE_TEST("Freezing Glare shouldn't freeze Psychic-types")
#else
SINGLE_BATTLE_TEST("Freezing Glare shouldn't freeze Psychic-types")
#endif
#endif
{
GIVEN {
ASSUME(gSpeciesInfo[SPECIES_ARTICUNO_GALARIAN].types[0] == TYPE_PSYCHIC);
@ -86,11 +102,11 @@ SINGLE_BATTLE_TEST("Freezing Glare shouldn't freeze Psychic-types")
HP_BAR(opponent);
#if B_STATUS_TYPE_IMMUNITY > GEN_1
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent);
STATUS_ICON(opponent, freeze: TRUE);
FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE);
#else
NONE_OF {
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent);
STATUS_ICON(opponent, freeze: TRUE);
FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE);
}
#endif
}

View File

@ -6,7 +6,11 @@ ASSUMPTIONS
ASSUME(MoveHasAdditionalEffect(MOVE_TRI_ATTACK, MOVE_EFFECT_TRI_ATTACK) == TRUE);
}
#if B_USE_FROSTBITE == TRUE
SINGLE_BATTLE_TEST("Tri Attack can inflict paralysis, burn or frostbite")
#else
SINGLE_BATTLE_TEST("Tri Attack can inflict paralysis, burn or freeze")
#endif
{
u8 statusAnim;
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; }
@ -26,14 +30,18 @@ SINGLE_BATTLE_TEST("Tri Attack can inflict paralysis, burn or freeze")
if (statusAnim == B_ANIM_STATUS_BRN) {
STATUS_ICON(opponent, burn: TRUE);
} else if (statusAnim == B_ANIM_STATUS_FRZ) {
STATUS_ICON(opponent, freeze: TRUE);
FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE);
} else if (statusAnim == B_ANIM_STATUS_PRZ) {
STATUS_ICON(opponent, paralysis: TRUE);
}
}
}
#if B_USE_FROSTBITE == TRUE
SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/frostbite electric/fire/ice types respectively")
#else
SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze electric/fire/ice types respectively")
#endif
{
u8 statusAnim;
u16 species;
@ -42,7 +50,7 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze electric/fire/ice typ
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; species = SPECIES_RAICHU; }
#endif // B_PARALYZE_ELECTRIC
PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = MOVE_EFFECT_BURN; species = SPECIES_ARCANINE; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE; species = SPECIES_GLALIE; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE_OR_FROSTBITE; species = SPECIES_GLALIE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species);
@ -57,7 +65,7 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze electric/fire/ice typ
if (statusAnim == B_ANIM_STATUS_BRN) {
STATUS_ICON(opponent, burn: TRUE);
} else if (statusAnim == B_ANIM_STATUS_FRZ) {
STATUS_ICON(opponent, freeze: TRUE);
FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE);
} else if (statusAnim == B_ANIM_STATUS_PRZ) {
STATUS_ICON(opponent, paralysis: TRUE);
}
@ -65,7 +73,11 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze electric/fire/ice typ
}
}
#if B_USE_FROSTBITE == TRUE
SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/frostbite pokemon with abilities preventing respective statuses")
#else
SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze pokemon with abilities preventing respective statuses")
#endif
{
u8 statusAnim;
u16 species, ability;
@ -75,8 +87,8 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze pokemon with abilitie
PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = MOVE_EFFECT_BURN; species = SPECIES_DEWPIDER; ability = ABILITY_WATER_BUBBLE; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = MOVE_EFFECT_BURN; species = SPECIES_SEAKING; ability = ABILITY_WATER_VEIL; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = MOVE_EFFECT_BURN; species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE; species = SPECIES_CAMERUPT; ability = ABILITY_MAGMA_ARMOR; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE; species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE_OR_FROSTBITE; species = SPECIES_CAMERUPT; ability = ABILITY_MAGMA_ARMOR; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE_OR_FROSTBITE; species = SPECIES_KOMALA; ability = ABILITY_COMATOSE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
@ -92,7 +104,7 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze pokemon with abilitie
if (statusAnim == B_ANIM_STATUS_BRN) {
STATUS_ICON(opponent, burn: TRUE);
} else if (statusAnim == B_ANIM_STATUS_FRZ) {
STATUS_ICON(opponent, freeze: TRUE);
FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE);
} else if (statusAnim == B_ANIM_STATUS_PRZ) {
STATUS_ICON(opponent, paralysis: TRUE);
}
@ -100,13 +112,17 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze pokemon with abilitie
}
}
#if B_USE_FROSTBITE == TRUE
SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/frostbite a mon which is already statused")
#else
SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze a mon which is already statused")
#endif
{
u8 statusAnim;
u32 rng;
PARAMETRIZE { statusAnim = B_ANIM_STATUS_PRZ; rng = MOVE_EFFECT_PARALYSIS; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_BRN; rng = MOVE_EFFECT_BURN; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE; }
PARAMETRIZE { statusAnim = B_ANIM_STATUS_FRZ; rng = MOVE_EFFECT_FREEZE_OR_FROSTBITE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); }
@ -121,7 +137,7 @@ SINGLE_BATTLE_TEST("Tri Attack cannot paralyze/burn/freeze a mon which is alread
if (statusAnim == B_ANIM_STATUS_BRN) {
STATUS_ICON(opponent, burn: TRUE);
} else if (statusAnim == B_ANIM_STATUS_FRZ) {
STATUS_ICON(opponent, freeze: TRUE);
FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE);
} else if (statusAnim == B_ANIM_STATUS_PRZ) {
STATUS_ICON(opponent, paralysis: TRUE);
}

View File

@ -5,7 +5,7 @@ ASSUMPTIONS
{
ASSUME(MoveHasAdditionalEffect(MOVE_THUNDER_FANG, MOVE_EFFECT_PARALYSIS) == TRUE);
ASSUME(MoveHasAdditionalEffect(MOVE_THUNDER_FANG, MOVE_EFFECT_FLINCH) == TRUE);
ASSUME(MoveHasAdditionalEffect(MOVE_ICE_FANG, MOVE_EFFECT_FREEZE) == TRUE);
ASSUME(MoveHasAdditionalEffect(MOVE_ICE_FANG, MOVE_EFFECT_FREEZE_OR_FROSTBITE) == TRUE);
ASSUME(MoveHasAdditionalEffect(MOVE_ICE_FANG, MOVE_EFFECT_FLINCH) == TRUE);
ASSUME(MoveHasAdditionalEffect(MOVE_FIRE_FANG, MOVE_EFFECT_BURN) == TRUE);
ASSUME(MoveHasAdditionalEffect(MOVE_FIRE_FANG, MOVE_EFFECT_FLINCH) == TRUE);
@ -33,7 +33,7 @@ SINGLE_BATTLE_TEST("Thunder, Ice and Fire Fang inflict status 10% of the time")
STATUS_ICON(opponent, paralysis: TRUE);
} if (move == MOVE_ICE_FANG) {
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_FRZ, opponent);
STATUS_ICON(opponent, freeze: TRUE);
FREEZE_OR_FROSTBURN_STATUS(opponent, TRUE);
} if (move == MOVE_FIRE_FANG) {
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponent);
STATUS_ICON(opponent, burn: TRUE);

View File

@ -113,3 +113,50 @@ TEST("ModifyPersonalityForNature can set any nature")
ModifyPersonalityForNature(&personality, nature);
EXPECT_EQ(GetNatureFromPersonality(personality), nature);
}
static const struct TrainerMon sTestParty2[] =
{
{
.species = SPECIES_WYNAUT,
.lvl = 5,
},
{
.species = SPECIES_WYNAUT,
.lvl = 5,
},
{
.species = SPECIES_WYNAUT,
.lvl = 5,
},
{
.species = SPECIES_WYNAUT,
.lvl = 5,
},
{
.species = SPECIES_WYNAUT,
.lvl = 5,
},
{
.species = SPECIES_WYNAUT,
.lvl = 5,
},
};
static const struct Trainer sTestTrainer2 =
{
.trainerName = _("Test2"),
.trainerClass = TRAINER_CLASS_BLACK_BELT,
.party = TRAINER_PARTY(sTestParty2),
};
TEST("Trainer Class Balls apply to the entire party")
{
u32 j;
struct Pokemon *testParty = Alloc(6 * sizeof(struct Pokemon));
CreateNPCTrainerPartyFromTrainer(testParty, &sTestTrainer2, TRUE, BATTLE_TYPE_TRAINER);
for(j = 0; j < 6; j++)
{
EXPECT(GetMonData(&testParty[j], MON_DATA_POKEBALL, 0) == gTrainerClasses[sTestTrainer2.trainerClass].ball);
}
Free(testParty);
}