Grudge, Destiny Bond and FaintBattler refactor (#8072)

Co-authored-by: Bassoonian <iasperbassoonian@gmail.com>
This commit is contained in:
Alex 2025-10-30 23:14:02 +01:00 committed by GitHub
parent ff557e3d0c
commit 1c1a98ee45
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 183 additions and 160 deletions

View File

@ -936,7 +936,7 @@
.4byte \failInstr
.endm
.macro trysetdestinybondtohappen
.macro unused_0xab
.byte 0xab
.endm
@ -1617,14 +1617,6 @@
.4byte \failInstr
.endm
.macro tryupdaterecoiltracker
callnative BS_TryUpdateRecoilTracker
.endm
.macro tryupdateleaderscresttracker
callnative BS_TryUpdateLeadersCrestTracker
.endm
.macro trytidyup clear:req, jumpInstr:req
callnative BS_TryTidyUp
.byte \clear

View File

@ -2306,6 +2306,7 @@ BattleScript_EffectMiracleEye::
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
setvolatile BS_TARGET, VOLATILE_MIRACLE_EYE
goto BattleScript_IdentifiedFoe
BattleScript_EffectGravity::
call BattleScript_EffectGravityInternal
goto BattleScript_MoveEnd
@ -2838,7 +2839,6 @@ BattleScript_EffectOHKO::
typecalc
jumpifmovehadnoeffect BattleScript_HitFromAtkAnimation
tryKO BattleScript_KOFail
trysetdestinybondtohappen
goto BattleScript_HitFromAtkAnimation
BattleScript_KOFail::
pause B_WAIT_TIME_LONG
@ -2853,6 +2853,7 @@ BattleScript_RecoilIfMiss::
healthbarupdate BS_ATTACKER, PASSIVE_HP_UPDATE
datahpupdate BS_ATTACKER, PASSIVE_HP_UPDATE
tryfaintmon BS_ATTACKER
BattleScript_RecoilEnd:
return
BattleScript_EffectMist::
@ -4393,40 +4394,23 @@ BattleScript_EffectCamouflage::
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_FaintAttacker::
tryillusionoff BS_ATTACKER
BattleScript_FaintBattler::
tryillusionoff BS_SCRIPTING
tryactivategulpmissile
playfaintcry BS_ATTACKER
playfaintcry BS_SCRIPTING
pause B_WAIT_TIME_LONG
dofaintanimation BS_ATTACKER
printstring STRINGID_ATTACKERFAINTED
cleareffectsonfaint BS_ATTACKER
dofaintanimation BS_SCRIPTING
printstring STRINGID_BATTLERFAINTED
cleareffectsonfaint BS_SCRIPTING
trytoclearprimalweather
tryrevertweatherform
flushtextbox
waitanimation
tryactivatesoulheart
tryactivatereceiver BS_ATTACKER
trytrainerslidemsgfirstoff BS_ATTACKER
tryactivatereceiver BS_SCRIPTING
trytrainerslidemsgfirstoff BS_SCRIPTING
return
BattleScript_FaintTarget::
tryillusionoff BS_TARGET
tryactivategulpmissile
tryupdateleaderscresttracker
playfaintcry BS_TARGET
pause B_WAIT_TIME_LONG
dofaintanimation BS_TARGET
printstring STRINGID_TARGETFAINTED
cleareffectsonfaint BS_TARGET
trytoclearprimalweather
tryrevertweatherform
flushtextbox
waitanimation
tryactivatesoulheart
tryactivatereceiver BS_TARGET
trytrainerslidemsgfirstoff BS_TARGET
return
BattleScript_GiveExp::
setbyte sGIVEEXP_STATE, 0
@ -6391,17 +6375,11 @@ BattleScript_MoveEffectConfusion::
return
BattleScript_MoveEffectRecoil::
jumpifmove MOVE_STRUGGLE, BattleScript_DoRecoil
jumpifability BS_ATTACKER, ABILITY_ROCK_HEAD, BattleScript_RecoilEnd
jumpifability BS_ATTACKER, ABILITY_MAGIC_GUARD, BattleScript_RecoilEnd
BattleScript_DoRecoil::
healthbarupdate BS_ATTACKER, PASSIVE_HP_UPDATE
datahpupdate BS_ATTACKER, PASSIVE_HP_UPDATE
printstring STRINGID_PKMNHITWITHRECOIL
waitmessage B_WAIT_TIME_LONG
tryupdaterecoiltracker
tryfaintmon BS_ATTACKER
BattleScript_RecoilEnd::
return
BattleScript_ItemSteal::

View File

@ -777,11 +777,12 @@ struct BattleStruct
u8 numHazards[NUM_BATTLE_SIDES];
u8 hazardsCounter:4; // Counter for applying hazard on switch in
enum SubmoveState submoveAnnouncement:2;
u8 padding3:2;
u8 tryDestinyBond:1;
u8 tryGrudge:1;
u16 flingItem;
u8 incrementEchoedVoice:1;
u8 echoedVoiceCounter:3;
u8 padding4:4;
u8 padding3:4;
};
struct AiBattleData

View File

@ -22,8 +22,7 @@ extern const u8 BattleScript_StatDown[];
extern const u8 BattleScript_AlreadyAtFullHp[];
extern const u8 BattleScript_PresentHealTarget[];
extern const u8 BattleScript_MoveUsedMustRecharge[];
extern const u8 BattleScript_FaintAttacker[];
extern const u8 BattleScript_FaintTarget[];
extern const u8 BattleScript_FaintBattler[];
extern const u8 BattleScript_GiveExp[];
extern const u8 BattleScript_HandleFaintedMon[];
extern const u8 BattleScript_LocalTrainerBattleWon[];

View File

@ -264,7 +264,7 @@ enum SemiInvulnerableExclusion
#define HITMARKER_UNUSED_21 (1 << 21)
#define HITMARKER_PLAYER_FAINTED (1 << 22)
#define HITMARKER_UNUSED_23 (1 << 23)
#define HITMARKER_GRUDGE (1 << 24)
#define HITMARKER_UNUSED_24 (1 << 24)
#define HITMARKER_OBEYS (1 << 25)
#define HITMARKER_UNUSED_26 (1 << 26)
#define HITMARKER_UNUSED_27 (1 << 27)

View File

@ -130,6 +130,8 @@ enum MoveEndEffects
{
MOVEEND_SET_VALUES,
MOVEEND_PROTECT_LIKE_EFFECT,
MOVEEND_GRUDGE,
MOVEEND_DESTINY_BOND,
MOVEEND_ABSORB,
MOVEEND_RAGE,
MOVEEND_SYNCHRONIZE_TARGET,

View File

@ -28,8 +28,7 @@ enum StringID
STRINGID_STATSWONTINCREASE2,
STRINGID_AVOIDEDDAMAGE,
STRINGID_ITDOESNTAFFECT,
STRINGID_ATTACKERFAINTED,
STRINGID_TARGETFAINTED,
STRINGID_BATTLERFAINTED,
STRINGID_PLAYERGOTMONEY,
STRINGID_PLAYERWHITEOUT,
STRINGID_PLAYERWHITEOUT2_WILD,

View File

@ -2250,7 +2250,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_DESTINY_BOND:
if (DoesDestinyBondFail(battlerAtk))
ADJUST_SCORE(-10);
if (gBattleMons[battlerDef].volatiles.destinyBond)
if (gBattleMons[battlerAtk].volatiles.destinyBond)
ADJUST_SCORE(-10);
else if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
ADJUST_SCORE(-10);

View File

@ -1540,7 +1540,6 @@ static bool32 (*const sEndTurnEffectHandlers[])(u32 battler) =
u32 DoEndTurnEffects(void)
{
u32 battler = MAX_BATTLERS_COUNT;
gHitMarker |= HITMARKER_GRUDGE;
for (;;)
{
@ -1555,10 +1554,7 @@ u32 DoEndTurnEffects(void)
// Jump out if possible after endTurnEventsCounter was increased in the above code block
if (gBattleStruct->endTurnEventsCounter == ENDTURN_COUNT)
{
gHitMarker &= ~HITMARKER_GRUDGE;
return FALSE;
}
battler = gBattlerAttacker = gBattlerByTurnOrder[gBattleStruct->turnEffectsBattlerId];

View File

@ -549,7 +549,7 @@ static void CB2_InitBattleInternal(void)
gReservedSpritePaletteCount = MAX_BATTLERS_COUNT;
SetVBlankCallback(VBlankCB_Battle);
SetUpBattleVarsAndBirchZigzagoon();
if ((IsMultibattleTest() && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
|| (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER)
|| (gBattleTypeFlags & BATTLE_TYPE_MULTI && gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER))
@ -5164,6 +5164,8 @@ static void TurnValuesCleanUp(bool8 var0)
gSideTimers[B_SIDE_OPPONENT].followmeTimer = 0;
gBattleStruct->pledgeMove = FALSE; // combined pledge move may not have been used due to a canceller
gBattleStruct->tryDestinyBond = FALSE;
gBattleStruct->tryGrudge = FALSE;
ClearPursuitValues();
ClearDamageCalcResults();
}

View File

@ -188,8 +188,7 @@ const u8 *const gBattleStringsTable[STRINGID_COUNT] =
[STRINGID_STATSWONTINCREASE2] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX}'s stats won't go any higher!"),
[STRINGID_AVOIDEDDAMAGE] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} avoided damage with {B_DEF_ABILITY}!"), //not in gen 5+, ability popup
[STRINGID_ITDOESNTAFFECT] = COMPOUND_STRING("It doesn't affect {B_DEF_NAME_WITH_PREFIX2}…"),
[STRINGID_ATTACKERFAINTED] = COMPOUND_STRING("{B_ATK_NAME_WITH_PREFIX} fainted!\p"),
[STRINGID_TARGETFAINTED] = COMPOUND_STRING("{B_DEF_NAME_WITH_PREFIX} fainted!\p"),
[STRINGID_BATTLERFAINTED] = COMPOUND_STRING("{B_SCR_NAME_WITH_PREFIX} fainted!\p"),
[STRINGID_PLAYERGOTMONEY] = COMPOUND_STRING("You got ¥{B_BUFF1} for winning!\p"),
[STRINGID_PLAYERWHITEOUT] = COMPOUND_STRING("You have no more Pokémon that can fight!\p"),
[STRINGID_PLAYERWHITEOUT2_WILD] = COMPOUND_STRING("You panicked and dropped ¥{B_BUFF1}…"),

View File

@ -310,7 +310,6 @@ enum GiveCaughtMonStates
#define TAG_LVLUP_BANNER_MON_ICON 55130
static void TrySetDestinyBondToHappen(void);
static u32 ChangeStatBuffs(u32 battler, s8 statValue, enum Stat statId, union StatChangeFlags flags, u32 stats, const u8 *BS_ptr);
static bool32 IsMonGettingExpSentOut(void);
static void InitLevelUpBanner(void);
@ -509,7 +508,7 @@ static void Cmd_setalwayshitflag(void);
static void Cmd_copymovepermanently(void);
static void Cmd_unused_0xA9(void);
static void Cmd_unused_AA(void);
static void Cmd_trysetdestinybondtohappen(void);
static void Cmd_unused_0xab(void);
static void Cmd_settailwind(void);
static void Cmd_tryspiteppreduce(void);
static void Cmd_healpartystatus(void);
@ -768,7 +767,7 @@ void (*const gBattleScriptingCommandsTable[])(void) =
Cmd_copymovepermanently, //0xA8
Cmd_unused_0xA9, //0xA9
Cmd_unused_AA, //0xAA
Cmd_trysetdestinybondtohappen, //0xAB
Cmd_unused_0xab, //0xAB
Cmd_settailwind, //0xAC
Cmd_tryspiteppreduce, //0xAD
Cmd_healpartystatus, //0xAE
@ -1317,8 +1316,6 @@ static void JumpIfMoveFailed(u32 adder, u32 move, u32 moveType, const u8 *failIn
}
else
{
TrySetDestinyBondToHappen();
if (CanAbilityAbsorbMove(gBattlerAttacker,
gBattlerTarget,
GetBattlerAbility(gBattlerTarget),
@ -3323,6 +3320,7 @@ void SetMoveEffect(u32 battler, u32 effectBattler, bool32 primary, bool32 certai
if (GetBattlerAbility(gEffectBattler) == ABILITY_PARENTAL_BOND)
recoil *= 2;
SetPassiveDamageAmount(gEffectBattler, recoil);
TryUpdateEvolutionTracker(IF_RECOIL_DAMAGE_GE, gBattleStruct->passiveHpUpdate[gBattlerAttacker], MOVE_NONE);
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_MoveEffectRecoil;
break;
@ -4175,7 +4173,7 @@ static void Cmd_clearvolatile(void)
static void Cmd_tryfaintmon(void)
{
CMD_ARGS(u8 battler, bool8 isSpikes, const u8 *instr);
u32 battler, destinyBondBattler;
u32 battler;
const u8 *faintScript;
battler = GetBattlerForBattleScript(cmd->battler);
@ -4202,16 +4200,12 @@ static void Cmd_tryfaintmon(void)
gBattlescriptCurrInstr = BattleScript_NeutralizingGasExits;
return;
}
if (cmd->battler == BS_ATTACKER)
{
destinyBondBattler = gBattlerTarget;
faintScript = BattleScript_FaintAttacker;
}
else
{
destinyBondBattler = gBattlerAttacker;
faintScript = BattleScript_FaintTarget;
}
TryUpdateEvolutionTracker(IF_DEFEAT_X_WITH_ITEMS, 1, MOVE_NONE);
gBattleScripting.battler = battler;
faintScript = BattleScript_FaintBattler;
if (!(gAbsentBattlerFlags & (1u << battler))
&& !IsBattlerAlive(battler))
{
@ -4233,30 +4227,11 @@ static void Cmd_tryfaintmon(void)
gBattleResults.lastOpponentSpecies = GetMonData(GetBattlerMon(battler), MON_DATA_SPECIES, NULL);
gSideTimers[B_SIDE_OPPONENT].retaliateTimer = 2;
}
if ((gHitMarker & HITMARKER_DESTINYBOND) && IsBattlerAlive(gBattlerAttacker)
&& !(GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX))
{
gHitMarker &= ~HITMARKER_DESTINYBOND;
BattleScriptPush(gBattlescriptCurrInstr);
gBattleStruct->passiveHpUpdate[destinyBondBattler] = gBattleMons[destinyBondBattler].hp;
gBattlescriptCurrInstr = BattleScript_DestinyBondTakesLife;
}
if (gBattleMons[gBattlerTarget].volatiles.grudge
&& !(gHitMarker & HITMARKER_GRUDGE)
&& !IsBattlerAlly(gBattlerAttacker, gBattlerTarget)
&& IsBattlerAlive(gBattlerAttacker)
&& gCurrentMove != MOVE_STRUGGLE)
{
u8 moveIndex = gBattleStruct->chosenMovePositions[gBattlerAttacker];
gBattleMons[gBattlerAttacker].pp[moveIndex] = 0;
BattleScriptPush(gBattlescriptCurrInstr);
gBattlescriptCurrInstr = BattleScript_GrudgeTakesPp;
BtlController_EmitSetMonData(gBattlerAttacker, B_COMM_TO_CONTROLLER, moveIndex + REQUEST_PPMOVE1_BATTLE, 0, sizeof(gBattleMons[gBattlerAttacker].pp[moveIndex]), &gBattleMons[gBattlerAttacker].pp[moveIndex]);
MarkBattlerForControllerExec(gBattlerAttacker);
PREPARE_MOVE_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerAttacker].moves[moveIndex])
}
if (gBattleMons[gBattlerTarget].volatiles.destinyBond)
gBattleStruct->tryDestinyBond = TRUE;
if (gBattleMons[gBattlerTarget].volatiles.grudge)
gBattleStruct->tryGrudge = TRUE;
TryDeactivateSleepClause(GetBattlerSide(battler), gBattlerPartyIndexes[battler]);
}
@ -4957,8 +4932,6 @@ static void MoveValuesCleanUp(void)
gBattleScripting.moveEffect = MOVE_EFFECT_NONE;
gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_NONE;
gBattleCommunication[MISS_TYPE] = 0;
if (!gMultiHitCounter)
gHitMarker &= ~HITMARKER_DESTINYBOND;
}
static void Cmd_movevaluescleanup(void)
@ -5819,7 +5792,13 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect)
case EFFECT_RECOIL:
if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker))
{
enum Ability ability = GetBattlerAbility(gBattlerAttacker);
if (IsAbilityAndRecord(gBattlerAttacker, ability, ABILITY_ROCK_HEAD)
|| IsAbilityAndRecord(gBattlerAttacker, ability, ABILITY_MAGIC_GUARD))
break;
SetPassiveDamageAmount(gBattlerAttacker, gBattleScripting.savedDmg * max(1, GetMoveRecoil(gCurrentMove)) / 100);
TryUpdateEvolutionTracker(IF_RECOIL_DAMAGE_GE, gBattleStruct->passiveHpUpdate[gBattlerAttacker], MOVE_NONE);
BattleScriptCall(BattleScript_MoveEffectRecoil);
effect = TRUE;
}
@ -5836,10 +5815,11 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect)
case EFFECT_MAX_HP_50_RECOIL:
if (IsBattlerAlive(gBattlerAttacker)
&& !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_FAILED)
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD)
&& !IsAbilityAndRecord(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker), ABILITY_MAGIC_GUARD))
{
s32 recoil = (GetNonDynamaxMaxHP(gBattlerAttacker) + 1) / 2; // Half of Max HP Rounded UP
SetPassiveDamageAmount(gBattlerAttacker, recoil);
TryUpdateEvolutionTracker(IF_RECOIL_DAMAGE_GE, gBattleStruct->passiveHpUpdate[gBattlerAttacker], MOVE_NONE);
BattleScriptCall(BattleScript_MaxHp50Recoil);
effect = TRUE;
}
@ -5847,8 +5827,14 @@ static bool32 HandleMoveEndMoveBlock(u32 moveEffect)
case EFFECT_CHLOROBLAST:
if (IsBattlerTurnDamaged(gBattlerTarget) && IsBattlerAlive(gBattlerAttacker))
{
enum Ability ability = GetBattlerAbility(gBattlerAttacker);
if (IsAbilityAndRecord(gBattlerAttacker, ability, ABILITY_ROCK_HEAD)
|| IsAbilityAndRecord(gBattlerAttacker, ability, ABILITY_MAGIC_GUARD))
break;
s32 recoil = (GetNonDynamaxMaxHP(gBattlerAttacker) + 1) / 2; // Half of Max HP Rounded UP
SetPassiveDamageAmount(gBattlerAttacker, recoil);
TryUpdateEvolutionTracker(IF_RECOIL_DAMAGE_GE, gBattleStruct->passiveHpUpdate[gBattlerAttacker], MOVE_NONE);
BattleScriptCall(BattleScript_MoveEffectRecoil);
effect = TRUE;
}
@ -6044,6 +6030,43 @@ static void Cmd_moveend(void)
}
gBattleScripting.moveendState++;
break;
case MOVEEND_GRUDGE:
if (gBattleStruct->tryGrudge
&& IsBattlerTurnDamaged(gBattlerTarget)
&& !IsBattlerAlive(gBattlerTarget)
&& IsBattlerAlive(gBattlerAttacker)
&& !IsBattlerAlly(gBattlerAttacker, gBattlerTarget)
&& !IsZMove(gCurrentMove)
&& gCurrentMove != MOVE_STRUGGLE)
{
u32 moveIndex = gBattleStruct->chosenMovePositions[gBattlerAttacker];
gBattleStruct->tryGrudge = FALSE;
gBattleMons[gBattlerAttacker].pp[moveIndex] = 0;
BattleScriptCall(BattleScript_GrudgeTakesPp);
BtlController_EmitSetMonData(gBattlerAttacker, B_COMM_TO_CONTROLLER, moveIndex + REQUEST_PPMOVE1_BATTLE, 0, sizeof(gBattleMons[gBattlerAttacker].pp[moveIndex]), &gBattleMons[gBattlerAttacker].pp[moveIndex]);
MarkBattlerForControllerExec(gBattlerAttacker);
PREPARE_MOVE_BUFFER(gBattleTextBuff1, gBattleMons[gBattlerAttacker].moves[moveIndex])
}
gBattleScripting.moveendState++;
break;
case MOVEEND_DESTINY_BOND:
if (gBattleStruct->tryDestinyBond
&& IsBattlerTurnDamaged(gBattlerTarget)
&& !IsBattlerAlive(gBattlerTarget)
&& IsBattlerAlive(gBattlerAttacker)
&& !(GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX)
&& !IsBattlerAlly(gBattlerAttacker, gBattlerTarget))
{
gBattleStruct->tryDestinyBond = FALSE;
gBattleStruct->passiveHpUpdate[gBattlerAttacker] = gBattleMons[gBattlerAttacker].hp;
BattleScriptCall(BattleScript_DestinyBondTakesLife);
effect = TRUE;
}
gBattleScripting.moveendState++;
break;
case MOVEEND_ABSORB:
if (gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE
|| !IsBattlerTurnDamaged(gBattlerTarget))
@ -7705,8 +7728,6 @@ static void UpdateSentMonFlags(u32 battler)
static void SetDmgHazardsBattlescript(u8 battler, u8 multistringId)
{
gBattleMons[battler].volatiles.destinyBond = 0;
gHitMarker &= ~HITMARKER_DESTINYBOND;
gBattleScripting.battler = battler;
gBattleCommunication[MULTISTRING_CHOOSER] = multistringId;
@ -11596,22 +11617,8 @@ static void Cmd_unused_AA(void)
{
}
static void TrySetDestinyBondToHappen(void)
static void Cmd_unused_0xab(void)
{
if (gBattleMons[gBattlerTarget].volatiles.destinyBond
&& !IsBattlerAlly(gBattlerAttacker, gBattlerTarget)
&& !(gHitMarker & HITMARKER_GRUDGE))
{
gHitMarker |= HITMARKER_DESTINYBOND;
}
}
static void Cmd_trysetdestinybondtohappen(void)
{
CMD_ARGS();
TrySetDestinyBondToHappen();
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_settailwind(void)
@ -15475,20 +15482,6 @@ static void TryUpdateEvolutionTracker(u32 evolutionCondition, u32 upAmount, u16
}
}
void BS_TryUpdateRecoilTracker(void)
{
NATIVE_ARGS();
TryUpdateEvolutionTracker(IF_RECOIL_DAMAGE_GE, gBattleStruct->moveDamage[gBattlerAttacker], MOVE_NONE);
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_TryUpdateLeadersCrestTracker(void)
{
NATIVE_ARGS();
TryUpdateEvolutionTracker(IF_DEFEAT_X_WITH_ITEMS, 1, MOVE_NONE);
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_TryTidyUp(void)
{
NATIVE_ARGS(u8 clear, const u8 *jumpInstr);

View File

@ -14,7 +14,7 @@
static bool8 IsNotSpecialBattleString(enum StringID stringId);
static void AddMovePoints(u8 caseId, u16 arg1, u8 arg2, u8 arg3);
static void TrySetBattleSeminarShow(void);
static void AddPointsOnFainting(bool8 targetFainted);
static void AddPointsOnFainting(void);
static void AddPointsBasedOnWeather(u16 weatherFlags, u16 move, u8 moveSlot);
static bool8 ShouldCalculateDamage(u16 move, s32 *dmg, u16 *powerOverride);
@ -124,12 +124,23 @@ static const u16 *const sPointsArray[] =
// even if current Pokémon does not have corresponding move
static const u16 sSpecialBattleStrings[] =
{
STRINGID_PKMNPERISHCOUNTFELL, STRINGID_PKMNWISHCAMETRUE, STRINGID_PKMNLOSTPPGRUDGE,
STRINGID_PKMNTOOKFOE, STRINGID_PKMNABSORBEDNUTRIENTS, STRINGID_PKMNANCHOREDITSELF,
STRINGID_PKMNAFFLICTEDBYCURSE, STRINGID_PKMNSAPPEDBYLEECHSEED, STRINGID_PKMNLOCKEDINNIGHTMARE,
STRINGID_PKMNHURTBY, STRINGID_PKMNHURTBYBURN, STRINGID_PKMNHURTBYPOISON,
STRINGID_PKMNHURTBYSPIKES, STRINGID_ATTACKERFAINTED, STRINGID_TARGETFAINTED,
STRINGID_PKMNHITWITHRECOIL, STRINGID_PKMNCRASHED, TABLE_END
STRINGID_PKMNPERISHCOUNTFELL,
STRINGID_PKMNWISHCAMETRUE,
STRINGID_PKMNLOSTPPGRUDGE,
STRINGID_PKMNTOOKFOE,
STRINGID_PKMNABSORBEDNUTRIENTS,
STRINGID_PKMNANCHOREDITSELF,
STRINGID_PKMNAFFLICTEDBYCURSE,
STRINGID_PKMNSAPPEDBYLEECHSEED,
STRINGID_PKMNLOCKEDINNIGHTMARE,
STRINGID_PKMNHURTBY,
STRINGID_PKMNHURTBYBURN,
STRINGID_PKMNHURTBYPOISON,
STRINGID_PKMNHURTBYSPIKES,
STRINGID_BATTLERFAINTED,
STRINGID_PKMNHITWITHRECOIL,
STRINGID_PKMNCRASHED,
TABLE_END
};
// code
@ -139,7 +150,7 @@ void BattleTv_SetDataBasedOnString(enum StringID stringId)
u32 atkSide, defSide, effSide, scriptingSide;
struct Pokemon *atkMon, *defMon;
u8 moveSlot;
u32 atkFlank, defFlank, effFlank;
u32 atkFlank, defFlank, effFlank, flank;
u8 *perishCount;
u16 *statStringId, *finishedMoveId;
@ -452,8 +463,6 @@ void BattleTv_SetDataBasedOnString(enum StringID stringId)
tvPtr->pos[atkSide][atkFlank].mudSportMonId = gBattlerPartyIndexes[gBattlerAttacker] + 1;
tvPtr->pos[atkSide][atkFlank].mudSportMoveSlot = moveSlot;
break;
case STRINGID_ATTACKERFAINTED:
AddPointsOnFainting(FALSE);
case STRINGID_RETURNMON:
if (tvPtr->pos[atkSide][atkFlank].waterSportMonId != 0)
{
@ -466,17 +475,22 @@ void BattleTv_SetDataBasedOnString(enum StringID stringId)
tvPtr->pos[atkSide][atkFlank].mudSportMoveSlot = 0;
}
break;
case STRINGID_TARGETFAINTED:
AddPointsOnFainting(TRUE);
if (tvPtr->pos[atkSide][defFlank].waterSportMonId != 0)
case STRINGID_BATTLERFAINTED:
AddPointsOnFainting();
if (gBattlerAttacker == gBattleScripting.battler)
flank = atkFlank;
else
flank = defFlank;
if (tvPtr->pos[atkSide][flank].waterSportMonId != 0)
{
tvPtr->pos[atkSide][defFlank].waterSportMonId = 0;
tvPtr->pos[atkSide][defFlank].waterSportMoveSlot = 0;
tvPtr->pos[atkSide][flank].waterSportMonId = 0;
tvPtr->pos[atkSide][flank].waterSportMoveSlot = 0;
}
if (tvPtr->pos[atkSide][defFlank].mudSportMonId != 0)
if (tvPtr->pos[atkSide][flank].mudSportMonId != 0)
{
tvPtr->pos[atkSide][defFlank].mudSportMonId = 0;
tvPtr->pos[atkSide][defFlank].mudSportMoveSlot = 0;
tvPtr->pos[atkSide][flank].mudSportMonId = 0;
tvPtr->pos[atkSide][flank].mudSportMoveSlot = 0;
}
break;
case STRINGID_PKMNRAISEDDEF:
@ -1064,7 +1078,7 @@ static void AddMovePoints(u8 caseId, u16 arg1, u8 arg2, u8 arg3)
}
}
static void AddPointsOnFainting(bool8 targetFainted)
static void AddPointsOnFainting(void)
{
struct BattleTv *tvPtr = &gBattleStruct->tv;
u32 atkSide = GetBattlerSide(gBattlerAttacker);
@ -1187,7 +1201,7 @@ static void AddPointsOnFainting(bool8 targetFainted)
}
break;
case FNT_RECOIL:
if (targetFainted == TRUE)
if (gBattlerAttacker == gBattleScripting.battler)
{
AddMovePoints(PTS_FAINT_SET_UP, 0, atkSide,
(gBattlerPartyIndexes[gBattlerAttacker]) * 4 + tvPtr->side[atkSide].usedMoveSlot);

View File

@ -912,8 +912,7 @@ void HandleAction_NothingIsFainted(void)
gCurrentTurnActionNumber++;
gCurrentActionFuncId = gActionsByTurnOrder[gCurrentTurnActionNumber];
gBattleStruct->synchronizeMoveEffect = MOVE_EFFECT_NONE;
gHitMarker &= ~(HITMARKER_DESTINYBOND
| HITMARKER_ATTACKSTRING_PRINTED
gHitMarker &= ~(HITMARKER_ATTACKSTRING_PRINTED
| HITMARKER_STATUS_ABILITY_EFFECT
| HITMARKER_OBEYS);
}
@ -926,8 +925,7 @@ void HandleAction_ActionFinished(void)
gCurrentTurnActionNumber++;
gCurrentActionFuncId = gActionsByTurnOrder[gCurrentTurnActionNumber];
memset(&gSpecialStatuses, 0, sizeof(gSpecialStatuses));
gHitMarker &= ~(HITMARKER_DESTINYBOND
| HITMARKER_ATTACKSTRING_PRINTED
gHitMarker &= ~(HITMARKER_ATTACKSTRING_PRINTED
| HITMARKER_STATUS_ABILITY_EFFECT
| HITMARKER_OBEYS);
@ -1786,8 +1784,6 @@ void TryToRevertMimicryAndFlags(void)
bool32 BattleArenaTurnEnd(void)
{
gHitMarker |= HITMARKER_GRUDGE;
if ((gBattleTypeFlags & BATTLE_TYPE_ARENA)
&& gBattleStruct->arenaTurnCounter == 2
&& IsBattlerAlive(B_POSITION_PLAYER_LEFT) && IsBattlerAlive(B_POSITION_OPPONENT_LEFT))
@ -1799,9 +1795,6 @@ bool32 BattleArenaTurnEnd(void)
BattleScriptExecute(BattleScript_ArenaDoJudgment);
return TRUE;
}
gHitMarker &= ~HITMARKER_GRUDGE;
return FALSE;
}

View File

@ -239,8 +239,8 @@ SINGLE_BATTLE_TEST("Dynamax: Dynamaxed Pokemon are affected by Grudge")
} SCENE {
MESSAGE("The opposing Wobbuffet used Grudge!");
MESSAGE("Wobbuffet used Max Strike!");
MESSAGE("Wobbuffet's Scratch lost all its PP due to the grudge!");
MESSAGE("The opposing Wobbuffet fainted!");
MESSAGE("Wobbuffet's Scratch lost all its PP due to the grudge!");
}
}
@ -860,7 +860,7 @@ SINGLE_BATTLE_TEST("Dynamax: Max Hailstorm sets up hail")
MESSAGE("It started to hail!");
MESSAGE("The opposing Wobbuffet used Celebrate!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HAIL_CONTINUES);
#endif
#endif
}
}

View File

@ -10,12 +10,18 @@ SINGLE_BATTLE_TEST("Destiny Bond faints the opposing mon if it fainted from the
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_DESTINY_BOND); MOVE(opponent, MOVE_SCRATCH); }
TURN {
MOVE(player, MOVE_DESTINY_BOND);
MOVE(opponent, MOVE_SCRATCH);
SEND_OUT(player, 1);
}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DESTINY_BOND, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
MESSAGE("Wobbuffet fainted!");
MESSAGE("Wobbuffet took its attacker down with it!");
MESSAGE("The opposing Wobbuffet fainted!");
}

View File

@ -2,3 +2,52 @@
#include "test/battle.h"
TO_DO_BATTLE_TEST("TODO: Write Grudge (Move Effect) test titles")
SINGLE_BATTLE_TEST("Grudge depletes all pp of the move that fainted the target")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_SCRATCH, MOVE_POUND, MOVE_SURF); };
} WHEN {
TURN {
MOVE(player, MOVE_GRUDGE);
MOVE(opponent, MOVE_SCRATCH);
SEND_OUT(player, 1);
}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_GRUDGE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
MESSAGE("Wobbuffet fainted!");
} THEN {
EXPECT_GT(opponent->pp[0], 0);
EXPECT_EQ(opponent->pp[1], 0);
EXPECT_GT(opponent->pp[2], 0);
EXPECT_GT(opponent->pp[3], 0);
}
}
SINGLE_BATTLE_TEST("Grudge does not depletes pp of a z-move")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); Moves(MOVE_CELEBRATE, MOVE_SCRATCH, MOVE_POUND, MOVE_SURF); };
} WHEN {
TURN {
MOVE(player, MOVE_GRUDGE);
MOVE(opponent, MOVE_SCRATCH, gimmick: GIMMICK_Z_MOVE);
SEND_OUT(player, 1);
}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_GRUDGE, player);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ZMOVE_ACTIVATE, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BREAKNECK_BLITZ, opponent);
MESSAGE("Wobbuffet fainted!");
} THEN {
EXPECT_GT(opponent->pp[0], 0);
EXPECT_GT(opponent->pp[1], 0);
EXPECT_GT(opponent->pp[2], 0);
EXPECT_GT(opponent->pp[3], 0);
}
}