Refactors Attackstring and PP deduction (#7402)

This commit is contained in:
Alex 2025-09-03 20:25:27 +02:00 committed by GitHub
parent a8e60a8ba3
commit 0422340356
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 708 additions and 1252 deletions

View File

@ -9,11 +9,11 @@
.2byte \move
.endm
.macro attackstring
.macro printattackstring
.byte 0x2
.endm
.macro ppreduce
.macro unused0x3
.byte 0x3
.endm
@ -877,7 +877,7 @@
.4byte \failInstr
.endm
.macro metronome
.macro setcalledmove
.byte 0x9e
.endm
@ -1453,11 +1453,6 @@
.4byte \sidestatus
.endm
.macro trycopycat failInstr:req
callnative BS_TryCopycat
.4byte \failInstr
.endm
.macro setzeffect
callnative BS_SetZEffect
.endm
@ -2207,11 +2202,6 @@
callnative BS_InvertStatStages
.endm
.macro trymefirst failInstr:req
callnative BS_TryMeFirst
.4byte \failInstr
.endm
.macro tryelectrify failInstr:req
callnative BS_TryElectrify
.4byte \failInstr

File diff suppressed because it is too large Load Diff

View File

@ -96,11 +96,11 @@ Each move's effect is governed by a script defined here. For a simple example, l
```
BattleScript_EffectFirstTurnOnly::
attackcanceler
jumpifnotfirstturn BattleScript_FailedFromAtkString
jumpifnotfirstturn BattleScript_ButItFailed
goto BattleScript_EffectHit
```
`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_FailedFromAtkString` 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 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.
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

@ -151,11 +151,10 @@ struct ProtectStruct
u32 unableToUseMove:1; // Not to be confused with HITMARKER_UNABLE_TO_USE_MOVE (It is questionable though if there is a difference. Needs further research)
u32 notFirstStrike:1;
u32 palaceUnableToUseMove:1;
u32 powderSelfDmg:1;
u32 statRaised:1;
u32 usedCustapBerry:1; // also quick claw
u32 touchedProtectLike:1;
u32 unused:8;
u32 unused:9;
// End of 32-bit bitfield
u16 disableEjectPack:1;
u16 tryEjectPack:1;
@ -666,7 +665,7 @@ struct BattleStruct
u8 multipleSwitchInState:2;
u8 multipleSwitchInCursor:3;
u8 sleepClauseNotBlocked:1;
u8 padding1:1;
u8 isSkyBattle:1;
u8 multipleSwitchInSortedBattlers[MAX_BATTLERS_COUNT];
void (*savedCallback)(void);
u16 usedHeldItems[PARTY_SIZE][NUM_BATTLE_SIDES]; // For each party member and side. For harvest, recycle
@ -745,7 +744,6 @@ struct BattleStruct
u8 bonusCritStages[MAX_BATTLERS_COUNT]; // G-Max Chi Strike boosts crit stages of allies.
u8 itemPartyIndex[MAX_BATTLERS_COUNT];
u8 itemMoveIndex[MAX_BATTLERS_COUNT];
u8 isSkyBattle:1;
s32 aiDelayTimer; // Counts number of frames AI takes to choose an action.
s32 aiDelayFrames; // Number of frames it took to choose an action.
s32 aiDelayCycles; // Number of cycles it took to choose an action.
@ -780,7 +778,8 @@ struct BattleStruct
u8 hazardsQueue[NUM_BATTLE_SIDES][HAZARDS_MAX_COUNT];
u8 numHazards[NUM_BATTLE_SIDES];
u8 hazardsCounter:4; // Counter for applying hazard on switch in
u8 padding2:4;
enum SubmoveState submoveAnnouncement:2;
u8 padding2:2;
};
struct AiBattleData

View File

@ -64,7 +64,6 @@ bool32 IsShieldsDownProtected(u32 battler, u32 ability);
u32 IsAbilityStatusProtected(u32 battler, u32 ability);
bool32 TryResetBattlerStatChanges(u8 battler);
bool32 CanCamouflage(u8 battlerId);
u32 GetNaturePowerMove(u32 battler);
void StealTargetItem(u8 battlerStealer, u8 battlerItem);
u8 GetCatchingBattler(void);
u32 GetHighestStatId(u32 battlerId);

View File

@ -9,11 +9,10 @@ extern const u8 BattleScript_NotAffected[];
extern const u8 BattleScript_HitFromCritCalc[];
extern const u8 BattleScript_MoveEnd[];
extern const u8 BattleScript_MakeMoveMissed[];
extern const u8 BattleScript_PrintMoveMissed[];
extern const u8 BattleScript_MoveMissedPause[];
extern const u8 BattleScript_MoveMissedPause[];
extern const u8 BattleScript_MoveMissed[];
extern const u8 BattleScript_FlingFailConsumeItem[];
extern const u8 BattleScript_FailedFromAtkString[];
extern const u8 BattleScript_FailedFromAtkCanceler[];
extern const u8 BattleScript_ButItFailed[];
extern const u8 BattleScript_StatUp[];
@ -180,11 +179,8 @@ extern const u8 BattleScript_DroughtActivates[];
extern const u8 BattleScript_TookAttack[];
extern const u8 BattleScript_SturdyPreventsOHKO[];
extern const u8 BattleScript_DampStopsExplosion[];
extern const u8 BattleScript_MoveHPDrain_PPLoss[];
extern const u8 BattleScript_MoveHPDrain[];
extern const u8 BattleScript_MonMadeMoveUseless_PPLoss[];
extern const u8 BattleScript_MonMadeMoveUseless[];
extern const u8 BattleScript_FlashFireBoost_PPLoss[];
extern const u8 BattleScript_FlashFireBoost[];
extern const u8 BattleScript_AbilityNoStatLoss[];
extern const u8 BattleScript_ItemNoStatLoss[];
@ -279,7 +275,6 @@ extern const u8 BattleScript_WaterSportEnds[];
extern const u8 BattleScript_SturdiedMsg[];
extern const u8 BattleScript_GravityEnds[];
extern const u8 BattleScript_MoveStatDrain[];
extern const u8 BattleScript_MoveStatDrain_PPLoss[];
extern const u8 BattleScript_TargetsStatWasMaxedOut[];
extern const u8 BattleScript_AttackerAbilityStatRaise[];
extern const u8 BattleScript_AttackerAbilityStatRaiseEnd3[];
@ -328,7 +323,6 @@ extern const u8 BattleScript_ProteanActivates[];
extern const u8 BattleScript_DazzlingProtected[];
extern const u8 BattleScript_MoveUsedPsychicTerrainPrevents[];
extern const u8 BattleScript_MoveUsedPowder[];
extern const u8 BattleScript_ZMoveActivatePowder[];
extern const u8 BattleScript_SelectingNotAllowedStuffCheeks[];
extern const u8 BattleScript_SelectingNotAllowedStuffCheeksInPalace[];
extern const u8 BattleScript_SelectingNotAllowedBelch[];
@ -581,7 +575,6 @@ extern const u8 BattleScript_EffectAbsorb[];
extern const u8 BattleScript_EffectAbsorbLiquidOoze[];
extern const u8 BattleScript_EffectExplosion[];
extern const u8 BattleScript_EffectDreamEater[];
extern const u8 BattleScript_EffectMirrorMove[];
extern const u8 BattleScript_EffectAttackUp[];
extern const u8 BattleScript_EffectDefenseUp[];
extern const u8 BattleScript_EffectSpeedUp[];
@ -631,7 +624,6 @@ extern const u8 BattleScript_EffectTwoTurnsAttack[];
extern const u8 BattleScript_EffectSubstitute[];
extern const u8 BattleScript_EffectRage[];
extern const u8 BattleScript_EffectMimic[];
extern const u8 BattleScript_EffectMetronome[];
extern const u8 BattleScript_EffectLeechSeed[];
extern const u8 BattleScript_EffectDoNothing[];
extern const u8 BattleScript_EffectHoldHands[];
@ -645,7 +637,6 @@ extern const u8 BattleScript_EffectSnore[];
extern const u8 BattleScript_EffectConversion2[];
extern const u8 BattleScript_EffectLockOn[];
extern const u8 BattleScript_EffectSketch[];
extern const u8 BattleScript_EffectSleepTalk[];
extern const u8 BattleScript_EffectDestinyBond[];
extern const u8 BattleScript_EffectSpite[];
extern const u8 BattleScript_EffectHealBell[];
@ -700,14 +691,12 @@ extern const u8 BattleScript_EffectNonVolatileStatus[];
extern const u8 BattleScript_EffectMemento[];
extern const u8 BattleScript_EffectFocusPunch[];
extern const u8 BattleScript_EffectFollowMe[];
extern const u8 BattleScript_EffectNaturePower[];
extern const u8 BattleScript_EffectCharge[];
extern const u8 BattleScript_EffectTaunt[];
extern const u8 BattleScript_EffectHelpingHand[];
extern const u8 BattleScript_EffectTrick[];
extern const u8 BattleScript_EffectRolePlay[];
extern const u8 BattleScript_EffectWish[];
extern const u8 BattleScript_EffectAssist[];
extern const u8 BattleScript_EffectIngrain[];
extern const u8 BattleScript_EffectMagicCoat[];
extern const u8 BattleScript_EffectRecycle[];
@ -768,7 +757,6 @@ extern const u8 BattleScript_EffectElectricTerrain[];
extern const u8 BattleScript_EffectPsychicTerrain[];
extern const u8 BattleScript_EffectAttackAccUp[];
extern const u8 BattleScript_EffectAttackSpAttackUp[];
extern const u8 BattleScript_EffectMeFirst[];
extern const u8 BattleScript_EffectQuiverDance[];
extern const u8 BattleScript_EffectCoil[];
extern const u8 BattleScript_EffectElectrify[];
@ -788,7 +776,6 @@ extern const u8 BattleScript_AbilityPreventsPhasingOutRet[];
extern const u8 BattleScript_PrintMonIsRootedRet[];
extern const u8 BattleScript_FinalGambit[];
extern const u8 BattleScript_EffectAutotomize[];
extern const u8 BattleScript_EffectCopycat[];
extern const u8 BattleScript_EffectDefog[];
extern const u8 BattleScript_EffectHitEnemyHealAlly[];
extern const u8 BattleScript_EffectSynchronoise[];
@ -865,5 +852,10 @@ extern const u8 BattleScript_FickleBeamDoubled[];
extern const u8 BattleScript_QuestionForfeitBattle[];
extern const u8 BattleScript_ForfeitBattleGaveMoney[];
extern const u8 BattleScript_AbilityPopUp[];
extern const u8 BattleScript_Attackstring[];
extern const u8 BattleScript_SubmoveAttackstring[];
extern const u8 BattleScript_MetronomeAttackstring[];
extern const u8 BattleScript_SleepTalkAttackstring[];
extern const u8 BattleScript_NaturePowerAttackstring[];
#endif // GUARD_BATTLE_SCRIPTS_H

View File

@ -110,20 +110,25 @@ enum MoveSuccessOrder
CANCELLER_SKY_DROP,
CANCELLER_RECHARGE,
CANCELLER_ASLEEP_OR_FROZEN,
CANCELLER_POWER_POINTS,
CANCELLER_OBEDIENCE,
CANCELLER_TRUANT,
CANCELLER_FLINCH,
CANCELLER_DISABLED,
CANCELLER_VOLATILE_BLOCKED,
CANCELLER_VOLATILE_BLOCKED, // Gravity / Heal Block / Throat Chop
CANCELLER_TAUNTED,
CANCELLER_IMPRISONED,
CANCELLER_CONFUSED,
CANCELLER_PARALYSED,
CANCELLER_INFATUATION,
CANCELLER_BIDE,
CANCELLER_Z_MOVES,
CANCELLER_CHOICE_LOCK,
CANCELLER_CALLSUBMOVE,
CANCELLER_THAW,
CANCELLER_STANCE_CHANGE_2,
CANCELLER_CHOICE_LOCK,
CANCELLER_ATTACKSTRING,
CANCELLER_PPDEDUCTION,
CANCELLER_WEATHER_PRIMAL,
CANCELLER_DYNAMAX_BLOCKED,
CANCELLER_POWDER_STATUS,
@ -131,7 +136,6 @@ enum MoveSuccessOrder
CANCELLER_PSYCHIC_TERRAIN,
CANCELLER_EXPLODING_DAMP,
CANCELLER_MULTIHIT_MOVES,
CANCELLER_Z_MOVES,
CANCELLER_MULTI_TARGET_MOVES,
CANCELLER_END,
};
@ -149,7 +153,8 @@ enum Obedience
enum MoveCanceller
{
MOVE_STEP_SUCCESS,
MOVE_STEP_BREAK,
MOVE_STEP_BREAK, // Breaks out of the function to run a script
MOVE_STEP_FAILURE, // Same as break but breaks out of it due to move failure and jumps to script that handles the failure
MOVE_STEP_REMOVES_STATUS,
};
@ -238,7 +243,6 @@ u32 DoEndTurnEffects(void);
bool32 HandleFaintedMonActions(void);
void TryClearRageAndFuryCutter(void);
enum MoveCanceller AtkCanceller_MoveSuccessOrder(void);
void SetAtkCancellerForCalledMove(void);
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);
@ -408,5 +412,7 @@ bool32 CanMoveSkipAccuracyCalc(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u
u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect);
bool32 IsSemiInvulnerable(u32 battler, enum SemiInvulnerableExclusion excludeCommander);
bool32 BreaksThroughSemiInvulnerablity(u32 battler, u32 move);
u32 GetNaturePowerMove(u32 battler);
u32 GetNaturePowerMove(u32 battler);
#endif // GUARD_BATTLE_UTIL_H

View File

@ -237,9 +237,9 @@ enum SemiInvulnerableExclusion
#define HITMARKER_DESTINYBOND (1 << 6)
#define HITMARKER_NO_ANIMATIONS (1 << 7) // set from battleSceneOff. Never changed during battle
#define HITMARKER_IGNORE_SUBSTITUTE (1 << 8)
#define HITMARKER_NO_ATTACKSTRING (1 << 9)
#define HITMARKER_ATTACKSTRING_PRINTED (1 << 10)
#define HITMARKER_NO_PPDEDUCT (1 << 11)
#define HITMARKER_ATTACKSTRING_PRINTED (1 << 9)
#define HITMARKER_UNUSED_10 (1 << 10)
#define HITMARKER_UNUSED_11 (1 << 11)
#define HITMARKER_UNUSED_12 (1 << 12)
#define HITMARKER_STATUS_ABILITY_EFFECT (1 << 13)
#define HITMARKER_UNUSED_14 (1 << 14)
@ -251,7 +251,7 @@ enum SemiInvulnerableExclusion
#define HITMARKER_PASSIVE_HP_UPDATE (1 << 20)
#define HITMARKER_UNUSED_21 (1 << 21)
#define HITMARKER_PLAYER_FAINTED (1 << 22)
#define HITMARKER_ALLOW_NO_PP (1 << 23)
#define HITMARKER_UNUSED_23 (1 << 23)
#define HITMARKER_GRUDGE (1 << 24)
#define HITMARKER_OBEYS (1 << 25)
#define HITMARKER_UNUSED_26 (1 << 26)
@ -679,4 +679,11 @@ enum __attribute__((packed)) CalcDamageState
CHECK_ACCURACY,
};
enum SubmoveState
{
SUBMOVE_NO_EFFECT,
SUBMOVE_SUCCESS,
SUBMOVE_FAILURE,
};
#endif // GUARD_CONSTANTS_BATTLE_H

View File

@ -226,7 +226,6 @@ enum StringID
STRINGID_BUTNOTHINGHAPPENED,
STRINGID_BUTITFAILED,
STRINGID_ITHURTCONFUSION,
STRINGID_MIRRORMOVEFAILED,
STRINGID_STARTEDTORAIN,
STRINGID_DOWNPOURSTARTED,
STRINGID_RAINCONTINUES,

View File

@ -870,10 +870,6 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
if (gBattleStruct->gimmick.usableGimmick[battlerAtk] && GetActiveGimmick(battlerAtk) == GIMMICK_NONE
&& gBattleStruct->gimmick.usableGimmick[battlerAtk] != GIMMICK_NONE && considerGimmickAtk == USE_GIMMICK)
{
// Set Z-Move variables if needed
if (gBattleStruct->gimmick.usableGimmick[battlerAtk] == GIMMICK_Z_MOVE && IsViableZMove(battlerAtk, move))
gBattleStruct->zmove.baseMoves[battlerAtk] = move;
toggledGimmickAtk = TRUE;
SetActiveGimmick(battlerAtk, gBattleStruct->gimmick.usableGimmick[battlerAtk]);
}
@ -973,7 +969,6 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
// Undo temporary settings
gBattleStruct->dynamicMoveType = 0;
gBattleStruct->swapDamageCategory = FALSE;
gBattleStruct->zmove.baseMoves[battlerAtk] = MOVE_NONE;
if (toggledGimmickAtk)
SetActiveGimmick(battlerAtk, GIMMICK_NONE);
if (toggledGimmickDef)

View File

@ -3982,8 +3982,8 @@ void BattleTurnPassed(void)
gBattleStruct->faintedActionsState = 0;
TurnValuesCleanUp(FALSE);
gHitMarker &= ~HITMARKER_NO_ATTACKSTRING;
gHitMarker &= ~HITMARKER_UNABLE_TO_USE_MOVE;
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
gHitMarker &= ~HITMARKER_PLAYER_FAINTED;
gHitMarker &= ~HITMARKER_PASSIVE_HP_UPDATE;
gBattleScripting.animTurn = 0;
@ -5378,7 +5378,7 @@ static void RunTurnActionsFunctions(void)
{
if (gBattleStruct->savedTurnActionNumber != gCurrentTurnActionNumber) // action turn has been done, clear hitmarker bits for another battler
{
gHitMarker &= ~HITMARKER_NO_ATTACKSTRING;
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
gHitMarker &= ~HITMARKER_UNABLE_TO_USE_MOVE;
}
}

View File

@ -385,7 +385,6 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] =
[STRINGID_BUTNOTHINGHAPPENED] = COMPOUND_STRING("But nothing happened!"),
[STRINGID_BUTITFAILED] = COMPOUND_STRING("But it failed!"),
[STRINGID_ITHURTCONFUSION] = COMPOUND_STRING("It hurt itself in its confusion!"),
[STRINGID_MIRRORMOVEFAILED] = COMPOUND_STRING("The Mirror Move failed!"), //not in gen 5+, uses "but it failed"
[STRINGID_STARTEDTORAIN] = COMPOUND_STRING("It started to rain!"),
[STRINGID_DOWNPOURSTARTED] = COMPOUND_STRING("A downpour started!"), // corresponds to DownpourText in pokegold and pokecrystal and is used by Rain Dance in GSC
[STRINGID_RAINCONTINUES] = COMPOUND_STRING("Rain continues to fall."), //not in gen 5+

View File

@ -339,8 +339,8 @@ static bool32 CanAbilityShieldActivateForBattler(u32 battler);
static void Cmd_attackcanceler(void);
static void Cmd_accuracycheck(void);
static void Cmd_attackstring(void);
static void Cmd_ppreduce(void);
static void Cmd_printattackstring(void);
static void Cmd_unused0x3(void);
static void Cmd_critcalc(void);
static void Cmd_damagecalc(void);
static void Cmd_typecalc(void);
@ -461,7 +461,7 @@ static void Cmd_tryexplosion(void);
static void Cmd_setatkhptozero(void);
static void Cmd_jumpifnexttargetvalid(void);
static void Cmd_tryhealhalfhealth(void);
static void Cmd_trymirrormove(void);
static void Cmd_unused_0x7e(void);
static void Cmd_setfieldweather(void);
static void Cmd_setreflect(void);
static void Cmd_setseeded(void);
@ -495,7 +495,7 @@ static void Cmd_setfocusenergy(void);
static void Cmd_transformdataexecution(void);
static void Cmd_setsubstitute(void);
static void Cmd_mimicattackcopy(void);
static void Cmd_metronome(void);
static void Cmd_setcalledmove(void);
static void Cmd_unused_0x9f(void);
static void Cmd_unused_0xA0(void);
static void Cmd_counterdamagecalculator(void);
@ -506,7 +506,7 @@ static void Cmd_painsplitdmgcalc(void);
static void Cmd_settypetorandomresistance(void);
static void Cmd_setalwayshitflag(void);
static void Cmd_copymovepermanently(void);
static void Cmd_trychoosesleeptalkmove(void);
static void Cmd_unused_0xA9(void);
static void Cmd_trysetdestinybond(void);
static void Cmd_trysetdestinybondtohappen(void);
static void Cmd_settailwind(void);
@ -541,7 +541,7 @@ static void Cmd_unused_c8(void);
static void Cmd_trymemento(void);
static void Cmd_setforcedtarget(void);
static void Cmd_setcharge(void);
static void Cmd_callenvironmentattack(void);
static void Cmd_unused_0xCC(void);
static void Cmd_curestatuswithmove(void);
static void Cmd_settorment(void);
static void Cmd_jumpifnodamage(void);
@ -559,7 +559,7 @@ static void Cmd_tryswapabilities(void);
static void Cmd_tryimprison(void);
static void Cmd_setstealthrock(void);
static void Cmd_trysetvolatile(void);
static void Cmd_assistattackselect(void);
static void Cmd_unused_0xde(void);
static void Cmd_trysetmagiccoat(void);
static void Cmd_trysetsnatch(void);
static void Cmd_unused2(void);
@ -598,8 +598,8 @@ void (*const gBattleScriptingCommandsTable[])(void) =
{
Cmd_attackcanceler, //0x0
Cmd_accuracycheck, //0x1
Cmd_attackstring, //0x2
Cmd_ppreduce, //0x3
Cmd_printattackstring, //0x2
Cmd_unused0x3, //0x3
Cmd_critcalc, //0x4
Cmd_damagecalc, //0x5
Cmd_typecalc, //0x6
@ -720,7 +720,7 @@ void (*const gBattleScriptingCommandsTable[])(void) =
Cmd_setatkhptozero, //0x79
Cmd_jumpifnexttargetvalid, //0x7A
Cmd_tryhealhalfhealth, //0x7B
Cmd_trymirrormove, //0x7C
Cmd_unused_0x7e, //0x7C
Cmd_setfieldweather, //0x7D
Cmd_setreflect, //0x7E
Cmd_setseeded, //0x7F
@ -754,7 +754,7 @@ void (*const gBattleScriptingCommandsTable[])(void) =
Cmd_transformdataexecution, //0x9B
Cmd_setsubstitute, //0x9C
Cmd_mimicattackcopy, //0x9D
Cmd_metronome, //0x9E
Cmd_setcalledmove, //0x9E
Cmd_unused_0x9f, //0x9F
Cmd_unused_0xA0, //0xA0
Cmd_counterdamagecalculator, //0xA1
@ -765,7 +765,7 @@ void (*const gBattleScriptingCommandsTable[])(void) =
Cmd_settypetorandomresistance, //0xA6
Cmd_setalwayshitflag, //0xA7
Cmd_copymovepermanently, //0xA8
Cmd_trychoosesleeptalkmove, //0xA9
Cmd_unused_0xA9, //0xA9
Cmd_trysetdestinybond, //0xAA
Cmd_trysetdestinybondtohappen, //0xAB
Cmd_settailwind, //0xAC
@ -800,7 +800,7 @@ void (*const gBattleScriptingCommandsTable[])(void) =
Cmd_trymemento, //0xC9
Cmd_setforcedtarget, //0xCA
Cmd_setcharge, //0xCB
Cmd_callenvironmentattack, //0xCC
Cmd_unused_0xCC, //0xCC
Cmd_curestatuswithmove, //0xCD
Cmd_settorment, //0xCE
Cmd_jumpifnodamage, //0xCF
@ -818,7 +818,7 @@ void (*const gBattleScriptingCommandsTable[])(void) =
Cmd_tryimprison, //0xDB
Cmd_setstealthrock, //0xDC
Cmd_trysetvolatile, //0xDD
Cmd_assistattackselect, //0xDE
Cmd_unused_0xde, //0xDE
Cmd_trysetmagiccoat, //0xDF
Cmd_trysetsnatch, //0xE0
Cmd_unused2, //0xE1
@ -1107,13 +1107,13 @@ static void Cmd_attackcanceler(void)
if (!IsBattlerAlive(gBattlerAttacker)
&& effect != EFFECT_EXPLOSION
&& effect != EFFECT_MISTY_EXPLOSION
&& !(gHitMarker & HITMARKER_NO_ATTACKSTRING))
&& effect != EFFECT_MISTY_EXPLOSION)
{
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
gBattlescriptCurrInstr = BattleScript_MoveEnd;
return;
}
if (AtkCanceller_MoveSuccessOrder() != MOVE_STEP_SUCCESS)
return;
@ -1129,7 +1129,6 @@ static void Cmd_attackcanceler(void)
return;
}
u32 abilityDef = GetBattlerAbility(gBattlerTarget);
if (CanAbilityBlockMove(
gBattlerAttacker,
@ -1155,17 +1154,6 @@ static void Cmd_attackcanceler(void)
if (IsMovePowderBlocked(gBattlerAttacker, gBattlerTarget, gCurrentMove))
return;
if (!gBattleMons[gBattlerAttacker].pp[gCurrMovePos] && gCurrentMove != MOVE_STRUGGLE
&& !(gHitMarker & (HITMARKER_ALLOW_NO_PP | HITMARKER_NO_ATTACKSTRING | HITMARKER_NO_PPDEDUCT))
&& !(gBattleMons[gBattlerAttacker].volatiles.multipleTurns))
{
gBattlescriptCurrInstr = BattleScript_NoPPForMove;
gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED;
return;
}
gHitMarker &= ~HITMARKER_ALLOW_NO_PP;
// 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)))
@ -1176,7 +1164,7 @@ static void Cmd_attackcanceler(void)
if (effect == 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_FailedFromAtkString;
gBattlescriptCurrInstr = BattleScript_ButItFailed;
if (!gBattleMoveEffects[effect].twoTurnEffect || (gBattleMons[gBattlerAttacker].volatiles.multipleTurns))
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
@ -1192,7 +1180,6 @@ static void Cmd_attackcanceler(void)
// Edge case for bouncing a powder move against a grass type pokemon.
ClearDamageCalcResults();
SetAtkCancellerForCalledMove();
gEffectBattler = gBattlerTarget;
if (BlocksPrankster(gCurrentMove, gBattlerTarget, gBattlerAttacker, TRUE))
{
@ -1226,7 +1213,6 @@ static void Cmd_attackcanceler(void)
if (gBattleStruct->bouncedMoveIsUsed)
{
ClearDamageCalcResults();
SetAtkCancellerForCalledMove(); // Edge case for bouncing a powder move against a grass type pokemon.
BattleScriptCall(BattleScript_MagicBounce);
gBattlerAbility = battler;
return;
@ -1489,76 +1475,20 @@ static void Cmd_accuracycheck(void)
AccuracyCheck(FALSE, cmd->nextInstr, cmd->failInstr, cmd->move);
}
static void Cmd_attackstring(void)
static void Cmd_printattackstring(void)
{
CMD_ARGS();
if (gBattleControllerExecFlags)
return;
if (!(gHitMarker & (HITMARKER_NO_ATTACKSTRING | HITMARKER_ATTACKSTRING_PRINTED)))
{
PrepareStringBattle(STRINGID_USEDMOVE, gBattlerAttacker);
gHitMarker |= HITMARKER_ATTACKSTRING_PRINTED;
}
gBattlescriptCurrInstr = cmd->nextInstr;
PrepareStringBattle(STRINGID_USEDMOVE, gBattlerAttacker);
gBattleCommunication[MSG_DISPLAY] = 0;
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_ppreduce(void)
static void Cmd_unused0x3(void)
{
CMD_ARGS();
s32 i, ppToDeduct = 1;
u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
if (gBattleControllerExecFlags)
return;
if (gBattleMons[gBattlerAttacker].volatiles.multipleTurns)
gHitMarker |= HITMARKER_NO_PPDEDUCT;
if (moveTarget == MOVE_TARGET_BOTH
|| moveTarget == MOVE_TARGET_FOES_AND_ALLY
|| moveTarget == MOVE_TARGET_ALL_BATTLERS
|| MoveForcesPressure(gCurrentMove))
{
for (i = 0; i < gBattlersCount; i++)
{
if (!IsBattlerAlly(i, gBattlerAttacker) && IsBattlerAlive(i))
ppToDeduct += (GetBattlerAbility(i) == ABILITY_PRESSURE);
}
}
else if (moveTarget != MOVE_TARGET_OPPONENTS_FIELD)
{
if (gBattlerAttacker != gBattlerTarget && GetBattlerAbility(gBattlerTarget) == ABILITY_PRESSURE)
ppToDeduct++;
}
if (!(gHitMarker & (HITMARKER_NO_PPDEDUCT | HITMARKER_NO_ATTACKSTRING)) && gBattleMons[gBattlerAttacker].pp[gCurrMovePos])
{
gProtectStructs[gBattlerAttacker].notFirstStrike = TRUE;
// For item Metronome, echoed voice
if (gCurrentMove != gLastResultingMoves[gBattlerAttacker] || WasUnableToUseMove(gBattlerAttacker))
gBattleStruct->sameMoveTurns[gBattlerAttacker] = 0;
if (gBattleMons[gBattlerAttacker].pp[gCurrMovePos] > ppToDeduct)
gBattleMons[gBattlerAttacker].pp[gCurrMovePos] -= ppToDeduct;
else
gBattleMons[gBattlerAttacker].pp[gCurrMovePos] = 0;
if (MOVE_IS_PERMANENT(gBattlerAttacker, gCurrMovePos))
{
BtlController_EmitSetMonData(gBattlerAttacker, B_COMM_TO_CONTROLLER, REQUEST_PPMOVE1_BATTLE + gCurrMovePos, 0,
sizeof(gBattleMons[gBattlerAttacker].pp[gCurrMovePos]),
&gBattleMons[gBattlerAttacker].pp[gCurrMovePos]);
MarkBattlerForControllerExec(gBattlerAttacker);
}
}
gHitMarker &= ~HITMARKER_NO_PPDEDUCT;
gBattlescriptCurrInstr = cmd->nextInstr;
}
// The chance is 1/N for each stage.
@ -6354,11 +6284,9 @@ static void Cmd_moveend(void)
&& IsDoubleBattle()
&& !gProtectStructs[gBattlerAttacker].chargingTurn
&& (moveTarget == MOVE_TARGET_BOTH
|| moveTarget == MOVE_TARGET_FOES_AND_ALLY)
&& !(gHitMarker & HITMARKER_NO_ATTACKSTRING))
|| moveTarget == MOVE_TARGET_FOES_AND_ALLY))
{
u32 nextTarget = GetNextTarget(moveTarget, FALSE);
gHitMarker |= HITMARKER_NO_PPDEDUCT;
if (nextTarget != MAX_BATTLERS_COUNT)
{
@ -6367,8 +6295,11 @@ static void Cmd_moveend(void)
MoveValuesCleanUp();
gBattleScripting.moveEffect = gBattleScripting.savedMoveEffect;
if (moveEffect == EFFECT_EXPLOSION || moveEffect == EFFECT_MISTY_EXPLOSION // Edge case for Explosion not changing targets
|| moveEffect == EFFECT_SYNCHRONOISE) // So we don't go back to the Synchronoise script
// Edge cases for moves that shouldn't repeat their own script
if (moveEffect == EFFECT_EXPLOSION
|| moveEffect == EFFECT_MISTY_EXPLOSION
|| moveEffect == EFFECT_MAGNITUDE
|| moveEffect == EFFECT_SYNCHRONOISE)
BattleScriptPush(gBattleMoveEffects[EFFECT_HIT].battleScript);
else
BattleScriptPush(GetMoveBattleScript(gCurrentMove));
@ -6398,9 +6329,6 @@ static void Cmd_moveend(void)
return;
}
}
gHitMarker |= HITMARKER_NO_ATTACKSTRING;
gHitMarker &= ~HITMARKER_NO_PPDEDUCT;
}
RecordLastUsedMoveBy(gBattlerAttacker, gCurrentMove);
gBattleScripting.moveendState++;
@ -6445,7 +6373,6 @@ static void Cmd_moveend(void)
if (gSpecialStatuses[gBattlerAttacker].parentalBondState)
gSpecialStatuses[gBattlerAttacker].parentalBondState--;
gHitMarker |= (HITMARKER_NO_PPDEDUCT | HITMARKER_NO_ATTACKSTRING);
gBattleScripting.animTargetsHit = 0;
gBattleScripting.moveendState = 0;
gSpecialStatuses[gBattlerAttacker].multiHitOn = TRUE;
@ -6879,7 +6806,6 @@ static void Cmd_moveend(void)
gSpecialStatuses[gBattlerTarget].berryReduced = FALSE;
gSpecialStatuses[gBattlerTarget].distortedTypeMatchups = FALSE;
gBattleScripting.moveEffect = MOVE_EFFECT_NONE;
gBattleStruct->isAtkCancelerForCalledMove = FALSE;
gBattleStruct->swapDamageCategory = FALSE;
gBattleStruct->categoryOverride = FALSE;
gBattleStruct->additionalEffectsCounter = 0;
@ -8422,7 +8348,7 @@ static void ResetValuesForCalledMove(void)
if (gBattlerByTurnOrder[gCurrentTurnActionNumber] != gBattlerAttacker)
gBattleStruct->atkCancellerTracker = 0;
else
SetAtkCancellerForCalledMove();
gBattleStruct->atkCancellerTracker = CANCELLER_VOLATILE_BLOCKED;
gBattleScripting.animTurn = 0;
gBattleScripting.animTargetsHit = 0;
SetTypeBeforeUsingMove(gCurrentMove, gBattlerAttacker);
@ -9627,59 +9553,8 @@ static void Cmd_tryhealhalfhealth(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void SetMoveForMirrorMove(u32 move)
static void Cmd_unused_0x7e(void)
{
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
// Edge case, we used Z Mirror Move, got the stat boost and now need to use the Z-move
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IsBattleMoveStatus(move))
{
gBattleStruct->zmove.baseMoves[gBattlerAttacker] = move;
gCurrentMove = GetTypeBasedZMove(move);
}
else
{
gCurrentMove = move;
}
gBattlerTarget = GetBattleMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
ResetValuesForCalledMove();
gBattlescriptCurrInstr = GetMoveBattleScript(gCurrentMove);
}
static void Cmd_trymirrormove(void)
{
CMD_ARGS();
s32 i, validMovesCount;
u16 move;
u16 validMoves[MAX_BATTLERS_COUNT] = {0};
for (validMovesCount = 0, i = 0; i < gBattlersCount; i++)
{
if (i != gBattlerAttacker)
{
move = gBattleStruct->lastTakenMoveFrom[gBattlerAttacker][i];
if (move != MOVE_NONE && move != MOVE_UNAVAILABLE)
{
validMoves[validMovesCount] = move;
validMovesCount++;
}
}
}
move = gBattleStruct->lastTakenMove[gBattlerAttacker];
if (move != MOVE_NONE && move != MOVE_UNAVAILABLE)
{
SetMoveForMirrorMove(move);
}
else if (validMovesCount != 0)
{
SetMoveForMirrorMove(validMoves[Random() % validMovesCount]);
}
else // no valid moves found
{
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
static void Cmd_setfieldweather(void)
@ -11255,41 +11130,11 @@ static void Cmd_mimicattackcopy(void)
}
}
static bool32 InvalidMetronomeMove(u32 move)
{
return GetMoveEffect(move) == EFFECT_PLACEHOLDER
|| IsMoveMetronomeBanned(move);
}
static void Cmd_metronome(void)
static void Cmd_setcalledmove(void)
{
CMD_ARGS();
#if B_METRONOME_MOVES >= GEN_9
u32 moveCount = MOVES_COUNT_GEN9;
#elif B_METRONOME_MOVES >= GEN_8
u32 moveCount = MOVES_COUNT_GEN8;
#elif B_METRONOME_MOVES >= GEN_7
u32 moveCount = MOVES_COUNT_GEN7;
#elif B_METRONOME_MOVES >= GEN_6
u32 moveCount = MOVES_COUNT_GEN6;
#elif B_METRONOME_MOVES >= GEN_5
u32 moveCount = MOVES_COUNT_GEN5;
#elif B_METRONOME_MOVES >= GEN_4
u32 moveCount = MOVES_COUNT_GEN4;
#elif B_METRONOME_MOVES >= GEN_3
u32 moveCount = MOVES_COUNT_GEN3;
#elif B_METRONOME_MOVES >= GEN_2
u32 moveCount = MOVES_COUNT_GEN2;
#else
u32 moveCount = MOVES_COUNT_GEN1;
#endif
gCurrentMove = RandomUniformExcept(RNG_METRONOME, 1, moveCount - 1, InvalidMetronomeMove);
PrepareStringBattle(STRINGID_WAGGLINGAFINGER, gBattlerAttacker);
gBattlescriptCurrInstr = GetMoveBattleScript(gCurrentMove);
gBattlerTarget = GetBattleMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
ResetValuesForCalledMove();
gCurrentMove = gCalledMove;
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_unused_0x9f(void)
@ -11635,49 +11480,8 @@ static void Cmd_copymovepermanently(void)
}
}
static void Cmd_trychoosesleeptalkmove(void)
static void Cmd_unused_0xA9(void)
{
CMD_ARGS(const u8 *failInstr);
u32 i, unusableMovesBits = 0, movePosition;
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (IsMoveSleepTalkBanned(gBattleMons[gBattlerAttacker].moves[i])
|| gBattleMoveEffects[GetMoveEffect(gBattleMons[gBattlerAttacker].moves[i])].twoTurnEffect)
{
unusableMovesBits |= (1 << (i));
}
}
unusableMovesBits = CheckMoveLimitations(gBattlerAttacker, unusableMovesBits, ~(MOVE_LIMITATION_PP | MOVE_LIMITATION_CHOICE_ITEM));
if (unusableMovesBits == ALL_MOVES_MASK) // all 4 moves cannot be chosen
{
gBattlescriptCurrInstr = cmd->nextInstr;
}
else // at least one move can be chosen
{
// Set Sleep Talk as used move, so it works with Last Resort.
gDisableStructs[gBattlerAttacker].usedMoves |= 1u << gCurrMovePos;
do
{
movePosition = MOD(Random(), MAX_MON_MOVES);
} while ((1u << movePosition) & unusableMovesBits);
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IsBattleMoveStatus(gBattleMons[gBattlerAttacker].moves[movePosition]))
{
gBattleStruct->zmove.baseMoves[gBattlerAttacker] = gBattleMons[gBattlerAttacker].moves[movePosition];
gCalledMove = GetTypeBasedZMove(gBattleMons[gBattlerAttacker].moves[movePosition]);
}
else
{
gCalledMove = gBattleMons[gBattlerAttacker].moves[movePosition];
}
gCurrMovePos = movePosition;
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
gBattlerTarget = GetBattleMoveTarget(gCalledMove, NO_TARGET_OVERRIDE);
gBattlescriptCurrInstr = cmd->failInstr;
}
}
static inline bool32 IsDanamaxMonPresent(void)
@ -12629,39 +12433,8 @@ static void Cmd_setcharge(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
// Nature Power
static void Cmd_callenvironmentattack(void)
static void Cmd_unused_0xCC(void)
{
CMD_ARGS();
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
gCurrentMove = GetNaturePowerMove(gBattlerAttacker);
gBattlerTarget = GetBattleMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
BattleScriptPush(GetMoveBattleScript(gCurrentMove));
gBattlescriptCurrInstr = cmd->nextInstr;
}
u32 GetNaturePowerMove(u32 battler)
{
u32 move = gBattleEnvironmentInfo[gBattleEnvironment].naturePower;
if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)
move = MOVE_MOONBLAST;
else if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)
move = MOVE_THUNDERBOLT;
else if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN)
move = MOVE_ENERGY_BALL;
else if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN)
move = MOVE_PSYCHIC;
else if (gBattleEnvironmentInfo[gBattleEnvironment].naturePower == MOVE_NONE)
move = MOVE_TRI_ATTACK;
if (GetActiveGimmick(battler) == GIMMICK_Z_MOVE)
{
gBattleStruct->zmove.baseMoves[gBattlerAttacker] = move;
move = GetTypeBasedZMove(move);
}
return move;
}
static void Cmd_curestatuswithmove(void)
@ -13192,53 +12965,8 @@ static void Cmd_trysetvolatile(void)
}
}
static void Cmd_assistattackselect(void)
static void Cmd_unused_0xde(void)
{
CMD_ARGS(const u8 *failInstr);
s32 chooseableMovesNo = 0;
struct Pokemon *party;
s32 monId, moveId;
u16 *validMoves = Alloc(sizeof(u16) * PARTY_SIZE * MAX_MON_MOVES);
if (validMoves != NULL)
{
party = GetBattlerParty(gBattlerAttacker);
for (monId = 0; monId < PARTY_SIZE; monId++)
{
if (monId == gBattlerPartyIndexes[gBattlerAttacker])
continue;
if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG) == SPECIES_NONE)
continue;
if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG) == SPECIES_EGG)
continue;
for (moveId = 0; moveId < MAX_MON_MOVES; moveId++)
{
u16 move = GetMonData(&party[monId], MON_DATA_MOVE1 + moveId);
if (IsMoveAssistBanned(move))
continue;
validMoves[chooseableMovesNo++] = move;
}
}
}
if (chooseableMovesNo)
{
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
gCalledMove = validMoves[Random() % chooseableMovesNo];
gBattlerTarget = GetBattleMoveTarget(gCalledMove, NO_TARGET_OVERRIDE);
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
{
gBattlescriptCurrInstr = cmd->failInstr;
}
TRY_FREE_AND_SET_NULL(validMoves);
}
static void Cmd_trysetmagiccoat(void)
@ -15362,9 +15090,6 @@ void BS_SetPledge(void)
if (gBattleStruct->pledgeMove && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE))
{
PrepareStringBattle(STRINGID_USEDMOVE, gBattlerAttacker);
gHitMarker |= HITMARKER_ATTACKSTRING_PRINTED;
if ((gCurrentMove == MOVE_GRASS_PLEDGE && partnerMove == MOVE_WATER_PLEDGE)
|| (gCurrentMove == MOVE_WATER_PLEDGE && partnerMove == MOVE_GRASS_PLEDGE))
{
@ -15531,36 +15256,6 @@ void BS_TryHealPulse(void)
}
}
void BS_TryCopycat(void)
{
NATIVE_ARGS(const u8 *failInstr);
if (gLastUsedMove == MOVE_NONE || gLastUsedMove == MOVE_UNAVAILABLE || IsMoveCopycatBanned(gLastUsedMove) || IsZMove(gLastUsedMove))
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else
{
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IsBattleMoveStatus(gLastUsedMove))
{
gBattleStruct->zmove.baseMoves[gBattlerAttacker] = gLastUsedMove;
gCalledMove = GetTypeBasedZMove(gLastUsedMove);
}
else if (IsMaxMove(gLastUsedMove))
{
gCalledMove = gBattleStruct->dynamax.lastUsedBaseMove;
}
else
{
gCalledMove = gLastUsedMove;
}
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
gBattlerTarget = GetBattleMoveTarget(gCalledMove, NO_TARGET_OVERRIDE);
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
void BS_TryDefog(void)
{
NATIVE_ARGS(u8 clear, const u8 *failInstr);
@ -17433,32 +17128,6 @@ void BS_InvertStatStages(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_TryMeFirst(void)
{
NATIVE_ARGS(const u8 *failInstr);
u16 move = gBattleMons[gBattlerTarget].moves[gBattleStruct->chosenMovePositions[gBattlerTarget]];
if (IsBattleMoveStatus(move) || IsMoveMeFirstBanned(move)
|| GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget))
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else
{
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IsBattleMoveStatus(move))
{
gBattleStruct->zmove.baseMoves[gBattlerAttacker] = move;
gCalledMove = GetTypeBasedZMove(move);
}
else
{
gCalledMove = move;
}
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
gBattlerTarget = GetBattleMoveTarget(gCalledMove, NO_TARGET_OVERRIDE);
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
void BS_TryElectrify(void)
{
NATIVE_ARGS(const u8 *failInstr);
@ -17571,7 +17240,6 @@ void BS_TryInstruct(void)
else
{
gEffectBattler = gBattleStruct->lastMoveTarget[gBattlerTarget];
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gBattlerTarget, gBattlerPartyIndexes[gBattlerTarget]);
gBattlescriptCurrInstr = cmd->nextInstr;
}

View File

@ -2,6 +2,7 @@
#include "battle.h"
#include "battle_anim.h"
#include "battle_arena.h"
#include "battle_environment.h"
#include "battle_pyramid.h"
#include "battle_util.h"
#include "battle_controllers.h"
@ -69,6 +70,14 @@ static bool32 IsAnyTargetAffected(u32 battlerAtk);
static bool32 IsNonVolatileStatusBlocked(u32 battlerDef, u32 abilityDef, u32 abilityAffected, const u8 *battleScript, enum FunctionCallOption option);
static bool32 CanSleepDueToSleepClause(u32 battlerAtk, u32 battlerDef, enum FunctionCallOption option);
// Submoves
static u32 GetMirrorMoveMove(void);
static u32 GetMetronomeMove(void);
static u32 GetAssistMove(void);
static u32 GetSleepTalkMove(void);
static u32 GetCopyCatMove(void);
static u32 GetMeFirstMove(void);
ARM_FUNC NOINLINE static uq4_12_t PercentToUQ4_12(u32 percent);
ARM_FUNC NOINLINE static uq4_12_t PercentToUQ4_12_Floored(u32 percent);
@ -410,7 +419,6 @@ void HandleAction_UseMove(void)
{
gProtectStructs[gBattlerAttacker].noValidMoves = FALSE;
gCurrentMove = gChosenMove = MOVE_STRUGGLE;
gHitMarker |= HITMARKER_NO_PPDEDUCT;
gBattleStruct->moveTarget[gBattlerAttacker] = GetBattleMoveTarget(MOVE_STRUGGLE, NO_TARGET_OVERRIDE);
}
else if (gBattleMons[gBattlerAttacker].volatiles.multipleTurns || gBattleMons[gBattlerAttacker].volatiles.recharge)
@ -880,9 +888,8 @@ void HandleAction_NothingIsFainted(void)
gCurrentActionFuncId = gActionsByTurnOrder[gCurrentTurnActionNumber];
gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_NONE;
gHitMarker &= ~(HITMARKER_DESTINYBOND
| HITMARKER_IGNORE_SUBSTITUTE
| HITMARKER_ATTACKSTRING_PRINTED
| HITMARKER_NO_PPDEDUCT
| HITMARKER_IGNORE_SUBSTITUTE
| HITMARKER_STATUS_ABILITY_EFFECT
| HITMARKER_PASSIVE_HP_UPDATE
| HITMARKER_OBEYS);
@ -899,7 +906,6 @@ void HandleAction_ActionFinished(void)
gHitMarker &= ~(HITMARKER_DESTINYBOND
| HITMARKER_IGNORE_SUBSTITUTE
| HITMARKER_ATTACKSTRING_PRINTED
| HITMARKER_NO_PPDEDUCT
| HITMARKER_STATUS_ABILITY_EFFECT
| HITMARKER_PASSIVE_HP_UPDATE
| HITMARKER_OBEYS
@ -1152,7 +1158,6 @@ bool32 WasUnableToUseMove(u32 battler)
{
if (gProtectStructs[battler].nonVolatileStatusImmobility
|| gProtectStructs[battler].unableToUseMove
|| gProtectStructs[battler].powderSelfDmg
|| gProtectStructs[battler].confusionSelfDmg)
return TRUE;
return FALSE;
@ -1898,12 +1903,6 @@ static inline bool32 TryActivatePowderStatus(u32 move)
return FALSE;
}
void SetAtkCancellerForCalledMove(void)
{
gBattleStruct->atkCancellerTracker = CANCELLER_VOLATILE_BLOCKED;
gBattleStruct->isAtkCancelerForCalledMove = TRUE;
}
static enum MoveCanceller CancellerFlags(void)
{
gBattleMons[gBattlerAttacker].volatiles.destinyBond = FALSE;
@ -1914,7 +1913,7 @@ static enum MoveCanceller CancellerFlags(void)
static enum MoveCanceller CancellerStanceChangeOne(void)
{
if (B_STANCE_CHANGE_FAIL < GEN_7 && TryFormChangeBeforeMove())
if (B_STANCE_CHANGE_FAIL < GEN_7 && gChosenMove == gCurrentMove && TryFormChangeBeforeMove())
return MOVE_STEP_BREAK;
return MOVE_STEP_SUCCESS;
}
@ -1926,7 +1925,7 @@ static enum MoveCanceller CancellerSkyDrop(void)
{
gBattlescriptCurrInstr = BattleScript_MoveEnd;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
return MOVE_STEP_BREAK;
return MOVE_STEP_FAILURE;
}
return MOVE_STEP_SUCCESS;
}
@ -1940,7 +1939,7 @@ static enum MoveCanceller CancellerRecharge(void)
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedMustRecharge;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
return MOVE_STEP_BREAK;
return MOVE_STEP_FAILURE;
}
return MOVE_STEP_SUCCESS;
}
@ -1996,7 +1995,7 @@ static enum MoveCanceller CancellerAsleepOrFrozen(void)
{
gProtectStructs[gBattlerAttacker].nonVolatileStatusImmobility = TRUE;
gBattlescriptCurrInstr = BattleScript_MoveUsedIsFrozen;
gHitMarker |= (HITMARKER_NO_ATTACKSTRING | HITMARKER_UNABLE_TO_USE_MOVE);
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
}
else // unfreeze
{
@ -2004,29 +2003,28 @@ static enum MoveCanceller CancellerAsleepOrFrozen(void)
BattleScriptCall(BattleScript_MoveUsedUnfroze);
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_DEFROSTED;
}
return MOVE_STEP_REMOVES_STATUS;
return MOVE_STEP_REMOVES_STATUS; // Move failure but also removes status
}
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerObedience(void)
{
enum Obedience obedienceResult = GetAttackerObedienceForAction();
if (!(gHitMarker & HITMARKER_NO_PPDEDUCT) // Don't check obedience after first hit of multi target move or multi hit moves
&& !gBattleMons[gBattlerAttacker].volatiles.multipleTurns)
if (!gBattleMons[gBattlerAttacker].volatiles.multipleTurns)
{
enum Obedience obedienceResult = GetAttackerObedienceForAction();
switch (obedienceResult)
{
case OBEYS:
gHitMarker |= HITMARKER_OBEYS;
break;
return MOVE_STEP_SUCCESS;
case DISOBEYS_LOAFS:
// Randomly select, then print a disobedient string
// B_MSG_LOAFING, B_MSG_WONT_OBEY, B_MSG_TURNED_AWAY, or B_MSG_PRETEND_NOT_NOTICE
gBattleCommunication[MULTISTRING_CHOOSER] = MOD(Random(), NUM_LOAF_STRINGS);
gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround;
gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED;
break;
return MOVE_STEP_FAILURE;
case DISOBEYS_HITS_SELF:
gBattlerTarget = gBattlerAttacker;
struct DamageContext ctx;
@ -2041,31 +2039,45 @@ static enum MoveCanceller CancellerObedience(void)
gBattlescriptCurrInstr = BattleScript_IgnoresAndHitsItself;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
gHitMarker |= HITMARKER_OBEYS;
break;
return MOVE_STEP_FAILURE; // Move doesn't fail but mon hits itself
case DISOBEYS_FALL_ASLEEP:
if (IsSleepClauseEnabled())
gBattleStruct->battlerState[gBattlerAttacker].sleepClauseEffectExempt = TRUE;
gBattlescriptCurrInstr = BattleScript_IgnoresAndFallsAsleep;
gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED;
return MOVE_STEP_FAILURE;
break;
case DISOBEYS_WHILE_ASLEEP:
gBattlescriptCurrInstr = BattleScript_IgnoresWhileAsleep;
gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED;
break;
return MOVE_STEP_FAILURE;
case DISOBEYS_RANDOM_MOVE:
gCalledMove = gBattleMons[gBattlerAttacker].moves[gCurrMovePos];
SetAtkCancellerForCalledMove();
gBattlescriptCurrInstr = BattleScript_IgnoresAndUsesRandomMove;
gCurrentMove = gCalledMove = gBattleMons[gBattlerAttacker].moves[gCurrMovePos];
BattleScriptCall(BattleScript_IgnoresAndUsesRandomMove);
gBattlerTarget = GetBattleMoveTarget(gCalledMove, NO_TARGET_OVERRIDE);
gHitMarker |= HITMARKER_OBEYS;
break;
return MOVE_STEP_BREAK;
}
return MOVE_STEP_BREAK;
}
gHitMarker |= HITMARKER_OBEYS;
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerPowerPoints(void)
{
if (gBattleMons[gBattlerAttacker].pp[gCurrMovePos] == 0
&& gCurrentMove != MOVE_STRUGGLE
&& !gSpecialStatuses[gBattlerAttacker].dancerUsedMove
&& !gBattleMons[gBattlerAttacker].volatiles.multipleTurns)
{
gBattlescriptCurrInstr = BattleScript_NoPPForMove;
gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED;
return MOVE_STEP_FAILURE;
}
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerTruant(void)
{
if (GetBattlerAbility(gBattlerAttacker) == ABILITY_TRUANT && gDisableStructs[gBattlerAttacker].truantCounter)
@ -2076,7 +2088,7 @@ static enum MoveCanceller CancellerTruant(void)
gBattlerAbility = gBattlerAttacker;
gBattlescriptCurrInstr = BattleScript_TruantLoafingAround;
gBattleStruct->moveResultFlags[gBattlerTarget] |= MOVE_RESULT_MISSED;
return MOVE_STEP_BREAK;
return MOVE_STEP_FAILURE;
}
return MOVE_STEP_SUCCESS;
}
@ -2089,7 +2101,7 @@ static enum MoveCanceller CancellerFlinch(void)
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedFlinched;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
return MOVE_STEP_BREAK;
return MOVE_STEP_FAILURE;
}
return MOVE_STEP_SUCCESS;
}
@ -2103,7 +2115,7 @@ static enum MoveCanceller CancellerDisabled(void)
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsDisabled;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
return MOVE_STEP_BREAK;
return MOVE_STEP_FAILURE;
}
return MOVE_STEP_SUCCESS;
}
@ -2117,7 +2129,7 @@ static enum MoveCanceller CancellerVolatileBlocked(void)
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedHealBlockPrevents;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
return MOVE_STEP_BREAK;
return MOVE_STEP_FAILURE;
}
else if (gFieldStatuses & STATUS_FIELD_GRAVITY && IsGravityPreventingMove(gCurrentMove))
{
@ -2126,7 +2138,7 @@ static enum MoveCanceller CancellerVolatileBlocked(void)
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedGravityPrevents;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
return MOVE_STEP_BREAK;
return MOVE_STEP_FAILURE;
}
else if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[gBattlerAttacker].throatChopTimer > gBattleTurnCounter && IsSoundMove(gCurrentMove))
{
@ -2134,7 +2146,7 @@ static enum MoveCanceller CancellerVolatileBlocked(void)
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsThroatChopPrevented;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
return MOVE_STEP_BREAK;
return MOVE_STEP_FAILURE;
}
return MOVE_STEP_SUCCESS;
}
@ -2160,16 +2172,13 @@ static enum MoveCanceller CancellerImprisoned(void)
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsImprisoned;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
return MOVE_STEP_BREAK;
return MOVE_STEP_FAILURE;
}
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerConfused(void)
{
if (gBattleStruct->isAtkCancelerForCalledMove)
return MOVE_STEP_SUCCESS;
if (gBattleMons[gBattlerAttacker].volatiles.confusionTurns)
{
if (!gBattleMons[gBattlerAttacker].volatiles.infiniteConfusion)
@ -2211,8 +2220,7 @@ static enum MoveCanceller CancellerConfused(void)
static enum MoveCanceller CancellerParalysed(void)
{
if (!gBattleStruct->isAtkCancelerForCalledMove
&& (gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS)
if (gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS
&& !(B_MAGIC_GUARD == GEN_4 && IsAbilityAndRecord(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), ABILITY_MAGIC_GUARD))
&& !RandomPercentage(RNG_PARALYSIS, 75))
{
@ -2221,19 +2229,20 @@ static enum MoveCanceller CancellerParalysed(void)
//CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsParalyzed;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
return MOVE_STEP_BREAK;
return MOVE_STEP_FAILURE;
}
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerInfatuation(void)
{
if (!gBattleStruct->isAtkCancelerForCalledMove && gBattleMons[gBattlerAttacker].volatiles.infatuation)
if (gBattleMons[gBattlerAttacker].volatiles.infatuation)
{
gBattleScripting.battler = gBattleMons[gBattlerAttacker].volatiles.infatuation - 1;
if (!RandomPercentage(RNG_INFATUATION, 50))
{
BattleScriptCall(BattleScript_MoveUsedIsInLove);
return MOVE_STEP_BREAK;
}
else
{
@ -2242,8 +2251,8 @@ static enum MoveCanceller CancellerInfatuation(void)
gProtectStructs[gBattlerAttacker].unableToUseMove = TRUE;
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedIsInLove;
return MOVE_STEP_FAILURE;
}
return MOVE_STEP_BREAK;
}
return MOVE_STEP_SUCCESS;
}
@ -2267,17 +2276,122 @@ static enum MoveCanceller CancellerBide(void)
if (gAbsentBattlerFlags & (1u << gBattlerTarget))
gBattlerTarget = GetBattleMoveTarget(MOVE_BIDE, MOVE_TARGET_SELECTED + 1);
gBattlescriptCurrInstr = BattleScript_BideAttack;
return MOVE_STEP_BREAK; // Jumps to a different script but no failure
}
else
{
gBattlescriptCurrInstr = BattleScript_BideNoEnergyToAttack;
return MOVE_STEP_FAILURE;
}
}
}
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerZMoves(void)
{
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE)
{
// attacker has a queued z move
RecordItemEffectBattle(gBattlerAttacker, HOLD_EFFECT_Z_CRYSTAL);
SetGimmickAsActivated(gBattlerAttacker, GIMMICK_Z_MOVE);
gBattleScripting.battler = gBattlerAttacker;
if (GetMoveCategory(gCurrentMove) == DAMAGE_CATEGORY_STATUS)
BattleScriptCall(BattleScript_ZMoveActivateStatus);
else
BattleScriptCall(BattleScript_ZMoveActivateDamaging);
return MOVE_STEP_BREAK;
}
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerChoiceLock(void)
{
u16 *choicedMoveAtk = &gBattleStruct->choicedMove[gBattlerAttacker];
enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE);
if (gChosenMove != MOVE_STRUGGLE
&& (*choicedMoveAtk == MOVE_NONE || *choicedMoveAtk == MOVE_UNAVAILABLE)
&& (IsHoldEffectChoice(holdEffect) || GetBattlerAbility(gBattlerAttacker) == ABILITY_GORILLA_TACTICS))
*choicedMoveAtk = gChosenMove;
u32 moveIndex;
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
{
if (gBattleMons[gBattlerAttacker].moves[moveIndex] == *choicedMoveAtk)
break;
}
if (moveIndex == MAX_MON_MOVES)
*choicedMoveAtk = MOVE_NONE;
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerCallSubmove(void)
{
u32 noEffect = FALSE;
u32 calledMove = MOVE_NONE;
u32 effect = GetMoveEffect(gCurrentMove);
const u8 *battleScript = NULL;
battleScript = BattleScript_SubmoveAttackstring;
switch(effect)
{
case EFFECT_MIRROR_MOVE:
calledMove = GetMirrorMoveMove();
break;
case EFFECT_METRONOME:
calledMove = GetMetronomeMove();
battleScript = BattleScript_MetronomeAttackstring;
break;
case EFFECT_ASSIST:
calledMove = GetAssistMove();
break;
case EFFECT_NATURE_POWER:
calledMove = GetNaturePowerMove(gBattlerAttacker);
battleScript = BattleScript_NaturePowerAttackstring;
break;
case EFFECT_SLEEP_TALK:
calledMove = GetSleepTalkMove();
battleScript = BattleScript_SleepTalkAttackstring;
break;
case EFFECT_COPYCAT:
calledMove = GetCopyCatMove();
break;
case EFFECT_ME_FIRST:
calledMove = GetMeFirstMove();
break;
default:
noEffect = TRUE;
break;
}
if (noEffect)
{
gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT;
return MOVE_STEP_SUCCESS;
}
if (calledMove != MOVE_NONE)
{
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IsBattleMoveStatus(calledMove))
calledMove = GetTypeBasedZMove(calledMove);
if (effect == EFFECT_COPYCAT && IsMaxMove(calledMove))
calledMove = gBattleStruct->dynamax.lastUsedBaseMove;
gBattleStruct->submoveAnnouncement = SUBMOVE_SUCCESS;
gCalledMove = calledMove;
BattleScriptCall(battleScript);
return MOVE_STEP_BREAK;
}
gBattleStruct->submoveAnnouncement = SUBMOVE_FAILURE;
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerThaw(void)
{
if (gBattleMons[gBattlerAttacker].status1 & STATUS1_FREEZE)
@ -2305,30 +2419,95 @@ static enum MoveCanceller CancellerThaw(void)
static enum MoveCanceller CancellerStanceChangeTwo(void)
{
if (B_STANCE_CHANGE_FAIL >= GEN_7 && !gBattleStruct->isAtkCancelerForCalledMove && TryFormChangeBeforeMove())
if (B_STANCE_CHANGE_FAIL >= GEN_7 && gChosenMove == gCurrentMove && TryFormChangeBeforeMove())
return MOVE_STEP_BREAK;
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerChoiceLock(void)
static enum MoveCanceller CancellerAttackstring(void)
{
u16 *choicedMoveAtk = &gBattleStruct->choicedMove[gBattlerAttacker];
enum ItemHoldEffect holdEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE);
gHitMarker |= HITMARKER_ATTACKSTRING_PRINTED;
BattleScriptCall(BattleScript_Attackstring);
return MOVE_STEP_BREAK;
}
if (gChosenMove != MOVE_STRUGGLE
&& (*choicedMoveAtk == MOVE_NONE || *choicedMoveAtk == MOVE_UNAVAILABLE)
&& (IsHoldEffectChoice(holdEffect) || GetBattlerAbility(gBattlerAttacker) == ABILITY_GORILLA_TACTICS))
*choicedMoveAtk = gChosenMove;
static enum MoveCanceller CancellerPPDeduction(void)
{
if (gBattleMons[gBattlerAttacker].volatiles.multipleTurns
|| gSpecialStatuses[gBattlerAttacker].dancerUsedMove
|| gCurrentMove == MOVE_STRUGGLE)
return MOVE_STEP_SUCCESS;
u32 moveIndex;
for (moveIndex = 0; moveIndex < MAX_MON_MOVES; moveIndex++)
s32 ppToDeduct = 1;
u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
u32 movePosition = gCurrMovePos;
if (gBattleStruct->submoveAnnouncement == SUBMOVE_SUCCESS)
movePosition = gChosenMovePos;
if (moveTarget == MOVE_TARGET_BOTH
|| moveTarget == MOVE_TARGET_FOES_AND_ALLY
|| moveTarget == MOVE_TARGET_ALL_BATTLERS
|| MoveForcesPressure(gCurrentMove))
{
if (gBattleMons[gBattlerAttacker].moves[moveIndex] == *choicedMoveAtk)
break;
for (u32 i = 0; i < gBattlersCount; i++)
{
if (!IsBattlerAlly(i, gBattlerAttacker) && IsBattlerAlive(i))
ppToDeduct += (GetBattlerAbility(i) == ABILITY_PRESSURE);
}
}
else if (moveTarget != MOVE_TARGET_OPPONENTS_FIELD)
{
if (gBattlerAttacker != gBattlerTarget && GetBattlerAbility(gBattlerTarget) == ABILITY_PRESSURE)
ppToDeduct++;
}
if (moveIndex == MAX_MON_MOVES)
*choicedMoveAtk = MOVE_NONE;
gProtectStructs[gBattlerAttacker].notFirstStrike = TRUE;
// For item Metronome, echoed voice
if (gCurrentMove != gLastResultingMoves[gBattlerAttacker] || WasUnableToUseMove(gBattlerAttacker))
gBattleStruct->sameMoveTurns[gBattlerAttacker] = 0;
if (gBattleMons[gBattlerAttacker].pp[movePosition] > ppToDeduct)
gBattleMons[gBattlerAttacker].pp[movePosition] -= ppToDeduct;
else
gBattleMons[gBattlerAttacker].pp[movePosition] = 0;
if (MOVE_IS_PERMANENT(gBattlerAttacker, movePosition))
{
BtlController_EmitSetMonData(gBattlerAttacker, B_COMM_TO_CONTROLLER, REQUEST_PPMOVE1_BATTLE + movePosition, 0,
sizeof(gBattleMons[gBattlerAttacker].pp[movePosition]),
&gBattleMons[gBattlerAttacker].pp[movePosition]);
MarkBattlerForControllerExec(gBattlerAttacker);
}
if (gBattleStruct->submoveAnnouncement != SUBMOVE_NO_EFFECT)
{
if (gBattleStruct->submoveAnnouncement == SUBMOVE_FAILURE)
{
gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT;
gBattlescriptCurrInstr = BattleScript_ButItFailed;
return MOVE_STEP_FAILURE;
}
else if (CancellerVolatileBlocked() == MOVE_STEP_FAILURE) // Check Gravity/Heal Block/Throat Chop for Submove
{
gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT;
return MOVE_STEP_FAILURE;
}
else
{
gBattleStruct->submoveAnnouncement = SUBMOVE_NO_EFFECT;
gBattlerTarget = GetBattleMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
gBattleScripting.animTurn = 0;
gBattleScripting.animTargetsHit = 0;
// Possibly better to just move type setting and redirection to attackcanceller as a new case at this point
SetTypeBeforeUsingMove(gCurrentMove, gBattlerAttacker);
HandleMoveTargetRedirection();
gBattlescriptCurrInstr = GetMoveBattleScript(gCurrentMove);
return MOVE_STEP_BREAK;
}
}
return MOVE_STEP_SUCCESS;
}
@ -2342,20 +2521,20 @@ static enum MoveCanceller CancellerWeatherPrimal(void)
if (moveType == TYPE_FIRE && (gBattleWeather & B_WEATHER_RAIN_PRIMAL) && (B_POWDER_RAIN >= GEN_7 || !TryActivatePowderStatus(gCurrentMove)))
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PRIMAL_WEATHER_FIZZLED_BY_RAIN;
effect = MOVE_STEP_BREAK;
effect = MOVE_STEP_FAILURE;
}
else if (moveType == TYPE_WATER && (gBattleWeather & B_WEATHER_SUN_PRIMAL))
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_PRIMAL_WEATHER_EVAPORATED_IN_SUN;
effect = MOVE_STEP_BREAK;
effect = MOVE_STEP_FAILURE;
}
if (effect == MOVE_STEP_BREAK)
if (effect == MOVE_STEP_FAILURE)
{
gBattleScripting.moveEffect = MOVE_EFFECT_NONE;
gProtectStructs[gBattlerAttacker].chargingTurn = FALSE;
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
BattleScriptCall(BattleScript_PrimalWeatherBlocksMove);
gBattlescriptCurrInstr = BattleScript_PrimalWeatherBlocksMove;
}
}
return effect;
@ -2366,8 +2545,8 @@ static enum MoveCanceller CancellerDynamaxBlocked(void)
if ((GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX) && IsMoveBlockedByDynamax(gCurrentMove))
{
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
BattleScriptCall(BattleScript_MoveBlockedByDynamax);
return MOVE_STEP_BREAK;
gBattlescriptCurrInstr = BattleScript_MoveBlockedByDynamax;
return MOVE_STEP_FAILURE;
}
return MOVE_STEP_SUCCESS;
}
@ -2376,10 +2555,10 @@ static enum MoveCanceller CancellerPowderStatus(void)
{
if (TryActivatePowderStatus(gCurrentMove))
{
gProtectStructs[gBattlerAttacker].powderSelfDmg = TRUE;
if (!IsAbilityAndRecord(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), ABILITY_MAGIC_GUARD))
gBattleStruct->moveDamage[gBattlerAttacker] = GetNonDynamaxMaxHP(gBattlerAttacker) / 4;
// This might be incorrect
if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE
|| HasTrainerUsedGimmick(gBattlerAttacker, GIMMICK_Z_MOVE))
gBattlescriptCurrInstr = BattleScript_MoveUsedPowder;
@ -2418,7 +2597,7 @@ static enum MoveCanceller CancellerPsychicTerrain(void)
CancelMultiTurnMoves(gBattlerAttacker, SKY_DROP_ATTACKCANCELLER_CHECK);
gBattlescriptCurrInstr = BattleScript_MoveUsedPsychicTerrainPrevents;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
return MOVE_STEP_BREAK;
return MOVE_STEP_FAILURE;
}
return MOVE_STEP_SUCCESS;
}
@ -2431,7 +2610,7 @@ static enum MoveCanceller CancellerExplodingDamp(void)
gBattleScripting.battler = dampBattler - 1;
gBattlescriptCurrInstr = BattleScript_DampStopsExplosion;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
return MOVE_STEP_BREAK;
return MOVE_STEP_FAILURE;
}
return MOVE_STEP_SUCCESS;
}
@ -2503,38 +2682,6 @@ static enum MoveCanceller CancellerMultihitMoves(void)
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerZMoves(void)
{
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE)
{
// For Z-Mirror Move, so it doesn't play the animation twice.
bool32 alreadyUsed = HasTrainerUsedGimmick(gBattlerAttacker, GIMMICK_Z_MOVE);
// attacker has a queued z move
RecordItemEffectBattle(gBattlerAttacker, HOLD_EFFECT_Z_CRYSTAL);
SetGimmickAsActivated(gBattlerAttacker, GIMMICK_Z_MOVE);
gBattleScripting.battler = gBattlerAttacker;
if (gProtectStructs[gBattlerAttacker].powderSelfDmg)
{
if (!alreadyUsed)
BattleScriptCall(BattleScript_ZMoveActivatePowder);
}
else if (GetMoveCategory(gCurrentMove) == DAMAGE_CATEGORY_STATUS)
{
if (!alreadyUsed)
BattleScriptCall(BattleScript_ZMoveActivateStatus);
}
else
{
if (!alreadyUsed)
BattleScriptCall(BattleScript_ZMoveActivateDamaging);
}
return MOVE_STEP_BREAK; // The original move is cancelled, not the z move
}
return MOVE_STEP_SUCCESS;
}
static enum MoveCanceller CancellerMultiTargetMoves(void)
{
u32 moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
@ -2591,19 +2738,24 @@ static enum MoveCanceller (*const sMoveSuccessOrderCancellers[])(void) =
[CANCELLER_RECHARGE] = CancellerRecharge,
[CANCELLER_ASLEEP_OR_FROZEN] = CancellerAsleepOrFrozen,
[CANCELLER_OBEDIENCE] = CancellerObedience,
[CANCELLER_POWER_POINTS] = CancellerPowerPoints,
[CANCELLER_TRUANT] = CancellerTruant,
[CANCELLER_FLINCH] = CancellerFlinch,
[CANCELLER_INFATUATION] = CancellerInfatuation,
[CANCELLER_DISABLED] = CancellerDisabled,
[CANCELLER_VOLATILE_BLOCKED] = CancellerVolatileBlocked,
[CANCELLER_TAUNTED] = CancellerTaunted,
[CANCELLER_IMPRISONED] = CancellerImprisoned,
[CANCELLER_CONFUSED] = CancellerConfused,
[CANCELLER_PARALYSED] = CancellerParalysed,
[CANCELLER_INFATUATION] = CancellerInfatuation,
[CANCELLER_BIDE] = CancellerBide,
[CANCELLER_Z_MOVES] = CancellerZMoves,
[CANCELLER_CHOICE_LOCK] = CancellerChoiceLock,
[CANCELLER_CALLSUBMOVE] = CancellerCallSubmove,
[CANCELLER_THAW] = CancellerThaw,
[CANCELLER_STANCE_CHANGE_2] = CancellerStanceChangeTwo,
[CANCELLER_CHOICE_LOCK] = CancellerChoiceLock,
[CANCELLER_ATTACKSTRING] = CancellerAttackstring,
[CANCELLER_PPDEDUCTION] = CancellerPPDeduction,
[CANCELLER_WEATHER_PRIMAL] = CancellerWeatherPrimal,
[CANCELLER_DYNAMAX_BLOCKED] = CancellerDynamaxBlocked,
[CANCELLER_POWDER_STATUS] = CancellerPowderStatus,
@ -2611,7 +2763,6 @@ static enum MoveCanceller (*const sMoveSuccessOrderCancellers[])(void) =
[CANCELLER_PSYCHIC_TERRAIN] = CancellerPsychicTerrain,
[CANCELLER_EXPLODING_DAMP] = CancellerExplodingDamp,
[CANCELLER_MULTIHIT_MOVES] = CancellerMultihitMoves,
[CANCELLER_Z_MOVES] = CancellerZMoves,
[CANCELLER_MULTI_TARGET_MOVES] = CancellerMultiTargetMoves,
};
@ -3017,29 +3168,17 @@ bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 a
{
case ABILITY_SOUNDPROOF:
if (IsSoundMove(move) && !(GetBattlerMoveTargetType(battlerAtk, move) & MOVE_TARGET_USER))
{
if (gBattleMons[battlerAtk].volatiles.multipleTurns)
gHitMarker |= HITMARKER_NO_PPDEDUCT;
battleScriptBlocksMove = BattleScript_SoundproofProtected;
}
break;
case ABILITY_BULLETPROOF:
if (IsBallisticMove(move))
{
if (gBattleMons[battlerAtk].volatiles.multipleTurns)
gHitMarker |= HITMARKER_NO_PPDEDUCT;
battleScriptBlocksMove = BattleScript_SoundproofProtected;
}
break;
case ABILITY_DAZZLING:
case ABILITY_QUEENLY_MAJESTY:
case ABILITY_ARMOR_TAIL:
if (atkPriority > 0 && !IsBattlerAlly(battlerAtk, battlerDef))
{
if (gBattleMons[battlerAtk].volatiles.multipleTurns)
gHitMarker |= HITMARKER_NO_PPDEDUCT;
battleScriptBlocksMove = BattleScript_DazzlingProtected;
}
break;
case ABILITY_GOOD_AS_GOLD:
if (IsBattleMoveStatus(move))
@ -3081,8 +3220,6 @@ bool32 CanAbilityBlockMove(u32 battlerAtk, u32 battlerDef, u32 abilityAtk, u32 a
case ABILITY_DAZZLING:
case ABILITY_QUEENLY_MAJESTY:
case ABILITY_ARMOR_TAIL:
if (gBattleMons[battlerAtk].volatiles.multipleTurns)
gHitMarker |= HITMARKER_NO_PPDEDUCT;
battlerAbility = partnerDef;
battleScriptBlocksMove = BattleScript_DazzlingProtected;
break;
@ -3190,18 +3327,11 @@ bool32 CanAbilityAbsorbMove(u32 battlerAtk, u32 battlerDef, u32 abilityDef, u32
gBattleStruct->pledgeMove = FALSE;
if (IsBattlerAtMaxHp(battlerDef) || (B_HEAL_BLOCKING >= GEN_5 && gBattleMons[battlerDef].volatiles.healBlock))
{
if ((gProtectStructs[battlerAtk].notFirstStrike))
battleScript = BattleScript_MonMadeMoveUseless;
else
battleScript = BattleScript_MonMadeMoveUseless_PPLoss;
battleScript = BattleScript_MonMadeMoveUseless;
}
else
{
if (gProtectStructs[battlerAtk].notFirstStrike)
battleScript = BattleScript_MoveHPDrain;
else
battleScript = BattleScript_MoveHPDrain_PPLoss;
battleScript = BattleScript_MoveHPDrain;
gBattleStruct->moveDamage[battlerDef] = GetNonDynamaxMaxHP(battlerDef) / 4;
if (gBattleStruct->moveDamage[battlerDef] == 0)
gBattleStruct->moveDamage[battlerDef] = 1;
@ -3212,18 +3342,11 @@ bool32 CanAbilityAbsorbMove(u32 battlerAtk, u32 battlerDef, u32 abilityDef, u32
gBattleStruct->pledgeMove = FALSE;
if (!CompareStat(battlerDef, statId, MAX_STAT_STAGE, CMP_LESS_THAN))
{
if ((gProtectStructs[battlerAtk].notFirstStrike))
battleScript = BattleScript_MonMadeMoveUseless;
else
battleScript = BattleScript_MonMadeMoveUseless_PPLoss;
battleScript = BattleScript_MonMadeMoveUseless;
}
else
{
if (gProtectStructs[battlerAtk].notFirstStrike)
battleScript = BattleScript_MoveStatDrain;
else
battleScript = BattleScript_MoveStatDrain_PPLoss;
battleScript = BattleScript_MoveStatDrain;
SET_STATCHANGER(statId, statAmount, FALSE);
if (B_ABSORBING_ABILITY_STRING < GEN_5)
PREPARE_STAT_BUFFER(gBattleTextBuff1, statId);
@ -3234,19 +3357,13 @@ bool32 CanAbilityAbsorbMove(u32 battlerAtk, u32 battlerDef, u32 abilityDef, u32
if (!gDisableStructs[battlerDef].flashFireBoosted)
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FLASH_FIRE_BOOST;
if (gProtectStructs[battlerAtk].notFirstStrike)
battleScript = BattleScript_FlashFireBoost;
else
battleScript = BattleScript_FlashFireBoost_PPLoss;
battleScript = BattleScript_FlashFireBoost;
gDisableStructs[battlerDef].flashFireBoosted = TRUE;
}
else
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FLASH_FIRE_NO_BOOST;
if (gProtectStructs[battlerAtk].notFirstStrike)
battleScript = BattleScript_FlashFireBoost;
else
battleScript = BattleScript_FlashFireBoost_PPLoss;
battleScript = BattleScript_FlashFireBoost;
}
break;
}
@ -5048,13 +5165,9 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
// Set the target to the original target of the mon that first used a Dance move
gBattlerTarget = gBattleScripting.savedBattler & 0x3;
// Edge case for dance moves that hit multiply targets
gHitMarker &= ~HITMARKER_NO_ATTACKSTRING;
// Make sure that the target isn't an ally - if it is, target the original user
if (IsBattlerAlly(gBattlerTarget, gBattlerAttacker))
gBattlerTarget = (gBattleScripting.savedBattler & 0xF0) >> 4;
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
BattleScriptExecute(BattleScript_DancerActivates);
effect++;
}
@ -7998,7 +8111,7 @@ static inline u32 CalcMoveBasePower(struct DamageContext *ctx)
u32 weight, hpFraction, speed;
if (GetActiveGimmick(battlerAtk) == GIMMICK_Z_MOVE)
return GetZMovePower(gBattleStruct->zmove.baseMoves[battlerAtk]);
return GetZMovePower(gCurrentMove);
if (GetActiveGimmick(battlerAtk) == GIMMICK_DYNAMAX)
return GetMaxMovePower(move);
@ -11862,3 +11975,180 @@ bool32 BreaksThroughSemiInvulnerablity(u32 battler, u32 move)
return FALSE;
}
static u32 GetMirrorMoveMove(void)
{
s32 i, validMovesCount;
u16 move = MOVE_NONE;
u16 validMoves[MAX_BATTLERS_COUNT] = {0};
for (validMovesCount = 0, i = 0; i < gBattlersCount; i++)
{
if (i != gBattlerAttacker)
{
move = gBattleStruct->lastTakenMoveFrom[gBattlerAttacker][i];
if (move != MOVE_NONE && move != MOVE_UNAVAILABLE)
{
validMoves[validMovesCount] = move;
validMovesCount++;
}
}
}
move = gBattleStruct->lastTakenMove[gBattlerAttacker];
if ((move == MOVE_NONE || move == MOVE_UNAVAILABLE) && validMovesCount != 0)
move = validMoves[Random() % validMovesCount];
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IsBattleMoveStatus(move))
move = GetTypeBasedZMove(move);
return move;
}
static bool32 InvalidMetronomeMove(u32 move)
{
return GetMoveEffect(move) == EFFECT_PLACEHOLDER
|| IsMoveMetronomeBanned(move);
}
static u32 GetMetronomeMove(void)
{
u32 move = MOVE_NONE;
#if B_METRONOME_MOVES >= GEN_9
u32 moveCount = MOVES_COUNT_GEN9;
#elif B_METRONOME_MOVES >= GEN_8
u32 moveCount = MOVES_COUNT_GEN8;
#elif B_METRONOME_MOVES >= GEN_7
u32 moveCount = MOVES_COUNT_GEN7;
#elif B_METRONOME_MOVES >= GEN_6
u32 moveCount = MOVES_COUNT_GEN6;
#elif B_METRONOME_MOVES >= GEN_5
u32 moveCount = MOVES_COUNT_GEN5;
#elif B_METRONOME_MOVES >= GEN_4
u32 moveCount = MOVES_COUNT_GEN4;
#elif B_METRONOME_MOVES >= GEN_3
u32 moveCount = MOVES_COUNT_GEN3;
#elif B_METRONOME_MOVES >= GEN_2
u32 moveCount = MOVES_COUNT_GEN2;
#else
u32 moveCount = MOVES_COUNT_GEN1;
#endif
move = RandomUniformExcept(RNG_METRONOME, 1, moveCount - 1, InvalidMetronomeMove);
return move;
}
static u32 GetAssistMove(void)
{
u32 move = MOVE_NONE;
s32 chooseableMovesNo = 0;
struct Pokemon *party;
u16 *validMoves = Alloc(sizeof(u16) * PARTY_SIZE * MAX_MON_MOVES);
if (validMoves != NULL)
{
party = GetBattlerParty(gBattlerAttacker);
for (u32 monId = 0; monId < PARTY_SIZE; monId++)
{
if (monId == gBattlerPartyIndexes[gBattlerAttacker])
continue;
if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG) == SPECIES_NONE)
continue;
if (GetMonData(&party[monId], MON_DATA_SPECIES_OR_EGG) == SPECIES_EGG)
continue;
for (u32 moveId = 0; moveId < MAX_MON_MOVES; moveId++)
{
u16 move = GetMonData(&party[monId], MON_DATA_MOVE1 + moveId);
if (IsMoveAssistBanned(move))
continue;
validMoves[chooseableMovesNo++] = move;
}
}
}
if (chooseableMovesNo)
move = validMoves[Random() % chooseableMovesNo];
TRY_FREE_AND_SET_NULL(validMoves);
return move;
}
u32 GetNaturePowerMove(u32 battler)
{
u32 move = gBattleEnvironmentInfo[gBattleEnvironment].naturePower;
if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)
move = MOVE_MOONBLAST;
else if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)
move = MOVE_THUNDERBOLT;
else if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN)
move = MOVE_ENERGY_BALL;
else if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN)
move = MOVE_PSYCHIC;
else if (gBattleEnvironmentInfo[gBattleEnvironment].naturePower == MOVE_NONE)
move = MOVE_TRI_ATTACK;
return move;
}
static u32 GetSleepTalkMove(void)
{
u32 move = MOVE_NONE;
u32 i, unusableMovesBits = 0, movePosition;
if (GetBattlerAbility(gBattlerAttacker) != ABILITY_COMATOSE
&& !(gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP))
return move;
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (IsMoveSleepTalkBanned(gBattleMons[gBattlerAttacker].moves[i])
|| gBattleMoveEffects[GetMoveEffect(gBattleMons[gBattlerAttacker].moves[i])].twoTurnEffect)
unusableMovesBits |= (1 << (i));
}
unusableMovesBits = CheckMoveLimitations(gBattlerAttacker, unusableMovesBits, ~(MOVE_LIMITATION_PP | MOVE_LIMITATION_CHOICE_ITEM));
if (unusableMovesBits == ALL_MOVES_MASK) // all 4 moves cannot be chosen
return move;
// Set Sleep Talk as used move, so it works with Last Resort.
gDisableStructs[gBattlerAttacker].usedMoves |= 1u << gCurrMovePos;
do
{
movePosition = MOD(Random(), MAX_MON_MOVES);
} while ((1u << movePosition) & unusableMovesBits);
move = gBattleMons[gBattlerAttacker].moves[movePosition];
gCurrMovePos = movePosition;
return move;
}
static u32 GetCopyCatMove(void)
{
if (gLastUsedMove == MOVE_NONE
|| gLastUsedMove == MOVE_UNAVAILABLE
|| IsMoveCopycatBanned(gLastUsedMove)
|| IsZMove(gLastUsedMove))
return MOVE_NONE;
return gLastUsedMove;
}
static u32 GetMeFirstMove(void)
{
u32 move = gBattleMons[gBattlerTarget].moves[gBattleStruct->chosenMovePositions[gBattlerTarget]];
if (IsBattleMoveStatus(move)
|| IsMoveMeFirstBanned(move)
|| GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget))
return MOVE_NONE;
return move;
}

View File

@ -161,7 +161,6 @@ u32 GetUsableZMove(u32 battler, u32 move)
void ActivateZMove(u32 battler)
{
gBattleStruct->zmove.baseMoves[battler] = gBattleMons[battler].moves[gBattleStruct->chosenMovePositions[battler]];
SetActiveGimmick(battler, GIMMICK_Z_MOVE);
}
@ -438,7 +437,7 @@ static void ZMoveSelectionDisplayMoveType(u16 zMove, u32 battler)
void SetZEffect(void)
{
u32 i;
u32 effect = GetMoveZEffect(gBattleStruct->zmove.baseMoves[gBattlerAttacker]);
u32 effect = GetMoveZEffect(gChosenMove);
if (effect == Z_EFFECT_CURSE)
{

View File

@ -49,7 +49,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_MIRROR_MOVE] =
{
.battleScript = BattleScript_EffectMirrorMove,
.battleScript = BattleScript_EffectHit,
.battleTvScore = 1,
},
@ -398,7 +398,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_METRONOME] =
{
.battleScript = BattleScript_EffectMetronome,
.battleScript = BattleScript_EffectHit,
.battleTvScore = 1,
},
@ -502,7 +502,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_SLEEP_TALK] =
{
.battleScript = BattleScript_EffectSleepTalk,
.battleScript = BattleScript_EffectHit,
.battleTvScore = 3,
.encourageEncore = TRUE,
},
@ -911,7 +911,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_NATURE_POWER] =
{
.battleScript = BattleScript_EffectNaturePower,
.battleScript = BattleScript_EffectHit,
.battleTvScore = 0, // TODO: Assign points
},
@ -956,7 +956,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_ASSIST] =
{
.battleScript = BattleScript_EffectAssist,
.battleScript = BattleScript_EffectHit,
.battleTvScore = 2,
},
@ -1486,7 +1486,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_ME_FIRST] =
{
.battleScript = BattleScript_EffectMeFirst,
.battleScript = BattleScript_EffectHit,
.battleTvScore = 0, // TODO: Assign points
},
@ -1601,7 +1601,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_COPYCAT] =
{
.battleScript = BattleScript_EffectCopycat,
.battleScript = BattleScript_EffectHit,
.battleTvScore = 0, // TODO: Assign points
},

View File

@ -21,6 +21,27 @@ TO_DO_BATTLE_TEST("Copycat ignores the recharging turn of recharging moves (Gen
TO_DO_BATTLE_TEST("Copycat can copy Bide on all turns");
TO_DO_BATTLE_TEST("Copycat copies moves called by other calling moves instead of the calling move (Gen 5+)");
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_COPYCAT) == EFFECT_COPYCAT);
}
SINGLE_BATTLE_TEST("Copycat deducts power points from itself, not the copied move")
{
ASSUME(GetMovePP(MOVE_COPYCAT) == 20);
ASSUME(GetMovePP(MOVE_POUND) == 35);
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_COPYCAT); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_POUND); }
} WHEN {
TURN { MOVE(opponent, MOVE_POUND); MOVE(player, MOVE_COPYCAT); }
} SCENE {
} THEN {
EXPECT_EQ(opponent->pp[0], 34);
EXPECT_EQ(player->pp[0], 19);
}
}
DOUBLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon can have their base moves copied by Copycat")
{
GIVEN {

View File

@ -1,6 +1,11 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_ME_FIRST) == EFFECT_ME_FIRST);
}
SINGLE_BATTLE_TEST("Me First copies the move from the target and increases it's power by 1.5", s16 damage)
{
u32 move;
@ -77,4 +82,21 @@ SINGLE_BATTLE_TEST("Me First can be selected if users holds Assault Vest")
}
}
SINGLE_BATTLE_TEST("Me Frist deducts power points from itself, not the copied move")
{
ASSUME(GetMovePP(MOVE_ME_FIRST) == 20);
ASSUME(GetMovePP(MOVE_POUND) == 35);
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Speed(100); Moves(MOVE_ME_FIRST); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(50); Moves(MOVE_POUND); }
} WHEN {
TURN { MOVE(player, MOVE_ME_FIRST); MOVE(opponent, MOVE_POUND); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_ME_FIRST, player);
} THEN {
EXPECT_EQ(opponent->pp[0], 34);
EXPECT_EQ(player->pp[0], 19);
}
}
// TO_DO_BATTLE_TEST: Not everything has been tested

View File

@ -32,7 +32,7 @@ SINGLE_BATTLE_TEST("Mirror Move fails if no move was used before")
TURN { MOVE(player, MOVE_MIRROR_MOVE); MOVE(opponent, MOVE_SCRATCH); }
} SCENE {
MESSAGE("Wobbuffet used Mirror Move!");
MESSAGE("The Mirror Move failed!");
MESSAGE("But it failed!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
HP_BAR(player);
}

View File

@ -146,3 +146,21 @@ DOUBLE_BATTLE_TEST("Sleep Talk calls move and that move may be redirected by Sto
ABILITY_POPUP(opponentRight, ABILITY_STORM_DRAIN);
}
}
SINGLE_BATTLE_TEST("Sleep Talk deducts power points from itself, not the called move")
{
ASSUME(GetMovePP(MOVE_SLEEP_TALK) == 10);
ASSUME(GetMovePP(MOVE_POUND) == 35);
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_SLEEP); Moves(MOVE_SLEEP_TALK, MOVE_POUND); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SLEEP_TALK); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SLEEP_TALK, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_POUND, player);
} THEN {
EXPECT_EQ(player->pp[0], 9);
EXPECT_EQ(player->pp[1], 35);
}
}