Added new trainer slide-in msg conditions

Now they can say something:
-When their last Pokémon's HP is between 25% and 50%.
-When a critical hit is dealt to their first Pokémon for the first time.
-When a super effective hit is dealt to their first Pokémon for the first time.
-When a STAB move is used against (but doesn't faint) the opponent's Pokémon.
-When the Player's Pokémon's move doesn't affect the opponent's Pokémon.
-When they're about to trigger a Mega Evolution.
-When they're about to trigger a Z-Move.

Misc. changes:
-Split GetEnemyMonCount for readability's sake.
-Optimized the size allocated to trainerSlideLowHpMsgDone.
This commit is contained in:
LOuroboros 2023-02-17 12:53:24 -03:00
parent 6e134bb7dc
commit 3a91a5c930
9 changed files with 356 additions and 178 deletions

View file

@ -2049,6 +2049,14 @@
.4byte \jumpInstr .4byte \jumpInstr
.endm .endm
.macro trytrainerslidezmovemsg battler:req
various \battler, VARIOUS_TRY_TRAINER_SLIDE_MSG_Z_MOVE
.endm
.macro trytrainerslidemegaevolutionmsg battler:req
various \battler, VARIOUS_TRY_TRAINER_SLIDE_MSG_MEGA_EVOLUTION
.endm
@ helpful macros @ helpful macros
.macro setstatchanger stat:req, stages:req, down:req .macro setstatchanger stat:req, stages:req, down:req
setbyte sSTATCHANGER, \stat | \stages << 3 | \down << 7 setbyte sSTATCHANGER, \stat | \stages << 3 | \down << 7

View file

@ -7763,6 +7763,8 @@ BattleScript_FocusPunchSetUp::
end2 end2
BattleScript_MegaEvolution:: BattleScript_MegaEvolution::
printstring STRINGID_EMPTYSTRING3
trytrainerslidemegaevolutionmsg BS_ATTACKER
printstring STRINGID_MEGAEVOREACTING printstring STRINGID_MEGAEVOREACTING
waitmessage B_WAIT_TIME_LONG waitmessage B_WAIT_TIME_LONG
setbyte gIsCriticalHit, 0 setbyte gIsCriticalHit, 0
@ -7777,6 +7779,8 @@ BattleScript_MegaEvolution::
end2 end2
BattleScript_WishMegaEvolution:: BattleScript_WishMegaEvolution::
printstring STRINGID_EMPTYSTRING3
trytrainerslidemegaevolutionmsg BS_ATTACKER
printstring STRINGID_FERVENTWISHREACHED printstring STRINGID_FERVENTWISHREACHED
waitmessage B_WAIT_TIME_LONG waitmessage B_WAIT_TIME_LONG
setbyte gIsCriticalHit, 0 setbyte gIsCriticalHit, 0
@ -9989,6 +9993,8 @@ BattleScript_JabocaRowapBerryActivate_Dmg:
@ z moves / effects @ z moves / effects
BattleScript_ZMoveActivateDamaging:: BattleScript_ZMoveActivateDamaging::
printstring STRINGID_EMPTYSTRING3
trytrainerslidezmovemsg BS_ATTACKER
printstring STRINGID_ZPOWERSURROUNDS printstring STRINGID_ZPOWERSURROUNDS
playanimation BS_ATTACKER, B_ANIM_ZMOVE_ACTIVATE, NULL playanimation BS_ATTACKER, B_ANIM_ZMOVE_ACTIVATE, NULL
printstring STRINGID_ZMOVEUNLEASHED printstring STRINGID_ZMOVEUNLEASHED
@ -9996,6 +10002,8 @@ BattleScript_ZMoveActivateDamaging::
return return
BattleScript_ZMoveActivateStatus:: BattleScript_ZMoveActivateStatus::
printstring STRINGID_EMPTYSTRING3
trytrainerslidezmovemsg BS_ATTACKER
savetarget savetarget
printstring STRINGID_ZPOWERSURROUNDS printstring STRINGID_ZPOWERSURROUNDS
playanimation BS_ATTACKER, B_ANIM_ZMOVE_ACTIVATE, NULL playanimation BS_ATTACKER, B_ANIM_ZMOVE_ACTIVATE, NULL

View file

@ -620,7 +620,7 @@ struct BattleStruct
struct MegaEvolutionData mega; struct MegaEvolutionData mega;
struct ZMoveData zmove; struct ZMoveData zmove;
const u8 *trainerSlideMsg; const u8 *trainerSlideMsg;
bool8 trainerSlideLowHpMsgDone; bool8 trainerSlideLowHpMsgDone:1;
u8 introState; u8 introState;
u8 ateBerry[2]; // array id determined by side, each party pokemon as bit u8 ateBerry[2]; // array id determined by side, each party pokemon as bit
u8 stolenStats[NUM_BATTLE_STATS]; // hp byte is used for which stats to raise, other inform about by how many stages u8 stolenStats[NUM_BATTLE_STATS]; // hp byte is used for which stats to raise, other inform about by how many stages
@ -655,6 +655,13 @@ struct BattleStruct
u16 overwrittenAbilities[MAX_BATTLERS_COUNT]; // abilities overwritten during battle (keep separate from battle history in case of switching) u16 overwrittenAbilities[MAX_BATTLERS_COUNT]; // abilities overwritten during battle (keep separate from battle history in case of switching)
bool8 allowedToChangeFormInWeather[PARTY_SIZE][2]; // For each party member and side, used by Ice Face. bool8 allowedToChangeFormInWeather[PARTY_SIZE][2]; // For each party member and side, used by Ice Face.
u8 battleBondTransformed[NUM_BATTLE_SIDES]; // Bitfield for each party. u8 battleBondTransformed[NUM_BATTLE_SIDES]; // Bitfield for each party.
bool8 trainerSlideHalfHpMsgDone:1;
u8 trainerSlideFirstCriticalHitMsgState:2;
u8 trainerSlideFirstSuperEffectiveHitMsgState:2;
u8 trainerSlideFirstSTABMoveMsgState:2;
u8 trainerSlidePlayerMonUnaffectedMsgState:2;
bool8 trainerSlideMegaEvolutionMsgDone:1;
bool8 trainerSlideZMoveMsgDone:1;
}; };
#define F_DYNAMIC_TYPE_1 (1 << 6) #define F_DYNAMIC_TYPE_1 (1 << 6)

View file

@ -229,6 +229,13 @@ enum
TRAINER_SLIDE_LAST_SWITCHIN, TRAINER_SLIDE_LAST_SWITCHIN,
TRAINER_SLIDE_LAST_LOW_HP, TRAINER_SLIDE_LAST_LOW_HP,
TRAINER_SLIDE_FIRST_DOWN, TRAINER_SLIDE_FIRST_DOWN,
TRAINER_SLIDE_LAST_HALF_HP,
TRAINER_SLIDE_FIRST_CRITICAL_HIT,
TRAINER_SLIDE_FIRST_SUPER_EFFECTIVE_HIT,
TRAINER_SLIDE_FIRST_STAB_MOVE,
TRAINER_SLIDE_PLAYER_MON_UNAFFECTED,
TRAINER_SLIDE_MEGA_EVOLUTION,
TRAINER_SLIDE_Z_MOVE,
}; };
void BufferStringBattle(u16 stringID); void BufferStringBattle(u16 stringID);

View file

@ -257,6 +257,8 @@
#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 165 #define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 165
#define VARIOUS_JUMP_IF_NO_VALID_TARGETS 166 #define VARIOUS_JUMP_IF_NO_VALID_TARGETS 166
#define VARIOUS_JUMP_IF_EMERGENCY_EXITED 167 #define VARIOUS_JUMP_IF_EMERGENCY_EXITED 167
#define VARIOUS_TRY_TRAINER_SLIDE_MSG_Z_MOVE 168
#define VARIOUS_TRY_TRAINER_SLIDE_MSG_MEGA_EVOLUTION 169
// Cmd_manipulatedamage // Cmd_manipulatedamage
#define DMG_CHANGE_SIGN 0 #define DMG_CHANGE_SIGN 0

View file

@ -3868,6 +3868,16 @@ void BattleTurnPassed(void)
BattleScriptExecute(BattleScript_ArenaTurnBeginning); BattleScriptExecute(BattleScript_ArenaTurnBeginning);
else if (ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), gTrainerBattleOpponent_A, TRAINER_SLIDE_LAST_LOW_HP)) else if (ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), gTrainerBattleOpponent_A, TRAINER_SLIDE_LAST_LOW_HP))
BattleScriptExecute(BattleScript_TrainerSlideMsgEnd2); BattleScriptExecute(BattleScript_TrainerSlideMsgEnd2);
else if (ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), gTrainerBattleOpponent_A, TRAINER_SLIDE_LAST_HALF_HP))
BattleScriptExecute(BattleScript_TrainerSlideMsgEnd2);
else if (ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), gTrainerBattleOpponent_A, TRAINER_SLIDE_FIRST_CRITICAL_HIT))
BattleScriptExecute(BattleScript_TrainerSlideMsgEnd2);
else if (ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), gTrainerBattleOpponent_A, TRAINER_SLIDE_FIRST_SUPER_EFFECTIVE_HIT))
BattleScriptExecute(BattleScript_TrainerSlideMsgEnd2);
else if (ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), gTrainerBattleOpponent_A, TRAINER_SLIDE_FIRST_STAB_MOVE))
BattleScriptExecute(BattleScript_TrainerSlideMsgEnd2);
else if (ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), gTrainerBattleOpponent_A, TRAINER_SLIDE_PLAYER_MON_UNAFFECTED))
BattleScriptExecute(BattleScript_TrainerSlideMsgEnd2);
} }
u8 IsRunningFromBattleImpossible(void) u8 IsRunningFromBattleImpossible(void)

View file

@ -3868,6 +3868,13 @@ struct TrainerSlide
const u8 *msgLastSwitchIn; const u8 *msgLastSwitchIn;
const u8 *msgLastLowHp; const u8 *msgLastLowHp;
const u8 *msgFirstDown; const u8 *msgFirstDown;
const u8 *msgLastHalfHp;
const u8 *msgFirstCriticalHit;
const u8 *msgFirstSuperEffectiveHit;
const u8 *msgFirstSTABMove;
const u8 *msgPlayerMonUnaffected;
const u8 *msgMegaEvolution;
const u8 *msgZMove;
}; };
static const struct TrainerSlide sTrainerSlides[] = static const struct TrainerSlide sTrainerSlides[] =
@ -3879,20 +3886,39 @@ static const struct TrainerSlide sTrainerSlides[] =
.msgLastSwitchIn = sText_AarghAlmostHadIt, .msgLastSwitchIn = sText_AarghAlmostHadIt,
.msgLastLowHp = sText_BoxIsFull, .msgLastLowHp = sText_BoxIsFull,
.msgFirstDown = sText_123Poof, .msgFirstDown = sText_123Poof,
.msgLastHalfHp = sText_ShootSoClose,
.msgFirstCriticalHit = sText_CriticalHit,
.msgFirstSuperEffectiveHit = sText_SuperEffective,
.msgFirstSTABMove = sText_ABoosted,
.msgPlayerMonUnaffected = sText_ButNoEffect,
.msgMegaEvolution = sText_PowderExplodes,
.msgZMove = sText_Electromagnetism,
}, },
*/ */
}; };
static u32 GetEnemyMonCount(bool32 onlyAlive) static u32 GetAliveEnemyMonCount(void)
{ {
u32 i, count = 0; u32 i, count = 0;
for (i = 0; i < PARTY_SIZE; i++) for (i = 0; i < PARTY_SIZE; i++)
{ {
u32 species = GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2, NULL); u32 species = GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2, NULL);
if (species != SPECIES_NONE if (species != SPECIES_NONE && species != SPECIES_EGG && GetMonData(&gEnemyParty[i], MON_DATA_HP, NULL))
&& species != SPECIES_EGG count++;
&& (!onlyAlive || GetMonData(&gEnemyParty[i], MON_DATA_HP, NULL))) }
return count;
}
static u32 GetTotalEnemyMonCount(void)
{
u32 i, count = 0;
for (i = 0; i < PARTY_SIZE; i++)
{
u32 species = GetMonData(&gEnemyParty[i], MON_DATA_SPECIES2, NULL);
if (species != SPECIES_NONE && species != SPECIES_EGG)
count++; count++;
} }
@ -3914,7 +3940,7 @@ bool32 ShouldDoTrainerSlide(u32 battlerId, u32 trainerId, u32 which)
switch (which) switch (which)
{ {
case TRAINER_SLIDE_LAST_SWITCHIN: case TRAINER_SLIDE_LAST_SWITCHIN:
if (sTrainerSlides[i].msgLastSwitchIn != NULL && GetEnemyMonCount(TRUE) == 1) if (sTrainerSlides[i].msgLastSwitchIn != NULL && GetAliveEnemyMonCount() == 1)
{ {
gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgLastSwitchIn; gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgLastSwitchIn;
return TRUE; return TRUE;
@ -3922,7 +3948,7 @@ bool32 ShouldDoTrainerSlide(u32 battlerId, u32 trainerId, u32 which)
break; break;
case TRAINER_SLIDE_LAST_LOW_HP: case TRAINER_SLIDE_LAST_LOW_HP:
if (sTrainerSlides[i].msgLastLowHp != NULL if (sTrainerSlides[i].msgLastLowHp != NULL
&& GetEnemyMonCount(TRUE) == 1 && GetAliveEnemyMonCount() == 1
&& gBattleMons[battlerId].hp <= (gBattleMons[battlerId].maxHP / 4) && gBattleMons[battlerId].hp <= (gBattleMons[battlerId].maxHP / 4)
&& !gBattleStruct->trainerSlideLowHpMsgDone) && !gBattleStruct->trainerSlideLowHpMsgDone)
{ {
@ -3932,12 +3958,77 @@ bool32 ShouldDoTrainerSlide(u32 battlerId, u32 trainerId, u32 which)
} }
break; break;
case TRAINER_SLIDE_FIRST_DOWN: case TRAINER_SLIDE_FIRST_DOWN:
if (sTrainerSlides[i].msgFirstDown != NULL && GetEnemyMonCount(TRUE) == GetEnemyMonCount(FALSE) - 1) if (sTrainerSlides[i].msgFirstDown != NULL && GetAliveEnemyMonCount() == GetTotalEnemyMonCount() - 1)
{ {
gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgFirstDown; gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgFirstDown;
return TRUE; return TRUE;
} }
break; break;
case TRAINER_SLIDE_LAST_HALF_HP:
if (sTrainerSlides[i].msgLastHalfHp != NULL
&& GetAliveEnemyMonCount() == 1
&& (gBattleMons[battlerId].hp <= (gBattleMons[battlerId].maxHP / 2) && gBattleMons[battlerId].hp > (gBattleMons[battlerId].maxHP / 4))
&& !gBattleStruct->trainerSlideHalfHpMsgDone)
{
gBattleStruct->trainerSlideHalfHpMsgDone = TRUE;
gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgLastHalfHp;
return TRUE;
}
break;
case TRAINER_SLIDE_FIRST_CRITICAL_HIT:
if (sTrainerSlides[i].msgFirstCriticalHit != NULL && gBattleStruct->trainerSlideFirstCriticalHitMsgState == 1)
{
gBattleStruct->trainerSlideFirstCriticalHitMsgState = 2;
gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgFirstCriticalHit;
return TRUE;
}
break;
case TRAINER_SLIDE_FIRST_SUPER_EFFECTIVE_HIT:
if (sTrainerSlides[i].msgFirstSuperEffectiveHit != NULL
&& gBattleStruct->trainerSlideFirstSuperEffectiveHitMsgState == 1
&& gBattleMons[battlerId].hp)
{
gBattleStruct->trainerSlideFirstSuperEffectiveHitMsgState = 2;
gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgFirstSuperEffectiveHit;
return TRUE;
}
break;
case TRAINER_SLIDE_FIRST_STAB_MOVE:
if (sTrainerSlides[i].msgFirstSTABMove != NULL
&& gBattleStruct->trainerSlideFirstSTABMoveMsgState == 1
&& GetAliveEnemyMonCount() == GetTotalEnemyMonCount())
{
gBattleStruct->trainerSlideFirstSTABMoveMsgState = 2;
gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgFirstSTABMove;
return TRUE;
}
break;
case TRAINER_SLIDE_PLAYER_MON_UNAFFECTED:
if (sTrainerSlides[i].msgPlayerMonUnaffected != NULL
&& gBattleStruct->trainerSlidePlayerMonUnaffectedMsgState == 1
&& GetAliveEnemyMonCount() == GetTotalEnemyMonCount())
{
gBattleStruct->trainerSlidePlayerMonUnaffectedMsgState = 2;
gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgPlayerMonUnaffected;
return TRUE;
}
break;
case TRAINER_SLIDE_MEGA_EVOLUTION:
if (sTrainerSlides[i].msgMegaEvolution != NULL && !gBattleStruct->trainerSlideMegaEvolutionMsgDone)
{
gBattleStruct->trainerSlideMegaEvolutionMsgDone = TRUE;
gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgMegaEvolution;
return TRUE;
}
break;
case TRAINER_SLIDE_Z_MOVE:
if (sTrainerSlides[i].msgZMove != NULL && !gBattleStruct->trainerSlideZMoveMsgDone)
{
gBattleStruct->trainerSlideZMoveMsgDone = TRUE;
gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgZMove;
return TRUE;
}
break;
} }
break; break;
} }

View file

@ -2540,6 +2540,11 @@ static void Cmd_critmessage(void)
if (gIsCriticalHit == TRUE && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)) if (gIsCriticalHit == TRUE && !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
{ {
PrepareStringBattle(STRINGID_CRITICALHIT, gBattlerAttacker); PrepareStringBattle(STRINGID_CRITICALHIT, gBattlerAttacker);
// Signal for the trainer slide-in system.
if (GetBattlerSide(gBattlerTarget) != B_SIDE_PLAYER && gBattleStruct->trainerSlideFirstCriticalHitMsgState != 2)
gBattleStruct->trainerSlideFirstCriticalHitMsgState = 1;
gBattleCommunication[MSG_DISPLAY] = 1; gBattleCommunication[MSG_DISPLAY] = 1;
} }
gBattlescriptCurrInstr = cmd->nextInstr; gBattlescriptCurrInstr = cmd->nextInstr;
@ -2619,7 +2624,13 @@ static void Cmd_resultmessage(void)
{ {
case MOVE_RESULT_SUPER_EFFECTIVE: case MOVE_RESULT_SUPER_EFFECTIVE:
if (!gMultiHitCounter) // Don't print effectiveness on each hit in a multi hit attack if (!gMultiHitCounter) // Don't print effectiveness on each hit in a multi hit attack
{
// Signal for the trainer slide-in system.
if (GetBattlerSide(gBattlerTarget) != B_SIDE_PLAYER && gBattleStruct->trainerSlideFirstSuperEffectiveHitMsgState != 2)
gBattleStruct->trainerSlideFirstSuperEffectiveHitMsgState = 1;
stringId = STRINGID_SUPEREFFECTIVE; stringId = STRINGID_SUPEREFFECTIVE;
}
break; break;
case MOVE_RESULT_NOT_VERY_EFFECTIVE: case MOVE_RESULT_NOT_VERY_EFFECTIVE:
if (!gMultiHitCounter) if (!gMultiHitCounter)
@ -11040,6 +11051,30 @@ static void Cmd_various(void)
gBattlescriptCurrInstr = cmd->nextInstr; gBattlescriptCurrInstr = cmd->nextInstr;
return; return;
} }
case VARIOUS_TRY_TRAINER_SLIDE_MSG_Z_MOVE:
{
VARIOUS_ARGS();
if (ShouldDoTrainerSlide(gActiveBattler, gTrainerBattleOpponent_A, TRAINER_SLIDE_Z_MOVE))
{
gBattleScripting.battler = gActiveBattler;
BattleScriptPush(cmd->nextInstr);
gBattlescriptCurrInstr = BattleScript_TrainerSlideMsgRet;
return;
}
break;
}
case VARIOUS_TRY_TRAINER_SLIDE_MSG_MEGA_EVOLUTION:
{
VARIOUS_ARGS();
if (ShouldDoTrainerSlide(gActiveBattler, gTrainerBattleOpponent_A, TRAINER_SLIDE_MEGA_EVOLUTION))
{
gBattleScripting.battler = gActiveBattler;
BattleScriptPush(cmd->nextInstr);
gBattlescriptCurrInstr = BattleScript_TrainerSlideMsgRet;
return;
}
break;
}
} // End of switch (cmd->id) } // End of switch (cmd->id)
gBattlescriptCurrInstr = cmd->nextInstr; gBattlescriptCurrInstr = cmd->nextInstr;

View file

@ -1607,6 +1607,12 @@ void PrepareStringBattle(u16 stringId, u8 battler)
} }
#endif #endif
// Signal for the trainer slide-in system.
if ((stringId == STRINGID_ITDOESNTAFFECT || stringId == STRINGID_PKMNWASNTAFFECTED || stringId == STRINGID_PKMNUNAFFECTED)
&& GetBattlerSide(gBattlerTarget) == B_SIDE_OPPONENT
&& gBattleStruct->trainerSlidePlayerMonUnaffectedMsgState != 2)
gBattleStruct->trainerSlidePlayerMonUnaffectedMsgState = 1;
gActiveBattler = battler; gActiveBattler = battler;
BtlController_EmitPrintString(BUFFER_A, stringId); BtlController_EmitPrintString(BUFFER_A, stringId);
MarkBattlerForControllerExec(gActiveBattler); MarkBattlerForControllerExec(gActiveBattler);
@ -9884,6 +9890,10 @@ static u16 CalcTypeEffectivenessMultiplierInternal(u16 move, u8 moveType, u8 bat
} }
} }
// Signal for the trainer slide-in system.
if (GetBattlerSide(battlerDef) != B_SIDE_PLAYER && modifier && gBattleStruct->trainerSlideFirstSTABMoveMsgState != 2)
gBattleStruct->trainerSlideFirstSTABMoveMsgState = 1;
return modifier; return modifier;
} }