Merge branch 'upcoming' into overworldsnow

This commit is contained in:
kittenchilly 2023-08-29 21:02:33 -05:00
commit c8aac3407e
49 changed files with 4543 additions and 13277 deletions

View File

@ -1637,6 +1637,11 @@
.byte \case
.endm
.macro handleultraburst battler:req, case:req
various \battler, VARIOUS_HANDLE_ULTRA_BURST
.byte \case
.endm
.macro handleformchange battler:req, case:req
various \battler, VARIOUS_HANDLE_FORM_CHANGE
.byte \case

View File

@ -0,0 +1,3 @@
gBattlerControllerFuncs
gBattleControllerData
gBattlerControllerEndFuncs

View File

@ -2,8 +2,6 @@ gPreBattleCallback1
gBattleMainFunc
gBattleResults
gLeveledUpInBattle
gBattlerControllerFuncs
gHealthboxSpriteIds
gMultiUsePlayerCursor
gNumberOfMovesToChoose
gBattleControllerData

View File

@ -947,6 +947,7 @@ gBattleAnims_General::
.4byte General_ZMoveActivate @ B_ANIM_ZMOVE_ACTIVATE
.4byte General_AffectionHangedOn @ B_ANIM_AFFECTION_HANGED_ON
.4byte General_Snow @ B_ANIM_SNOW_CONTINUES
.4byte General_UltraBurst @ B_ANIM_ULTRA_BURST
.align 2
gBattleAnims_Special::
@ -27002,6 +27003,43 @@ General_PrimalReversion_Omega:
blendoff
end
General_UltraBurst::
loadspritegfx ANIM_TAG_ULTRA_BURST_SYMBOL
loadspritegfx ANIM_TAG_SPARK_2 @spark
loadspritegfx ANIM_TAG_LEAF @green
loadspritegfx ANIM_TAG_ELECTRIC_ORBS @charge particles
loadspritegfx ANIM_TAG_CIRCLE_OF_LIGHT @psycho boost
monbg ANIM_ATTACKER
setalpha 12, 8
createvisualtask AnimTask_BlendBattleAnimPal, 0xa, (F_PAL_BG | F_PAL_ADJACENT), 0x2, 0x0, 0xF, 0x0000
waitforvisualfinish
createvisualtask AnimTask_ElectricChargingParticles, 2, ANIM_ATTACKER, 60, 2, 12 @ charge particles to attacker
delay 0x1e
loopsewithpan SE_M_CHARGE, SOUND_PAN_ATTACKER, 0xe, 0xa
createsprite gSuperpowerOrbSpriteTemplate, ANIM_TARGET, 3, 0x0
call LightThatBurnsTheSkyGreenSparks
call LightThatBurnsTheSkyGreenSparks
call LightThatBurnsTheSkyGreenSparks
call LightThatBurnsTheSkyGreenSparks
call LightThatBurnsTheSkyGreenSparks
call LightThatBurnsTheSkyGreenSparks
call LightThatBurnsTheSkyGreenSparks
call LightThatBurnsTheSkyGreenSparks
call LightThatBurnsTheSkyGreenSparks
delay 20
createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 0, 16, RGB_WHITEALPHA
createvisualtask AnimTask_TransformMon, 2, 1, 0
createsprite gUltraBurstSymbolSpriteTemplate, ANIM_ATTACKER, 0x0, 0x0, 0x0, 0x0, 0x0
waitforvisualfinish
createvisualtask AnimTask_BlendBattleAnimPalExclude, 5, 5, 2, 16, 0, RGB_WHITEALPHA
createvisualtask AnimTask_HorizontalShake, 5, ANIM_TARGET, 5, 14
waitforvisualfinish
createvisualtask SoundTask_PlayNormalCry, 0
waitforvisualfinish
clearmonbg ANIM_ATK_PARTNER
blendoff
end
General_AffectionHangedOn::
loadspritegfx ANIM_TAG_RED_HEART
loopsewithpan SE_M_CHARM, SOUND_PAN_ATTACKER, 12, 3

View File

@ -7796,6 +7796,21 @@ BattleScript_PrimalReversionRet::
switchinabilities BS_ATTACKER
return
BattleScript_UltraBurst::
printstring STRINGID_EMPTYSTRING3
trytrainerslidezmovemsg BS_ATTACKER
printstring STRINGID_ULTRABURSTREACTING
waitmessage B_WAIT_TIME_LONG
setbyte gIsCriticalHit, 0
handleultraburst BS_ATTACKER, 0
playanimation BS_ATTACKER, B_ANIM_ULTRA_BURST
waitanimation
handleultraburst BS_ATTACKER, 1
printstring STRINGID_ULTRABURSTCOMPLETED
waitmessage B_WAIT_TIME_LONG
switchinabilities BS_ATTACKER
end3
BattleScript_AttackerFormChange::
pause 5
copybyte gBattlerAbility, gBattlerAttacker

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 B

View File

@ -292,6 +292,8 @@ struct AiLogicData
s32 simulatedDmg[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex
u8 effectiveness[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT][MAX_MON_MOVES]; // attacker, target, moveIndex
u8 moveLimitations[MAX_BATTLERS_COUNT];
bool8 shouldSwitchMon; // Because all available moves have no/little effect. Each bit per battler.
u8 monToSwitchId[MAX_BATTLERS_COUNT]; // ID of the mon to switch.
};
struct AI_ThinkingStruct
@ -304,8 +306,7 @@ struct AI_ThinkingStruct
u32 aiFlags;
u8 aiAction;
u8 aiLogicId;
struct AI_SavedBattleMon saved[4];
bool8 switchMon; // Because all available moves have no/little effect.
struct AI_SavedBattleMon saved[MAX_BATTLERS_COUNT];
};
#define AI_MOVE_HISTORY_COUNT 3
@ -487,6 +488,15 @@ struct MegaEvolutionData
u8 triggerSpriteId;
};
struct UltraBurstData
{
u8 toBurst; // As flags using gBitTable.
bool8 alreadyBursted[4]; // Array id is used for mon position.
u8 battlerId;
bool8 playerSelect;
u8 triggerSpriteId;
};
struct Illusion
{
u8 on;
@ -615,6 +625,7 @@ struct BattleStruct
u8 abilityPopUpSpriteIds[MAX_BATTLERS_COUNT][2]; // two per battler
bool8 throwingPokeBall;
struct MegaEvolutionData mega;
struct UltraBurstData burst;
struct ZMoveData zmove;
const u8 *trainerSlideMsg;
bool8 trainerSlideLowHpMsgDone;
@ -1002,11 +1013,9 @@ extern void (*gPreBattleCallback1)(void);
extern void (*gBattleMainFunc)(void);
extern struct BattleResults gBattleResults;
extern u8 gLeveledUpInBattle;
extern void (*gBattlerControllerFuncs[MAX_BATTLERS_COUNT])(void);
extern u8 gHealthboxSpriteIds[MAX_BATTLERS_COUNT];
extern u8 gMultiUsePlayerCursor;
extern u8 gNumberOfMovesToChoose;
extern u8 gBattleControllerData[MAX_BATTLERS_COUNT];
extern bool8 gHasFetchedBall;
extern u8 gLastUsedBall;
extern u16 gLastThrownBall;

View File

@ -99,6 +99,7 @@ enum {
// Special return values in gBattleBufferB from Battle Controller functions.
#define RET_VALUE_LEVELED_UP 11
#define RET_MEGA_EVOLUTION 0x80
#define RET_ULTRA_BURST 0x70
struct UnusedControllerStruct
{
@ -129,6 +130,7 @@ struct ChooseMoveStruct
u8 monType2;
u8 monType3;
struct MegaEvolutionData mega;
struct UltraBurstData burst;
struct ZMoveData zmove;
};
@ -197,6 +199,9 @@ enum
};
extern struct UnusedControllerStruct gUnusedControllerStruct;
extern void (*gBattlerControllerFuncs[MAX_BATTLERS_COUNT])(u32 battler);
extern void (*gBattlerControllerEndFuncs[MAX_BATTLERS_COUNT])(u32 battler);
extern u8 gBattleControllerData[MAX_BATTLERS_COUNT];
// general functions
void HandleLinkBattleSetup(void);
@ -251,43 +256,88 @@ void BtlController_EmitResetActionMoveSelection(u8 bufferId, u8 caseId);
void BtlController_EmitEndLinkBattle(u8 bufferId, u8 battleOutcome);
void BtlController_EmitDebugMenu(u8 bufferId);
void BattleControllerComplete(u32 battler); // Can be used for all the controllers.
void BtlController_Empty(u32 battler); // Empty command, does nothing, only completes the execution.
void BtlController_TerminatorNop(u32 battler); // Dummy function at the end of the table.
void BattleControllerDummy(u32 battler);
void StartSendOutAnim(u32 battler, bool32 dontClearSubstituteBit);
void Controller_WaitForString(u32 battler);
void Controller_WaitForHealthBar(u32 battler);
// handlers
void BtlController_HandleGetMonData(u32 battler);
void BtlController_HandleGetRawMonData(u32 battler);
void BtlController_HandleSetMonData(u32 battler);
void BtlController_HandleSetRawMonData(u32 battler);
void BtlController_HandleLoadMonSprite(u32 battler, void (*controllerCallback)(u32 battler));
void BtlController_HandleSwitchInAnim(u32 battler, bool32 isPlayerSide, void (*controllerCallback)(u32 battler));
void BtlController_HandleReturnMonToBall(u32 battler);
void BtlController_HandleDrawTrainerPic(u32 battlerId, u32 trainerPicId, bool32 isFrontPic, s16 xPos, s16 yPos, s32 subpriority);
void BtlController_HandleTrainerSlide(u32 battler, u32 trainerPicId);
void BtlController_HandleTrainerSlideBack(u32 battlerId, s16 data0, bool32 startAnim);
void BtlController_HandleFaintAnimation(u32 battler);
void BtlController_HandleSuccessBallThrowAnim(u32 battler, u32 target, u32 animId, bool32 allowCriticalCapture);
void BtlController_HandleBallThrowAnim(u32 battler, u32 target, u32 animId, bool32 allowCriticalCapture);
void BtlController_HandleMoveAnimation(u32 battler, bool32 updateTvData);
void BtlController_HandlePrintString(u32 battler, bool32 updateTvData, bool32 arenaPtsDeduct);
void BtlController_HandleHealthBarUpdate(u32 battler, bool32 updateHpText);
void DoStatusIconUpdate(u32 battler);
void BtlController_HandleStatusIconUpdate(u32 battler);
void BtlController_HandleStatusAnimation(u32 battler);
void BtlController_HandleClearUnkVar(u32 battler);
void BtlController_HandleSetUnkVar(u32 battler);
void BtlController_HandleClearUnkFlag(u32 battler);
void BtlController_HandleToggleUnkFlag(u32 battler);
void BtlController_HandleHitAnimation(u32 battler);
void BtlController_HandlePlaySE(u32 battler);
void BtlController_HandlePlayFanfareOrBGM(u32 battler);
void BtlController_HandleFaintingCry(u32 battler);
void BtlController_HandleIntroSlide(u32 battler);
void BtlController_HandleSpriteInvisibility(u32 battler);
bool32 TwoPlayerIntroMons(u32 battlerId); // Double battle with both player pokemon active.
bool32 TwoOpponentIntroMons(u32 battlerId); // Double battle with both opponent pokemon active.
void BtlController_HandleIntroTrainerBallThrow(u32 battler, u16 tagTrainerPal, const u32 *trainerPal, s16 framesToWait, void (*controllerCallback)(u32 battler));
void BtlController_HandleDrawPartyStatusSummary(u32 battler, u32 side, bool32 considerDelay);
void BtlController_HandleHidePartyStatusSummary(u32 battler);
void BtlController_HandleBattleAnimation(u32 battler, bool32 ignoreSE, bool32 updateTvData);
// player controller
void SetControllerToPlayer(void);
void BattleControllerDummy(void);
void PlayerHandleGetRawMonData(void);
void SetBattleEndCallbacks(void);
void SpriteCB_FreePlayerSpriteLoadMonSprite(struct Sprite *sprite);
void SetControllerToPlayer(u32 battler);
void SetBattleEndCallbacks(u32 battler);
void PlayerHandleExpUpdate(u32 battler);
u32 LinkPlayerGetTrainerPicId(u32 multiplayerId);
void CB2_SetUpReshowBattleScreenAfterMenu(void);
void CB2_SetUpReshowBattleScreenAfterMenu2(void);
void Task_PlayerController_RestoreBgmAfterCry(u8 taskId);
void ActionSelectionCreateCursorAt(u8 cursorPos, u8 unused);
void ActionSelectionDestroyCursorAt(u8 cursorPos);
void InitMoveSelectionsVarsAndStrings(void);
void InitMoveSelectionsVarsAndStrings(u32 battler);
void MoveSelectionCreateCursorAt(u8 cursorPos, u8 arg1);
void MoveSelectionDestroyCursorAt(u8 cursorPosition);
// recorded player controller
void SetControllerToRecordedPlayer(void);
void SetControllerToRecordedPlayer(u32 battler);
// opponent controller
void SetControllerToOpponent(void);
void SetControllerToOpponent(u32 battler);
// player partner controller
void SetControllerToPlayerPartner(void);
void Controller_PlayerPartnerShowIntroHealthbox(u32 battler); // Also used by the link partner.
void SetControllerToPlayerPartner(u32 battler);
// safari controller
void SetControllerToSafari(void);
void SetControllerToSafari(u32 battler);
// wally controller
void SetControllerToWally(void);
void SetControllerToWally(u32 battler);
// recorded opponent controller
void SetControllerToRecordedOpponent(void);
void SetControllerToRecordedOpponent(u32 battler);
// link opponent
void SetControllerToLinkOpponent(void);
void SetControllerToLinkOpponent(u32 battler);
// link partner
void SetControllerToLinkPartner(void);
void SetControllerToLinkPartner(u32 battler);
#endif // GUARD_BATTLE_CONTROLLERS_H

View File

@ -1,8 +1,6 @@
#ifndef GUARD_BATTLE_DOME_H
#define GUARD_BATTLE_DOME_H
extern u32 gPlayerPartyLostHP;
int GetDomeTrainerSelectedMons(u16 tournamentTrainerId);
int TrainerIdToDomeTournamentId(u16 trainerId);

View File

@ -10,12 +10,10 @@ void InitAndLaunchChosenStatusAnimation(bool8 isStatus2, u32 status);
bool8 TryHandleLaunchBattleTableAnimation(u8 activeBattlerId, u8 attacker, u8 target, u8 tableId, u16 argument);
void InitAndLaunchSpecialAnimation(u8 activeBattlerId, u8 attacker, u8 target, u8 tableId);
bool8 IsBattleSEPlaying(u8 battlerId);
void BattleLoadOpponentMonSpriteGfx(struct Pokemon *mon, u8 battlerId);
void BattleLoadPlayerMonSpriteGfx(struct Pokemon *mon, u8 battlerId);
void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battlerId);
void BattleGfxSfxDummy2(u16 species);
void DecompressTrainerFrontPic(u16 frontPicId, u8 battlerId);
void DecompressTrainerBackPic(u16 backPicId, u8 battlerId);
void BattleGfxSfxDummy3(u8 gender);
void FreeTrainerFrontPicPalette(u16 frontPicId);
bool8 BattleLoadAllHealthBoxesGfx(u8 state);
void LoadBattleBarGfx(u8 unused);

View File

@ -53,11 +53,13 @@ enum
#define TAG_ALPHA_INDICATOR_TILE 0xD779
#define TAG_OMEGA_INDICATOR_TILE 0xD77A
#define TAG_ZMOVE_TRIGGER_TILE 0xD77B
#define TAG_BURST_TRIGGER_TILE 0xD77C
#define TAG_MEGA_TRIGGER_PAL 0xD777
#define TAG_MEGA_INDICATOR_PAL 0xD778
#define TAG_ALPHA_OMEGA_INDICATOR_PAL 0xD779 // Alpha and Omega indicators use the same palette as each of them only uses 4 different colors.
#define TAG_ZMOVE_TRIGGER_PAL 0xD77B
#define TAG_BURST_TRIGGER_PAL 0xD77C
enum
{
@ -91,6 +93,11 @@ void CreateMegaTriggerSprite(u8 battlerId, u8 palId);
bool32 IsMegaTriggerSpriteActive(void);
void HideMegaTriggerSprite(void);
void DestroyMegaTriggerSprite(void);
void ChangeBurstTriggerSprite(u8 spriteId, u8 animId);
void CreateBurstTriggerSprite(u8 battlerId, u8 palId);
bool32 IsBurstTriggerSpriteActive(void);
void HideBurstTriggerSprite(void);
void DestroyBurstTriggerSprite(void);
void MegaIndicator_LoadSpritesGfx(void);
u8 CreatePartyStatusSummarySprites(u8 battler, struct HpAndStatus *partyInfo, bool8 skipPlayer, bool8 isBattleStart);
void Task_HidePartyStatusSummary(u8 taskId);

View File

@ -472,6 +472,7 @@ extern const u8 BattleScript_SpikesActivates[];
extern const u8 BattleScript_BerserkGeneRet[];
extern const u8 BattleScript_TargetFormChangeWithStringNoPopup[];
extern const u8 BattleScript_DefDown[];
extern const u8 BattleScript_UltraBurst[];
// zmoves
extern const u8 BattleScript_ZMoveActivateDamaging[];

View File

@ -177,8 +177,10 @@ uq4_12_t GetTypeModifier(u8 atkType, u8 defType);
s32 GetStealthHazardDamage(u8 hazardType, u8 battlerId);
s32 GetStealthHazardDamageByTypesAndHP(u8 hazardType, u8 type1, u8 type2, u32 maxHp);
bool32 CanMegaEvolve(u8 battlerId);
bool32 CanUltraBurst(u8 battlerId);
bool32 IsBattlerMegaEvolved(u8 battlerId);
bool32 IsBattlerPrimalReverted(u8 battlerId);
bool32 IsBattlerUltraBursted(u8 battlerId);
u16 GetBattleFormChangeTargetSpecies(u8 battlerId, u16 method);
bool32 TryBattleFormChange(u8 battlerId, u16 method);
bool32 DoBattlersShareType(u32 battler1, u32 battler2);

View File

@ -495,4 +495,9 @@
#define PARENTAL_BOND_2ND_HIT 1
#define PARENTAL_BOND_OFF 0
// Constants for if HandleScriptMegaPrimalBurst should handle Mega Evolution, Primal Reversion, or Ultra Burst.
#define HANDLE_TYPE_MEGA_EVOLUTION 0
#define HANDLE_TYPE_PRIMAL_REVERSION 1
#define HANDLE_TYPE_ULTRA_BURST 2
#endif // GUARD_CONSTANTS_BATTLE_H

View File

@ -548,6 +548,7 @@
#define B_ANIM_ZMOVE_ACTIVATE 34 // Using Z Moves
#define B_ANIM_AFFECTION_HANGED_ON 35
#define B_ANIM_SNOW_CONTINUES 36
#define B_ANIM_ULTRA_BURST 37
// special animations table (gBattleAnims_Special)
#define B_ANIM_LVL_UP 0

View File

@ -257,6 +257,7 @@
#define VARIOUS_TRY_REVIVAL_BLESSING 165
#define VARIOUS_TRY_TRAINER_SLIDE_MSG_Z_MOVE 166
#define VARIOUS_TRY_TRAINER_SLIDE_MSG_MEGA_EVOLUTION 167
#define VARIOUS_HANDLE_ULTRA_BURST 168
// Cmd_manipulatedamage
#define DMG_CHANGE_SIGN 0

View File

@ -665,8 +665,10 @@
#define STRINGID_SNOWSTOPPED 663
#define STRINGID_SNOWWARNINGSNOW 664
#define STRINGID_PKMNITEMMELTED 665
#define STRINGID_ULTRABURSTREACTING 666
#define STRINGID_ULTRABURSTCOMPLETED 667
#define BATTLESTRINGS_COUNT 666
#define BATTLESTRINGS_COUNT 668
// This is the string id that gBattleStringsTable starts with.
// String ids before this (e.g. STRINGID_INTROMSG) are not in the table,

View File

@ -96,4 +96,9 @@
// param1: ability to check.
#define FORM_CHANGE_BATTLE_TURN_END 15
// Form change that activates when the mon has the defined item.
// If it's on the player's side, it also requires for the player to trigger it by pressing START before selecting a move.
// param1: item to hold.
#define FORM_CHANGE_BATTLE_ULTRA_BURST 16
#endif // GUARD_CONSTANTS_FORM_CHANGE_TYPES_H

View File

@ -327,6 +327,7 @@
#define SPECIES_FLAG_HISUIAN_FORM (1 << 7)
#define SPECIES_FLAG_ALL_PERFECT_IVS (1 << 8)
#define SPECIES_FLAG_CANNOT_BE_TRADED (1 << 9)
#define SPECIES_FLAG_ULTRA_BURST (1 << 10)
#define LEGENDARY_PERFECT_IV_COUNT 3

View File

@ -796,6 +796,8 @@ struct MoveContext
u16 explicitSecondaryEffect:1;
u16 megaEvolve:1;
u16 explicitMegaEvolve:1;
u16 ultraBurst:1;
u16 explicitUltraBurst:1;
// TODO: u8 zMove:1;
u16 allowed:1;
u16 explicitAllowed:1;

View File

@ -403,6 +403,78 @@ void GetAiLogicData(void)
}
}
static bool32 AI_SwitchMonIfSuitable(u32 battlerId)
{
u32 monToSwitchId = GetMostSuitableMonToSwitchInto();
if (monToSwitchId != PARTY_SIZE)
{
AI_DATA->shouldSwitchMon |= gBitTable[battlerId];
AI_DATA->monToSwitchId[battlerId] = monToSwitchId;
return TRUE;
}
return FALSE;
}
static bool32 AI_ShouldSwitchIfBadMoves(u32 battlerId, bool32 doubleBattle)
{
u32 i, j;
// If can switch.
if (CountUsablePartyMons(battlerId) > 0
&& !IsBattlerTrapped(battlerId, TRUE)
&& !(gBattleTypeFlags & (BATTLE_TYPE_ARENA | BATTLE_TYPE_PALACE))
&& AI_THINKING_STRUCT->aiFlags & (AI_FLAG_CHECK_VIABILITY | AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_PREFER_BATON_PASS))
{
// Consider switching if all moves are worthless to use.
if (GetTotalBaseStat(gBattleMons[battlerId].species) >= 310 // Mon is not weak.
&& gBattleMons[battlerId].hp >= gBattleMons[battlerId].maxHP / 2) // Mon has more than 50% of its HP
{
s32 cap = AI_THINKING_STRUCT->aiFlags & (AI_FLAG_CHECK_VIABILITY) ? 95 : 93;
if (doubleBattle)
{
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
{
if (i != battlerId && IsBattlerAlive(i))
{
for (j = 0; j < MAX_MON_MOVES; j++)
{
if (gBattleStruct->aiFinalScore[battlerId][i][j] > cap)
break;
}
if (j != MAX_MON_MOVES)
break;
}
}
if (i == MAX_BATTLERS_COUNT && AI_SwitchMonIfSuitable(battlerId))
return TRUE;
}
else
{
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (AI_THINKING_STRUCT->score[i] > cap)
break;
}
if (i == MAX_MON_MOVES && AI_SwitchMonIfSuitable(battlerId))
return TRUE;
}
}
// Consider switching if your mon with truant is bodied by Protect spam.
// Or is using a double turn semi invulnerable move(such as Fly) and is faster.
if (GetBattlerAbility(battlerId) == ABILITY_TRUANT
&& IsTruantMonVulnerable(battlerId, gBattlerTarget)
&& gDisableStructs[battlerId].truantCounter
&& gBattleMons[battlerId].hp >= gBattleMons[battlerId].maxHP / 2
&& AI_SwitchMonIfSuitable(battlerId))
{
return TRUE;
}
}
return FALSE;
}
static u8 ChooseMoveOrAction_Singles(void)
{
u8 currentMoveArray[MAX_MON_MOVES];
@ -436,46 +508,9 @@ static u8 ChooseMoveOrAction_Singles(void)
gActiveBattler = sBattler_AI;
// If can switch.
if (CountUsablePartyMons(sBattler_AI) > 0
&& !IsAbilityPreventingEscape(sBattler_AI)
&& !(gBattleMons[gActiveBattler].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION))
&& !(gStatuses3[gActiveBattler] & STATUS3_ROOTED)
&& !(gBattleTypeFlags & (BATTLE_TYPE_ARENA | BATTLE_TYPE_PALACE))
&& AI_THINKING_STRUCT->aiFlags & (AI_FLAG_CHECK_VIABILITY | AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_PREFER_BATON_PASS))
{
// Consider switching if all moves are worthless to use.
if (GetTotalBaseStat(gBattleMons[sBattler_AI].species) >= 310 // Mon is not weak.
&& gBattleMons[sBattler_AI].hp >= gBattleMons[sBattler_AI].maxHP / 2)
{
s32 cap = AI_THINKING_STRUCT->aiFlags & (AI_FLAG_CHECK_VIABILITY) ? 95 : 93;
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (AI_THINKING_STRUCT->score[i] > cap)
break;
}
if (i == MAX_MON_MOVES && GetMostSuitableMonToSwitchInto() != PARTY_SIZE)
{
AI_THINKING_STRUCT->switchMon = TRUE;
return AI_CHOICE_SWITCH;
}
}
// Consider switching if your mon with truant is bodied by Protect spam.
// Or is using a double turn semi invulnerable move(such as Fly) and is faster.
if (GetBattlerAbility(sBattler_AI) == ABILITY_TRUANT
&& IsTruantMonVulnerable(sBattler_AI, gBattlerTarget)
&& gDisableStructs[sBattler_AI].truantCounter
&& gBattleMons[sBattler_AI].hp >= gBattleMons[sBattler_AI].maxHP / 2)
{
if (GetMostSuitableMonToSwitchInto() != PARTY_SIZE)
{
AI_THINKING_STRUCT->switchMon = TRUE;
return AI_CHOICE_SWITCH;
}
}
}
// Switch mon if there are no good moves to use.
if (AI_ShouldSwitchIfBadMoves(sBattler_AI, FALSE))
return AI_CHOICE_SWITCH;
numOfBestMoves = 1;
currentMoveArray[0] = AI_THINKING_STRUCT->score[0];
@ -590,7 +625,6 @@ static u8 ChooseMoveOrAction_Doubles(void)
if (i == BATTLE_PARTNER(sBattler_AI) && bestMovePointsForTarget[i] < 100)
{
bestMovePointsForTarget[i] = -1;
mostViableMovesScores[0] = mostViableMovesScores[0]; // Needed to match.
}
}
@ -600,6 +634,10 @@ static u8 ChooseMoveOrAction_Doubles(void)
}
}
// Switch mon if all of the moves are bad to use against any of the target.
if (AI_ShouldSwitchIfBadMoves(sBattler_AI, TRUE))
return AI_CHOICE_SWITCH;
mostMovePoints = bestMovePointsForTarget[0];
mostViableTargetsArray[0] = 0;
mostViableTargetsNo = 1;
@ -1647,18 +1685,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
break;
case EFFECT_FAKE_OUT:
if (!gDisableStructs[battlerAtk].isFirstTurn)
{
score -= 10;
}
else if (move == MOVE_FAKE_OUT) // filter out first impression
{
if ((AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_CHOICE_BAND || AI_DATA->abilities[battlerAtk] == ABILITY_GORILLA_TACTICS)
&& (CountUsablePartyMons(battlerDef) > 0 || !CanIndexMoveFaintTarget(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex, 0)))
{
if (CountUsablePartyMons(battlerAtk) == 0)
score -= 10; // Don't lock the attacker into Fake Out if they can't switch out afterwards.
}
}
break;
case EFFECT_STOCKPILE:
if (gDisableStructs[battlerAtk].stockpileCounter >= 3)
@ -3094,7 +3121,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
|| IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_POISON)
|| IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_ROCK))
score -= 10; // partner will be hit by earthquake and is weak to it
else
else if (IsBattlerAlive(battlerAtkPartner))
score -= 3;
break;
}
@ -4137,9 +4164,13 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_DEF, &score);
break;
case EFFECT_FAKE_OUT:
if (move == MOVE_FAKE_OUT // filter out first impression
&& ShouldFakeOut(battlerAtk, battlerDef, move))
score += 8;
if (move == MOVE_FAKE_OUT) // filter out first impression
{
if (ShouldFakeOut(battlerAtk, battlerDef, move))
score += 4;
else
score -= 10;
}
break;
case EFFECT_STOCKPILE:
if (AI_DATA->abilities[battlerAtk] == ABILITY_CONTRARY)

View File

@ -60,10 +60,10 @@ void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId)
static bool8 ShouldSwitchIfAllBadMoves(void)
{
if (gBattleResources->ai->switchMon)
if (AI_DATA->shouldSwitchMon & gBitTable[gActiveBattler])
{
gBattleResources->ai->switchMon = 0;
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = PARTY_SIZE;
AI_DATA->shouldSwitchMon &= ~(gBitTable[gActiveBattler]);
gBattleStruct->AI_monToSwitchIntoId[gActiveBattler] = AI_DATA->monToSwitchId[gActiveBattler];
BtlController_EmitTwoReturnValues(BUFFER_B, B_ACTION_SWITCH, 0);
return TRUE;
}
@ -142,11 +142,12 @@ static bool8 ShouldSwitchIfWonderGuard(void)
static bool8 FindMonThatAbsorbsOpponentsMove(void)
{
u8 battlerIn1, battlerIn2;
u16 absorbingTypeAbility;
u8 numAbsorbingAbilities = 0;
u16 absorbingTypeAbilities[3]; // Array size is maximum number of absorbing abilities for a single type
s32 firstId;
s32 lastId; // + 1
struct Pokemon *party;
s32 i;
s32 i, j;
if (HasSuperEffectiveMoveAgainstOpponents(TRUE) && Random() % 3 != 0)
return FALSE;
@ -171,17 +172,42 @@ static bool8 FindMonThatAbsorbsOpponentsMove(void)
battlerIn2 = gActiveBattler;
}
// Create an array of possible absorb abilities so the AI considers all of them
if (gBattleMoves[gLastLandedMoves[gActiveBattler]].type == TYPE_FIRE)
absorbingTypeAbility = ABILITY_FLASH_FIRE;
{
absorbingTypeAbilities[0] = ABILITY_FLASH_FIRE;
numAbsorbingAbilities = 1;
}
else if (gBattleMoves[gLastLandedMoves[gActiveBattler]].type == TYPE_WATER)
absorbingTypeAbility = ABILITY_WATER_ABSORB;
{
absorbingTypeAbilities[0] = ABILITY_WATER_ABSORB;
absorbingTypeAbilities[1] = ABILITY_STORM_DRAIN;
absorbingTypeAbilities[2] = ABILITY_DRY_SKIN;
numAbsorbingAbilities = 3;
}
else if (gBattleMoves[gLastLandedMoves[gActiveBattler]].type == TYPE_ELECTRIC)
absorbingTypeAbility = ABILITY_VOLT_ABSORB;
{
absorbingTypeAbilities[0] = ABILITY_VOLT_ABSORB;
absorbingTypeAbilities[1] = ABILITY_MOTOR_DRIVE;
absorbingTypeAbilities[2] = ABILITY_LIGHTNING_ROD;
numAbsorbingAbilities = 3;
}
else if (gBattleMoves[gLastLandedMoves[gActiveBattler]].type == TYPE_GRASS)
{
absorbingTypeAbilities[0] = ABILITY_SAP_SIPPER;
numAbsorbingAbilities = 1;
}
else
{
return FALSE;
}
if (AI_DATA->abilities[gActiveBattler] == absorbingTypeAbility)
return FALSE;
// Check current mon for all absorbing abilities
for (i = 0; i < numAbsorbingAbilities; i++)
{
if (AI_DATA->abilities[gActiveBattler] == absorbingTypeAbilities[i])
return FALSE;
}
GetAIPartyIndexes(gActiveBattler, &firstId, &lastId);
@ -208,15 +234,18 @@ static bool8 FindMonThatAbsorbsOpponentsMove(void)
continue;
monAbility = GetMonAbility(&party[i]);
if (absorbingTypeAbility == monAbility && Random() & 1)
for (j = 0; j < numAbsorbingAbilities; j++)
{
// we found a mon.
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = i;
BtlController_EmitTwoReturnValues(BUFFER_B, B_ACTION_SWITCH, 0);
return TRUE;
if (absorbingTypeAbilities[j] == monAbility && Random() & 1)
{
// we found a mon.
*(gBattleStruct->AI_monToSwitchIntoId + gActiveBattler) = i;
BtlController_EmitTwoReturnValues(1, B_ACTION_SWITCH, 0);
return TRUE;
}
}
}
return FALSE;
}

View File

@ -2964,24 +2964,23 @@ bool32 AI_CanBeInfatuated(u8 battlerAtk, u8 battlerDef, u16 defAbility)
u32 ShouldTryToFlinch(u8 battlerAtk, u8 battlerDef, u16 atkAbility, u16 defAbility, u16 move)
{
if (defAbility == ABILITY_INNER_FOCUS
if (((AI_DATA->abilities[battlerAtk] != ABILITY_MOLD_BREAKER && (defAbility == ABILITY_SHIELD_DUST || defAbility == ABILITY_INNER_FOCUS))
|| AI_GetHoldEffect(battlerDef) == HOLD_EFFECT_COVERT_CLOAK
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
|| AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER) // Opponent goes first
|| AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER)) // Opponent goes first
{
return 0; // don't try to flinch
return 0;
}
else if ((gBattleMons[battlerDef].status1 & STATUS1_SLEEP) && !HasMoveEffect(battlerDef, EFFECT_SLEEP_TALK) && !HasMoveEffect(battlerDef, EFFECT_SNORE))
{
return 0; // don't try to flinch sleeping pokemon
}
else if (atkAbility == ABILITY_SERENE_GRACE
else if ((atkAbility == ABILITY_SERENE_GRACE
|| gBattleMons[battlerDef].status1 & STATUS1_PARALYSIS
|| gBattleMons[battlerDef].status2 & STATUS2_INFATUATION
|| gBattleMons[battlerDef].status2 & STATUS2_CONFUSION)
|| ((AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER) && CanTargetFaintAi(battlerDef, battlerAtk)))
{
return 2; // good idea to flinch
}
return 1; // decent idea to flinch
return 0; // don't try to flinch
}
bool32 ShouldTrap(u8 battlerAtk, u8 battlerDef, u16 move)
@ -3000,15 +2999,16 @@ bool32 ShouldTrap(u8 battlerAtk, u8 battlerDef, u16 move)
bool32 ShouldFakeOut(u8 battlerAtk, u8 battlerDef, u16 move)
{
if (AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_CHOICE_BAND && CountUsablePartyMons(battlerAtk) == 0)
return FALSE; // don't lock attacker into fake out if can't switch out
if (!gDisableStructs[battlerAtk].isFirstTurn
|| AI_DATA->abilities[battlerAtk] == ABILITY_GORILLA_TACTICS
|| AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_CHOICE_BAND
|| AI_GetHoldEffect(battlerDef) == HOLD_EFFECT_COVERT_CLOAK
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
|| (AI_DATA->abilities[battlerAtk] != ABILITY_MOLD_BREAKER
&& (AI_DATA->abilities[battlerDef] == ABILITY_SHIELD_DUST || AI_DATA->abilities[battlerDef] == ABILITY_INNER_FOCUS)))
return FALSE;
if (gDisableStructs[battlerAtk].isFirstTurn
&& ShouldTryToFlinch(battlerAtk, battlerDef, AI_DATA->abilities[battlerAtk], AI_DATA->abilities[battlerDef], move)
&& !DoesSubstituteBlockMove(battlerAtk, battlerDef, move))
return TRUE;
return FALSE;
return TRUE;
}
static u32 FindMoveUsedXTurnsAgo(u32 battlerId, u32 x)
@ -3676,7 +3676,8 @@ void IncreaseStatUpScore(u8 battlerAtk, u8 battlerDef, u8 statId, s16 *score)
void IncreasePoisonScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
{
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
if (((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|| AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_PSN || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS)
return;
if (AI_CanPoison(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove) && AI_DATA->hpPercents[battlerDef] > 20)
@ -3699,7 +3700,8 @@ void IncreasePoisonScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
void IncreaseBurnScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
{
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
if (((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|| AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_BRN || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS)
return;
if (AI_CanBurn(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove))
@ -3718,7 +3720,8 @@ void IncreaseBurnScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
void IncreaseParalyzeScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
{
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
if (((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|| AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_PAR || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS)
return;
if (AI_CanParalyze(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove))
@ -3739,7 +3742,8 @@ void IncreaseParalyzeScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
void IncreaseSleepScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
{
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
if (((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|| AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_SLP || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS)
return;
if (AI_CanPutToSleep(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove))
@ -3757,7 +3761,8 @@ void IncreaseSleepScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
void IncreaseConfusionScore(u8 battlerAtk, u8 battlerDef, u16 move, s16 *score)
{
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
if (((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|| AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_CONFUSION || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS)
return;
if (AI_CanConfuse(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)

View File

@ -275,6 +275,7 @@ void LaunchBattleAnimation(u32 animType, u32 animId)
case B_ANIM_WISH_HEAL:
case B_ANIM_MEGA_EVOLUTION:
case B_ANIM_PRIMAL_REVERSION:
case B_ANIM_ULTRA_BURST:
case B_ANIM_GULP_MISSILE:
sAnimHideHpBoxes = TRUE;
break;

View File

@ -4761,6 +4761,18 @@ const struct SpriteTemplate gSpriteTemplate_BitterMaliceRing = {
.callback = AnimParticleInVortex
};
//ultra burst
const struct SpriteTemplate gUltraBurstSymbolSpriteTemplate =
{
.tileTag = ANIM_TAG_ULTRA_BURST_SYMBOL,
.paletteTag = ANIM_TAG_ULTRA_BURST_SYMBOL,
.oam = &gOamData_AffineDouble_ObjBlend_32x32,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gAffineAnims_LusterPurgeCircle,
.callback = AnimSpriteOnMonPos
};
// Z MOVES
//activate
const struct SpriteTemplate gZMoveSymbolSpriteTemplate =

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -22,160 +22,112 @@
#include "window.h"
#include "constants/battle_anim.h"
#include "constants/songs.h"
#include "constants/trainers.h"
#include "constants/rgb.h"
static void SafariHandleGetMonData(void);
static void SafariHandleGetRawMonData(void);
static void SafariHandleSetMonData(void);
static void SafariHandleSetRawMonData(void);
static void SafariHandleLoadMonSprite(void);
static void SafariHandleSwitchInAnim(void);
static void SafariHandleReturnMonToBall(void);
static void SafariHandleDrawTrainerPic(void);
static void SafariHandleTrainerSlide(void);
static void SafariHandleTrainerSlideBack(void);
static void SafariHandleFaintAnimation(void);
static void SafariHandlePaletteFade(void);
static void SafariHandleSuccessBallThrowAnim(void);
static void SafariHandleBallThrowAnim(void);
static void SafariHandlePause(void);
static void SafariHandleMoveAnimation(void);
static void SafariHandlePrintString(void);
static void SafariHandlePrintSelectionString(void);
static void SafariHandleChooseAction(void);
static void SafariHandleYesNoBox(void);
static void SafariHandleChooseMove(void);
static void SafariHandleChooseItem(void);
static void SafariHandleChoosePokemon(void);
static void SafariHandleCmd23(void);
static void SafariHandleHealthBarUpdate(void);
static void SafariHandleExpUpdate(void);
static void SafariHandleStatusIconUpdate(void);
static void SafariHandleStatusAnimation(void);
static void SafariHandleStatusXor(void);
static void SafariHandleDataTransfer(void);
static void SafariHandleDMA3Transfer(void);
static void SafariHandlePlayBGM(void);
static void SafariHandleCmd32(void);
static void SafariHandleTwoReturnValues(void);
static void SafariHandleChosenMonReturnValue(void);
static void SafariHandleOneReturnValue(void);
static void SafariHandleOneReturnValue_Duplicate(void);
static void SafariHandleClearUnkVar(void);
static void SafariHandleSetUnkVar(void);
static void SafariHandleClearUnkFlag(void);
static void SafariHandleToggleUnkFlag(void);
static void SafariHandleHitAnimation(void);
static void SafariHandleCantSwitch(void);
static void SafariHandlePlaySE(void);
static void SafariHandlePlayFanfareOrBGM(void);
static void SafariHandleFaintingCry(void);
static void SafariHandleIntroSlide(void);
static void SafariHandleIntroTrainerBallThrow(void);
static void SafariHandleDrawPartyStatusSummary(void);
static void SafariHandleHidePartyStatusSummary(void);
static void SafariHandleEndBounceEffect(void);
static void SafariHandleSpriteInvisibility(void);
static void SafariHandleBattleAnimation(void);
static void SafariHandleLinkStandbyMsg(void);
static void SafariHandleResetActionMoveSelection(void);
static void SafariHandleEndLinkBattle(void);
static void SafariHandleBattleDebug(void);
static void SafariCmdEnd(void);
static void SafariHandleDrawTrainerPic(u32 battler);
static void SafariHandleSuccessBallThrowAnim(u32 battler);
static void SafariHandleBallThrowAnim(u32 battler);
static void SafariHandlePrintString(u32 battler);
static void SafariHandlePrintSelectionString(u32 battler);
static void SafariHandleChooseAction(u32 battler);
static void SafariHandleChooseItem(u32 battler);
static void SafariHandleStatusIconUpdate(u32 battler);
static void SafariHandleFaintingCry(u32 battler);
static void SafariHandleIntroTrainerBallThrow(u32 battler);
static void SafariHandleBattleAnimation(u32 battler);
static void SafariHandleEndLinkBattle(u32 battler);
static void SafariBufferRunCommand(void);
static void SafariBufferExecCompleted(void);
static void CompleteWhenChosePokeblock(void);
static void SafariBufferRunCommand(u32 battler);
static void SafariBufferExecCompleted(u32 battler);
static void CompleteWhenChosePokeblock(u32 battler);
static void (*const sSafariBufferCommands[CONTROLLER_CMDS_COUNT])(void) =
static void (*const sSafariBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) =
{
[CONTROLLER_GETMONDATA] = SafariHandleGetMonData,
[CONTROLLER_GETRAWMONDATA] = SafariHandleGetRawMonData,
[CONTROLLER_SETMONDATA] = SafariHandleSetMonData,
[CONTROLLER_SETRAWMONDATA] = SafariHandleSetRawMonData,
[CONTROLLER_LOADMONSPRITE] = SafariHandleLoadMonSprite,
[CONTROLLER_SWITCHINANIM] = SafariHandleSwitchInAnim,
[CONTROLLER_RETURNMONTOBALL] = SafariHandleReturnMonToBall,
[CONTROLLER_GETMONDATA] = BtlController_Empty,
[CONTROLLER_GETRAWMONDATA] = BtlController_Empty,
[CONTROLLER_SETMONDATA] = BtlController_Empty,
[CONTROLLER_SETRAWMONDATA] = BtlController_Empty,
[CONTROLLER_LOADMONSPRITE] = BtlController_Empty,
[CONTROLLER_SWITCHINANIM] = BtlController_Empty,
[CONTROLLER_RETURNMONTOBALL] = BtlController_Empty,
[CONTROLLER_DRAWTRAINERPIC] = SafariHandleDrawTrainerPic,
[CONTROLLER_TRAINERSLIDE] = SafariHandleTrainerSlide,
[CONTROLLER_TRAINERSLIDEBACK] = SafariHandleTrainerSlideBack,
[CONTROLLER_FAINTANIMATION] = SafariHandleFaintAnimation,
[CONTROLLER_PALETTEFADE] = SafariHandlePaletteFade,
[CONTROLLER_TRAINERSLIDE] = BtlController_Empty,
[CONTROLLER_TRAINERSLIDEBACK] = BtlController_Empty,
[CONTROLLER_FAINTANIMATION] = BtlController_Empty,
[CONTROLLER_PALETTEFADE] = BtlController_Empty,
[CONTROLLER_SUCCESSBALLTHROWANIM] = SafariHandleSuccessBallThrowAnim,
[CONTROLLER_BALLTHROWANIM] = SafariHandleBallThrowAnim,
[CONTROLLER_PAUSE] = SafariHandlePause,
[CONTROLLER_MOVEANIMATION] = SafariHandleMoveAnimation,
[CONTROLLER_PAUSE] = BtlController_Empty,
[CONTROLLER_MOVEANIMATION] = BtlController_Empty,
[CONTROLLER_PRINTSTRING] = SafariHandlePrintString,
[CONTROLLER_PRINTSTRINGPLAYERONLY] = SafariHandlePrintSelectionString,
[CONTROLLER_CHOOSEACTION] = SafariHandleChooseAction,
[CONTROLLER_YESNOBOX] = SafariHandleYesNoBox,
[CONTROLLER_CHOOSEMOVE] = SafariHandleChooseMove,
[CONTROLLER_YESNOBOX] = BtlController_Empty,
[CONTROLLER_CHOOSEMOVE] = BtlController_Empty,
[CONTROLLER_OPENBAG] = SafariHandleChooseItem,
[CONTROLLER_CHOOSEPOKEMON] = SafariHandleChoosePokemon,
[CONTROLLER_23] = SafariHandleCmd23,
[CONTROLLER_HEALTHBARUPDATE] = SafariHandleHealthBarUpdate,
[CONTROLLER_EXPUPDATE] = SafariHandleExpUpdate,
[CONTROLLER_CHOOSEPOKEMON] = BtlController_Empty,
[CONTROLLER_23] = BtlController_Empty,
[CONTROLLER_HEALTHBARUPDATE] = BtlController_Empty,
[CONTROLLER_EXPUPDATE] = BtlController_Empty,
[CONTROLLER_STATUSICONUPDATE] = SafariHandleStatusIconUpdate,
[CONTROLLER_STATUSANIMATION] = SafariHandleStatusAnimation,
[CONTROLLER_STATUSXOR] = SafariHandleStatusXor,
[CONTROLLER_DATATRANSFER] = SafariHandleDataTransfer,
[CONTROLLER_DMA3TRANSFER] = SafariHandleDMA3Transfer,
[CONTROLLER_PLAYBGM] = SafariHandlePlayBGM,
[CONTROLLER_32] = SafariHandleCmd32,
[CONTROLLER_TWORETURNVALUES] = SafariHandleTwoReturnValues,
[CONTROLLER_CHOSENMONRETURNVALUE] = SafariHandleChosenMonReturnValue,
[CONTROLLER_ONERETURNVALUE] = SafariHandleOneReturnValue,
[CONTROLLER_ONERETURNVALUE_DUPLICATE] = SafariHandleOneReturnValue_Duplicate,
[CONTROLLER_CLEARUNKVAR] = SafariHandleClearUnkVar,
[CONTROLLER_SETUNKVAR] = SafariHandleSetUnkVar,
[CONTROLLER_CLEARUNKFLAG] = SafariHandleClearUnkFlag,
[CONTROLLER_TOGGLEUNKFLAG] = SafariHandleToggleUnkFlag,
[CONTROLLER_HITANIMATION] = SafariHandleHitAnimation,
[CONTROLLER_CANTSWITCH] = SafariHandleCantSwitch,
[CONTROLLER_PLAYSE] = SafariHandlePlaySE,
[CONTROLLER_PLAYFANFAREORBGM] = SafariHandlePlayFanfareOrBGM,
[CONTROLLER_STATUSANIMATION] = BtlController_Empty,
[CONTROLLER_STATUSXOR] = BtlController_Empty,
[CONTROLLER_DATATRANSFER] = BtlController_Empty,
[CONTROLLER_DMA3TRANSFER] = BtlController_Empty,
[CONTROLLER_PLAYBGM] = BtlController_Empty,
[CONTROLLER_32] = BtlController_Empty,
[CONTROLLER_TWORETURNVALUES] = BtlController_Empty,
[CONTROLLER_CHOSENMONRETURNVALUE] = BtlController_Empty,
[CONTROLLER_ONERETURNVALUE] = BtlController_Empty,
[CONTROLLER_ONERETURNVALUE_DUPLICATE] = BtlController_Empty,
[CONTROLLER_CLEARUNKVAR] = BtlController_Empty,
[CONTROLLER_SETUNKVAR] = BtlController_Empty,
[CONTROLLER_CLEARUNKFLAG] = BtlController_Empty,
[CONTROLLER_TOGGLEUNKFLAG] = BtlController_Empty,
[CONTROLLER_HITANIMATION] = BtlController_Empty,
[CONTROLLER_CANTSWITCH] = BtlController_Empty,
[CONTROLLER_PLAYSE] = BtlController_HandlePlaySE,
[CONTROLLER_PLAYFANFAREORBGM] = BtlController_HandlePlayFanfareOrBGM,
[CONTROLLER_FAINTINGCRY] = SafariHandleFaintingCry,
[CONTROLLER_INTROSLIDE] = SafariHandleIntroSlide,
[CONTROLLER_INTROSLIDE] = BtlController_HandleIntroSlide,
[CONTROLLER_INTROTRAINERBALLTHROW] = SafariHandleIntroTrainerBallThrow,
[CONTROLLER_DRAWPARTYSTATUSSUMMARY] = SafariHandleDrawPartyStatusSummary,
[CONTROLLER_HIDEPARTYSTATUSSUMMARY] = SafariHandleHidePartyStatusSummary,
[CONTROLLER_ENDBOUNCE] = SafariHandleEndBounceEffect,
[CONTROLLER_SPRITEINVISIBILITY] = SafariHandleSpriteInvisibility,
[CONTROLLER_DRAWPARTYSTATUSSUMMARY] = BtlController_Empty,
[CONTROLLER_HIDEPARTYSTATUSSUMMARY] = BtlController_Empty,
[CONTROLLER_ENDBOUNCE] = BtlController_Empty,
[CONTROLLER_SPRITEINVISIBILITY] = BtlController_Empty,
[CONTROLLER_BATTLEANIMATION] = SafariHandleBattleAnimation,
[CONTROLLER_LINKSTANDBYMSG] = SafariHandleLinkStandbyMsg,
[CONTROLLER_RESETACTIONMOVESELECTION] = SafariHandleResetActionMoveSelection,
[CONTROLLER_LINKSTANDBYMSG] = BtlController_Empty,
[CONTROLLER_RESETACTIONMOVESELECTION] = BtlController_Empty,
[CONTROLLER_ENDLINKBATTLE] = SafariHandleEndLinkBattle,
[CONTROLLER_DEBUGMENU] = SafariHandleBattleDebug,
[CONTROLLER_TERMINATOR_NOP] = SafariCmdEnd
[CONTROLLER_DEBUGMENU] = BtlController_Empty,
[CONTROLLER_TERMINATOR_NOP] = BtlController_TerminatorNop
};
static void SpriteCB_Null4(void)
void SetControllerToSafari(u32 battler)
{
gBattlerControllerEndFuncs[battler] = SafariBufferExecCompleted;
gBattlerControllerFuncs[battler] = SafariBufferRunCommand;
}
void SetControllerToSafari(void)
static void SafariBufferRunCommand(u32 battler)
{
gBattlerControllerFuncs[gActiveBattler] = SafariBufferRunCommand;
}
static void SafariBufferRunCommand(void)
{
if (gBattleControllerExecFlags & gBitTable[gActiveBattler])
if (gBattleControllerExecFlags & gBitTable[battler])
{
if (gBattleResources->bufferA[gActiveBattler][0] < ARRAY_COUNT(sSafariBufferCommands))
sSafariBufferCommands[gBattleResources->bufferA[gActiveBattler][0]]();
if (gBattleResources->bufferA[battler][0] < ARRAY_COUNT(sSafariBufferCommands))
sSafariBufferCommands[gBattleResources->bufferA[battler][0]](battler);
else
SafariBufferExecCompleted();
SafariBufferExecCompleted(battler);
}
}
static void HandleInputChooseAction(void)
static void HandleInputChooseAction(u32 battler)
{
if (JOY_NEW(A_BUTTON))
{
PlaySE(SE_SELECT);
switch (gActionSelectionCursor[gActiveBattler])
switch (gActionSelectionCursor[battler])
{
case 0:
BtlController_EmitTwoReturnValues(BUFFER_B, B_ACTION_SAFARI_BALL, 0);
@ -190,69 +142,57 @@ static void HandleInputChooseAction(void)
BtlController_EmitTwoReturnValues(BUFFER_B, B_ACTION_SAFARI_RUN, 0);
break;
}
SafariBufferExecCompleted();
SafariBufferExecCompleted(battler);
}
else if (JOY_NEW(DPAD_LEFT))
{
if (gActionSelectionCursor[gActiveBattler] & 1)
if (gActionSelectionCursor[battler] & 1)
{
PlaySE(SE_SELECT);
ActionSelectionDestroyCursorAt(gActionSelectionCursor[gActiveBattler]);
gActionSelectionCursor[gActiveBattler] ^= 1;
ActionSelectionCreateCursorAt(gActionSelectionCursor[gActiveBattler], 0);
ActionSelectionDestroyCursorAt(gActionSelectionCursor[battler]);
gActionSelectionCursor[battler] ^= 1;
ActionSelectionCreateCursorAt(gActionSelectionCursor[battler], 0);
}
}
else if (JOY_NEW(DPAD_RIGHT))
{
if (!(gActionSelectionCursor[gActiveBattler] & 1))
if (!(gActionSelectionCursor[battler] & 1))
{
PlaySE(SE_SELECT);
ActionSelectionDestroyCursorAt(gActionSelectionCursor[gActiveBattler]);
gActionSelectionCursor[gActiveBattler] ^= 1;
ActionSelectionCreateCursorAt(gActionSelectionCursor[gActiveBattler], 0);
ActionSelectionDestroyCursorAt(gActionSelectionCursor[battler]);
gActionSelectionCursor[battler] ^= 1;
ActionSelectionCreateCursorAt(gActionSelectionCursor[battler], 0);
}
}
else if (JOY_NEW(DPAD_UP))
{
if (gActionSelectionCursor[gActiveBattler] & 2)
if (gActionSelectionCursor[battler] & 2)
{
PlaySE(SE_SELECT);
ActionSelectionDestroyCursorAt(gActionSelectionCursor[gActiveBattler]);
gActionSelectionCursor[gActiveBattler] ^= 2;
ActionSelectionCreateCursorAt(gActionSelectionCursor[gActiveBattler], 0);
ActionSelectionDestroyCursorAt(gActionSelectionCursor[battler]);
gActionSelectionCursor[battler] ^= 2;
ActionSelectionCreateCursorAt(gActionSelectionCursor[battler], 0);
}
}
else if (JOY_NEW(DPAD_DOWN))
{
if (!(gActionSelectionCursor[gActiveBattler] & 2))
if (!(gActionSelectionCursor[battler] & 2))
{
PlaySE(SE_SELECT);
ActionSelectionDestroyCursorAt(gActionSelectionCursor[gActiveBattler]);
gActionSelectionCursor[gActiveBattler] ^= 2;
ActionSelectionCreateCursorAt(gActionSelectionCursor[gActiveBattler], 0);
ActionSelectionDestroyCursorAt(gActionSelectionCursor[battler]);
gActionSelectionCursor[battler] ^= 2;
ActionSelectionCreateCursorAt(gActionSelectionCursor[battler], 0);
}
}
}
static void CompleteOnBattlerSpriteCallbackDummy(void)
static void Controller_WaitForHealthbox(u32 battler)
{
if (gSprites[gBattlerSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy)
SafariBufferExecCompleted();
if (gSprites[gHealthboxSpriteIds[battler]].callback == SpriteCallbackDummy)
SafariBufferExecCompleted(battler);
}
static void CompleteOnInactiveTextPrinter(void)
{
if (!IsTextPrinterActive(B_WIN_MSG))
SafariBufferExecCompleted();
}
static void CompleteOnHealthboxSpriteCallbackDummy(void)
{
if (gSprites[gHealthboxSpriteIds[gActiveBattler]].callback == SpriteCallbackDummy)
SafariBufferExecCompleted();
}
static void SafariSetBattleEndCallbacks(void)
static void SafariSetBattleEndCallbacks(u32 battler)
{
if (!gPaletteFade.active)
{
@ -262,437 +202,141 @@ static void SafariSetBattleEndCallbacks(void)
}
}
static void CompleteOnSpecialAnimDone(void)
{
if (!gDoingBattleAnim || !gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].specialAnimActive)
SafariBufferExecCompleted();
}
static void SafariOpenPokeblockCase(void)
static void SafariOpenPokeblockCase(u32 battler)
{
if (!gPaletteFade.active)
{
gBattlerControllerFuncs[gActiveBattler] = CompleteWhenChosePokeblock;
gBattlerControllerFuncs[battler] = CompleteWhenChosePokeblock;
FreeAllWindowBuffers();
OpenPokeblockCaseInBattle();
}
}
static void CompleteWhenChosePokeblock(void)
static void CompleteWhenChosePokeblock(u32 battler)
{
if (gMain.callback2 == BattleMainCB2 && !gPaletteFade.active)
{
BtlController_EmitOneReturnValue(BUFFER_B, gSpecialVar_ItemId);
SafariBufferExecCompleted();
SafariBufferExecCompleted(battler);
}
}
static void CompleteOnFinishedBattleAnimation(void)
static void SafariBufferExecCompleted(u32 battler)
{
if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].animFromTableActive)
SafariBufferExecCompleted();
}
static void SafariBufferExecCompleted(void)
{
gBattlerControllerFuncs[gActiveBattler] = SafariBufferRunCommand;
gBattlerControllerFuncs[battler] = SafariBufferRunCommand;
if (gBattleTypeFlags & BATTLE_TYPE_LINK)
{
u8 playerId = GetMultiplayerId();
PrepareBufferDataTransferLink(2, 4, &playerId);
gBattleResources->bufferA[gActiveBattler][0] = CONTROLLER_TERMINATOR_NOP;
gBattleResources->bufferA[battler][0] = CONTROLLER_TERMINATOR_NOP;
}
else
{
gBattleControllerExecFlags &= ~gBitTable[gActiveBattler];
gBattleControllerExecFlags &= ~gBitTable[battler];
}
}
static void CompleteOnFinishedStatusAnimation(void)
static void SafariHandleDrawTrainerPic(u32 battler)
{
if (!gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].statusAnimActive)
SafariBufferExecCompleted();
u32 trainerPicId = gSaveBlock2Ptr->playerGender + TRAINER_BACK_PIC_BRENDAN;
BtlController_HandleDrawTrainerPic(battler, trainerPicId, FALSE,
80, 80 + 4 * (8 - gTrainerBackPicCoords[trainerPicId].size),
30);
}
static void SafariHandleGetMonData(void)
static void SafariHandleSuccessBallThrowAnim(u32 battler)
{
SafariBufferExecCompleted();
BtlController_HandleSuccessBallThrowAnim(battler, GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), B_ANIM_BALL_THROW_WITH_TRAINER, FALSE);
}
static void SafariHandleGetRawMonData(void)
static void SafariHandleBallThrowAnim(u32 battler)
{
SafariBufferExecCompleted();
BtlController_HandleBallThrowAnim(battler, GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), B_ANIM_BALL_THROW_WITH_TRAINER, FALSE);
}
static void SafariHandleSetMonData(void)
static void SafariHandlePrintString(u32 battler)
{
SafariBufferExecCompleted();
BtlController_HandlePrintString(battler, FALSE, FALSE);
}
static void SafariHandleSetRawMonData(void)
static void SafariHandlePrintSelectionString(u32 battler)
{
SafariBufferExecCompleted();
}
static void SafariHandleLoadMonSprite(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleSwitchInAnim(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleReturnMonToBall(void)
{
SafariBufferExecCompleted();
}
#define sSpeedX data[0]
static void SafariHandleDrawTrainerPic(void)
{
DecompressTrainerBackPic(gSaveBlock2Ptr->playerGender, gActiveBattler);
SetMultiuseSpriteTemplateToTrainerBack(gSaveBlock2Ptr->playerGender, GetBattlerPosition(gActiveBattler));
gBattlerSpriteIds[gActiveBattler] = CreateSprite(
&gMultiuseSpriteTemplate,
80,
(8 - gTrainerBackPicCoords[gSaveBlock2Ptr->playerGender].size) * 4 + 80,
30);
gSprites[gBattlerSpriteIds[gActiveBattler]].oam.paletteNum = gActiveBattler;
gSprites[gBattlerSpriteIds[gActiveBattler]].x2 = DISPLAY_WIDTH;
gSprites[gBattlerSpriteIds[gActiveBattler]].sSpeedX = -2;
gSprites[gBattlerSpriteIds[gActiveBattler]].callback = SpriteCB_TrainerSlideIn;
gBattlerControllerFuncs[gActiveBattler] = CompleteOnBattlerSpriteCallbackDummy;
}
#undef sSpeedX
static void SafariHandleTrainerSlide(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleTrainerSlideBack(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleFaintAnimation(void)
{
SafariBufferExecCompleted();
}
static void SafariHandlePaletteFade(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleSuccessBallThrowAnim(void)
{
gBattleSpritesDataPtr->animationData->ballThrowCaseId = BALL_3_SHAKES_SUCCESS;
gDoingBattleAnim = TRUE;
InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), B_ANIM_BALL_THROW_WITH_TRAINER);
gBattlerControllerFuncs[gActiveBattler] = CompleteOnSpecialAnimDone;
}
static void SafariHandleBallThrowAnim(void)
{
u8 ballThrowCaseId = gBattleResources->bufferA[gActiveBattler][1];
gBattleSpritesDataPtr->animationData->ballThrowCaseId = ballThrowCaseId;
gDoingBattleAnim = TRUE;
InitAndLaunchSpecialAnimation(gActiveBattler, gActiveBattler, GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT), B_ANIM_BALL_THROW_WITH_TRAINER);
gBattlerControllerFuncs[gActiveBattler] = CompleteOnSpecialAnimDone;
}
static void SafariHandlePause(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleMoveAnimation(void)
{
SafariBufferExecCompleted();
}
static void SafariHandlePrintString(void)
{
u16 *stringId;
gBattle_BG0_X = 0;
gBattle_BG0_Y = 0;
stringId = (u16 *)(&gBattleResources->bufferA[gActiveBattler][2]);
BufferStringBattle(*stringId);
BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MSG);
gBattlerControllerFuncs[gActiveBattler] = CompleteOnInactiveTextPrinter;
}
static void SafariHandlePrintSelectionString(void)
{
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
SafariHandlePrintString();
if (GetBattlerSide(battler) == B_SIDE_PLAYER)
SafariHandlePrintString(battler);
else
SafariBufferExecCompleted();
SafariBufferExecCompleted(battler);
}
static void HandleChooseActionAfterDma3(void)
static void HandleChooseActionAfterDma3(u32 battler)
{
if (!IsDma3ManagerBusyWithBgCopy())
{
gBattle_BG0_X = 0;
gBattle_BG0_Y = DISPLAY_HEIGHT;
gBattlerControllerFuncs[gActiveBattler] = HandleInputChooseAction;
gBattlerControllerFuncs[battler] = HandleInputChooseAction;
}
}
static void SafariHandleChooseAction(void)
static void SafariHandleChooseAction(u32 battler)
{
s32 i;
gBattlerControllerFuncs[gActiveBattler] = HandleChooseActionAfterDma3;
gBattlerControllerFuncs[battler] = HandleChooseActionAfterDma3;
BattlePutTextOnWindow(gText_SafariZoneMenu, B_WIN_ACTION_MENU);
for (i = 0; i < 4; i++)
ActionSelectionDestroyCursorAt(i);
ActionSelectionCreateCursorAt(gActionSelectionCursor[gActiveBattler], 0);
ActionSelectionCreateCursorAt(gActionSelectionCursor[battler], 0);
BattleStringExpandPlaceholdersToDisplayedString(gText_WhatWillPkmnDo2);
BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_ACTION_PROMPT);
}
static void SafariHandleYesNoBox(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleChooseMove(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleChooseItem(void)
static void SafariHandleChooseItem(u32 battler)
{
BeginNormalPaletteFade(PALETTES_ALL, 0, 0, 0x10, RGB_BLACK);
gBattlerControllerFuncs[gActiveBattler] = SafariOpenPokeblockCase;
gBattlerInMenuId = gActiveBattler;
gBattlerControllerFuncs[battler] = SafariOpenPokeblockCase;
gBattlerInMenuId = battler;
}
static void SafariHandleChoosePokemon(void)
static void SafariHandleStatusIconUpdate(u32 battler)
{
SafariBufferExecCompleted();
UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &gPlayerParty[gBattlerPartyIndexes[battler]], HEALTHBOX_SAFARI_BALLS_TEXT);
SafariBufferExecCompleted(battler);
}
static void SafariHandleCmd23(void)
// All of the other controllers(except Wally's) use CRY_MODE_FAINT.
// Player is not a pokemon, so it can't really faint in the Safari anyway.
static void SafariHandleFaintingCry(u32 battler)
{
SafariBufferExecCompleted();
}
static void SafariHandleHealthBarUpdate(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleExpUpdate(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleStatusIconUpdate(void)
{
UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler], &gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], HEALTHBOX_SAFARI_BALLS_TEXT);
SafariBufferExecCompleted();
}
static void SafariHandleStatusAnimation(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleStatusXor(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleDataTransfer(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleDMA3Transfer(void)
{
SafariBufferExecCompleted();
}
static void SafariHandlePlayBGM(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleCmd32(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleTwoReturnValues(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleChosenMonReturnValue(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleOneReturnValue(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleOneReturnValue_Duplicate(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleClearUnkVar(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleSetUnkVar(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleClearUnkFlag(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleToggleUnkFlag(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleHitAnimation(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleCantSwitch(void)
{
SafariBufferExecCompleted();
}
static void SafariHandlePlaySE(void)
{
s8 pan;
if (GetBattlerSide(gActiveBattler) == B_SIDE_PLAYER)
pan = SOUND_PAN_ATTACKER;
else
pan = SOUND_PAN_TARGET;
PlaySE12WithPanning(gBattleResources->bufferA[gActiveBattler][1] | (gBattleResources->bufferA[gActiveBattler][2] << 8), pan);
SafariBufferExecCompleted();
}
static void SafariHandlePlayFanfareOrBGM(void)
{
if (gBattleResources->bufferA[gActiveBattler][3])
{
BattleStopLowHpSound();
PlayBGM(gBattleResources->bufferA[gActiveBattler][1] | (gBattleResources->bufferA[gActiveBattler][2] << 8));
}
else
{
PlayFanfare(gBattleResources->bufferA[gActiveBattler][1] | (gBattleResources->bufferA[gActiveBattler][2] << 8));
}
SafariBufferExecCompleted();
}
static void SafariHandleFaintingCry(void)
{
u16 species = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_SPECIES);
u16 species = GetMonData(&gPlayerParty[gBattlerPartyIndexes[battler]], MON_DATA_SPECIES);
PlayCry_Normal(species, 25);
SafariBufferExecCompleted();
SafariBufferExecCompleted(battler);
SafariBufferExecCompleted(battler);
}
static void SafariHandleIntroSlide(void)
static void SafariHandleIntroTrainerBallThrow(u32 battler)
{
HandleIntroSlide(gBattleResources->bufferA[gActiveBattler][1]);
gIntroSlideFlags |= 1;
SafariBufferExecCompleted();
UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], &gPlayerParty[gBattlerPartyIndexes[battler]], HEALTHBOX_SAFARI_ALL_TEXT);
StartHealthboxSlideIn(battler);
SetHealthboxSpriteVisible(gHealthboxSpriteIds[battler]);
gBattlerControllerFuncs[battler] = Controller_WaitForHealthbox;
}
static void SafariHandleIntroTrainerBallThrow(void)
static void SafariHandleBattleAnimation(u32 battler)
{
UpdateHealthboxAttribute(gHealthboxSpriteIds[gActiveBattler], &gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], HEALTHBOX_SAFARI_ALL_TEXT);
StartHealthboxSlideIn(gActiveBattler);
SetHealthboxSpriteVisible(gHealthboxSpriteIds[gActiveBattler]);
gBattlerControllerFuncs[gActiveBattler] = CompleteOnHealthboxSpriteCallbackDummy;
BtlController_HandleBattleAnimation(battler, TRUE, FALSE);
}
static void SafariHandleDrawPartyStatusSummary(void)
static void SafariHandleEndLinkBattle(u32 battler)
{
SafariBufferExecCompleted();
}
static void SafariHandleHidePartyStatusSummary(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleEndBounceEffect(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleSpriteInvisibility(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleBattleAnimation(void)
{
u8 animationId = gBattleResources->bufferA[gActiveBattler][1];
u16 argument = gBattleResources->bufferA[gActiveBattler][2] | (gBattleResources->bufferA[gActiveBattler][3] << 8);
if (TryHandleLaunchBattleTableAnimation(gActiveBattler, gActiveBattler, gActiveBattler, animationId, argument))
SafariBufferExecCompleted();
else
gBattlerControllerFuncs[gActiveBattler] = CompleteOnFinishedBattleAnimation;
}
static void SafariHandleLinkStandbyMsg(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleResetActionMoveSelection(void)
{
SafariBufferExecCompleted();
}
static void SafariHandleEndLinkBattle(void)
{
gBattleOutcome = gBattleResources->bufferA[gActiveBattler][1];
gBattleOutcome = gBattleResources->bufferA[battler][1];
FadeOutMapMusic(5);
BeginFastPaletteFade(3);
SafariBufferExecCompleted();
SafariBufferExecCompleted(battler);
if ((gBattleTypeFlags & BATTLE_TYPE_LINK) && !(gBattleTypeFlags & BATTLE_TYPE_IS_MASTER))
gBattlerControllerFuncs[gActiveBattler] = SafariSetBattleEndCallbacks;
}
static void SafariHandleBattleDebug(void)
{
SafariBufferExecCompleted();
}
static void SafariCmdEnd(void)
{
gBattlerControllerFuncs[battler] = SafariSetBattleEndCallbacks;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -157,8 +157,6 @@ static void BufferLastDomeWinnerName(void);
static void InitRandomTourneyTreeResults(void);
static void InitDomeTrainers(void);
EWRAM_DATA u32 gPlayerPartyLostHP = 0; // never read
static EWRAM_DATA u32 sPlayerPartyMaxHP = 0; // never read
static EWRAM_DATA struct TourneyTreeInfoCard *sInfoCard = {0};
static EWRAM_DATA u8 *sTilemapBuffer = NULL;
@ -2544,9 +2542,6 @@ static void BufferDomeOpponentName(void)
static void InitDomeOpponentParty(void)
{
gPlayerPartyLostHP = 0;
sPlayerPartyMaxHP = GetMonData(&gPlayerParty[0], MON_DATA_MAX_HP, NULL);
sPlayerPartyMaxHP += GetMonData(&gPlayerParty[1], MON_DATA_MAX_HP, NULL);
CalculatePlayerPartyCount();
CreateDomeOpponentMons(TrainerIdToTournamentId(gTrainerBattleOpponent_A));
}

View File

@ -528,7 +528,7 @@ bool8 IsBattleSEPlaying(u8 battlerId)
if (IsSEPlaying())
{
gBattleSpritesDataPtr->healthBoxesData[battlerId].soundTimer++;
if (gBattleSpritesDataPtr->healthBoxesData[gActiveBattler].soundTimer < 30)
if (gBattleSpritesDataPtr->healthBoxesData[battlerId].soundTimer < 30)
return TRUE;
m4aMPlayStop(&gMPlayInfo_SE1);
@ -544,7 +544,7 @@ bool8 IsBattleSEPlaying(u8 battlerId)
return TRUE;
}
static void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battlerId, bool32 opponent)
void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battlerId)
{
u32 monsPersonality, currentPersonality, otId, currentOtId, species, paletteOffset, position;
const void *lzPaletteData;
@ -577,7 +577,7 @@ static void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battlerId, bool32 op
}
position = GetBattlerPosition(battlerId);
if (opponent)
if (GetBattlerSide(battlerId) == B_SIDE_OPPONENT)
{
HandleLoadSpecialPokePic(TRUE,
gMonSpritesGfxPtr->sprites.ptr[position],
@ -609,16 +609,6 @@ static void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battlerId, bool32 op
}
}
void BattleLoadOpponentMonSpriteGfx(struct Pokemon *mon, u8 battlerId)
{
BattleLoadMonSpriteGfx(mon, battlerId, TRUE);
}
void BattleLoadPlayerMonSpriteGfx(struct Pokemon *mon, u8 battlerId)
{
BattleLoadMonSpriteGfx(mon, battlerId, FALSE);
}
void BattleGfxSfxDummy2(u16 species)
{
}
@ -642,10 +632,6 @@ void DecompressTrainerBackPic(u16 backPicId, u8 battlerId)
OBJ_PLTT_ID(battlerId), PLTT_SIZE_4BPP);
}
void BattleGfxSfxDummy3(u8 gender)
{
}
void FreeTrainerFrontPicPalette(u16 frontPicId)
{
FreeSpritePaletteByTag(gTrainerFrontPicPaletteTable[frontPicId].tag);
@ -968,12 +954,7 @@ void BattleLoadSubstituteOrMonSpriteGfx(u8 battlerId, bool8 loadMonSprite)
else
{
if (!IsContest())
{
if (GetBattlerSide(battlerId) != B_SIDE_PLAYER)
BattleLoadOpponentMonSpriteGfx(&gEnemyParty[gBattlerPartyIndexes[battlerId]], battlerId);
else
BattleLoadPlayerMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[battlerId]], battlerId);
}
BattleLoadMonSpriteGfx(&GetBattlerParty(battlerId)[gBattlerPartyIndexes[battlerId]], battlerId);
}
}

View File

@ -194,6 +194,7 @@ static void SpriteCB_StatusSummaryBalls_Exit(struct Sprite *);
static void SpriteCB_StatusSummaryBalls_OnSwitchout(struct Sprite *);
static void SpriteCb_MegaTrigger(struct Sprite *);
static void SpriteCb_BurstTrigger(struct Sprite *);
static void MegaIndicator_SetVisibilities(u32 healthboxId, bool32 invisible);
static void MegaIndicator_UpdateLevel(u32 healthboxId, u32 level);
static void MegaIndicator_CreateSprite(u32 battlerId, u32 healthboxSpriteId);
@ -676,6 +677,64 @@ static const struct SpriteTemplate sSpriteTemplate_MegaTrigger =
.callback = SpriteCb_MegaTrigger
};
static const u8 ALIGNED(4) sBurstTriggerGfx[] = INCBIN_U8("graphics/battle_interface/burst_trigger.4bpp");
static const u16 sBurstTriggerPal[] = INCBIN_U16("graphics/battle_interface/burst_trigger.gbapal");
static const struct SpriteSheet sSpriteSheet_BurstTrigger =
{
sBurstTriggerGfx, sizeof(sBurstTriggerGfx), TAG_BURST_TRIGGER_TILE
};
static const struct SpritePalette sSpritePalette_BurstTrigger =
{
sBurstTriggerPal, TAG_BURST_TRIGGER_PAL
};
static const struct OamData sOamData_BurstTrigger =
{
.y = 0,
.affineMode = 0,
.objMode = 0,
.mosaic = 0,
.bpp = 0,
.shape = ST_OAM_SQUARE,
.x = 0,
.matrixNum = 0,
.size = 2,
.tileNum = 0,
.priority = 1,
.paletteNum = 0,
.affineParam = 0,
};
static const union AnimCmd sSpriteAnim_BurstTriggerOff[] =
{
ANIMCMD_FRAME(0, 0),
ANIMCMD_END
};
static const union AnimCmd sSpriteAnim_BurstTriggerOn[] =
{
ANIMCMD_FRAME(16, 0),
ANIMCMD_END
};
static const union AnimCmd *const sSpriteAnimTable_BurstTrigger[] =
{
sSpriteAnim_BurstTriggerOff,
sSpriteAnim_BurstTriggerOn,
};
static const struct SpriteTemplate sSpriteTemplate_BurstTrigger =
{
.tileTag = TAG_BURST_TRIGGER_TILE,
.paletteTag = TAG_BURST_TRIGGER_PAL,
.oam = &sOamData_BurstTrigger,
.anims = sSpriteAnimTable_BurstTrigger,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_BurstTrigger
};
// Because the healthbox is too large to fit into one sprite, it is divided into two sprites.
// healthboxLeft or healthboxMain is the left part that is used as the 'main' sprite.
// healthboxRight or healthboxOther is the right part of the healthbox.
@ -1418,6 +1477,7 @@ void HideMegaTriggerSprite(void)
void HideTriggerSprites(void)
{
HideMegaTriggerSprite();
HideBurstTriggerSprite();
HideZMoveTriggerSprite();
}
@ -1433,6 +1493,128 @@ void DestroyMegaTriggerSprite(void)
#undef tBattler
#undef tHide
// Ultra Burst Trigger icon functions.
void ChangeBurstTriggerSprite(u8 spriteId, u8 animId)
{
StartSpriteAnim(&gSprites[spriteId], animId);
}
#define SINGLES_BURST_TRIGGER_POS_X_OPTIMAL (30)
#define SINGLES_BURST_TRIGGER_POS_X_PRIORITY (31)
#define SINGLES_BURST_TRIGGER_POS_X_SLIDE (15)
#define SINGLES_BURST_TRIGGER_POS_Y_DIFF (-11)
#define DOUBLES_BURST_TRIGGER_POS_X_OPTIMAL (30)
#define DOUBLES_BURST_TRIGGER_POS_X_PRIORITY (31)
#define DOUBLES_BURST_TRIGGER_POS_X_SLIDE (15)
#define DOUBLES_BURST_TRIGGER_POS_Y_DIFF (-4)
#define tBattler data[0]
#define tHide data[1]
void CreateBurstTriggerSprite(u8 battlerId, u8 palId)
{
LoadSpritePalette(&sSpritePalette_BurstTrigger);
if (GetSpriteTileStartByTag(TAG_BURST_TRIGGER_TILE) == 0xFFFF)
LoadSpriteSheet(&sSpriteSheet_BurstTrigger);
if (gBattleStruct->burst.triggerSpriteId == 0xFF)
{
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
gBattleStruct->burst.triggerSpriteId = CreateSprite(&sSpriteTemplate_BurstTrigger,
gSprites[gHealthboxSpriteIds[battlerId]].x - DOUBLES_BURST_TRIGGER_POS_X_SLIDE,
gSprites[gHealthboxSpriteIds[battlerId]].y - DOUBLES_BURST_TRIGGER_POS_Y_DIFF, 0);
else
gBattleStruct->burst.triggerSpriteId = CreateSprite(&sSpriteTemplate_BurstTrigger,
gSprites[gHealthboxSpriteIds[battlerId]].x - SINGLES_BURST_TRIGGER_POS_X_SLIDE,
gSprites[gHealthboxSpriteIds[battlerId]].y - SINGLES_BURST_TRIGGER_POS_Y_DIFF, 0);
}
gSprites[gBattleStruct->burst.triggerSpriteId].tBattler = battlerId;
gSprites[gBattleStruct->burst.triggerSpriteId].tHide = FALSE;
ChangeBurstTriggerSprite(gBattleStruct->burst.triggerSpriteId, palId);
}
static void SpriteCb_BurstTrigger(struct Sprite *sprite)
{
s32 xSlide, xPriority, xOptimal;
s32 yDiff;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
xSlide = DOUBLES_BURST_TRIGGER_POS_X_SLIDE;
xPriority = DOUBLES_BURST_TRIGGER_POS_X_PRIORITY;
xOptimal = DOUBLES_BURST_TRIGGER_POS_X_OPTIMAL;
yDiff = DOUBLES_BURST_TRIGGER_POS_Y_DIFF;
}
else
{
xSlide = SINGLES_BURST_TRIGGER_POS_X_SLIDE;
xPriority = SINGLES_BURST_TRIGGER_POS_X_PRIORITY;
xOptimal = SINGLES_BURST_TRIGGER_POS_X_OPTIMAL;
yDiff = SINGLES_BURST_TRIGGER_POS_Y_DIFF;
}
if (sprite->tHide)
{
if (sprite->x != gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xSlide)
sprite->x++;
if (sprite->x >= gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xPriority)
sprite->oam.priority = 2;
else
sprite->oam.priority = 1;
sprite->y = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y - yDiff;
sprite->y2 = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y2 - yDiff;
if (sprite->x == gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xSlide)
DestroyBurstTriggerSprite();
}
else
{
if (sprite->x != gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xOptimal)
sprite->x--;
if (sprite->x >= gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xPriority)
sprite->oam.priority = 2;
else
sprite->oam.priority = 1;
sprite->y = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y - yDiff;
sprite->y2 = gSprites[gHealthboxSpriteIds[sprite->tBattler]].y2 - yDiff;
}
}
bool32 IsBurstTriggerSpriteActive(void)
{
if (GetSpriteTileStartByTag(TAG_BURST_TRIGGER_TILE) == 0xFFFF)
return FALSE;
else if (IndexOfSpritePaletteTag(TAG_BURST_TRIGGER_PAL) != 0xFF)
return TRUE;
else
return FALSE;
}
void HideBurstTriggerSprite(void)
{
if (gBattleStruct->burst.triggerSpriteId >= MAX_SPRITES)
return;
ChangeBurstTriggerSprite(gBattleStruct->burst.triggerSpriteId, 0);
gSprites[gBattleStruct->burst.triggerSpriteId].tHide = TRUE;
}
void DestroyBurstTriggerSprite(void)
{
FreeSpritePaletteByTag(TAG_BURST_TRIGGER_PAL);
FreeSpriteTilesByTag(TAG_BURST_TRIGGER_TILE);
if (gBattleStruct->burst.triggerSpriteId != 0xFF)
DestroySprite(&gSprites[gBattleStruct->burst.triggerSpriteId]);
gBattleStruct->burst.triggerSpriteId = 0xFF;
}
#undef tBattler
#undef tHide
// Code for Mega Evolution (And Alpha/Omega) Trigger icon visible on the battler's healthbox.
enum
{

View File

@ -251,11 +251,9 @@ void (*gPreBattleCallback1)(void);
void (*gBattleMainFunc)(void);
struct BattleResults gBattleResults;
u8 gLeveledUpInBattle;
void (*gBattlerControllerFuncs[MAX_BATTLERS_COUNT])(void);
u8 gHealthboxSpriteIds[MAX_BATTLERS_COUNT];
u8 gMultiUsePlayerCursor;
u8 gNumberOfMovesToChoose;
u8 gBattleControllerData[MAX_BATTLERS_COUNT]; // Used by the battle controllers to store misc sprite/task IDs for each battler
static const struct ScanlineEffectParams sIntroScanlineParams16Bit =
{
@ -567,8 +565,8 @@ static void CB2_InitBattleInternal(void)
gBattle_BG3_X = 0;
gBattle_BG3_Y = 0;
#if DEBUG_OVERWORLD_MENU == FALSE
#if DEBUG_OVERWORLD_MENU == FALSE
gBattleTerrain = BattleSetup_GetTerrainId();
#else
if (!gIsDebugBattle)
@ -596,7 +594,7 @@ static void CB2_InitBattleInternal(void)
else
SetMainCallback2(CB2_HandleStartBattle);
#if DEBUG_OVERWORLD_MENU == FALSE
#if DEBUG_OVERWORLD_MENU == FALSE
if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED)))
{
CreateNPCTrainerParty(&gEnemyParty[0], gTrainerBattleOpponent_A, TRUE);
@ -2743,8 +2741,6 @@ void SpriteCB_FaintOpponentMon(struct Sprite *sprite)
else
species = sprite->sSpeciesId;
GetMonData(&gEnemyParty[gBattlerPartyIndexes[battler]], MON_DATA_PERSONALITY); // Unused return value.
if (species == SPECIES_UNOWN)
{
species = GetUnownSpeciesId(personality);
@ -3006,7 +3002,7 @@ static void BattleMainCB1(void)
gBattleMainFunc();
for (gActiveBattler = 0; gActiveBattler < gBattlersCount; gActiveBattler++)
gBattlerControllerFuncs[gActiveBattler]();
gBattlerControllerFuncs[gActiveBattler](gActiveBattler);
}
static void BattleStartClearSetData(void)
@ -3114,6 +3110,7 @@ static void BattleStartClearSetData(void)
gBattleStruct->arenaLostOpponentMons = 0;
gBattleStruct->mega.triggerSpriteId = 0xFF;
gBattleStruct->burst.triggerSpriteId = 0xFF;
for (i = 0; i < ARRAY_COUNT(gSideTimers); i++)
{
@ -4289,6 +4286,7 @@ static void HandleTurnActionSelectionState(void)
}
gBattleStruct->mega.toEvolve &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(gActiveBattler))]);
gBattleStruct->burst.toBurst &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(gActiveBattler))]);
gBattleStruct->zmove.toBeUsed[BATTLE_PARTNER(GetBattlerPosition(gActiveBattler))] = MOVE_NONE;
BtlController_EmitEndBounceEffect(BUFFER_A);
MarkBattlerForControllerExec(gActiveBattler);
@ -4375,11 +4373,13 @@ static void HandleTurnActionSelectionState(void)
RecordedBattle_SetBattlerAction(gActiveBattler, gBattleResources->bufferB[gActiveBattler][2]);
RecordedBattle_SetBattlerAction(gActiveBattler, gBattleResources->bufferB[gActiveBattler][3]);
}
*(gBattleStruct->chosenMovePositions + gActiveBattler) = gBattleResources->bufferB[gActiveBattler][2] & ~RET_MEGA_EVOLUTION;
*(gBattleStruct->chosenMovePositions + gActiveBattler) = gBattleResources->bufferB[gActiveBattler][2] & ~(RET_MEGA_EVOLUTION | RET_ULTRA_BURST);
gChosenMoveByBattler[gActiveBattler] = gBattleMons[gActiveBattler].moves[*(gBattleStruct->chosenMovePositions + gActiveBattler)];
*(gBattleStruct->moveTarget + gActiveBattler) = gBattleResources->bufferB[gActiveBattler][3];
if (gBattleResources->bufferB[gActiveBattler][2] & RET_MEGA_EVOLUTION)
gBattleStruct->mega.toEvolve |= gBitTable[gActiveBattler];
else if (gBattleResources->bufferB[gActiveBattler][2] & RET_ULTRA_BURST)
gBattleStruct->burst.toBurst |= gBitTable[gActiveBattler];
gBattleCommunication[gActiveBattler]++;
}
break;
@ -4997,7 +4997,7 @@ static void PopulateArrayWithBattlers(u8 *battlers)
static bool32 TryDoMegaEvosBeforeMoves(void)
{
if (!(gHitMarker & HITMARKER_RUN) && gBattleStruct->mega.toEvolve)
if (!(gHitMarker & HITMARKER_RUN) && (gBattleStruct->mega.toEvolve || gBattleStruct->burst.toBurst))
{
u32 i;
struct Pokemon *party;
@ -5022,6 +5022,18 @@ static bool32 TryDoMegaEvosBeforeMoves(void)
BattleScriptExecute(BattleScript_MegaEvolution);
return TRUE;
}
if (gBattleStruct->burst.toBurst & gBitTable[megaOrder[i]]
&& !(gProtectStructs[megaOrder[i]].noValidMoves))
{
gActiveBattler = gBattlerAttacker = megaOrder[i];
gBattleStruct->burst.toBurst &= ~(gBitTable[gActiveBattler]);
gLastUsedItem = gBattleMons[gActiveBattler].item;
party = GetBattlerParty(gActiveBattler);
mon = &party[gBattlerPartyIndexes[gActiveBattler]];
BattleScriptExecute(BattleScript_UltraBurst);
return TRUE;
}
}
}

View File

@ -800,6 +800,9 @@ static const u8 sText_ItemRestoredSpeciesPP[] = _("{B_BUFF1} had its\nPP restore
static const u8 sText_AtkTrappedDef[] = _("{B_ATK_NAME_WITH_PREFIX} trapped\nthe {B_DEF_NAME_WITH_PREFIX}!");
static const u8 sText_MirrorHerbCopied[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} used its {B_LAST_ITEM}\nto mirror its opponent's stat changes!");
static const u8 sText_PkmnItemMelted[] = _("{B_ATK_NAME_WITH_PREFIX} corroded\n{B_DEF_NAME_WITH_PREFIX}'s {B_LAST_ITEM}!");
static const u8 sText_UltraBurstReacting[] = _("Bright light is about to\nburst out of {B_ATK_NAME_WITH_PREFIX}!");
static const u8 sText_UltraBurstCompleted[] = _("{B_ATK_NAME_WITH_PREFIX} regained its\ntrue power through Ultra Burst!");
const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
{
@ -1456,6 +1459,8 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
[STRINGID_PKMNFROSTBITEHEALED - BATTLESTRINGS_TABLE_START] = sText_PkmnFrostbiteHealed,
[STRINGID_PKMNFROSTBITEHEALED2 - BATTLESTRINGS_TABLE_START] = sText_PkmnFrostbiteHealed2,
[STRINGID_PKMNFROSTBITEHEALEDBY - BATTLESTRINGS_TABLE_START] = sText_PkmnFrostbiteHealedBy,
[STRINGID_ULTRABURSTREACTING - BATTLESTRINGS_TABLE_START] = sText_UltraBurstReacting,
[STRINGID_ULTRABURSTCOMPLETED - BATTLESTRINGS_TABLE_START] = sText_UltraBurstCompleted,
};
const u16 gTrainerUsedItemStringIds[] =

View File

@ -4998,7 +4998,8 @@ static void Cmd_playanimation(void)
|| animId == B_ANIM_ILLUSION_OFF
|| animId == B_ANIM_FORM_CHANGE
|| animId == B_ANIM_SUBSTITUTE_FADE
|| animId == B_ANIM_PRIMAL_REVERSION)
|| animId == B_ANIM_PRIMAL_REVERSION
|| animId == B_ANIM_ULTRA_BURST)
{
BtlController_EmitBattleAnimation(BUFFER_A, animId, *argPtr);
MarkBattlerForControllerExec(gActiveBattler);
@ -5049,7 +5050,8 @@ static void Cmd_playanimation_var(void)
|| *animIdPtr == B_ANIM_ILLUSION_OFF
|| *animIdPtr == B_ANIM_FORM_CHANGE
|| *animIdPtr == B_ANIM_SUBSTITUTE_FADE
|| *animIdPtr == B_ANIM_PRIMAL_REVERSION)
|| *animIdPtr == B_ANIM_PRIMAL_REVERSION
|| *animIdPtr == B_ANIM_ULTRA_BURST)
{
BtlController_EmitBattleAnimation(BUFFER_A, *animIdPtr, *argPtr);
MarkBattlerForControllerExec(gActiveBattler);
@ -8561,7 +8563,7 @@ static bool32 CourtChangeSwapSideStatuses(void)
SWAP(sideTimerPlayer->stickyWebBattlerSide, sideTimerOpp->stickyWebBattlerSide, temp);
}
static void HandleScriptMegaPrimal(u32 caseId, u32 battlerId, bool32 isMega)
static void HandleScriptMegaPrimalBurst(u32 caseId, u32 battlerId, u32 type)
{
struct Pokemon *party = GetBattlerParty(battlerId);
struct Pokemon *mon = &party[gBattlerPartyIndexes[battlerId]];
@ -8571,13 +8573,15 @@ static void HandleScriptMegaPrimal(u32 caseId, u32 battlerId, bool32 isMega)
// Change species.
if (caseId == 0)
{
if (isMega)
if (type == HANDLE_TYPE_MEGA_EVOLUTION)
{
if (!TryBattleFormChange(battlerId, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM))
TryBattleFormChange(battlerId, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE);
}
else
else if (type == HANDLE_TYPE_PRIMAL_REVERSION)
TryBattleFormChange(battlerId, FORM_CHANGE_BATTLE_PRIMAL_REVERSION);
else
TryBattleFormChange(battlerId, FORM_CHANGE_BATTLE_ULTRA_BURST);
PREPARE_SPECIES_BUFFER(gBattleTextBuff1, gBattleMons[battlerId].species);
@ -8590,8 +8594,10 @@ static void HandleScriptMegaPrimal(u32 caseId, u32 battlerId, bool32 isMega)
UpdateHealthboxAttribute(gHealthboxSpriteIds[battlerId], mon, HEALTHBOX_ALL);
if (side == B_SIDE_OPPONENT)
SetBattlerShadowSpriteCallback(battlerId, gBattleMons[battlerId].species);
if (isMega)
if (type == HANDLE_TYPE_MEGA_EVOLUTION)
gBattleStruct->mega.alreadyEvolved[position] = TRUE;
if (type == HANDLE_TYPE_ULTRA_BURST)
gBattleStruct->burst.alreadyBursted[position] = TRUE;
}
}
@ -9621,14 +9627,21 @@ static void Cmd_various(void)
case VARIOUS_HANDLE_MEGA_EVO:
{
VARIOUS_ARGS(u8 case_);
HandleScriptMegaPrimal(cmd->case_, gActiveBattler, TRUE);
HandleScriptMegaPrimalBurst(cmd->case_, gActiveBattler, HANDLE_TYPE_MEGA_EVOLUTION);
gBattlescriptCurrInstr = cmd->nextInstr;
return;
}
case VARIOUS_HANDLE_PRIMAL_REVERSION:
{
VARIOUS_ARGS(u8 case_);
HandleScriptMegaPrimal(cmd->case_, gActiveBattler, FALSE);
HandleScriptMegaPrimalBurst(cmd->case_, gActiveBattler, HANDLE_TYPE_PRIMAL_REVERSION);
gBattlescriptCurrInstr = cmd->nextInstr;
return;
}
case VARIOUS_HANDLE_ULTRA_BURST:
{
VARIOUS_ARGS(u8 case_);
HandleScriptMegaPrimalBurst(cmd->case_, gActiveBattler, HANDLE_TYPE_ULTRA_BURST);
gBattlescriptCurrInstr = cmd->nextInstr;
return;
}
@ -9961,13 +9974,13 @@ static void Cmd_various(void)
if (IsBattlerAlive(gActiveBattler))
{
SetBattlerShadowSpriteCallback(gActiveBattler, gBattleMons[gActiveBattler].species);
BattleLoadOpponentMonSpriteGfx(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler);
BattleLoadMonSpriteGfx(&gEnemyParty[gBattlerPartyIndexes[gActiveBattler]], gActiveBattler);
}
i = BATTLE_PARTNER(gActiveBattler);
if (IsBattlerAlive(i))
{
SetBattlerShadowSpriteCallback(i, gBattleMons[i].species);
BattleLoadOpponentMonSpriteGfx(&gEnemyParty[gBattlerPartyIndexes[i]], i);
BattleLoadMonSpriteGfx(&gEnemyParty[gBattlerPartyIndexes[i]], i);
}
}
gBattlescriptCurrInstr = cmd->nextInstr;

View File

@ -1612,7 +1612,7 @@ static bool32 IsBelchPreventingMove(u32 battler, u32 move)
u8 TrySetCantSelectMoveBattleScript(void)
{
u32 limitations = 0;
u8 moveId = gBattleResources->bufferB[gActiveBattler][2] & ~RET_MEGA_EVOLUTION;
u8 moveId = gBattleResources->bufferB[gActiveBattler][2] & ~(RET_MEGA_EVOLUTION | RET_ULTRA_BURST);
u32 move = gBattleMons[gActiveBattler].moves[moveId];
u32 holdEffect = GetBattlerHoldEffect(gActiveBattler, TRUE);
u16 *choicedMove = &gBattleStruct->choicedMove[gActiveBattler];
@ -1884,7 +1884,7 @@ u8 CheckMoveLimitations(u8 battlerId, u8 unusableMoves, u16 check)
else if (check & MOVE_LIMITATION_THROAT_CHOP && gDisableStructs[battlerId].throatChopTimer && gBattleMoves[gBattleMons[battlerId].moves[i]].soundMove)
unusableMoves |= gBitTable[i];
// Stuff Cheeks
else if (check & MOVE_LIMITATION_STUFF_CHEEKS && gBattleMons[battlerId].moves[i] == MOVE_STUFF_CHEEKS && ItemId_GetPocket(gBattleMons[gActiveBattler].item) != POCKET_BERRIES)
else if (check & MOVE_LIMITATION_STUFF_CHEEKS && gBattleMons[battlerId].moves[i] == MOVE_STUFF_CHEEKS && ItemId_GetPocket(gBattleMons[battlerId].item) != POCKET_BERRIES)
unusableMoves |= gBitTable[i];
// Gorilla Tactics
else if (check & MOVE_LIMITATION_CHOICE_ITEM && GetBattlerAbility(battlerId) == ABILITY_GORILLA_TACTICS && *choicedMove != MOVE_NONE && *choicedMove != MOVE_UNAVAILABLE && *choicedMove != gBattleMons[battlerId].moves[i])
@ -4903,7 +4903,7 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
&& gBattleResults.catchAttempts[gLastUsedBall - ITEM_ULTRA_BALL] >= 1
&& !gHasFetchedBall)
{
gBattleScripting.battler = battler;
gBattleScripting.battler = gActiveBattler = battler;
BtlController_EmitSetMonData(BUFFER_A, REQUEST_HELDITEM_BATTLE, 0, 2, &gLastUsedBall);
MarkBattlerForControllerExec(battler);
gHasFetchedBall = TRUE;
@ -10116,6 +10116,7 @@ bool32 DoesSpeciesUseHoldItemToChangeForm(u16 species, u16 heldItemId)
{
case FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM:
case FORM_CHANGE_BATTLE_PRIMAL_REVERSION:
case FORM_CHANGE_BATTLE_ULTRA_BURST:
case FORM_CHANGE_ITEM_HOLD:
if (formChanges[i].param1 == heldItemId)
return TRUE;
@ -10190,6 +10191,61 @@ bool32 CanMegaEvolve(u8 battlerId)
return FALSE;
}
bool32 CanUltraBurst(u8 battlerId)
{
u32 itemId, holdEffect, species;
struct Pokemon *mon;
u8 battlerPosition = GetBattlerPosition(battlerId);
u8 partnerPosition = GetBattlerPosition(BATTLE_PARTNER(battlerId));
// Check if Player has a Z Ring
if ((GetBattlerPosition(battlerId) == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battlerId) == B_POSITION_PLAYER_RIGHT))
&& !CheckBagHasItem(ITEM_Z_POWER_RING, 1))
return FALSE;
// Check if trainer already ultra bursted a pokemon.
if (gBattleStruct->burst.alreadyBursted[battlerPosition])
return FALSE;
// Cannot use z move and ultra burst on same turn
if (gBattleStruct->zmove.toBeUsed[battlerId])
return FALSE;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
&& IsPartnerMonFromSameTrainer(battlerId)
&& (gBattleStruct->burst.alreadyBursted[partnerPosition] || (gBattleStruct->burst.toBurst & gBitTable[BATTLE_PARTNER(battlerId)])))
return FALSE;
// Check if mon is currently held by Sky Drop
if (gStatuses3[battlerId] & STATUS3_SKY_DROPPED)
return FALSE;
// Gets mon data.
if (GetBattlerSide(battlerId) == B_SIDE_OPPONENT)
mon = &gEnemyParty[gBattlerPartyIndexes[battlerId]];
else
mon = &gPlayerParty[gBattlerPartyIndexes[battlerId]];
species = GetMonData(mon, MON_DATA_SPECIES);
itemId = GetMonData(mon, MON_DATA_HELD_ITEM);
// Check if there is an entry in the evolution table for Ultra Burst.
if (GetBattleFormChangeTargetSpecies(battlerId, FORM_CHANGE_BATTLE_ULTRA_BURST) != SPECIES_NONE)
{
if (itemId == ITEM_ENIGMA_BERRY_E_READER)
holdEffect = gEnigmaBerries[battlerId].holdEffect;
else
holdEffect = ItemId_GetHoldEffect(itemId);
// Can Ultra Burst via Z Crystal.
if (holdEffect == HOLD_EFFECT_Z_CRYSTAL)
return TRUE;
}
// No checks passed, the mon CAN'T ultra burst.
return FALSE;
}
bool32 IsBattlerMegaEvolved(u8 battlerId)
{
// While Transform does copy stats and visuals, it shouldn't be counted as true Mega Evolution.
@ -10206,6 +10262,14 @@ bool32 IsBattlerPrimalReverted(u8 battlerId)
return (gSpeciesInfo[gBattleMons[battlerId].species].flags & SPECIES_FLAG_PRIMAL_REVERSION);
}
bool32 IsBattlerUltraBursted(u8 battlerId)
{
// While Transform does copy stats and visuals, it shouldn't be counted as true Ultra Burst.
if (gBattleMons[battlerId].status2 & STATUS2_TRANSFORMED)
return FALSE;
return (gSpeciesInfo[gBattleMons[battlerId].species].flags & SPECIES_FLAG_ULTRA_BURST);
}
// Returns SPECIES_NONE if no form change is possible
u16 GetBattleFormChangeTargetSpecies(u8 battlerId, u16 method)
{
@ -10229,6 +10293,7 @@ u16 GetBattleFormChangeTargetSpecies(u8 battlerId, u16 method)
{
case FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM:
case FORM_CHANGE_BATTLE_PRIMAL_REVERSION:
case FORM_CHANGE_BATTLE_ULTRA_BURST:
if (heldItem == formChanges[i].param1)
targetSpecies = formChanges[i].targetSpecies;
break;
@ -10299,8 +10364,8 @@ bool32 CanBattlerFormChange(u8 battlerId, u16 method)
if (gBattleMons[battlerId].status2 & STATUS2_TRANSFORMED
&& B_TRANSFORM_FORM_CHANGES >= GEN_5)
return FALSE;
// Mega Evolved Pokémon should always revert to normal upon fainting or ending the battle.
if (IsBattlerMegaEvolved(battlerId) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE))
// Mega Evolved and Ultra Bursted Pokémon should always revert to normal upon fainting or ending the battle.
if ((IsBattlerMegaEvolved(battlerId) || IsBattlerUltraBursted(battlerId)) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE))
return TRUE;
else if (IsBattlerPrimalReverted(battlerId) && (method == FORM_CHANGE_END_BATTLE))
return TRUE;
@ -10336,8 +10401,8 @@ bool32 TryBattleFormChange(u8 battlerId, u16 method)
{
bool8 restoreSpecies = FALSE;
// Mega Evolved Pokémon should always revert to normal upon fainting or ending the battle, so no need to add it to the form change tables.
if (IsBattlerMegaEvolved(battlerId) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE))
// Mega Evolved and Ultra Bursted Pokémon should always revert to normal upon fainting or ending the battle, so no need to add it to the form change tables.
if ((IsBattlerMegaEvolved(battlerId) || IsBattlerUltraBursted(battlerId)) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE))
restoreSpecies = TRUE;
// Unlike Megas, Primal Reversion isn't canceled on fainting.
@ -10782,7 +10847,7 @@ void TrySaveExchangedItem(u8 battlerId, u16 stolenItem)
bool32 IsBattlerAffectedByHazards(u8 battlerId, bool32 toxicSpikes)
{
bool32 ret = TRUE;
u32 holdEffect = GetBattlerHoldEffect(gActiveBattler, TRUE);
u32 holdEffect = GetBattlerHoldEffect(battlerId, TRUE);
if (toxicSpikes && holdEffect == HOLD_EFFECT_HEAVY_DUTY_BOOTS && !IS_BATTLER_OF_TYPE(battlerId, TYPE_POISON))
{
ret = FALSE;

View File

@ -210,6 +210,8 @@ const struct FormChange *const gFormChangeTablePointers[NUM_SPECIES] =
[SPECIES_MINIOR_CORE_VIOLET] = sMiniorVioletFormChangeTable,
[SPECIES_MINIOR_METEOR_YELLOW] = sMiniorYellowFormChangeTable,
[SPECIES_MINIOR_CORE_YELLOW] = sMiniorYellowFormChangeTable,
[SPECIES_NECROZMA_DUSK_MANE] = sNecrozmaDuskManeFormChangeTable,
[SPECIES_NECROZMA_DAWN_WINGS] = sNecrozmaDawnWingsFormChangeTable,
#endif
#if P_GEN_8_POKEMON == TRUE
[SPECIES_CRAMORANT] = sCramorantFormChangeTable,

View File

@ -548,6 +548,14 @@ static const struct FormChange sMiniorYellowFormChangeTable[] = {
{FORM_CHANGE_END_BATTLE, SPECIES_MINIOR_CORE_YELLOW},
{FORM_CHANGE_TERMINATOR},
};
static const struct FormChange sNecrozmaDuskManeFormChangeTable[] = {
{FORM_CHANGE_BATTLE_ULTRA_BURST, SPECIES_NECROZMA_ULTRA, ITEM_ULTRANECROZIUM_Z},
{FORM_CHANGE_TERMINATOR},
};
static const struct FormChange sNecrozmaDawnWingsFormChangeTable[] = {
{FORM_CHANGE_BATTLE_ULTRA_BURST, SPECIES_NECROZMA_ULTRA, ITEM_ULTRANECROZIUM_Z},
{FORM_CHANGE_TERMINATOR},
};
#endif
#if P_GEN_8_POKEMON == TRUE

View File

@ -24484,7 +24484,7 @@ const struct SpeciesInfo gSpeciesInfo[] =
.abilities = {ABILITY_NEUROFORCE, ABILITY_NONE},
.bodyColor = BODY_COLOR_YELLOW,
.noFlip = TRUE,
.flags = SPECIES_FLAG_LEGENDARY,
.flags = SPECIES_FLAG_LEGENDARY | SPECIES_FLAG_ULTRA_BURST,
},
[SPECIES_MAGEARNA_ORIGINAL_COLOR] = MAGEARNA_SPECIES_INFO(BODY_COLOR_RED),

View File

@ -187,7 +187,7 @@ static bool8 LoadBattlerSpriteGfx(u8 battler)
if (GetBattlerSide(battler) != B_SIDE_PLAYER)
{
if (!gBattleSpritesDataPtr->battlerData[battler].behindSubstitute)
BattleLoadOpponentMonSpriteGfx(&gEnemyParty[gBattlerPartyIndexes[battler]], battler);
BattleLoadMonSpriteGfx(&gEnemyParty[gBattlerPartyIndexes[battler]], battler);
else
BattleLoadSubstituteOrMonSpriteGfx(battler, FALSE);
}
@ -196,7 +196,7 @@ static bool8 LoadBattlerSpriteGfx(u8 battler)
else if (gBattleTypeFlags & BATTLE_TYPE_WALLY_TUTORIAL && battler == B_POSITION_PLAYER_LEFT) // Should be checking position, not battler.
DecompressTrainerBackPic(TRAINER_BACK_PIC_WALLY, battler);
else if (!gBattleSpritesDataPtr->battlerData[battler].behindSubstitute)
BattleLoadPlayerMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[battler]], battler);
BattleLoadMonSpriteGfx(&gPlayerParty[gBattlerPartyIndexes[battler]], battler);
else
BattleLoadSubstituteOrMonSpriteGfx(battler, FALSE);

View File

@ -37,6 +37,7 @@ gReservedSpritePaletteCount:
.include "link_rfu_2.o"
.include "rtc.o"
.include "battle_main.o"
.include "battle_controllers.o"
.include "random.o"
.include "load_save.o"
.include "berry_blender.o"

View File

@ -0,0 +1,127 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Dusk Mane Necrozma can Ultra Burst holding Ultranecrozium Z")
{
GIVEN {
ASSUME(P_GEN_7_POKEMON == TRUE);
PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, ultraBurst: TRUE); }
} SCENE {
MESSAGE("Bright light is about to burst out of Necrozma!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ULTRA_BURST, player);
MESSAGE("Necrozma regained its true power through Ultra Burst!");
} THEN {
EXPECT_EQ(player->species, SPECIES_NECROZMA_ULTRA);
}
}
DOUBLE_BATTLE_TEST("Ultra Burst's order is determined by Speed - opponent faster")
{
GIVEN {
ASSUME(P_GEN_7_POKEMON == TRUE);
PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); Speed(1); }
PLAYER(SPECIES_WOBBUFFET) { Speed(3); }
OPPONENT(SPECIES_NECROZMA_DAWN_WINGS) { Item(ITEM_ULTRANECROZIUM_Z); Speed(3); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(4); }
} WHEN {
TURN { MOVE(opponentLeft, MOVE_CELEBRATE, ultraBurst: TRUE); MOVE(playerLeft, MOVE_CELEBRATE, ultraBurst: TRUE); }
} SCENE {
MESSAGE("Bright light is about to burst out of Foe Necrozma!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ULTRA_BURST, opponentLeft);
MESSAGE("Foe Necrozma regained its true power through Ultra Burst!");
MESSAGE("Bright light is about to burst out of Necrozma!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ULTRA_BURST, playerLeft);
MESSAGE("Necrozma regained its true power through Ultra Burst!");
}
}
DOUBLE_BATTLE_TEST("Ultra Burst's order is determined by Speed - player faster")
{
GIVEN {
ASSUME(P_GEN_7_POKEMON == TRUE);
PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); Speed(5); }
PLAYER(SPECIES_WOBBUFFET) { Speed(3); }
OPPONENT(SPECIES_NECROZMA_DAWN_WINGS) { Item(ITEM_ULTRANECROZIUM_Z); Speed(2); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(4); }
} WHEN {
TURN { MOVE(opponentLeft, MOVE_CELEBRATE, ultraBurst: TRUE); MOVE(playerLeft, MOVE_CELEBRATE, ultraBurst: TRUE); }
} SCENE {
MESSAGE("Bright light is about to burst out of Necrozma!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ULTRA_BURST, playerLeft);
MESSAGE("Necrozma regained its true power through Ultra Burst!");
MESSAGE("Bright light is about to burst out of Foe Necrozma!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ULTRA_BURST, opponentLeft);
MESSAGE("Foe Necrozma regained its true power through Ultra Burst!");
}
}
SINGLE_BATTLE_TEST("Ultra Burst affects turn order")
{
GIVEN {
ASSUME(P_GEN_7_POKEMON == TRUE);
ASSUME(B_MEGA_EVO_TURN_ORDER);
PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); Speed(105); }
OPPONENT(SPECIES_WOBBUFFET) { Speed(106); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, ultraBurst: TRUE); }
} SCENE {
MESSAGE("Necrozma used Celebrate!");
MESSAGE("Foe Wobbuffet used Celebrate!");
} THEN {
ASSUME(player->speed == 263);
}
}
DOUBLE_BATTLE_TEST("Ultra Burst happens after switching, but before Focus Punch-like Moves")
{
GIVEN {
ASSUME(P_GEN_7_POKEMON == TRUE);
ASSUME(gBattleMoves[MOVE_FOCUS_PUNCH].effect == EFFECT_FOCUS_PUNCH);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); }
OPPONENT(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { SWITCH(opponentRight, 2); MOVE(playerRight, MOVE_FOCUS_PUNCH, ultraBurst: TRUE, target: opponentLeft); MOVE(playerLeft, MOVE_FOCUS_PUNCH, target: opponentLeft); }
TURN {}
} SCENE {
MESSAGE("2 withdrew Wobbuffet!");
MESSAGE("2 sent out Wobbuffet!");
MESSAGE("Bright light is about to burst out of Necrozma!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ULTRA_BURST, playerRight);
MESSAGE("Necrozma regained its true power through Ultra Burst!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FOCUS_PUNCH_SETUP, playerRight);
MESSAGE("Necrozma is tightening its focus!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_FOCUS_PUNCH_SETUP, playerLeft);
MESSAGE("Wobbuffet is tightening its focus!");
}
}
SINGLE_BATTLE_TEST("Ultra Burst and Mega Evolution can happen on the same turn")
{
GIVEN {
ASSUME(P_GEN_7_POKEMON == TRUE);
PLAYER(SPECIES_NECROZMA_DUSK_MANE) { Item(ITEM_ULTRANECROZIUM_Z); Speed(3); }
OPPONENT(SPECIES_GARDEVOIR) { Item(ITEM_GARDEVOIRITE); Speed(2); }
} WHEN {
TURN { MOVE(player, MOVE_CELEBRATE, ultraBurst: TRUE); MOVE(opponent, MOVE_CELEBRATE, megaEvolve: TRUE); }
} SCENE {
MESSAGE("Bright light is about to burst out of Necrozma!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_ULTRA_BURST, player);
MESSAGE("Necrozma regained its true power through Ultra Burst!");
MESSAGE("Foe Gardevoir's Gardevoirite is reacting to 's Mega Ring!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_MEGA_EVOLUTION, opponent);
MESSAGE("Foe Gardevoir has Mega Evolved into Mega Gardevoir!");
} THEN {
EXPECT_EQ(player->species, SPECIES_NECROZMA_ULTRA);
EXPECT_EQ(opponent->species, SPECIES_GARDEVOIR_MEGA);
}
}

View File

@ -1483,6 +1483,9 @@ void Move(u32 sourceLine, struct BattlePokemon *battler, struct MoveContext ctx)
if (ctx.explicitMegaEvolve && ctx.megaEvolve)
moveSlot |= RET_MEGA_EVOLUTION;
if (ctx.explicitUltraBurst && ctx.ultraBurst)
moveSlot |= RET_ULTRA_BURST;
if (ctx.explicitTarget)
{
target = ctx.target - gBattleMons;