diff --git a/asm/macros/battle_script.inc b/asm/macros/battle_script.inc index 4b323ab8ff..d6645f602b 100644 --- a/asm/macros/battle_script.inc +++ b/asm/macros/battle_script.inc @@ -1553,6 +1553,19 @@ various \battler, VARIOUS_JUMP_IF_NOT_GROUNDED .4byte \ptr .endm + + .macro handletrainerslidemsg battler, field + various \battler, VARIOUS_HANDLE_TRAINER_SLIDE_MSG + .byte \field + .endm + + .macro trytrainerslidefirstdownmsg battler + various \battler, VARIOUS_TRY_TRAINER_SLIDE_MSG_FIRST_OFF + .endm + + .macro trytrainerslidelastonmsg battler + various \battler, VARIOUS_TRY_TRAINER_SLIDE_MSG_LAST_ON + .endm @ helpful macros .macro setstatchanger stat, stages, down diff --git a/data/battle_scripts_1.s b/data/battle_scripts_1.s index b0fb9ba0c1..657cbf4214 100644 --- a/data/battle_scripts_1.s +++ b/data/battle_scripts_1.s @@ -4213,6 +4213,7 @@ BattleScript_FaintAttacker:: dofaintanimation BS_ATTACKER cleareffectsonfaint BS_ATTACKER printstring STRINGID_ATTACKERFAINTED + trytrainerslidefirstdownmsg BS_ATTACKER return BattleScript_FaintTarget:: @@ -4223,6 +4224,7 @@ BattleScript_FaintTarget:: printstring STRINGID_TARGETFAINTED tryactivatemoxie BS_ATTACKER tryactivatefellstinger BS_ATTACKER + trytrainerslidefirstdownmsg BS_TARGET return BattleScript_GiveExp:: @@ -4288,6 +4290,7 @@ BattleScript_FaintedMonChooseAnother:: switchinanim BS_FAINTED, FALSE waitstate various7 BS_ATTACKER + trytrainerslidelastonmsg BS_FAINTED switchineffects BS_FAINTED jumpifbattletype BATTLE_TYPE_DOUBLE, BattleScript_FaintedMonEnd cancelallactions diff --git a/data/battle_scripts_2.s b/data/battle_scripts_2.s index 86ef91e71e..53c4cf901d 100644 --- a/data/battle_scripts_2.s +++ b/data/battle_scripts_2.s @@ -213,3 +213,17 @@ BattleScript_ActionWallyThrow: printstring STRINGID_YOUTHROWABALLNOWRIGHT waitmessage 0x40 end2 + +BattleScript_TrainerSlideMsgRet:: + handletrainerslidemsg BS_SCRIPTING, 0 + trainerslidein 1 + handletrainerslidemsg BS_SCRIPTING, 1 + waitstate + trainerslideout 1 + handletrainerslidemsg BS_SCRIPTING, 2 + waitstate + return + +BattleScript_TrainerSlideMsgEnd2:: + call BattleScript_TrainerSlideMsgRet + end2 diff --git a/include/battle.h b/include/battle.h index 6240a51b04..8764ebadf8 100644 --- a/include/battle.h +++ b/include/battle.h @@ -583,6 +583,8 @@ struct BattleStruct u8 activeAbilityPopUps; // as bits for each battler bool8 throwingPokeBall; struct MegaEvolutionData mega; + const u8 *trainerSlideMsg; + bool8 trainerSlideLowHpMsgDone; }; #define GET_MOVE_TYPE(move, typeArg) \ diff --git a/include/battle_message.h b/include/battle_message.h index 449df1fb33..cb39fa7fec 100644 --- a/include/battle_message.h +++ b/include/battle_message.h @@ -214,12 +214,20 @@ struct BattleMsgData u8 textBuffs[3][TEXT_BUFF_ARRAY_COUNT]; }; +enum +{ + TRAINER_SLIDE_LAST_SWITCHIN, + TRAINER_SLIDE_LAST_LOW_HP, + TRAINER_SLIDE_FIRST_DOWN, +}; + void BufferStringBattle(u16 stringID); u32 BattleStringExpandPlaceholdersToDisplayedString(const u8* src); u32 BattleStringExpandPlaceholders(const u8* src, u8* dst); void BattlePutTextOnWindow(const u8* text, u8 arg1); void SetPpNumbersPaletteInMoveSelection(void); u8 GetCurrentPpToMaxPpState(u8 currentPp, u8 maxPp); +bool32 ShouldDoTrainerSlide(u32 battlerId, u32 trainerId, u32 which); extern struct BattleMsgData *gBattleMsgDataPtr; diff --git a/include/battle_scripts.h b/include/battle_scripts.h index 40bebc1c34..736da30fc3 100644 --- a/include/battle_scripts.h +++ b/include/battle_scripts.h @@ -280,5 +280,7 @@ extern const u8 BattleScript_ForceRandomSwitch[]; extern const u8 BattleScript_SideStatusWoreOffReturn[]; extern const u8 BattleScript_MoveEffectSmackDown[]; extern const u8 BattleScript_MoveEffectFlameBurst[]; +extern const u8 BattleScript_TrainerSlideMsgRet[]; +extern const u8 BattleScript_TrainerSlideMsgEnd2[]; #endif // GUARD_BATTLE_SCRIPTS_H diff --git a/include/constants/battle_script_commands.h b/include/constants/battle_script_commands.h index 899e619f57..8d0117d676 100644 --- a/include/constants/battle_script_commands.h +++ b/include/constants/battle_script_commands.h @@ -120,6 +120,9 @@ #define VARIOUS_BESTOW 65 #define VARIOUS_ARGUMENT_TO_MOVE_EFFECT 66 #define VARIOUS_JUMP_IF_NOT_GROUNDED 67 +#define VARIOUS_HANDLE_TRAINER_SLIDE_MSG 68 +#define VARIOUS_TRY_TRAINER_SLIDE_MSG_FIRST_OFF 69 +#define VARIOUS_TRY_TRAINER_SLIDE_MSG_LAST_ON 70 // atk80, dmg manipulation #define ATK80_DMG_CHANGE_SIGN 0 diff --git a/include/constants/battle_string_ids.h b/include/constants/battle_string_ids.h index ac0b0bc3c2..fa8c359b25 100644 --- a/include/constants/battle_string_ids.h +++ b/include/constants/battle_string_ids.h @@ -9,6 +9,7 @@ #define STRINGID_SWITCHINMON 3 #define STRINGID_USEDMOVE 4 #define STRINGID_BATTLEEND 5 +#define STRINGID_TRAINERSLIDE 6 // todo: make some of those names less vague: attacker/target vs pkmn, etc. #define STRINGID_TRAINER1LOSETEXT 12 diff --git a/src/battle_main.c b/src/battle_main.c index 2604359a29..3ffaad8920 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -4052,6 +4052,8 @@ void BattleTurnPassed(void) BattleScriptExecute(BattleScript_82DB881); else if (gBattleTypeFlags & BATTLE_TYPE_ARENA && gBattleStruct->field_DA == 0) BattleScriptExecute(BattleScript_ArenaTurnBeginning); + else if (ShouldDoTrainerSlide(GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), gTrainerBattleOpponent_A, TRAINER_SLIDE_LAST_LOW_HP)) + BattleScriptExecute(BattleScript_TrainerSlideMsgEnd2); } u8 IsRunningFromBattleImpossible(void) diff --git a/src/battle_message.c b/src/battle_message.c index 46e951b2bc..b78c8252cd 100644 --- a/src/battle_message.c +++ b/src/battle_message.c @@ -3,6 +3,7 @@ #include "battle_message.h" #include "constants/battle_string_ids.h" #include "constants/moves.h" +#include "constants/species.h" #include "text.h" #include "string_util.h" #include "constants/items.h" @@ -2519,6 +2520,9 @@ void BufferStringBattle(u16 stringID) } } break; + case STRINGID_TRAINERSLIDE: + stringPtr = gBattleStruct->trainerSlideMsg; + break; default: // load a string from the table if (stringID >= BATTLESTRINGS_COUNT + BATTLESTRINGS_ID_ADDER) { @@ -3457,3 +3461,87 @@ u8 GetCurrentPpToMaxPpState(u8 currentPp, u8 maxPp) return 0; } + +struct TrainerSlide +{ + u16 trainerId; + const u8 *msgLastSwitchIn; + const u8 *msgLastLowHp; + const u8 *msgFirstDown; +}; + +static const struct TrainerSlide sTrainerSlides[] = +{ + {0x291, sText_AarghAlmostHadIt, sText_BoxIsFull, sText_123Poof}, +}; + +static u32 GetEnemyMonCount(bool32 onlyAlive) +{ + 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 + && (!onlyAlive || GetMonData(&gEnemyParty[i], MON_DATA_HP, NULL))) + count++; + } + + return count; +} + +static bool32 IsBattlerHpLow(u32 battler) +{ + if ((gBattleMons[battler].hp * 100) / gBattleMons[battler].maxHP < 25) + return TRUE; + else + return FALSE; +} + +bool32 ShouldDoTrainerSlide(u32 battlerId, u32 trainerId, u32 which) +{ + s32 i; + + if (!(gBattleTypeFlags & BATTLE_TYPE_TRAINER) || GetBattlerSide(battlerId) != B_SIDE_OPPONENT) + return FALSE; + + for (i = 0; i < ARRAY_COUNT(sTrainerSlides); i++) + { + if (trainerId == sTrainerSlides[i].trainerId) + { + gBattleScripting.battler = battlerId; + switch (which) + { + case TRAINER_SLIDE_LAST_SWITCHIN: + if (sTrainerSlides[i].msgLastSwitchIn != NULL && GetEnemyMonCount(TRUE) == 1) + { + gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgLastSwitchIn; + return TRUE; + } + break; + case TRAINER_SLIDE_LAST_LOW_HP: + if (sTrainerSlides[i].msgLastLowHp != NULL + && GetEnemyMonCount(TRUE) == 1 + && IsBattlerHpLow(battlerId) + && !gBattleStruct->trainerSlideLowHpMsgDone) + { + gBattleStruct->trainerSlideLowHpMsgDone = TRUE; + gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgLastLowHp; + return TRUE; + } + break; + case TRAINER_SLIDE_FIRST_DOWN: + if (sTrainerSlides[i].msgFirstDown != NULL && GetEnemyMonCount(TRUE) == GetEnemyMonCount(FALSE) - 1) + { + gBattleStruct->trainerSlideMsg = sTrainerSlides[i].msgFirstDown; + return TRUE; + } + break; + } + break; + } + } + + return FALSE; +} diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index 693482bcb2..d8bf50be17 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -7073,6 +7073,42 @@ static void atk76_various(void) else gBattlescriptCurrInstr += 7; return; + case VARIOUS_HANDLE_TRAINER_SLIDE_MSG: + if (gBattlescriptCurrInstr[3] == 0) + { + gBattleScripting.savedDmg = gBattlerSpriteIds[gActiveBattler]; + } + else if (gBattlescriptCurrInstr[3] == 1) + { + BtlController_EmitPrintString(0, STRINGID_TRAINERSLIDE); + MarkBattlerForControllerExec(gActiveBattler); + } + else + { + gBattlerSpriteIds[gActiveBattler] = gBattleScripting.savedDmg; + if (gBattleMons[gActiveBattler].hp != 0) + { + BattleLoadOpponentMonSpriteGfx(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler); + } + } + gBattlescriptCurrInstr += 4; + return; + case VARIOUS_TRY_TRAINER_SLIDE_MSG_FIRST_OFF: + if (ShouldDoTrainerSlide(gActiveBattler, gTrainerBattleOpponent_A, TRAINER_SLIDE_FIRST_DOWN)) + { + BattleScriptPush(gBattlescriptCurrInstr + 3); + gBattlescriptCurrInstr = BattleScript_TrainerSlideMsgRet; + return; + } + break; + case VARIOUS_TRY_TRAINER_SLIDE_MSG_LAST_ON: + if (ShouldDoTrainerSlide(gActiveBattler, gTrainerBattleOpponent_A, TRAINER_SLIDE_LAST_SWITCHIN)) + { + BattleScriptPush(gBattlescriptCurrInstr + 3); + gBattlescriptCurrInstr = BattleScript_TrainerSlideMsgRet; + return; + } + break; } gBattlescriptCurrInstr += 3;