Attackcanceller fixes and improvements (#7698)

This commit is contained in:
Alex 2025-09-16 18:00:30 +02:00 committed by GitHub
parent 4822d94667
commit 32f8464fec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 860 additions and 777 deletions

View File

@ -724,14 +724,12 @@
.byte \mode
.endm
.macro trysetrest failInstr:req
.macro trysetrest
.byte 0x81
.4byte \failInstr
.endm
.macro jumpifnotfirstturn jumpInstr:req
.macro unused_0x82
.byte 0x82
.4byte \jumpInstr
.endm
.macro unused_0x83
@ -748,9 +746,8 @@
.byte \id
.endm
.macro stockpiletobasedamage failInstr:req
.macro stockpiletobasedamage
.byte 0x86
.4byte \failInstr
.endm
.macro stockpiletohpheal failInstr:req
@ -1045,9 +1042,8 @@
.byte 0xc2
.endm
.macro trysetfutureattack failInstr:req
.macro setfutureattack
.byte 0xc3
.4byte \failInstr
.endm
.macro trydobeatup endInstr, failInstr
@ -1224,10 +1220,8 @@
.4byte \jumpInstr
.endm
.macro jumpifnotcurrentmoveargtype battler:req, failInstr:req
.macro unused_0xE4
.byte 0xe4
.byte \battler
.4byte \failInstr
.endm
.macro pickup
@ -1378,12 +1372,6 @@
.4byte \failInstr
.endm
.macro jumpifcantfling battler:req, jumpInstr:req
callnative BS_JumpIfCantFling
.byte \battler
.4byte \jumpInstr
.endm
.macro itemstatchangeeffects battler:req
callnative BS_RunStatChangeItems
.byte \battler
@ -1438,10 +1426,6 @@
.4byte \failInstr
.endm
.macro setglaiverush
callnative BS_SetGlaiveRush
.endm
.macro setpledge jumpInstr:req
callnative BS_SetPledge
.4byte \jumpInstr
@ -2179,11 +2163,6 @@
.4byte \failInstr
.endm
.macro suckerpunchcheck failInstr:req
callnative BS_SuckerPunchCheck
.4byte \failInstr
.endm
.macro setsimplebeam failInstr:req
callnative BS_SetSimpleBeam
.4byte \failInstr
@ -2218,11 +2197,6 @@
.byte \case_
.endm
.macro trylastresort failInstr:req
callnative BS_TryLastResort
.4byte \failInstr
.endm
.macro tryautotomize failInstr:req
callnative BS_TryAutotomize
.4byte \failInstr
@ -2411,13 +2385,8 @@
.4byte \failInstr
.endm
.macro checkpoltergeist failInstr:req
callnative BS_CheckPoltergeist
.4byte \failInstr
.endm
.macro trynoretreat failInstr:req
callnative BS_TryNoRetreat
.macro setpoltergeistmessage failInstr:req
callnative BS_SetPoltergeistMessage
.4byte \failInstr
.endm

View File

@ -210,7 +210,7 @@ BattleScript_EffectDoodleMoveEnd:
BattleScript_EffectGlaiveRush::
call BattleScript_EffectHit_Ret
jumpifmoveresultflags MOVE_RESULT_DOESNT_AFFECT_FOE, BattleScript_TryFaintMon
setglaiverush
setvolatile BS_ATTACKER, VOLATILE_GLAIVE_RUSH, 2
goto BattleScript_TryFaintMon
BattleScript_SyrupBombActivates::
@ -685,7 +685,6 @@ BattleScript_SkyDropFlyingAlreadyConfused:
BattleScript_EffectFling::
attackcanceler
jumpifcantfling BS_ATTACKER, BattleScript_ButItFailed
setlastuseditem BS_ATTACKER
accuracycheck BattleScript_FlingMissed, ACC_CURR_MOVE
pause B_WAIT_TIME_SHORT
@ -776,11 +775,6 @@ BattleScript_FlingMissed:
removeitem BS_ATTACKER
goto BattleScript_MoveMissedPause
BattleScript_EffectAuraWheel:: @ Aura Wheel can only be used by Morpeko
jumpifspecies SPECIES_MORPEKO_FULL_BELLY, BattleScript_EffectHit
jumpifspecies SPECIES_MORPEKO_HANGRY, BattleScript_EffectHit
goto BattleScript_PokemonCantUseTheMove
BattleScript_EffectClangorousSoul::
attackcanceler
cutonethirdhpandraisestats BattleScript_ButItFailed
@ -819,7 +813,7 @@ BattleScript_OctlockTurnDmgEnd:
BattleScript_EffectPoltergeist::
attackcanceler
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
checkpoltergeist BattleScript_ButItFailed
setpoltergeistmessage BattleScript_ButItFailed
printstring STRINGID_ABOUTTOUSEPOLTERGEIST
waitmessage B_WAIT_TIME_LONG
goto BattleScript_HitFromCritCalc
@ -844,7 +838,8 @@ BattleScript_TryTarShot:
BattleScript_EffectNoRetreat::
attackcanceler
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
trynoretreat BattleScript_ButItFailed
jumpifvolatile BS_TARGET, VOLATILE_NO_RETREAT, BattleScript_ButItFailed
setvolatile BS_TARGET, VOLATILE_NO_RETREAT
attackanimation
waitanimation
call BattleScript_AllStatsUp
@ -901,7 +896,6 @@ BattleScript_MoveEffectLightScreen::
BattleScript_EffectStuffCheeks::
attackcanceler
jumpifnotberry BS_ATTACKER, BattleScript_ButItFailed
attackanimation
waitanimation
setbyte sBERRY_OVERRIDE, 1
@ -1049,12 +1043,6 @@ BattleScript_EffectFairyLock::
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_FailIfNotArgType::
attackcanceler
jumpifnotcurrentmoveargtype BS_ATTACKER, BattleScript_ButItFailed
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
goto BattleScript_HitFromCritCalc
BattleScript_RemoveFireType::
printstring STRINGID_ATTACKERLOSTFIRETYPE
waitmessage B_WAIT_TIME_LONG
@ -1747,12 +1735,6 @@ BattleScript_ShellSmashTrySpeed:
BattleScript_ShellSmashEnd:
goto BattleScript_MoveEnd
BattleScript_EffectLastResort::
attackcanceler
trylastresort BattleScript_ButItFailed
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
goto BattleScript_HitFromCritCalc
BattleScript_EffectGrowth::
attackcanceler
jumpifstat BS_ATTACKER, CMP_LESS_THAN, STAT_ATK, MAX_STAT_STAGE, BattleScript_GrowthDoMoveAnim
@ -2064,12 +2046,6 @@ BattleScript_EffectSimpleBeam::
tryendneutralizinggas
goto BattleScript_MoveEnd
BattleScript_EffectSuckerPunch::
attackcanceler
suckerpunchcheck BattleScript_ButItFailed
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
goto BattleScript_HitFromAtkString
BattleScript_EffectLuckyChant::
attackcanceler
setluckychant BattleScript_ButItFailed
@ -2577,7 +2553,7 @@ BattleScript_AbilityProtectsDoesntAffect::
setmoveresultflags MOVE_RESULT_FAILED
goto BattleScript_MoveEnd
BattleScript_InsomniaProtects:
BattleScript_InsomniaProtects::
pause B_WAIT_TIME_SHORT
call BattleScript_AbilityPopUp
printstring STRINGID_PKMNSTAYEDAWAKEUSING
@ -2866,16 +2842,10 @@ BattleScript_EffectLightScreen::
BattleScript_EffectRest::
attackcanceler
jumpifstatus BS_ATTACKER, STATUS1_SLEEP, BattleScript_RestIsAlreadyAsleep
jumpifability BS_ATTACKER, ABILITY_COMATOSE, BattleScript_RestIsAlreadyAsleep
jumpifuproarwakes BattleScript_RestCantSleep
jumpifability BS_TARGET, ABILITY_INSOMNIA, BattleScript_InsomniaProtects
jumpifability BS_TARGET, ABILITY_VITAL_SPIRIT, BattleScript_InsomniaProtects
jumpifability BS_ATTACKER, ABILITY_PURIFYING_SALT, BattleScript_InsomniaProtects
.if B_LEAF_GUARD_PREVENTS_REST >= GEN_5
jumpifleafguardprotected BS_TARGET, BattleScript_LeafGuardPreventsRest
.endif
trysetrest BattleScript_AlreadyAtFullHp
trysetrest
pause B_WAIT_TIME_SHORT
printfromtable gRestUsedStringIds
waitmessage B_WAIT_TIME_LONG
@ -3288,10 +3258,6 @@ BattleScript_EffectPainSplit::
BattleScript_EffectSnore::
attackcanceler
jumpifability BS_ATTACKER, ABILITY_COMATOSE, BattleScript_SnoreIsAsleep
jumpifstatus BS_ATTACKER, STATUS1_SLEEP, BattleScript_SnoreIsAsleep
goto BattleScript_ButItFailed
BattleScript_SnoreIsAsleep::
jumpifhalfword CMP_EQUAL, gChosenMove, MOVE_SLEEP_TALK, BattleScript_DoSnore
printstring STRINGID_PKMNFASTASLEEP
waitmessage B_WAIT_TIME_LONG
@ -3331,7 +3297,7 @@ BattleScript_EffectSketch::
BattleScript_EffectDestinyBond::
attackcanceler
trysetdestinybond BattleScript_ButItFailed
setvolatile BS_ATTACKER, VOLATILE_DESTINY_BOND, 2
attackanimation
waitanimation
printstring STRINGID_PKMNTRYINGTOTAKEFOE
@ -3463,15 +3429,9 @@ BattleScript_DoGhostCurse::
tryfaintmon BS_ATTACKER
goto BattleScript_MoveEnd
BattleScript_EffectMatBlock::
attackcanceler
jumpifnotfirstturn BattleScript_ButItFailed
goto BattleScript_ProtectLikeAttack
BattleScript_EffectProtect::
BattleScript_EffectEndure::
attackcanceler
BattleScript_ProtectLikeAttack:
setprotectlike
attackanimation
waitanimation
@ -3785,7 +3745,7 @@ BattleScript_EffectMirrorCoat::
BattleScript_EffectFutureSight::
attackcanceler
trysetfutureattack BattleScript_ButItFailed
setfutureattack
attackanimation
waitanimation
printfromtable gFutureMoveUsedStringIds
@ -3880,11 +3840,6 @@ BattleScript_AlreadyAtFullHp::
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_EffectFirstTurnOnly::
attackcanceler
jumpifnotfirstturn BattleScript_ButItFailed
goto BattleScript_EffectHit
BattleScript_FailedFromAtkCanceler::
attackcanceler
BattleScript_ButItFailed::
@ -3920,7 +3875,7 @@ BattleScript_EffectStockpile::
stockpile 0
attackanimation
waitanimation
printfromtable gStockpileUsedStringIds
printstring STRINGID_PKMNSTOCKPILED
waitmessage B_WAIT_TIME_LONG
.if B_STOCKPILE_RAISES_DEFS < GEN_4
goto BattleScript_EffectStockpileEnd
@ -3965,29 +3920,22 @@ BattleScript_EffectSpitUp::
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
damagecalc
adjustdamage
stockpiletobasedamage BattleScript_SpitUpFail
stockpiletobasedamage
call BattleScript_Hit_RetFromAtkAnimation
tryfaintmon BS_TARGET
removestockpilecounters
goto BattleScript_SpitUpEnd
BattleScript_SpitUpFail::
checkparentalbondcounter 2, BattleScript_SpitUpEnd
pause B_WAIT_TIME_SHORT
printstring STRINGID_FAILEDTOSPITUP
waitmessage B_WAIT_TIME_LONG
BattleScript_SpitUpEnd:
goto BattleScript_MoveEnd
BattleScript_SpitUpFailProtect::
pause B_WAIT_TIME_LONG
stockpiletobasedamage BattleScript_SpitUpFail
stockpiletobasedamage
resultmessage
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_EffectSwallow::
attackcanceler
stockpiletohpheal BattleScript_SwallowFail
stockpiletohpheal BattleScript_ButItFailed
attackanimation
waitanimation
orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE
@ -3998,13 +3946,6 @@ BattleScript_EffectSwallow::
removestockpilecounters
goto BattleScript_MoveEnd
BattleScript_SwallowFail::
pause B_WAIT_TIME_SHORT
printfromtable gSwallowFailStringIds
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_EffectHail::
attackcanceler
call BattleScript_CheckPrimalWeather
@ -4112,9 +4053,6 @@ BattleScript_EffectFocusPunch::
BattleScript_EffectFollowMe::
attackcanceler
.if B_UPDATED_MOVE_DATA >= GEN_8
jumpifnotbattletype BATTLE_TYPE_DOUBLE, BattleScript_ButItFailed
.endif
setforcedtarget
attackanimation
waitanimation
@ -4324,7 +4262,8 @@ BattleScript_EffectRefresh::
BattleScript_EffectGrudge::
attackcanceler
trysetvolatile BS_ATTACKER, VOLATILE_GRUDGE, BattleScript_ButItFailed
jumpifvolatile BS_ATTACKER, VOLATILE_GRUDGE, BattleScript_ButItFailed
setvolatile BS_ATTACKER, VOLATILE_GRUDGE, 2
attackanimation
waitanimation
printstring STRINGID_PKMNWANTSGRUDGE
@ -7246,8 +7185,10 @@ BattleScript_DazzlingProtected::
goto BattleScript_MoveEnd
BattleScript_MoveUsedPsychicTerrainPrevents::
printstring STRINGID_POKEMONCANNOTUSEMOVE
pause B_WAIT_TIME_SHORT
printstring STRINGID_PSYCHICTERRAINPREVENTS
waitmessage B_WAIT_TIME_LONG
setmoveresultflags MOVE_RESULT_NO_EFFECT
goto BattleScript_MoveEnd
BattleScript_GrassyTerrainHeals::
@ -8396,11 +8337,6 @@ BattleScript_TryFaint:
tryfaintmon BS_TARGET
goto BattleScript_MoveEnd
BattleScript_EffectSteelRoller::
attackcanceler
jumpifhalfword CMP_NO_COMMON_BITS, gFieldStatuses, STATUS_FIELD_TERRAIN_ANY, BattleScript_ButItFailed
goto BattleScript_HitFromAccCheck
BattleScript_RemoveTerrain::
removeterrain
playanimation BS_ATTACKER, B_ANIM_RESTORE_BG

View File

@ -93,14 +93,21 @@ Contains more fundamental functions that control the flow of the battle. Functio
### data/battle_scripts_1.s
Each move's effect is governed by a script defined here. For a simple example, let's look at the script for Fake Out/First Impression:
TODO: New Script
```
BattleScript_EffectFirstTurnOnly::
BattleScript_EffectTaunt::
attackcanceler
jumpifnotfirstturn BattleScript_ButItFailed
goto BattleScript_EffectHit
jumpifability BS_TARGET_SIDE, ABILITY_AROMA_VEIL, BattleScript_AromaVeilProtects
accuracycheck BattleScript_ButItFailed, ACC_CURR_MOVE
settaunt BattleScript_ButItFailed
attackanimation
waitanimation
printstring STRINGID_PKMNFELLFORTAUNT
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
```
`attackcanceler` is a command that covers all the cases that could cause a move to fail before it's even attempted (e.g. paralysis). And as we can tell from the commands, if it's not the first turn, we go to `BattleScript_ButItFailed` which evidently causes us to print the `attackstring` ("POKEMON used MOVE") then fail ("But it failed!"). Otherwise, we go to the generic "hit" effect which is the same script for moves that just deal damage and nothing else.
`attackcanceler` is a command that covers all cases that could cause a move to fail before it's even attempted (e.g. paralysis). The next command is a jump command. A jump command can check anything and usually comes with a jump instruction. Usually it jumps to a place from where the move should pick up because of certain conditions. The next one is an accuracy check. Accuracy checks happen after all prior move failure checks happened. The next set of commands are unique to a certain move, they are mostly the same for damaging moves but can widely differ for status moves. Lastly there is `BattleScript_MoveEnd` which the move after a succesful hit. An ability activation or specific move effect like Burn, Freeze, Absorb etc.
This is the most advanced part of the ROM. There are dozens upon dozens of commands and hundreds of scripts so this guide would go on forever if I were to go into more detail. To learn how these scripts work, it's best to look at a few examples of moves you know.

View File

@ -113,7 +113,6 @@ struct DisableStruct
u8 usedMoves:4;
u8 truantCounter:1;
u8 truantSwitchInHack:1;
u8 noRetreat:1;
u8 tarShot:1;
u8 octolock:1;
u8 cudChew:1;
@ -123,6 +122,7 @@ struct DisableStruct
u8 usedProteanLibero:1;
u8 flashFireBoosted:1;
u8 boosterEnergyActivated:1;
u8 padding1:1;
u16 overwrittenAbility; // abilities overwritten during battle (keep separate from battle history in case of switching)
u8 roostActive:1;
u8 unburdenActive:1;
@ -130,9 +130,9 @@ struct DisableStruct
u8 iceFaceActivationPrevention:1; // fixes hit escape move edge case
u8 unnerveActivated:1; // Unnerve and As One (Unnerve part) activate only once per switch in
u8 hazardsDone:1;
u8 padding1:1;
u8 padding2:1;
u8 octolockedBy:3;
u8 padding2:5;
u8 padding3:5;
};
// Fully Cleared each turn after end turn effects are done. A few things are cleared before end turn effects

View File

@ -155,6 +155,7 @@ u32 CountPositiveStatStages(u32 battlerId);
u32 CountNegativeStatStages(u32 battlerId);
// move checks
bool32 Ai_IsPriorityBlocked(u32 battlerAtk, u32 battlerDef, u32 move, struct AiLogicData *aiData);
bool32 IsAffectedByPowder(u32 battler, u32 ability, enum ItemHoldEffect holdEffect);
bool32 MovesWithCategoryUnusable(u32 attacker, u32 target, enum DamageCategory category);
enum MoveComparisonResult AI_WhichMoveBetter(u32 move1, u32 move2, u32 battlerAtk, u32 battlerDef, s32 noOfHitsToKo);
@ -312,7 +313,7 @@ bool32 IsNaturalEnemy(u32 speciesAttacker, u32 speciesTarget);
#define AI_EFFECT_TERRAIN (1 << 1)
#define AI_EFFECT_CLEAR_HAZARDS (1 << 2)
#define AI_EFFECT_BREAK_SCREENS (1 << 3)
#define AI_EFFECT_RESET_STATS (1 << 4)
#define AI_EFFECT_RESET_STATS (1 << 4)
#define AI_EFFECT_FORCE_SWITCH (1 << 5)
#define AI_EFFECT_TORMENT (1 << 6)
#define AI_EFFECT_LIGHT_SCREEN (1 << 7)

View File

@ -11,7 +11,6 @@ u16 GetNonDynamaxHP(u32 battler);
u16 GetNonDynamaxMaxHP(u32 battler);
void UndoDynamax(u32 battler);
bool32 IsMoveBlockedByMaxGuard(u32 move);
bool32 IsMoveBlockedByDynamax(u32 move);
u16 GetMaxMove(u32 battler, u32 baseMove);
u32 GetMaxMovePower(u32 move);

View File

@ -332,7 +332,6 @@ extern const u8 BattleScript_MistySurgeActivates[];
extern const u8 BattleScript_ElectricSurgeActivates[];
extern const u8 BattleScript_EffectSpectralThief[];
extern const u8 BattleScript_EffectLifeDew[];
extern const u8 BattleScript_EffectSteelRoller[];
extern const u8 BattleScript_AbilityRaisesDefenderStat[];
extern const u8 BattleScript_PowderMoveNoEffect[];
extern const u8 BattleScript_GrassyTerrainHeals[];
@ -597,6 +596,8 @@ extern const u8 BattleScript_EffectConversion[];
extern const u8 BattleScript_EffectRestoreHp[];
extern const u8 BattleScript_EffectLightScreen[];
extern const u8 BattleScript_EffectRest[];
extern const u8 BattleScript_RestIsAlreadyAsleep[];
extern const u8 BattleScript_InsomniaProtects[];
extern const u8 BattleScript_EffectOHKO[];
extern const u8 BattleScript_EffectHealBlock[];
extern const u8 BattleScript_RecoilIfMiss[];
@ -677,7 +678,6 @@ extern const u8 BattleScript_EffectBeatUp[];
extern const u8 BattleScript_EffectSemiInvulnerable[];
extern const u8 BattleScript_EffectDefenseCurl[];
extern const u8 BattleScript_EffectSoftboiled[];
extern const u8 BattleScript_EffectFirstTurnOnly[];
extern const u8 BattleScript_EffectStockpile[];
extern const u8 BattleScript_EffectSpitUp[];
extern const u8 BattleScript_EffectSwallow[];
@ -742,7 +742,6 @@ extern const u8 BattleScript_EffectGuardSplit[];
extern const u8 BattleScript_EffectStickyWeb[];
extern const u8 BattleScript_EffectMetalBurst[];
extern const u8 BattleScript_EffectLuckyChant[];
extern const u8 BattleScript_EffectSuckerPunch[];
extern const u8 BattleScript_EffectSimpleBeam[];
extern const u8 BattleScript_EffectEntrainment[];
extern const u8 BattleScript_EffectHealPulse[];
@ -761,7 +760,6 @@ extern const u8 BattleScript_EffectElectrify[];
extern const u8 BattleScript_EffectReflectType[];
extern const u8 BattleScript_EffectSoak[];
extern const u8 BattleScript_EffectGrowth[];
extern const u8 BattleScript_EffectLastResort[];
extern const u8 BattleScript_EffectShellSmash[];
extern const u8 BattleScript_EffectShiftGear[];
extern const u8 BattleScript_EffectDefenseUp3[];
@ -790,14 +788,12 @@ extern const u8 BattleScript_EffectAcupressure[];
extern const u8 BattleScript_EffectAromaticMist[];
extern const u8 BattleScript_EffectPowder[];
extern const u8 BattleScript_EffectPartingShot[];
extern const u8 BattleScript_EffectMatBlock[];
extern const u8 BattleScript_EffectInstruct[];
extern const u8 BattleScript_EffectLaserFocus[];
extern const u8 BattleScript_EffectMagneticFlux[];
extern const u8 BattleScript_EffectGearUp[];
extern const u8 BattleScript_EffectStrengthSap[];
extern const u8 BattleScript_EffectPurify[];
extern const u8 BattleScript_FailIfNotArgType[];
extern const u8 BattleScript_EffectShoreUp[];
extern const u8 BattleScript_EffectGeomancy[];
extern const u8 BattleScript_EffectFairyLock[];
@ -814,7 +810,6 @@ extern const u8 BattleScript_MoveEffectLeechSeed[];
extern const u8 BattleScript_MoveEffectHaze[];
extern const u8 BattleScript_MoveEffectIonDeluge[];
extern const u8 BattleScript_EffectHyperspaceFury[];
extern const u8 BattleScript_EffectAuraWheel[];
extern const u8 BattleScript_EffectNoRetreat[];
extern const u8 BattleScript_EffectTarShot[];
extern const u8 BattleScript_EffectPoltergeist[];
@ -855,5 +850,6 @@ extern const u8 BattleScript_SubmoveAttackstring[];
extern const u8 BattleScript_MetronomeAttackstring[];
extern const u8 BattleScript_SleepTalkAttackstring[];
extern const u8 BattleScript_NaturePowerAttackstring[];
extern const u8 BattleScript_PokemonCantUseTheMove[];
#endif // GUARD_BATTLE_SCRIPTS_H

View File

@ -7,7 +7,7 @@ bool32 CanTerastallize(u32 battler);
u32 GetBattlerTeraType(u32 battler);
void ExpendTypeStellarBoost(u32 battler, u32 type);
bool32 IsTypeStellarBoosted(u32 battler, u32 type);
uq4_12_t GetTeraMultiplier(u32 battler, u32 type);
uq4_12_t GetTeraMultiplier(struct DamageContext *ctx);
u16 GetTeraTypeRGB(u32 type);

View File

@ -105,7 +105,7 @@ struct TypePower
enum MoveSuccessOrder
{
CANCELLER_FLAGS,
CANCELLER_CLEAR_FLAGS,
CANCELLER_STANCE_CHANGE_1,
CANCELLER_SKY_DROP,
CANCELLER_RECHARGE,
@ -130,10 +130,10 @@ enum MoveSuccessOrder
CANCELLER_ATTACKSTRING,
CANCELLER_PPDEDUCTION,
CANCELLER_WEATHER_PRIMAL,
CANCELLER_DYNAMAX_BLOCKED,
CANCELLER_MOVE_FAILURE,
CANCELLER_POWDER_STATUS,
CANCELLER_PRIORITY_BLOCK,
CANCELLER_PROTEAN,
CANCELLER_PSYCHIC_TERRAIN,
CANCELLER_EXPLODING_DAMP,
CANCELLER_MULTIHIT_MOVES,
CANCELLER_MULTI_TARGET_MOVES,
@ -180,6 +180,15 @@ struct DamageContext
enum ItemHoldEffect holdEffectDef:16;
};
struct BattleContext
{
u32 battlerAtk:3;
u32 battlerDef:3;
u32 currentMove:16;
enum BattleMoveEffects moveEffect:10;
u16 ability[MAX_BATTLERS_COUNT];
};
enum SleepClauseBlock
{
NOT_BLOCKED_BY_SLEEP_CLAUSE,
@ -242,7 +251,7 @@ bool32 IsAbilityAndRecord(u32 battler, u32 battlerAbility, u32 abilityToCheck);
u32 DoEndTurnEffects(void);
bool32 HandleFaintedMonActions(void);
void TryClearRageAndFuryCutter(void);
enum MoveCanceller AtkCanceller_MoveSuccessOrder(void);
enum MoveCanceller AtkCanceller_MoveSuccessOrder(struct BattleContext *ctx);
bool32 HasNoMonsToSwitch(u32 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2);
bool32 TryChangeBattleWeather(u32 battler, u32 battleWeatherId, bool32 viaAbility);
bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 abilityDef, u32 move, enum FunctionCallOption option);
@ -414,5 +423,6 @@ bool32 BreaksThroughSemiInvulnerablity(u32 battler, u32 move);
u32 GetNaturePowerMove(u32 battler);
u32 GetNaturePowerMove(u32 battler);
void RemoveAbilityFlags(u32 battler);
bool32 IsDazzlingAbility(u32 ability);
#endif // GUARD_BATTLE_UTIL_H

View File

@ -162,7 +162,7 @@ enum VolatileFlags
F(VOLATILE_RECHARGE, recharge, (u32, 1)) \
F(VOLATILE_RAGE, rage, (u32, 1)) \
F(VOLATILE_SUBSTITUTE, substitute, (u32, 1), V_BATON_PASSABLE) \
F(VOLATILE_DESTINY_BOND, destinyBond, (u32, 1)) \
F(VOLATILE_DESTINY_BOND, destinyBond, (u32, 2)) \
F(VOLATILE_ESCAPE_PREVENTION, escapePrevention, (u32, 1), V_BATON_PASSABLE) \
F(VOLATILE_NIGHTMARE, nightmare, (u32, 1)) \
F(VOLATILE_CURSED, cursed, (u32, 1), V_BATON_PASSABLE) \
@ -196,6 +196,7 @@ enum VolatileFlags
F(VOLATILE_AQUA_RING, aquaRing, (u32, 1), V_BATON_PASSABLE) \
F(VOLATILE_LASER_FOCUS, laserFocus, (u32, 1)) \
F(VOLATILE_POWER_TRICK, powerTrick, (u32, 1), V_BATON_PASSABLE) \
F(VOLATILE_NO_RETREAT, noRetreat, (u32, 1), V_BATON_PASSABLE) \
F(VOLATILE_VESSEL_OF_RUIN, vesselOfRuin, (u32, 1)) \
F(VOLATILE_SWORD_OF_RUIN, swordOfRuin, (u32, 1)) \
F(VOLATILE_TABLETS_OF_RUIN, tabletsOfRuin, (u32, 1)) \

View File

@ -113,7 +113,6 @@ enum StringID
STRINGID_PKMNCALMEDDOWN,
STRINGID_PKMNCANTSLEEPINUPROAR,
STRINGID_PKMNSTOCKPILED,
STRINGID_PKMNCANTSTOCKPILE,
STRINGID_PKMNCANTSLEEPINUPROAR2,
STRINGID_UPROARKEPTPKMNAWAKE,
STRINGID_PKMNSTAYEDAWAKEUSING,
@ -239,8 +238,6 @@ enum StringID
STRINGID_STARTEDHAIL,
STRINGID_HAILCONTINUES,
STRINGID_HAILSTOPPED,
STRINGID_FAILEDTOSPITUP,
STRINGID_FAILEDTOSWALLOW,
STRINGID_STATCHANGESGONE,
STRINGID_COINSSCATTERED,
STRINGID_TOOWEAKFORSUBSTITUTE,
@ -858,20 +855,6 @@ enum UproarOverTurnStringID
B_MSG_UPROAR_ENDS,
};
// gStockpileUsedStringIds
enum StockpileUsedStringID
{
B_MSG_STOCKPILED,
B_MSG_CANT_STOCKPILE,
};
// gSwallowFailStringIds
enum SwallowFailStringID
{
B_MSG_SWALLOW_FAILED,
B_MSG_SWALLOW_FULL_HP,
};
// gKOFailedStringIds
enum KOFailedStringID
{

View File

@ -1141,6 +1141,9 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
// check non-user target
if (!(moveTarget & MOVE_TARGET_USER))
{
if (Ai_IsPriorityBlocked(battlerAtk, battlerDef, move, aiData))
return TRUE;
if (CanAbilityBlockMove(battlerAtk, battlerDef, abilityAtk, abilityDef, move, AI_CHECK))
RETURN_SCORE_MINUS(20);
@ -2871,7 +2874,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-10);
break;
case EFFECT_NO_RETREAT:
if (gDisableStructs[battlerAtk].noRetreat)
if (gBattleMons[battlerAtk].volatiles.noRetreat)
ADJUST_SCORE(-10);
break;
case EFFECT_EXTREME_EVOBOOST:
@ -3106,7 +3109,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
switch (effect)
{
case EFFECT_HELPING_HAND:
if (!hasPartner
if (!hasPartner
|| !HasDamagingMove(battlerAtkPartner)
|| (aiData->partnerMove != MOVE_NONE && IsBattleMoveStatus(aiData->partnerMove)))
{
@ -3122,7 +3125,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (hasTwoOpponents)
{
// Might be about to die
if (CanTargetFaintAi(LEFT_FOE(battlerAtk), battlerAtk) && CanTargetFaintAi(RIGHT_FOE(battlerAtk), battlerAtk)
if (CanTargetFaintAi(LEFT_FOE(battlerAtk), battlerAtk) && CanTargetFaintAi(RIGHT_FOE(battlerAtk), battlerAtk)
&& AI_IsSlower(battlerAtk, LEFT_FOE(battlerAtk), move, predictedMove, DONT_CONSIDER_PRIORITY)
&& AI_IsSlower(battlerAtk, RIGHT_FOE(battlerAtk), move, predictedMove, DONT_CONSIDER_PRIORITY))
ADJUST_SCORE(GOOD_EFFECT);
@ -3144,14 +3147,14 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
else if (IsBattlerAlive(RIGHT_FOE(battlerAtk)))
{
// Might be about to die
if (CanTargetFaintAi(RIGHT_FOE(battlerAtk), battlerAtk)
if (CanTargetFaintAi(RIGHT_FOE(battlerAtk), battlerAtk)
&& AI_IsSlower(battlerAtk, RIGHT_FOE(battlerAtk), move, predictedMove, DONT_CONSIDER_PRIORITY))
ADJUST_SCORE(GOOD_EFFECT);
if (ownHitsToKOFoe2 > partnerHitsToKOFoe2 && partnerHitsToKOFoe2 > 1)
ADJUST_SCORE(GOOD_EFFECT);
}
}
}
break;
case EFFECT_PERISH_SONG:
@ -3178,7 +3181,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(GOOD_EFFECT);
break;
case EFFECT_COACHING:
if (!hasPartner
if (!hasPartner
|| !HasMoveWithCategory(battlerAtkPartner, DAMAGE_CATEGORY_PHYSICAL))
{
ADJUST_SCORE(WORST_EFFECT);
@ -5271,7 +5274,7 @@ case EFFECT_GUARD_SPLIT:
ADJUST_SCORE(GOOD_EFFECT);
// Set it for next pokemon in singles.
else if (!(gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && !hasPartner && (CountUsablePartyMons(battlerAtk) != 0))
ADJUST_SCORE(DECENT_EFFECT);
ADJUST_SCORE(DECENT_EFFECT);
// Don't unset it on last turn.
else if (gFieldStatuses & STATUS_FIELD_TRICK_ROOM && gFieldTimers.trickRoomTimer != gBattleTurnCounter && ShouldClearFieldStatus(battlerAtk, STATUS_FIELD_TRICK_ROOM))
ADJUST_SCORE(GOOD_EFFECT);
@ -5406,7 +5409,7 @@ case EFFECT_GUARD_SPLIT:
u32 partnerSpeed = aiData->speedStats[BATTLE_PARTNER(battlerAtk)];
u32 foe1Speed = aiData->speedStats[LEFT_FOE(battlerAtk)];
u32 foe2Speed = aiData->speedStats[RIGHT_FOE(battlerAtk)];
if (speed <= foe1Speed && (speed * 2) > foe1Speed)
tailwindScore += 1;
if (speed <= foe2Speed && (speed * 2) > foe2Speed)

View File

@ -501,11 +501,11 @@ bool32 IsBattlerTrapped(u32 battlerAtk, u32 battlerDef)
u32 GetTotalBaseStat(u32 species)
{
return GetSpeciesBaseHP(species)
+ GetSpeciesBaseAttack(species)
+ GetSpeciesBaseDefense(species)
+ GetSpeciesBaseSpeed(species)
+ GetSpeciesBaseSpAttack(species)
+ GetSpeciesBaseSpDefense(species);
+ GetSpeciesBaseAttack(species)
+ GetSpeciesBaseDefense(species)
+ GetSpeciesBaseSpeed(species)
+ GetSpeciesBaseSpAttack(species)
+ GetSpeciesBaseSpDefense(species);
}
bool32 IsTruantMonVulnerable(u32 battlerAI, u32 opposingBattler)
@ -535,6 +535,24 @@ bool32 IsAffectedByPowder(u32 battler, u32 ability, enum ItemHoldEffect holdEffe
return TRUE;
}
bool32 Ai_IsPriorityBlocked(u32 battlerAtk, u32 battlerDef, u32 move, struct AiLogicData *aiData)
{
s32 atkPriority = GetBattleMovePriority(battlerAtk, aiData->abilities[battlerAtk], move);
if (atkPriority <= 0 || IsBattlerAlly(battlerAtk, battlerDef))
return FALSE;
if (IsMoldBreakerTypeAbility(battlerAtk, aiData->abilities[battlerAtk]) || MoveIgnoresTargetAbility(move))
return FALSE;
if (IsDazzlingAbility(aiData->abilities[battlerDef]))
return TRUE;
if (IsDoubleBattle() && IsDazzlingAbility(aiData->abilities[BATTLE_PARTNER(battlerDef)]))
return TRUE;
return FALSE;
}
// This function checks if all physical/special moves are either unusable or unreasonable to use.
// Consider a pokemon boosting their attack against a ghost pokemon having only normal-type physical attacks.
bool32 MovesWithCategoryUnusable(u32 attacker, u32 target, enum DamageCategory category)
@ -630,6 +648,9 @@ bool32 IsDamageMoveUnusable(struct DamageContext *ctx)
partnerDefAbility = aiData->abilities[BATTLE_PARTNER(ctx->battlerDef)];
}
if (Ai_IsPriorityBlocked(ctx->battlerAtk, ctx->battlerDef, ctx->move, aiData))
return TRUE;
if (CanAbilityBlockMove(ctx->battlerAtk, ctx->battlerDef, ctx->abilityAtk, battlerDefAbility, ctx->move, AI_CHECK))
return TRUE;

View File

@ -234,21 +234,6 @@ bool32 IsMoveBlockedByMaxGuard(u32 move)
return FALSE;
}
// Weight-based moves (and some other moves in Raids) are blocked by Dynamax.
bool32 IsMoveBlockedByDynamax(u32 move)
{
// TODO: Certain moves are banned in raids.
switch (GetMoveEffect(move))
{
case EFFECT_HEAT_CRASH:
case EFFECT_LOW_KICK:
return TRUE;
default:
break;
}
return FALSE;
}
static u16 GetTypeBasedMaxMove(u32 battler, u32 type)
{
// Gigantamax check

View File

@ -4840,7 +4840,9 @@ s32 GetBattleMovePriority(u32 battler, u32 ability, u32 move)
priority++;
}
else if (ability == ABILITY_TRIAGE && IsHealingMove(move))
{
priority += 3;
}
if (gProtectStructs[battler].quash)
priority = -8;

View File

@ -272,7 +272,6 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] =
[STRINGID_PKMNCALMEDDOWN] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} calmed down."),
[STRINGID_PKMNCANTSLEEPINUPROAR] = COMPOUND_STRING("But {B_DEF_NAME_WITH_PREFIX2} can't sleep in an uproar!"),
[STRINGID_PKMNSTOCKPILED] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} stockpiled {B_BUFF1}!"),
[STRINGID_PKMNCANTSTOCKPILE] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} can't stockpile any more!"), //I think this was replaced with just "But it failed!"
[STRINGID_PKMNCANTSLEEPINUPROAR2] = COMPOUND_STRING("But {B_DEF_NAME_WITH_PREFIX2} can't sleep in an uproar!"),
[STRINGID_UPROARKEPTPKMNAWAKE] = COMPOUND_STRING("But the uproar kept {B_DEF_NAME_WITH_PREFIX2} awake!"),
[STRINGID_PKMNSTAYEDAWAKEUSING] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} stayed awake using its {B_DEF_ABILITY}!"), //not in gen 5+, ability popup
@ -398,8 +397,6 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] =
[STRINGID_STARTEDHAIL] = COMPOUND_STRING("It started to hail!"),
[STRINGID_HAILCONTINUES] = COMPOUND_STRING("The hail is crashing down."),
[STRINGID_HAILSTOPPED] = COMPOUND_STRING("The hail stopped."),
[STRINGID_FAILEDTOSPITUP] = COMPOUND_STRING("But it failed to spit up a thing!"), //not in gen 5+, uses "but it failed"
[STRINGID_FAILEDTOSWALLOW] = COMPOUND_STRING("But it failed to swallow a thing!"), //not in gen 5+, uses "but it failed"
[STRINGID_STATCHANGESGONE] = COMPOUND_STRING("All stat changes were eliminated!"),
[STRINGID_COINSSCATTERED] = COMPOUND_STRING("Coins were scattered everywhere!"),
[STRINGID_TOOWEAKFORSUBSTITUTE] = COMPOUND_STRING("But it does not have enough HP left to make a substitute!"),
@ -663,7 +660,7 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] =
[STRINGID_MISTYTERRAINPREVENTS] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} surrounds itself with a protective mist!"),
[STRINGID_GRASSYTERRAINHEALS] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} is healed by the grassy terrain!"),
[STRINGID_ELECTRICTERRAINPREVENTS] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} surrounds itself with electrified terrain!"),
[STRINGID_PSYCHICTERRAINPREVENTS] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} surrounds itself with psychic terrain!"),
[STRINGID_PSYCHICTERRAINPREVENTS] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} is protected by the Psychic Terrain!"),
[STRINGID_SAFETYGOGGLESPROTECTED] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} is not affected thanks to its {B_LAST_ITEM}!"),
[STRINGID_FLOWERVEILPROTECTED] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} surrounded itself with a veil of petals!"),
[STRINGID_SWEETVEILPROTECTED] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} can't fall asleep due to a veil of sweetness!"),
@ -1073,24 +1070,12 @@ const u16 gUproarOverTurnStringIds[] =
[B_MSG_UPROAR_ENDS] = STRINGID_PKMNCALMEDDOWN
};
const u16 gStockpileUsedStringIds[] =
{
[B_MSG_STOCKPILED] = STRINGID_PKMNSTOCKPILED,
[B_MSG_CANT_STOCKPILE] = STRINGID_PKMNCANTSTOCKPILE,
};
const u16 gWokeUpStringIds[] =
{
[B_MSG_WOKE_UP] = STRINGID_PKMNWOKEUP,
[B_MSG_WOKE_UP_UPROAR] = STRINGID_PKMNWOKEUPINUPROAR
};
const u16 gSwallowFailStringIds[] =
{
[B_MSG_SWALLOW_FAILED] = STRINGID_FAILEDTOSWALLOW,
[B_MSG_SWALLOW_FULL_HP] = STRINGID_PKMNHPFULL
};
const u16 gUproarAwakeStringIds[] =
{
[B_MSG_CANT_SLEEP_UPROAR] = STRINGID_PKMNCANTSLEEPINUPROAR2,

View File

@ -467,7 +467,7 @@ static void Cmd_setreflect(void);
static void Cmd_setseeded(void);
static void Cmd_manipulatedamage(void);
static void Cmd_trysetrest(void);
static void Cmd_jumpifnotfirstturn(void);
static void Cmd_unused_0x82(void);
static void Cmd_unused_0x83(void);
static void Cmd_jumpifuproarwakes(void);
static void Cmd_stockpile(void);
@ -507,7 +507,7 @@ static void Cmd_settypetorandomresistance(void);
static void Cmd_setalwayshitflag(void);
static void Cmd_copymovepermanently(void);
static void Cmd_unused_0xA9(void);
static void Cmd_trysetdestinybond(void);
static void Cmd_unused_AA(void);
static void Cmd_trysetdestinybondtohappen(void);
static void Cmd_settailwind(void);
static void Cmd_tryspiteppreduce(void);
@ -532,7 +532,7 @@ static void Cmd_unused_0xBF(void);
static void Cmd_recoverbasedonsunlight(void);
static void Cmd_setstickyweb(void);
static void Cmd_selectfirstvalidtarget(void);
static void Cmd_trysetfutureattack(void);
static void Cmd_setfutureattack(void);
static void Cmd_trydobeatup(void);
static void Cmd_setsemiinvulnerablebit(void);
static void Cmd_tryfiretwoturnmovenowbyeffect(void);
@ -565,7 +565,7 @@ static void Cmd_trysetsnatch(void);
static void Cmd_unused2(void);
static void Cmd_switchoutabilities(void);
static void Cmd_jumpifhasnohp(void);
static void Cmd_jumpifnotcurrentmoveargtype(void);
static void Cmd_unused_0xE4(void);
static void Cmd_pickup(void);
static void Cmd_unused_0xE6(void);
static void Cmd_unused_0xE7(void);
@ -726,7 +726,7 @@ void (*const gBattleScriptingCommandsTable[])(void) =
Cmd_setseeded, //0x7F
Cmd_manipulatedamage, //0x80
Cmd_trysetrest, //0x81
Cmd_jumpifnotfirstturn, //0x82
Cmd_unused_0x82, //0x82
Cmd_unused_0x83, //0x83
Cmd_jumpifuproarwakes, //0x84
Cmd_stockpile, //0x85
@ -766,7 +766,7 @@ void (*const gBattleScriptingCommandsTable[])(void) =
Cmd_setalwayshitflag, //0xA7
Cmd_copymovepermanently, //0xA8
Cmd_unused_0xA9, //0xA9
Cmd_trysetdestinybond, //0xAA
Cmd_unused_AA, //0xAA
Cmd_trysetdestinybondtohappen, //0xAB
Cmd_settailwind, //0xAC
Cmd_tryspiteppreduce, //0xAD
@ -791,7 +791,7 @@ void (*const gBattleScriptingCommandsTable[])(void) =
Cmd_recoverbasedonsunlight, //0xC0
Cmd_setstickyweb, //0xC1
Cmd_selectfirstvalidtarget, //0xC2
Cmd_trysetfutureattack, //0xC3
Cmd_setfutureattack, //0xC3
Cmd_trydobeatup, //0xC4
Cmd_setsemiinvulnerablebit, //0xC5
Cmd_tryfiretwoturnmovenowbyeffect, //0xC6
@ -824,7 +824,7 @@ void (*const gBattleScriptingCommandsTable[])(void) =
Cmd_unused2, //0xE1
Cmd_switchoutabilities, //0xE2
Cmd_jumpifhasnohp, //0xE3
Cmd_jumpifnotcurrentmoveargtype, //0xE4
Cmd_unused_0xE4, //0xE4
Cmd_pickup, //0xE5
Cmd_unused_0xE6, //0xE6
Cmd_unused_0xE7, //0xE7
@ -1040,23 +1040,23 @@ u32 NumFaintedBattlersByAttacker(u32 battlerAtk)
return numMonsFainted;
}
bool32 IsMovePowderBlocked(u32 battlerAtk, u32 battlerDef, u32 move)
bool32 IsMovePowderBlocked(struct BattleContext *ctx)
{
bool32 effect = FALSE;
if (IsPowderMove(move) && (battlerAtk != battlerDef))
if (IsPowderMove(ctx->currentMove) && (ctx->battlerAtk != ctx->battlerDef))
{
if (B_POWDER_GRASS >= GEN_6
&& (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || GetBattlerAbility(battlerDef) == ABILITY_OVERCOAT))
&& (IS_BATTLER_OF_TYPE(ctx->battlerDef, TYPE_GRASS) || ctx->ability[ctx->battlerDef] == ABILITY_OVERCOAT))
{
gBattlerAbility = battlerDef;
RecordAbilityBattle(gBattlerTarget, ABILITY_OVERCOAT);
gBattlerAbility = ctx->battlerDef;
RecordAbilityBattle(ctx->battlerDef, ABILITY_OVERCOAT);
effect = TRUE;
}
else if (GetBattlerHoldEffect(battlerDef, TRUE) == HOLD_EFFECT_SAFETY_GOGGLES)
else if (GetBattlerHoldEffect(ctx->battlerDef, TRUE) == HOLD_EFFECT_SAFETY_GOGGLES)
{
RecordItemEffectBattle(battlerDef, HOLD_EFFECT_SAFETY_GOGGLES);
gLastUsedItem = gBattleMons[battlerDef].item;
RecordItemEffectBattle(ctx->battlerDef, HOLD_EFFECT_SAFETY_GOGGLES);
gLastUsedItem = gBattleMons[ctx->battlerDef].item;
effect = TRUE;
}
@ -1103,22 +1103,30 @@ static void Cmd_attackcanceler(void)
return;
}
enum BattleMoveEffects effect = GetMoveEffect(gCurrentMove);
struct BattleContext ctx = {0};
ctx.battlerAtk = gBattlerAttacker;
ctx.battlerDef = gBattlerTarget;
ctx.currentMove = gCurrentMove;
ctx.moveEffect = GetMoveEffect(ctx.currentMove);
if (!IsBattlerAlive(gBattlerAttacker)
&& effect != EFFECT_EXPLOSION
&& effect != EFFECT_MISTY_EXPLOSION)
&& ctx.moveEffect != EFFECT_EXPLOSION
&& ctx.moveEffect != EFFECT_MISTY_EXPLOSION)
{
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
gBattlescriptCurrInstr = BattleScript_MoveEnd;
return;
}
if (AtkCanceller_MoveSuccessOrder() != MOVE_STEP_SUCCESS)
// With how attackcanceller works right now we only need attacker and target abilities. Might change in the future
ctx.ability[ctx.battlerAtk] = GetBattlerAbility(ctx.battlerAtk);
ctx.ability[ctx.battlerDef] = GetBattlerAbility(ctx.battlerDef);
if (AtkCanceller_MoveSuccessOrder(&ctx) != MOVE_STEP_SUCCESS)
return;
if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_OFF
&& GetBattlerAbility(gBattlerAttacker) == ABILITY_PARENTAL_BOND
&& ctx.ability[ctx.battlerAtk] == ABILITY_PARENTAL_BOND
&& IsMoveAffectedByParentalBond(gCurrentMove, gBattlerAttacker)
&& !(gAbsentBattlerFlags & (1u << gBattlerTarget))
&& GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE)
@ -1129,44 +1137,43 @@ static void Cmd_attackcanceler(void)
return;
}
u32 abilityDef = GetBattlerAbility(gBattlerTarget);
if (CanAbilityBlockMove(
gBattlerAttacker,
gBattlerTarget,
GetBattlerAbility(gBattlerAttacker),
abilityDef,
gCurrentMove,
ctx.battlerAtk,
ctx.battlerDef,
ctx.ability[ctx.battlerAtk],
ctx.ability[ctx.battlerDef],
ctx.currentMove,
RUN_SCRIPT))
return;
if (GetMoveNonVolatileStatus(gCurrentMove) == MOVE_EFFECT_PARALYSIS)
if (GetMoveNonVolatileStatus(ctx.currentMove) == MOVE_EFFECT_PARALYSIS)
{
if (CanAbilityAbsorbMove(
gBattlerAttacker,
gBattlerTarget,
abilityDef,
gCurrentMove,
GetBattleMoveType(gCurrentMove),
ctx.battlerAtk,
ctx.battlerDef,
ctx.ability[ctx.battlerDef],
ctx.currentMove,
GetBattleMoveType(ctx.currentMove),
RUN_SCRIPT))
return;
}
if (IsMovePowderBlocked(gBattlerAttacker, gBattlerTarget, gCurrentMove))
if (IsMovePowderBlocked(&ctx))
return;
// Check if no available target present on the field or if Sky Battles ban the move
if ((NoTargetPresent(gBattlerAttacker, gCurrentMove)
&& (!gBattleMoveEffects[effect].twoTurnEffect || (gBattleMons[gBattlerAttacker].volatiles.multipleTurns)))
&& (!gBattleMoveEffects[ctx.moveEffect].twoTurnEffect || (gBattleMons[gBattlerAttacker].volatiles.multipleTurns)))
|| (IsMoveNotAllowedInSkyBattles(gCurrentMove)))
{
gBattleStruct->noTargetPresent = TRUE;
if (effect == EFFECT_FLING) // Edge case for removing a mon's item when there is no target available after using Fling.
if (ctx.moveEffect == EFFECT_FLING) // Edge case for removing a mon's item when there is no target available after using Fling.
gBattlescriptCurrInstr = BattleScript_FlingFailConsumeItem;
else
gBattlescriptCurrInstr = BattleScript_ButItFailed;
if (!gBattleMoveEffects[effect].twoTurnEffect || (gBattleMons[gBattlerAttacker].volatiles.multipleTurns))
if (!gBattleMoveEffects[ctx.moveEffect].twoTurnEffect || (gBattleMons[gBattlerAttacker].volatiles.multipleTurns))
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
return;
}
@ -1197,7 +1204,7 @@ static void Cmd_attackcanceler(void)
{
u32 battler = gBattlerTarget;
if (abilityDef == ABILITY_MAGIC_BOUNCE)
if (ctx.ability[ctx.battlerDef] == ABILITY_MAGIC_BOUNCE)
{
battler = gBattlerTarget;
gBattleStruct->bouncedMoveIsUsed = TRUE;
@ -1257,11 +1264,11 @@ static void Cmd_attackcanceler(void)
RecordAbilityBattle(gBattlerTarget, gLastUsedAbility);
}
else if (IsBattlerProtected(gBattlerAttacker, gBattlerTarget, gCurrentMove)
&& (effect != EFFECT_CURSE || IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST))
&& (!gBattleMoveEffects[effect].twoTurnEffect || (gBattleMons[gBattlerAttacker].volatiles.multipleTurns))
&& effect != EFFECT_SUCKER_PUNCH
&& effect != EFFECT_COUNTER
&& effect != EFFECT_UPPER_HAND)
&& (ctx.moveEffect != EFFECT_CURSE || IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST))
&& (!gBattleMoveEffects[ctx.moveEffect].twoTurnEffect || (gBattleMons[gBattlerAttacker].volatiles.multipleTurns))
&& ctx.moveEffect != EFFECT_SUCKER_PUNCH
&& ctx.moveEffect != EFFECT_COUNTER
&& ctx.moveEffect != EFFECT_UPPER_HAND)
{
if (!CanBattlerAvoidContactEffects(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerAttacker), GetBattlerHoldEffect(gBattlerAttacker, TRUE), gCurrentMove))
gProtectStructs[gBattlerAttacker].touchedProtectLike = TRUE;
@ -3238,7 +3245,7 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai
gBattlescriptCurrInstr++;
break;
case MOVE_EFFECT_PREVENT_ESCAPE:
if (!gBattleMons[gBattlerTarget].volatiles.escapePrevention)
if (!gBattleMons[gBattlerTarget].volatiles.escapePrevention) // Do we need to check if the status is already set?
{
gBattleMons[gBattlerTarget].volatiles.escapePrevention = TRUE;
gDisableStructs[gBattlerTarget].battlerPreventingEscape = gBattlerAttacker;
@ -6800,6 +6807,9 @@ static void Cmd_moveend(void)
DebugPrintfLevel(MGBA_LOG_WARN, "savedTargetCount is greater than 0! More calls to SaveBattlerTarget than RestoreBattlerTarget!");
// #endif
}
if (gBattleMons[gBattlerAttacker].volatiles.destinyBond > 0)
gBattleMons[gBattlerAttacker].volatiles.destinyBond--;
gProtectStructs[gBattlerAttacker].shellTrap = FALSE;
gBattleStruct->battlerState[gBattlerAttacker].ateBoost = FALSE;
gSpecialStatuses[gBattlerAttacker].gemBoost = FALSE;
@ -7572,7 +7582,7 @@ static void UpdateSentMonFlags(u32 battler)
static void SetDmgHazardsBattlescript(u8 battler, u8 multistringId)
{
gBattleMons[battler].volatiles.destinyBond = FALSE;
gBattleMons[battler].volatiles.destinyBond = 0;
gHitMarker &= ~HITMARKER_DESTINYBOND;
gBattleScripting.battler = battler;
gBattleCommunication[MULTISTRING_CHOOSER] = multistringId;
@ -9659,17 +9669,12 @@ static void Cmd_manipulatedamage(void)
static void Cmd_trysetrest(void)
{
CMD_ARGS(const u8 *failInstr);
CMD_ARGS();
const u8 *failInstr = cmd->failInstr;
gBattlerTarget = gBattlerAttacker;
gBattleStruct->moveDamage[gBattlerTarget] = gBattleMons[gBattlerTarget].maxHP * (-1);
if (gBattleMons[gBattlerTarget].hp == gBattleMons[gBattlerTarget].maxHP)
{
gBattlescriptCurrInstr = failInstr;
}
else if (IsBattlerTerrainAffected(gBattlerTarget, STATUS_FIELD_ELECTRIC_TERRAIN))
if (IsBattlerTerrainAffected(gBattlerTarget, STATUS_FIELD_ELECTRIC_TERRAIN))
{
gBattlescriptCurrInstr = BattleScript_ElectricTerrainPrevents;
}
@ -9691,16 +9696,8 @@ static void Cmd_trysetrest(void)
}
}
static void Cmd_jumpifnotfirstturn(void)
static void Cmd_unused_0x82(void)
{
CMD_ARGS(const u8 *jumpInstr);
const u8 *jumpInstr = cmd->jumpInstr;
if (gDisableStructs[gBattlerAttacker].isFirstTurn && !(gSpecialStatuses[gBattlerAttacker].instructedChosenTarget))
gBattlescriptCurrInstr = cmd->nextInstr;
else
gBattlescriptCurrInstr = jumpInstr;
}
static void Cmd_unused_0x83(void)
@ -9752,19 +9749,10 @@ static void Cmd_stockpile(void)
switch (cmd->id)
{
case 0:
if (gDisableStructs[gBattlerAttacker].stockpileCounter >= 3)
{
gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_CANT_STOCKPILE;
}
else
{
gDisableStructs[gBattlerAttacker].stockpileCounter++;
gDisableStructs[gBattlerAttacker].stockpileBeforeDef = gBattleMons[gBattlerAttacker].statStages[STAT_DEF];
gDisableStructs[gBattlerAttacker].stockpileBeforeSpDef = gBattleMons[gBattlerAttacker].statStages[STAT_SPDEF];
PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff1, 1, gDisableStructs[gBattlerAttacker].stockpileCounter);
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STOCKPILED;
}
gDisableStructs[gBattlerAttacker].stockpileCounter++;
gDisableStructs[gBattlerAttacker].stockpileBeforeDef = gBattleMons[gBattlerAttacker].statStages[STAT_DEF];
gDisableStructs[gBattlerAttacker].stockpileBeforeSpDef = gBattleMons[gBattlerAttacker].statStages[STAT_SPDEF];
PREPARE_BYTE_NUMBER_BUFFER(gBattleTextBuff1, 1, gDisableStructs[gBattlerAttacker].stockpileCounter);
break;
case 1: // Save def/sp def stats.
if (!(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT))
@ -9780,20 +9768,12 @@ static void Cmd_stockpile(void)
static void Cmd_stockpiletobasedamage(void)
{
CMD_ARGS(const u8 *failInstr);
CMD_ARGS();
const u8 *failInstr = cmd->failInstr;
if (gDisableStructs[gBattlerAttacker].stockpileCounter == 0)
{
gBattlescriptCurrInstr = failInstr;
}
else
{
if (gBattleCommunication[MISS_TYPE] != B_MSG_PROTECTED)
gBattleScripting.animTurn = gDisableStructs[gBattlerAttacker].stockpileCounter;
if (gBattleCommunication[MISS_TYPE] != B_MSG_PROTECTED)
gBattleScripting.animTurn = gDisableStructs[gBattlerAttacker].stockpileCounter;
gBattlescriptCurrInstr = cmd->nextInstr;
}
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_stockpiletohpheal(void)
@ -9802,40 +9782,31 @@ static void Cmd_stockpiletohpheal(void)
const u8 *failInstr = cmd->failInstr;
if (gDisableStructs[gBattlerAttacker].stockpileCounter == 0 && !gBattleStruct->snatchedMoveIsUsed)
if (gBattleMons[gBattlerAttacker].maxHP == gBattleMons[gBattlerAttacker].hp)
{
gDisableStructs[gBattlerAttacker].stockpileCounter = 0;
gBattlescriptCurrInstr = failInstr;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWALLOW_FAILED;
gBattlerTarget = gBattlerAttacker;
}
else
{
if (gBattleMons[gBattlerAttacker].maxHP == gBattleMons[gBattlerAttacker].hp)
if (gDisableStructs[gBattlerAttacker].stockpileCounter > 0)
{
gDisableStructs[gBattlerAttacker].stockpileCounter = 0;
gBattlescriptCurrInstr = failInstr;
gBattlerTarget = gBattlerAttacker;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWALLOW_FULL_HP;
gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / (1 << (3 - gDisableStructs[gBattlerAttacker].stockpileCounter));
gBattleScripting.animTurn = gDisableStructs[gBattlerAttacker].stockpileCounter;
}
else
else // Snatched move
{
if (gDisableStructs[gBattlerAttacker].stockpileCounter > 0)
{
gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / (1 << (3 - gDisableStructs[gBattlerAttacker].stockpileCounter));
gBattleScripting.animTurn = gDisableStructs[gBattlerAttacker].stockpileCounter;
}
else // Snatched move
{
gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 4;
gBattleScripting.animTurn = 1;
}
if (gBattleStruct->moveDamage[gBattlerAttacker] == 0)
gBattleStruct->moveDamage[gBattlerAttacker] = 1;
gBattleStruct->moveDamage[gBattlerAttacker] *= -1;
gBattlescriptCurrInstr = cmd->nextInstr;
gBattlerTarget = gBattlerAttacker;
gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 4;
gBattleScripting.animTurn = 1;
}
if (gBattleStruct->moveDamage[gBattlerAttacker] == 0)
gBattleStruct->moveDamage[gBattlerAttacker] = 1;
gBattleStruct->moveDamage[gBattlerAttacker] *= -1;
gBattlescriptCurrInstr = cmd->nextInstr;
gBattlerTarget = gBattlerAttacker;
}
}
@ -11488,23 +11459,8 @@ static inline bool32 IsDanamaxMonPresent(void)
return FALSE;
}
static void Cmd_trysetdestinybond(void)
static void Cmd_unused_AA(void)
{
CMD_ARGS(const u8 *failInstr);
if (IsDanamaxMonPresent())
{
gBattlescriptCurrInstr = BattleScript_MoveBlockedByDynamax;
}
else if (DoesDestinyBondFail(gBattlerAttacker))
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else
{
gBattleMons[gBattlerAttacker].volatiles.destinyBond = TRUE;
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
static void TrySetDestinyBondToHappen(void)
@ -12231,29 +12187,22 @@ static void Cmd_selectfirstvalidtarget(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_trysetfutureattack(void)
static void Cmd_setfutureattack(void)
{
CMD_ARGS(const u8 *failInstr);
CMD_ARGS();
if (gWishFutureKnock.futureSightCounter[gBattlerTarget] > gBattleTurnCounter)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
gSideStatuses[GetBattlerSide(gBattlerTarget)] |= SIDE_STATUS_FUTUREATTACK;
gWishFutureKnock.futureSightMove[gBattlerTarget] = gCurrentMove;
gWishFutureKnock.futureSightBattlerIndex[gBattlerTarget] = gBattlerAttacker;
gWishFutureKnock.futureSightPartyIndex[gBattlerTarget] = gBattlerPartyIndexes[gBattlerAttacker];
gWishFutureKnock.futureSightCounter[gBattlerTarget] = gBattleTurnCounter + 3;
if (gCurrentMove == MOVE_DOOM_DESIRE)
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DOOM_DESIRE;
else
{
gSideStatuses[GetBattlerSide(gBattlerTarget)] |= SIDE_STATUS_FUTUREATTACK;
gWishFutureKnock.futureSightMove[gBattlerTarget] = gCurrentMove;
gWishFutureKnock.futureSightBattlerIndex[gBattlerTarget] = gBattlerAttacker;
gWishFutureKnock.futureSightPartyIndex[gBattlerTarget] = gBattlerPartyIndexes[gBattlerAttacker];
gWishFutureKnock.futureSightCounter[gBattlerTarget] = gBattleTurnCounter + 3;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FUTURE_SIGHT;
if (gCurrentMove == MOVE_DOOM_DESIRE)
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DOOM_DESIRE;
else
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FUTURE_SIGHT;
gBattlescriptCurrInstr = cmd->nextInstr;
}
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_trydobeatup(void)
@ -13054,18 +13003,8 @@ static void Cmd_jumpifhasnohp(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_jumpifnotcurrentmoveargtype(void)
static void Cmd_unused_0xE4(void)
{
CMD_ARGS(u8 battler, const u8 *failInstr);
u8 battler = GetBattlerForBattleScript(cmd->battler);
const u8 *failInstr = cmd->failInstr;
u32 type = GetMoveArgType(gCurrentMove);
if (!IS_BATTLER_OF_TYPE(battler, type))
gBattlescriptCurrInstr = failInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_pickup(void)
@ -14396,17 +14335,6 @@ void BS_CalcMetalBurstDmg(void)
}
}
void BS_JumpIfCantFling(void)
{
NATIVE_ARGS(u8 battler, const u8 *jumpInstr);
u32 battler = GetBattlerForBattleScript(cmd->battler);
if (!CanFling(battler))
gBattlescriptCurrInstr = cmd->jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_JumpIfMoreThanHalfHP(void)
{
NATIVE_ARGS(u8 battler, const u8 *jumpInstr);
@ -15070,13 +14998,6 @@ void BS_TrySetOctolock(void)
}
}
void BS_SetGlaiveRush(void)
{
NATIVE_ARGS();
gBattleMons[gBattlerAttacker].volatiles.glaiveRush = TRUE;
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_SetPledge(void)
{
NATIVE_ARGS(const u8 *jumpInstr);
@ -17039,19 +16960,6 @@ void BS_SetLuckyChant(void)
}
}
void BS_SuckerPunchCheck(void)
{
NATIVE_ARGS(const u8 *failInstr);
if (gProtectStructs[gBattlerTarget].protected == PROTECT_OBSTRUCT)
gBattlescriptCurrInstr = cmd->failInstr;
else if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget))
gBattlescriptCurrInstr = cmd->failInstr;
else if (IsBattleMoveStatus(gBattleMons[gBattlerTarget].moves[gBattleStruct->chosenMovePositions[gBattlerTarget]]) && !gProtectStructs[gBattlerTarget].noValidMoves)
gBattlescriptCurrInstr = cmd->failInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_SetSimpleBeam(void)
{
NATIVE_ARGS(const u8 *failInstr);
@ -17183,15 +17091,6 @@ void BS_HandleFormChange(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_TryLastResort(void)
{
NATIVE_ARGS(const u8 *failInstr);
if (CanUseLastResort(gBattlerAttacker))
gBattlescriptCurrInstr = cmd->nextInstr;
else
gBattlescriptCurrInstr = cmd->failInstr;
}
void BS_TryAutotomize(void)
{
NATIVE_ARGS(const u8 *failInstr);
@ -17441,8 +17340,7 @@ void BS_SetAuroraVeil(void)
{
NATIVE_ARGS();
u32 side = GetBattlerSide(gBattlerAttacker);
if (gSideStatuses[side] & SIDE_STATUS_AURORA_VEIL
|| !(HasWeatherEffect() && gBattleWeather & (B_WEATHER_HAIL | B_WEATHER_SNOW)))
if (gSideStatuses[side] & SIDE_STATUS_AURORA_VEIL)
{
gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED;
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
@ -17877,9 +17775,7 @@ void BS_CutOneThirdHpAndRaiseStats(void)
{
NATIVE_ARGS(const u8 *failInstr);
bool8 atLeastOneStatBoosted = FALSE;
u16 hpFraction = max(1, GetNonDynamaxMaxHP(gBattlerAttacker) / 3);
bool32 atLeastOneStatBoosted = FALSE;
for (u32 stat = 1; stat < NUM_STATS; stat++)
{
if (CompareStat(gBattlerAttacker, stat, MAX_STAT_STAGE, CMP_LESS_THAN))
@ -17888,9 +17784,9 @@ void BS_CutOneThirdHpAndRaiseStats(void)
break;
}
}
if (atLeastOneStatBoosted && gBattleMons[gBattlerAttacker].hp > hpFraction)
if (atLeastOneStatBoosted)
{
gBattleStruct->moveDamage[gBattlerAttacker] = hpFraction;
gBattleStruct->moveDamage[gBattlerAttacker] = max(1, GetNonDynamaxMaxHP(gBattlerAttacker) / 3);
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
@ -17899,36 +17795,12 @@ void BS_CutOneThirdHpAndRaiseStats(void)
}
}
void BS_CheckPoltergeist(void)
void BS_SetPoltergeistMessage(void)
{
NATIVE_ARGS(const u8 *failInstr);
if (gBattleMons[gBattlerTarget].item == ITEM_NONE
|| gFieldStatuses & STATUS_FIELD_MAGIC_ROOM
|| GetBattlerAbility(gBattlerTarget) == ABILITY_KLUTZ)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else
{
PREPARE_ITEM_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerTarget].item);
gLastUsedItem = gBattleMons[gBattlerTarget].item;
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
void BS_TryNoRetreat(void)
{
NATIVE_ARGS(const u8 *failInstr);
if (gDisableStructs[gBattlerTarget].noRetreat)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else
{
if (!gBattleMons[gBattlerTarget].volatiles.escapePrevention)
gDisableStructs[gBattlerTarget].noRetreat = TRUE;
gBattlescriptCurrInstr = cmd->nextInstr;
}
PREPARE_ITEM_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerTarget].item);
gLastUsedItem = gBattleMons[gBattlerTarget].item;
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_CureCertainStatuses(void)

View File

@ -132,20 +132,19 @@ bool32 IsTypeStellarBoosted(u32 battler, u32 type)
// Returns the STAB power multiplier to use when Terastallized.
// Power multipliers from Smogon Research thread.
uq4_12_t GetTeraMultiplier(u32 battler, u32 type)
uq4_12_t GetTeraMultiplier(struct DamageContext *ctx)
{
u32 teraType = GetBattlerTeraType(battler);
bool32 hasAdaptability = (GetBattlerAbility(battler) == ABILITY_ADAPTABILITY);
u32 teraType = GetBattlerTeraType(ctx->battlerAtk);
// Safety check.
if (GetActiveGimmick(battler) != GIMMICK_TERA)
if (GetActiveGimmick(ctx->battlerAtk) != GIMMICK_TERA)
return UQ_4_12(1.0);
// Stellar-type checks.
if (teraType == TYPE_STELLAR)
{
bool32 shouldBoost = IsTypeStellarBoosted(battler, type);
if (IS_BATTLER_OF_BASE_TYPE(battler, type))
bool32 shouldBoost = IsTypeStellarBoosted(ctx->battlerAtk, ctx->moveType);
if (IS_BATTLER_OF_BASE_TYPE(ctx->battlerAtk, ctx->moveType))
{
if (shouldBoost)
return UQ_4_12(2.0);
@ -158,18 +157,18 @@ uq4_12_t GetTeraMultiplier(u32 battler, u32 type)
return UQ_4_12(1.0);
}
// Base and Tera type.
if (type == teraType && IS_BATTLER_OF_BASE_TYPE(battler, type))
if (ctx->moveType == teraType && IS_BATTLER_OF_BASE_TYPE(ctx->battlerAtk, ctx->moveType))
{
if (hasAdaptability)
if (ctx->abilityAtk == ABILITY_ADAPTABILITY)
return UQ_4_12(2.25);
else
return UQ_4_12(2.0);
}
// Base or Tera type only.
else if ((type == teraType && !IS_BATTLER_OF_BASE_TYPE(battler, type))
|| (type != teraType && IS_BATTLER_OF_BASE_TYPE(battler, type)))
else if ((ctx->moveType == teraType && !IS_BATTLER_OF_BASE_TYPE(ctx->battlerAtk, ctx->moveType))
|| (ctx->moveType != teraType && IS_BATTLER_OF_BASE_TYPE(ctx->battlerAtk, ctx->moveType)))
{
if (hasAdaptability)
if (ctx->abilityAtk == ABILITY_ADAPTABILITY)
return UQ_4_12(2.0);
else
return UQ_4_12(1.5);

File diff suppressed because it is too large Load Diff

View File

@ -819,7 +819,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_FIRST_TURN_ONLY] =
{
.battleScript = BattleScript_EffectFirstTurnOnly,
.battleScript = BattleScript_EffectHit,
.battleTvScore = 4,
.encourageEncore = TRUE,
},
@ -1392,7 +1392,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_SUCKER_PUNCH] =
{
.battleScript = BattleScript_EffectSuckerPunch,
.battleScript = BattleScript_EffectHit,
.battleTvScore = 0, // TODO: Assign points
},
@ -1531,7 +1531,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_LAST_RESORT] =
{
.battleScript = BattleScript_EffectLastResort,
.battleScript = BattleScript_EffectHit,
.battleTvScore = 0, // TODO: Assign points
},
@ -1721,7 +1721,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_MAT_BLOCK] =
{
.battleScript = BattleScript_EffectMatBlock,
.battleScript = BattleScript_EffectProtect,
.battleTvScore = 0, // TODO: Assign points
.encourageEncore = TRUE,
},
@ -1774,7 +1774,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_FAIL_IF_NOT_ARG_TYPE] =
{
.battleScript = BattleScript_FailIfNotArgType,
.battleScript = BattleScript_EffectHit,
.battleTvScore = 0, // TODO: Assign points
},
@ -1881,7 +1881,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_AURA_WHEEL] =
{
.battleScript = BattleScript_EffectAuraWheel,
.battleScript = BattleScript_EffectHit,
.battleTvScore = 0, // TODO: Assign points
},
@ -2233,7 +2233,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_STEEL_ROLLER] =
{
.battleScript = BattleScript_EffectSteelRoller,
.battleScript = BattleScript_EffectHit,
.battleTvScore = 0, // TODO: Assign points
},

View File

@ -96,3 +96,25 @@ SINGLE_BATTLE_TEST("Dazzling, Queenly Majesty and Armor Tail protect from all mu
}
}
}
SINGLE_BATTLE_TEST("Dazzling, Queenly Majesty and Armor Tail prevent Protean activation")
{
u32 species, ability;
PARAMETRIZE { species = SPECIES_BRUXISH; ability = ABILITY_DAZZLING; }
PARAMETRIZE { species = SPECIES_FARIGIRAF; ability = ABILITY_ARMOR_TAIL; }
PARAMETRIZE { species = SPECIES_TSAREENA; ability = ABILITY_QUEENLY_MAJESTY; }
GIVEN {
PLAYER(SPECIES_KECLEON) { Ability(ABILITY_PROTEAN); }
OPPONENT(species) { Ability(ability); }
} WHEN {
TURN { MOVE(player, MOVE_WATER_SHURIKEN); }
} SCENE {
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_SHURIKEN, player);
ABILITY_POPUP(player, ABILITY_PROTEAN);
}
ABILITY_POPUP(opponent, ability);
}
}

View File

@ -44,6 +44,7 @@ SINGLE_BATTLE_TEST("Teraform Zero can be replaced")
PLAYER(SPECIES_TERAPAGOS);
OPPONENT(SPECIES_WHIMSICOTT) { Ability(ABILITY_PRANKSTER); }
} WHEN {
TURN { MOVE(opponent, MOVE_POUND); }
TURN { MOVE(opponent, MOVE_WORRY_SEED); MOVE(player, MOVE_REST, gimmick: GIMMICK_TERA); }
} SCENE {
MESSAGE("The opposing Whimsicott used Worry Seed!");

View File

@ -903,7 +903,7 @@ SINGLE_BATTLE_TEST("Dynamax: Max Mindstorm sets up Psychic Terrain")
} SCENE {
MESSAGE("The opposing Wobbuffet used Extreme Speed!");
MESSAGE("Wobbuffet used Max Mindstorm!");
MESSAGE("The opposing Wobbuffet cannot use Extreme Speed!");
MESSAGE("Wobbuffet is protected by the Psychic Terrain!");
MESSAGE("Wobbuffet used Max Mindstorm!");
}
}
@ -1648,18 +1648,5 @@ SINGLE_BATTLE_TEST("Dynamax: Dynamax is reverted before switch out")
}
}
SINGLE_BATTLE_TEST("Dynamax: Destiny Bond fails if a dynamaxed battler is present on field")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_DESTINY_BOND) == EFFECT_DESTINY_BOND);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_DESTINY_BOND); MOVE(player, MOVE_SCRATCH, gimmick: GIMMICK_DYNAMAX); }
} SCENE {
MESSAGE("The move was blocked by the power of Dynamax!");
}
}
TO_DO_BATTLE_TEST("Dynamax: Contrary inverts stat-lowering Max Moves, without showing a message")
TO_DO_BATTLE_TEST("Dynamax: Contrary inverts stat-increasing Max Moves, without showing a message")

View File

@ -588,7 +588,7 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Genesis Supernova sets up psychic terrain")
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_GENESIS_SUPERNOVA, player);
NOT { ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, player); }
MESSAGE("Mew cannot use Quick Attack!");
MESSAGE("The opposing Wobbuffet is protected by the Psychic Terrain!");
}
}

View File

@ -25,6 +25,35 @@ SINGLE_BATTLE_TEST("Aurora Veil can only be used in Hail and Snow")
}
}
SINGLE_BATTLE_TEST("Aurora Veil will prevent Protean activation if it fails due to no Snow/Hail")
{
GIVEN {
PLAYER(SPECIES_KECLEON) { Ability(ABILITY_PROTEAN); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_AURORA_VEIL); }
} SCENE {
MESSAGE("But it failed!");
NOT ABILITY_POPUP(player, ABILITY_PROTEAN);
}
}
SINGLE_BATTLE_TEST("Aurora Veil wont prevent Protean activation when it fails due to being set up already")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_KECLEON) { Ability(ABILITY_PROTEAN); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SNOWSCAPE); MOVE(player, MOVE_AURORA_VEIL); }
TURN { SWITCH(player, 1); }
TURN { MOVE(player, MOVE_AURORA_VEIL); }
} SCENE {
ABILITY_POPUP(player, ABILITY_PROTEAN);
MESSAGE("But it failed!");
}
}
TO_DO_BATTLE_TEST("Aurora Veil reduces damage done to the user by half in singles")
TO_DO_BATTLE_TEST("Aurora Veil reduces damage done to the user by roughly a third in doubles")
TO_DO_BATTLE_TEST("Aurora Veil's damage reduction is ignored by Critical Hits")

View File

@ -1,11 +1,52 @@
#include "global.h"
#include "test/battle.h"
TO_DO_BATTLE_TEST("Clangorous Soul raises the user's Attack by 1 stage");
TO_DO_BATTLE_TEST("Clangorous Soul raises the user's Defense by 1 stage");
TO_DO_BATTLE_TEST("Clangorous Soul raises the user's Sp. Attack by 1 stage");
TO_DO_BATTLE_TEST("Clangorous Soul raises the user's Sp. Defense by 1 stage");
TO_DO_BATTLE_TEST("Clangorous Soul raises the user's Speed by 1 stage");
TO_DO_BATTLE_TEST("Clangorous Soul cuts the user's HP by 1/3");
SINGLE_BATTLE_TEST("Clangorous Soul cuts the user's HP by 1/3")
{
s16 dmg;
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(300); MaxHP(300); };
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CLANGOROUS_SOUL); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CLANGOROUS_SOUL, player);
HP_BAR(player, captureDamage: &dmg);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
} THEN {
EXPECT_EQ(gBattleMons[0].maxHP / 3, dmg);
}
}
SINGLE_BATTLE_TEST("Clangorous Soul raises the user's Atk, Def, Sp. Atk. Sp. Def and Speed by 1 stage")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(300); MaxHP(300); };
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CLANGOROUS_SOUL); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_CLANGOROUS_SOUL, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 1);
}
}
SINGLE_BATTLE_TEST("Clangorous Soul fails if the user's HP is less or equal than 1/3")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(100); MaxHP(300); };
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CLANGOROUS_SOUL); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_CLANGOROUS_SOUL, player);
}
}
TO_DO_BATTLE_TEST("Clangorous Soul fails if Attack, Defense, Sp. Attack, Sp. Defense and Speed are all maxed out");
TO_DO_BATTLE_TEST("Clangorous Soul fails if the user's HP is less or equal than 1/3");

View File

@ -42,7 +42,6 @@ TO_DO_BATTLE_TEST("Burn Up doesn't thaw the user if it fails due to the user not
SINGLE_BATTLE_TEST("Burn Up fails if the user has Protean/Libero and is not a Fire-type")
{
KNOWN_FAILING;
GIVEN {
WITH_CONFIG(GEN_PROTEAN_LIBERO, GEN_6);
PLAYER(SPECIES_REGIROCK);

View File

@ -2,3 +2,60 @@
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write No Retreat (Move Effect) test titles")
SINGLE_BATTLE_TEST("No Retreat raises user's Atk/Def/Sp.Atk/Sp.Def/Speed unless No Retreat was already used by user")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_NO_RETREAT); }
TURN { MOVE(player, MOVE_NO_RETREAT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_NO_RETREAT, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_NO_RETREAT, player);
} THEN {
EXPECT_EQ(player->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(player->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(player->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1);
EXPECT_EQ(player->statStages[STAT_SPEED], DEFAULT_STAT_STAGE + 1);
bool32 escapePrevention = gBattleMons[0].volatiles.escapePrevention;
EXPECT_EQ(escapePrevention, TRUE);
}
}
// Question: If No Retreat is used is the mon blocking the switch out changed?
SINGLE_BATTLE_TEST("No Retreat won't fail if user is prevented from escaping")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_MEAN_LOOK); MOVE(player, MOVE_NO_RETREAT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_MEAN_LOOK, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_NO_RETREAT, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
}
}
SINGLE_BATTLE_TEST("No Retreat won't activate Protean if it fails due to already being used by the user")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_KECLEON) { Ability(ABILITY_PROTEAN); }
} WHEN {
TURN { MOVE(player, MOVE_NO_RETREAT); MOVE(opponent, MOVE_SKILL_SWAP); }
TURN { MOVE(player, MOVE_NO_RETREAT); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_NO_RETREAT, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, opponent);
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_NO_RETREAT, player);
ABILITY_POPUP(player, ABILITY_PROTEAN);
}
}
}

View File

@ -11,7 +11,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain protects grounded battlers from priority mov
TURN { MOVE(player, MOVE_QUICK_ATTACK); MOVE(opponent, MOVE_QUICK_ATTACK); }
} SCENE {
MESSAGE("Claydol used Psychic Terrain!");
MESSAGE("Claydol cannot use Quick Attack!");
MESSAGE("The opposing Wobbuffet is protected by the Psychic Terrain!");
NOT { HP_BAR(opponent); }
MESSAGE("The opposing Wobbuffet used Quick Attack!");
HP_BAR(player);
@ -41,7 +41,7 @@ SINGLE_BATTLE_TEST("Psychic Terrain increases power of Psychic-type moves by 30/
}
}
SINGLE_BATTLE_TEST("Psychic Terrain doesn't block priority moves that target the user")
SINGLE_BATTLE_TEST("Psychic Terrain doesn't blocks priority moves that target the user")
{
GIVEN {
PLAYER(SPECIES_SABLEYE) { Ability(ABILITY_PRANKSTER); HP(1); }
@ -142,3 +142,37 @@ SINGLE_BATTLE_TEST("Psychic Terrain lasts for 5 turns")
MESSAGE("The weirdness disappeared from the battlefield!");
}
}
DOUBLE_BATTLE_TEST("Psychic Terrain protects grounded battlers from priority moves in doubles - Left")
{
GIVEN {
PLAYER(SPECIES_CLAYDOL) { Ability(ABILITY_LEVITATE); }
PLAYER(SPECIES_TAPU_LELE) { Ability(ABILITY_PSYCHIC_SURGE); }
OPPONENT(SPECIES_VOLBEAT) { Ability(ABILITY_PRANKSTER); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_COTTON_SPORE); }
} SCENE {
ABILITY_POPUP(playerRight, ABILITY_PSYCHIC_SURGE);
ANIMATION(ANIM_TYPE_MOVE, MOVE_COTTON_SPORE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
}
}
DOUBLE_BATTLE_TEST("Psychic Terrain protects grounded battlers from priority moves in doubles - Right")
{
GIVEN {
PLAYER(SPECIES_TAPU_LELE) { Ability(ABILITY_PSYCHIC_SURGE); }
PLAYER(SPECIES_CLAYDOL) { Ability(ABILITY_LEVITATE); }
OPPONENT(SPECIES_VOLBEAT) { Ability(ABILITY_PRANKSTER); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_COTTON_SPORE); }
} SCENE {
ABILITY_POPUP(playerLeft, ABILITY_PSYCHIC_SURGE);
ANIMATION(ANIM_TYPE_MOVE, MOVE_COTTON_SPORE, opponentLeft);
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
}
}

View File

@ -30,7 +30,7 @@ SINGLE_BATTLE_TEST("Stockpile's count can go up only to 3")
MESSAGE("Wobbuffet stockpiled 3!");
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_STOCKPILE, player);
MESSAGE("Wobbuffet can't stockpile any more!");
MESSAGE("But it failed!");
}
}
@ -49,9 +49,9 @@ SINGLE_BATTLE_TEST("Spit Up and Swallow don't work if used without Stockpile")
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, move, player);
if (move == MOVE_SWALLOW)
MESSAGE("But it failed to swallow a thing!");
MESSAGE("But it failed!");
else
MESSAGE("But it failed to spit up a thing!");
MESSAGE("But it failed!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_STOCKPILE, player);
MESSAGE("Wobbuffet stockpiled 1!");