merge w upcoming

This commit is contained in:
ghoulslash 2024-07-08 14:12:00 -04:00
commit 48d2bff553
186 changed files with 6623 additions and 3643 deletions

View File

@ -26,8 +26,18 @@ jobs:
run: |
cd docs
mdbook build
- name: Check if Pages is enabled
uses: octokit/request-action@v2.x
id: check_pages
continue-on-error: true
with:
route: GET /repos/{repo}/pages
repo: ${{ github.repository }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Setup Pages
uses: actions/configure-pages@v4
if: steps.check_pages.outcome == 'success'
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
@ -35,3 +45,4 @@ jobs:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
if: steps.check_pages.outcome == 'success'

View File

@ -475,6 +475,13 @@ devkitARM is now installed.
Then proceed to [Choosing where to store pokeemerald Expansion (Linux)](#choosing-where-to-store-pokeemerald-expansion-linux).
### NixOS
Run the following command to start an interactive shell with the necessary packages:
```bash
nix-shell -p pkgsCross.arm-embedded.stdenv.cc git pkg-config libpng
```
Then proceed to [Choosing where to store pokeemerald Expansion (Linux)](#choosing-where-to-store-pokeemerald-expansion-linux).
### Other distributions
_(Specific instructions for other distributions would be greatly appreciated!)_

View File

@ -80,7 +80,7 @@ MODERN_ELF_NAME := $(MODERN_ROM_NAME:.gba=.elf)
MODERN_MAP_NAME := $(MODERN_ROM_NAME:.gba=.map)
MODERN_OBJ_DIR_NAME := build/modern
SHELL := /bin/bash -o pipefail
SHELL := bash -o pipefail
ELF = $(ROM:.gba=.elf)
MAP = $(ROM:.gba=.map)

View File

@ -1672,6 +1672,10 @@
callnative BS_ApplyTerastallization
.endm
.macro damagetoquartertargethp
callnative BS_DamageToQuarterTargetHP
.endm
@ various command changed to more readable macros
.macro cancelmultiturnmoves battler:req
various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES

View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
if [[ -d "$DEVKITARM/bin/" ]]; then
OBJDUMP_BIN="$DEVKITARM/bin/arm-none-eabi-objdump"

View File

@ -1,4 +1,4 @@
#include "config.h"
#include "config/general.h"
#include "config/battle.h"
#include "constants/battle.h"
#include "constants/battle_anim.h"
@ -4221,7 +4221,6 @@ Move_SMACK_DOWN::
createvisualtask AnimTask_SmokescreenImpact, 0x8, 0x400, 0x1902
fadetobg BG_IN_AIR
waitbgfadeout
createvisualtask AnimTask_StartSlidingBg, 5, 0x0, 0x0, 0x0, 0xffff
createvisualtask AnimTask_SeismicTossBgAccelerateDownAtEnd, 3
goto SeismicTossWeak

View File

@ -1,4 +1,4 @@
#include "config.h"
#include "config/general.h"
#include "config/battle.h"
#include "constants/global.h"
#include "constants/battle.h"
@ -20,6 +20,16 @@
.section script_data, "aw", %progbits
BattleScript_DamageToQuarterTargetHP::
attackcanceler
accuracycheck BattleScript_PrintMoveMissed, ACC_CURR_MOVE
attackstring
ppreduce
typecalc
bichalfword gMoveResultFlags, MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE
damagetoquartertargethp
goto BattleScript_HitFromAtkAnimation
BattleScript_Terastallization::
@ TODO: no string prints in S/V, but right now this helps with clarity
printstring STRINGID_PKMNSTORINGENERGY
@ -5496,7 +5506,7 @@ BattleScript_GiveExp::
BattleScript_HandleFaintedMon::
setbyte sSHIFT_SWITCHED, 0
checkteamslost BattleScript_LinkHandleFaintedMonMultiple
checkteamslost BattleScript_HandleFaintedMonMultiple
jumpifbyte CMP_NOT_EQUAL, gBattleOutcome, 0, BattleScript_FaintedMonEnd
jumpifbattletype BATTLE_TYPE_TRAINER | BATTLE_TYPE_DOUBLE, BattleScript_FaintedMonTryChoose
jumpifword CMP_NO_COMMON_BITS, gHitMarker, HITMARKER_PLAYER_FAINTED, BattleScript_FaintedMonTryChoose
@ -5577,13 +5587,13 @@ BattleScript_FaintedMonShiftSwitched:
copybyte gBattlerTarget, sSAVED_BATTLER
goto BattleScript_FaintedMonSendOutNewEnd
BattleScript_LinkHandleFaintedMonMultiple::
openpartyscreen BS_FAINTED_LINK_MULTIPLE_1, BattleScript_LinkHandleFaintedMonMultipleStart
BattleScript_LinkHandleFaintedMonMultipleStart::
BattleScript_HandleFaintedMonMultiple::
openpartyscreen BS_FAINTED_MULTIPLE_1, BattleScript_HandleFaintedMonMultipleStart
BattleScript_HandleFaintedMonMultipleStart::
switchhandleorder BS_FAINTED, 0
openpartyscreen BS_FAINTED_LINK_MULTIPLE_2, BattleScript_LinkHandleFaintedMonMultipleEnd
openpartyscreen BS_FAINTED_MULTIPLE_2, BattleScript_HandleFaintedMonMultipleEnd
switchhandleorder BS_FAINTED, 0
BattleScript_LinkHandleFaintedMonLoop::
BattleScript_HandleFaintedMonLoop::
switchhandleorder BS_FAINTED, 3
drawpartystatussummary BS_FAINTED
getswitchedmondata BS_FAINTED
@ -5595,9 +5605,10 @@ BattleScript_LinkHandleFaintedMonLoop::
hidepartystatussummary BS_FAINTED
switchinanim BS_FAINTED, FALSE
waitstate
switchineffects BS_FAINTED_LINK_MULTIPLE_1
jumpifbytenotequal gBattlerFainted, gBattlersCount, BattleScript_LinkHandleFaintedMonLoop
BattleScript_LinkHandleFaintedMonMultipleEnd::
switchineffects BS_FAINTED_MULTIPLE_1
jumpifbytenotequal gBattlerFainted, gBattlersCount, BattleScript_HandleFaintedMonLoop
BattleScript_HandleFaintedMonMultipleEnd::
switchineffects BS_FAINTED_MULTIPLE_2
end2
BattleScript_LocalTrainerBattleWon::
@ -5828,6 +5839,7 @@ BattleScript_DoSwitchOut::
BattleScript_PursuitDmgOnSwitchOut::
pause B_WAIT_TIME_SHORT
orword gHitMarker, HITMARKER_OBEYS
attackstring
ppreduce
critcalc
@ -5845,11 +5857,12 @@ BattleScript_PursuitDmgOnSwitchOut::
resultmessage
waitmessage B_WAIT_TIME_LONG
tryfaintmon BS_TARGET
moveendfromto MOVEEND_ABILITIES, MOVEEND_CHOICE_MOVE
moveendfromto MOVEEND_ABILITIES, MOVEEND_ATTACKER_INVISIBLE @ MOVEEND_CHOICE_MOVE has to be included
jumpiffainted BS_TARGET, FALSE, BattleScript_PursuitDmgOnSwitchOutRet
setbyte sGIVEEXP_STATE, 0
getexp BS_TARGET
BattleScript_PursuitDmgOnSwitchOutRet:
bicword gHitMarker, HITMARKER_OBEYS
return
BattleScript_Pausex20::
@ -6034,6 +6047,10 @@ BattleScript_MagicRoomEnds::
waitmessage B_WAIT_TIME_LONG
end2
BattleScript_GrassyTerrainEnds::
call BattleScript_GrassyTerrainHeals_Ret
goto BattleScript_TerrainEnds
BattleScript_TerrainEnds_Ret::
printfromtable gTerrainStringIds
waitmessage B_WAIT_TIME_LONG
@ -6526,6 +6543,9 @@ BattleScript_PerishSongCountGoesDown::
waitmessage B_WAIT_TIME_LONG
end2
BattleScript_AllStatsUpZMove::
printfromtable gZEffectStringIds
waitmessage B_WAIT_TIME_LONG
BattleScript_AllStatsUp::
jumpifstat BS_ATTACKER, CMP_LESS_THAN, STAT_ATK, MAX_STAT_STAGE, BattleScript_AllStatsUpAtk
jumpifstat BS_ATTACKER, CMP_LESS_THAN, STAT_DEF, MAX_STAT_STAGE, BattleScript_AllStatsUpAtk
@ -6984,13 +7004,13 @@ BattleScript_MegaEvolution::
BattleScript_MegaEvolutionAfterString:
waitmessage B_WAIT_TIME_LONG
setbyte gIsCriticalHit, 0
handlemegaevo BS_ATTACKER, 0
playanimation BS_ATTACKER, B_ANIM_MEGA_EVOLUTION
handlemegaevo BS_SCRIPTING, 0
playanimation BS_SCRIPTING, B_ANIM_MEGA_EVOLUTION
waitanimation
handlemegaevo BS_ATTACKER, 1
handlemegaevo BS_SCRIPTING, 1
printstring STRINGID_MEGAEVOEVOLVED
waitmessage B_WAIT_TIME_LONG
switchinabilities BS_ATTACKER
switchinabilities BS_SCRIPTING
end3
BattleScript_WishMegaEvolution::
@ -7027,13 +7047,13 @@ BattleScript_UltraBurst::
printstring STRINGID_ULTRABURSTREACTING
waitmessage B_WAIT_TIME_LONG
setbyte gIsCriticalHit, 0
handleultraburst BS_ATTACKER, 0
playanimation BS_ATTACKER, B_ANIM_ULTRA_BURST
handleultraburst BS_SCRIPTING, 0
playanimation BS_SCRIPTING, B_ANIM_ULTRA_BURST
waitanimation
handleultraburst BS_ATTACKER, 1
handleultraburst BS_SCRIPTING, 1
printstring STRINGID_ULTRABURSTCOMPLETED
waitmessage B_WAIT_TIME_LONG
switchinabilities BS_ATTACKER
switchinabilities BS_SCRIPTING
end3
BattleScript_GulpMissileFormChange::
@ -8342,6 +8362,10 @@ BattleScript_MoveUsedPsychicTerrainPrevents::
goto BattleScript_MoveEnd
BattleScript_GrassyTerrainHeals::
call BattleScript_GrassyTerrainHeals_Ret
end2
BattleScript_GrassyTerrainHeals_Ret::
setbyte gBattleCommunication, 0
BattleScript_GrassyTerrainLoop:
copyarraywithindex gBattlerAttacker, gBattlerByTurnOrder, gBattleCommunication, 1
@ -8359,7 +8383,7 @@ BattleScript_GrassyTerrainLoopEnd::
bicword gHitMarker, HITMARKER_IGNORE_BIDE | HITMARKER_IGNORE_SUBSTITUTE | HITMARKER_PASSIVE_DAMAGE
jumpifword CMP_COMMON_BITS, gFieldStatuses, STATUS_FIELD_TERRAIN_PERMANENT, BattleScript_GrassyTerrainHealEnd
BattleScript_GrassyTerrainHealEnd:
end2
return
BattleScript_AbilityNoSpecificStatLoss::
pause B_WAIT_TIME_SHORT
@ -8700,7 +8724,7 @@ BattleScript_SynchronizeActivates::
return
BattleScript_NoItemSteal::
pause B_WAIT_TIME_SHORT
call BattleScript_AbilityPopUpTarget
printstring STRINGID_PKMNSXMADEYINEFFECTIVE
waitmessage B_WAIT_TIME_LONG
return

View File

@ -1,4 +1,4 @@
#include "config.h"
#include "config/general.h"
#include "constants/global.h"
#include "constants/contest.h"
.include "asm/macros.inc"

View File

@ -1,4 +1,4 @@
#include "config.h"
#include "config/general.h"
#include "config/battle.h"
#include "config/item.h"
#include "constants/global.h"

View File

@ -313,6 +313,9 @@ MossdeepCity_SpaceCenter_2F_EventScript_DefeatedMaxieTabitha::
setobjectmovementtype LOCALID_SCIENTIST, MOVEMENT_TYPE_WANDER_AROUND
addobject LOCALID_SCIENTIST
fadescreen FADE_FROM_BLACK
#ifdef BUGFIX
releaseall
#endif
end
MossdeepCity_SpaceCenter_2F_EventScript_StevenFacePlayer::

Binary file not shown.

Before

Width:  |  Height:  |  Size: 314 B

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 B

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 451 B

After

Width:  |  Height:  |  Size: 388 B

View File

@ -15,5 +15,5 @@ JASC-PAL
248 136 136
88 104 96
184 192 192
0 0 0
248 248 248
0 0 0

View File

@ -14,6 +14,6 @@ JASC-PAL
248 224 40
248 136 136
88 104 96
184 192 192
0 0 0
200 192 128
247 240 184
0 0 0

Binary file not shown.

Before

Width:  |  Height:  |  Size: 708 B

After

Width:  |  Height:  |  Size: 620 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 666 B

After

Width:  |  Height:  |  Size: 571 B

View File

@ -10,8 +10,8 @@ JASC-PAL
120 24 24
232 56 40
136 120 104
224 216 208
192 176 160
248 248 248
200 192 176
200 160 80
232 208 136
248 248 248

Binary file not shown.

Before

Width:  |  Height:  |  Size: 837 B

After

Width:  |  Height:  |  Size: 816 B

View File

@ -15,5 +15,5 @@ JASC-PAL
248 160 72
112 72 24
176 112 48
0 0 0
0 0 0
8 64 88
176 168 176

Binary file not shown.

Before

Width:  |  Height:  |  Size: 898 B

After

Width:  |  Height:  |  Size: 820 B

View File

@ -16,6 +16,7 @@
#include "battle_debug.h"
#include "battle_dynamax.h"
#include "battle_terastal.h"
#include "battle_gimmick.h"
#include "random.h" // for rng_value_t
// Helper for accessing command arguments and advancing gBattlescriptCurrInstr.
@ -370,8 +371,6 @@ struct AiLogicData
bool8 weatherHasEffect; // The same as WEATHER_HAS_EFFECT. Stored here, so it's called only once.
u8 mostSuitableMonId[MAX_BATTLERS_COUNT]; // Stores result of GetMostSuitableMonToSwitchInto, which decides which generic mon the AI would switch into if they decide to switch. This can be overruled by specific mons found in ShouldSwitch; the final resulting mon is stored in AI_monToSwitchIntoId.
struct SwitchinCandidate switchinCandidate; // Struct used for deciding which mon to switch to in battle_ai_switch_items.c
bool8 shouldTerastal[MAX_BATTLERS_COUNT];
bool8 shouldDynamax[MAX_BATTLERS_COUNT];
};
struct AI_ThinkingStruct
@ -558,24 +557,6 @@ struct LinkBattlerHeader
struct BattleEnigmaBerry battleEnigmaBerry;
};
struct MegaEvolutionData
{
u8 toEvolve; // As flags using gBitTable.
bool8 alreadyEvolved[4]; // Array id is used for mon position.
u8 battlerId;
bool8 playerSelect;
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;
@ -587,48 +568,30 @@ struct Illusion
struct ZMoveData
{
u8 viable:1; // current move can become a z move
u8 viable:1; // current move can become a z move
u8 viewing:1; // if player is viewing the z move name instead of regular moves
u8 active:1; // is z move being used this turn
u8 zStatusActive:1;
u8 healReplacement:1;
u8 activeCategory:2; // active z move category
u8 zUnused:1;
u8 triggerSpriteId;
u8 healReplacement:6;
u8 possibleZMoves[MAX_BATTLERS_COUNT];
u16 chosenZMove; // z move of move cursor is on
u8 effect;
u8 used[MAX_BATTLERS_COUNT]; //one per bank for multi-battles
u16 toBeUsed[MAX_BATTLERS_COUNT]; // z moves per battler to be used
u16 baseMoves[MAX_BATTLERS_COUNT];
u8 categories[MAX_BATTLERS_COUNT];
};
struct DynamaxData
{
bool8 playerSelect;
u8 triggerSpriteId;
u8 toDynamax; // flags using gBitTable
bool8 alreadyDynamaxed[NUM_BATTLE_SIDES];
bool8 dynamaxed[MAX_BATTLERS_COUNT];
u8 dynamaxTurns[MAX_BATTLERS_COUNT];
u8 usingMaxMove[MAX_BATTLERS_COUNT];
u8 activeCategory;
u8 categories[MAX_BATTLERS_COUNT];
u16 baseMove[MAX_BATTLERS_COUNT]; // base move of Max Move
u16 baseMoves[MAX_BATTLERS_COUNT]; // base move of Max Move
u16 lastUsedBaseMove;
u16 levelUpHP;
};
struct TeraData
struct BattleGimmickData
{
bool8 toTera; // flags using gBitTable
bool8 isTerastallized[NUM_BATTLE_SIDES]; // stored as a bitfield for each side's party members
bool8 alreadyTerastallized[MAX_BATTLERS_COUNT];
bool8 playerSelect;
u32 stellarBoostFlags[NUM_BATTLE_SIDES]; // stored as a bitfield of flags for all types for each side
u8 usableGimmick[MAX_BATTLERS_COUNT]; // first usable gimmick that can be selected for each battler
bool8 playerSelect; // used to toggle trigger and update battle UI
u8 triggerSpriteId;
u8 indicatorSpriteId[MAX_BATTLERS_COUNT];
u8 toActivate; // stores whether a battler should transform at start of turn as bitfield
u8 activeGimmick[NUM_BATTLE_SIDES][PARTY_SIZE]; // stores the active gimmick for each party member
bool8 activated[MAX_BATTLERS_COUNT][GIMMICKS_COUNT]; // stores whether a trainer has used gimmick
};
struct LostItem
@ -703,7 +666,11 @@ struct BattleStruct
u16 abilityPreventingSwitchout;
u8 hpScale;
u16 synchronizeMoveEffect;
bool8 anyMonHasTransformed;
u8 anyMonHasTransformed:1; // Only used in battle_tv.c
u8 multipleSwitchInBattlers:4; // One bit per battler
u8 multipleSwitchInState:2;
u8 multipleSwitchInCursor:3;
u8 multipleSwitchInSortedBattlers[MAX_BATTLERS_COUNT];
void (*savedCallback)(void);
u16 usedHeldItems[PARTY_SIZE][NUM_BATTLE_SIDES]; // For each party member and side. For harvest, recycle
u16 chosenItem[MAX_BATTLERS_COUNT];
@ -753,11 +720,9 @@ struct BattleStruct
u8 activeAbilityPopUps; // as bits for each battler
u8 abilityPopUpSpriteIds[MAX_BATTLERS_COUNT][2]; // two per battler
bool8 throwingPokeBall;
struct MegaEvolutionData mega;
struct UltraBurstData burst;
struct ZMoveData zmove;
struct DynamaxData dynamax;
struct TeraData tera;
struct BattleGimmickData gimmick;
const u8 *trainerSlideMsg;
bool8 trainerSlideLowHpMsgDone;
u8 introState;
@ -779,12 +744,15 @@ struct BattleStruct
u16 moveEffect2; // For Knock Off
u16 changedSpecies[NUM_BATTLE_SIDES][PARTY_SIZE]; // For forms when multiple mons can change into the same pokemon.
u8 quickClawBattlerId;
struct LostItem itemLost[PARTY_SIZE]; // Player's team that had items consumed or stolen (two bytes per party member)
struct LostItem itemLost[NUM_BATTLE_SIDES][PARTY_SIZE]; // Pokemon that had items consumed or stolen (two bytes per party member per side)
u8 forcedSwitch:4; // For each battler
u8 additionalEffectsCounter:4; // A counter for the additionalEffects applied by the current move in Cmd_setadditionaleffects
u8 blunderPolicy:1; // should blunder policy activate
u8 swapDamageCategory:1; // Photon Geyser, Shell Side Arm, Light That Burns the Sky
u8 bouncedMoveIsUsed:1;
u8 descriptionSubmenu:1; // For Move Description window in move selection screen
u8 ackBallUseBtn:1; // Used for the last used ball feature
u8 ballSwapped:1; // Used for the last used ball feature
u8 ballSpriteIds[2]; // item gfx, window gfx
u8 appearedInBattle; // Bitfield to track which Pokemon appeared in battle. Used for Burmy's form change
u8 skyDropTargets[MAX_BATTLERS_COUNT]; // For Sky Drop, to account for if multiple Pokemon use Sky Drop in a double battle.
@ -829,6 +797,8 @@ struct BattleStruct
u8 shellSideArmCategory[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT];
u8 boosterEnergyActivates;
u8 distortedTypeMatchups;
u8 categoryOverride; // for Z-Moves and Max Moves
u32 stellarBoostFlags[NUM_BATTLE_SIDES]; // stored as a bitfield of flags for all types for each side
};
// The palaceFlags member of struct BattleStruct contains 1 flag per move to indicate which moves the AI should consider,

View File

@ -29,8 +29,9 @@
#define STAT_CHANGE_ACC 10
#define STAT_CHANGE_EVASION 11
#define BEST_DAMAGE_MOVE 1 // Move with the most amount of hits with the best accuracy/effect
#define POWERFUL_STATUS_MOVE 10 // Moves with this score will be chosen over a move that faints target
#define BEST_DAMAGE_MOVE 1 // Move with the most amount of hits with the best accuracy/effect
#define POWERFUL_STATUS_MOVE 10 // Moves with this score will be chosen over a move that faints target
#define NO_DAMAGE_OR_FAILS -20 // Move fails or does no damage
// Scores given in AI_CalcMoveEffectScore
#define WEAK_EFFECT 1
@ -64,6 +65,14 @@
score += val; \
} while (0) \
#define ADJUST_AND_RETURN_SCORE(val) \
do \
{ \
TestRunner_Battle_AIAdjustScore(__FILE__, __LINE__, sBattler_AI, AI_THINKING_STRUCT->movesetIndex, val); \
score += val; \
return score; \
} while (0) \
#define ADJUST_SCORE_PTR(val) \
do \
{ \

View File

@ -3,7 +3,7 @@
void GetAIPartyIndexes(u32 battlerId, s32 *firstId, s32 *lastId);
void AI_TrySwitchOrUseItem(u32 battler);
u8 GetMostSuitableMonToSwitchInto(u32 battler, bool32 switchAfterMonKOd);
u32 GetMostSuitableMonToSwitchInto(u32 battler, bool32 switchAfterMonKOd);
bool32 ShouldSwitch(u32 battler, bool32 emitResult);
#endif // GUARD_BATTLE_AI_SWITCH_ITEMS_H

View File

@ -146,10 +146,8 @@ bool32 HasMagicCoatAffectedMove(u32 battler);
bool32 HasSnatchAffectedMove(u32 battler);
// status checks
bool32 AI_CanBeBurned(u32 battler, u32 ability);
bool32 AI_CanGetFrostbite(u32 battler, u32 ability);
bool32 AI_CanBeConfused(u32 battlerAtk, u32 battlerDef, u32 move, u32 ability);
bool32 AI_CanSleep(u32 battler, u32 ability);
bool32 IsBattlerIncapacitated(u32 battler, u32 ability);
bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove);
bool32 ShouldPoisonSelf(u32 battler, u32 ability);

View File

@ -203,10 +203,10 @@ u8 GetBattlerSpriteDefault_Y(u8 battlerId);
u8 GetSubstituteSpriteDefault_Y(u8 battlerId);
// battle_anim_status_effects.c
#define STAT_ANIM_PLUS1 MOVE_EFFECT_ATK_PLUS_1 - 1
#define STAT_ANIM_PLUS2 MOVE_EFFECT_ATK_PLUS_2 - 1
#define STAT_ANIM_MINUS1 MOVE_EFFECT_ATK_MINUS_1 - 1
#define STAT_ANIM_MINUS2 MOVE_EFFECT_ATK_MINUS_2 - 1
#define STAT_ANIM_PLUS1 (MOVE_EFFECT_ATK_PLUS_1 - 1)
#define STAT_ANIM_PLUS2 (MOVE_EFFECT_ATK_PLUS_2 - 1)
#define STAT_ANIM_MINUS1 (MOVE_EFFECT_ATK_MINUS_1 - 1)
#define STAT_ANIM_MINUS2 (MOVE_EFFECT_ATK_MINUS_2 - 1)
#define STAT_ANIM_MULTIPLE_PLUS1 55
#define STAT_ANIM_MULTIPLE_PLUS2 56
#define STAT_ANIM_MULTIPLE_MINUS1 57

View File

@ -98,10 +98,7 @@ enum {
// Special return values in gBattleBufferB from Battle Controller functions.
#define RET_VALUE_LEVELED_UP 11
#define RET_MEGA_EVOLUTION (1 << 7)
#define RET_ULTRA_BURST (1 << 6)
#define RET_DYNAMAX (1 << 5)
#define RET_TERASTAL (1 << 4)
#define RET_GIMMICK (1 << 7)
struct UnusedControllerStruct
{
@ -131,8 +128,6 @@ struct ChooseMoveStruct
u8 monType1;
u8 monType2;
u8 monType3;
struct MegaEvolutionData mega;
struct UltraBurstData burst;
struct ZMoveData zmove;
};

View File

@ -56,21 +56,19 @@ enum MaxMoveEffect
MAX_EFFECT_BYPASS_PROTECT,
};
bool32 IsDynamaxed(u16 battlerId);
bool32 CanDynamax(u16 battlerId);
bool32 IsGigantamaxed(u16 battlerId);
bool32 CanDynamax(u32 battler);
bool32 IsGigantamaxed(u32 battler);
void ApplyDynamaxHPMultiplier(u32 battler, struct Pokemon* mon);
void PrepareBattlerForDynamax(u16 battlerId);
u16 GetNonDynamaxHP(u16 battlerId);
u16 GetNonDynamaxMaxHP(u32 battlerId);
void UndoDynamax(u16 battlerId);
bool32 IsMoveBlockedByMaxGuard(u16 move);
bool32 IsMoveBlockedByDynamax(u16 move);
void ActivateDynamax(u32 battler);
u16 GetNonDynamaxHP(u32 battler);
u16 GetNonDynamaxMaxHP(u32 battler);
void UndoDynamax(u32 battler);
bool32 IsMoveBlockedByMaxGuard(u32 move);
bool32 IsMoveBlockedByDynamax(u32 move);
bool32 ShouldUseMaxMove(u16 battlerId, u16 baseMove);
u16 GetMaxMove(u16 battlerId, u16 baseMove);
u8 GetMaxMovePower(u16 move);
bool32 IsMaxMove(u16 move);
u16 GetMaxMove(u32 battler, u32 baseMove);
u8 GetMaxMovePower(u32 move);
bool32 IsMaxMove(u32 move);
void ChooseDamageNonTypesString(u8 type);
void BS_UpdateDynamax(void);
@ -83,10 +81,4 @@ void BS_HealOneSixth(void);
void BS_TryRecycleBerry(void);
void BS_JumpIfDynamaxed(void);
void ChangeDynamaxTriggerSprite(u8 spriteId, u8 animId);
void CreateDynamaxTriggerSprite(u8, bool8);
void HideDynamaxTriggerSprite(void);
bool32 IsDynamaxTriggerSpriteActive(void);
void DestroyDynamaxTriggerSprite(void);
#endif

51
include/battle_gimmick.h Normal file
View File

@ -0,0 +1,51 @@
#ifndef GUARD_BATTLE_GIMMICK_H
#define GUARD_BATTLE_GIMMICK_H
enum Gimmick
{
GIMMICK_NONE,
GIMMICK_MEGA,
GIMMICK_ULTRA_BURST,
GIMMICK_Z_MOVE,
GIMMICK_DYNAMAX,
GIMMICK_TERA,
GIMMICKS_COUNT,
};
struct GimmickInfo
{
const struct SpritePalette *triggerPal; // trigger gfx data
const struct SpriteSheet *triggerSheet;
const struct SpriteTemplate *triggerTemplate;
const struct SpritePalette *indicatorPal; // indicator gfx data
const struct SpriteSheet *indicatorSheet;
bool32 (*CanActivate)(u32 battler);
void (*ActivateGimmick)(u32 battler);
};
void AssignUsableGimmicks(void);
bool32 CanActivateGimmick(u32 battler, enum Gimmick gimmick);
bool32 IsGimmickSelected(u32 battler, enum Gimmick gimmick);
void SetActiveGimmick(u32 battler, enum Gimmick gimmick);
enum Gimmick GetActiveGimmick(u32 battler);
bool32 ShouldTrainerBattlerUseGimmick(u32 battler, enum Gimmick gimmick);
bool32 HasTrainerUsedGimmick(u32 battler, enum Gimmick gimmick);
void SetGimmickAsActivated(u32 battler, enum Gimmick gimmick);
void ChangeGimmickTriggerSprite(u32 spriteId, u32 animId);
void CreateGimmickTriggerSprite(u32 battler);
bool32 IsGimmickTriggerSpriteActive(void);
void HideGimmickTriggerSprite(void);
void DestroyGimmickTriggerSprite(void);
void LoadIndicatorSpritesGfx(void);
u32 GetIndicatorTileTag(u32 battler);
u32 GetIndicatorPalTag(u32 battler);
void UpdateIndicatorVisibilityAndType(u32 healthboxId, bool32 invisible);
void UpdateIndicatorOamPriority(u32 healthboxId, u32 oamPriority);
void UpdateIndicatorLevelData(u32 healthboxId, u32 level);
void CreateIndicatorSprite(u32 battler);
extern const struct GimmickInfo gGimmicksInfo[];
#endif

View File

@ -48,45 +48,38 @@ enum
#define TAG_HEALTHBAR_PAL TAG_HEALTHBAR_PLAYER1_TILE
#define TAG_HEALTHBOX_PAL TAG_HEALTHBOX_PLAYER1_TILE
#define TAG_MEGA_TRIGGER_TILE 0xD777
#define TAG_GIMMICK_TRIGGER_TILE 0xD777
#define TAG_MEGA_INDICATOR_TILE 0xD778
#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_DYNAMAX_TRIGGER_TILE 0xD77D
#define TAG_DYNAMAX_INDICATOR_TILE 0xD77E
#define TAG_DYNAMAX_INDICATOR_TILE 0xD77B
#define TAG_NORMAL_INDICATOR_TILE 0xD77F
#define TAG_FIGHTING_INDICATOR_TILE 0xD780
#define TAG_FLYING_INDICATOR_TILE 0xD781
#define TAG_POISON_INDICATOR_TILE 0xD782
#define TAG_GROUND_INDICATOR_TILE 0xD783
#define TAG_ROCK_INDICATOR_TILE 0xD784
#define TAG_BUG_INDICATOR_TILE 0xD785
#define TAG_GHOST_INDICATOR_TILE 0xD786
#define TAG_STEEL_INDICATOR_TILE 0xD787
#define TAG_NORMAL_INDICATOR_TILE 0xD77C
#define TAG_FIGHTING_INDICATOR_TILE 0xD77D
#define TAG_FLYING_INDICATOR_TILE 0xD77E
#define TAG_POISON_INDICATOR_TILE 0xD77F
#define TAG_GROUND_INDICATOR_TILE 0xD780
#define TAG_ROCK_INDICATOR_TILE 0xD781
#define TAG_BUG_INDICATOR_TILE 0xD782
#define TAG_GHOST_INDICATOR_TILE 0xD783
#define TAG_STEEL_INDICATOR_TILE 0xD784
// empty spot for TYPE_MYSTERY
#define TAG_FIRE_INDICATOR_TILE 0xD789
#define TAG_WATER_INDICATOR_TILE 0xD78A
#define TAG_GRASS_INDICATOR_TILE 0xD78B
#define TAG_ELECTRIC_INDICATOR_TILE 0xD78C
#define TAG_PSYCHIC_INDICATOR_TILE 0xD78D
#define TAG_ICE_INDICATOR_TILE 0xD78E
#define TAG_DRAGON_INDICATOR_TILE 0xD78F
#define TAG_DARK_INDICATOR_TILE 0xD790
#define TAG_FAIRY_INDICATOR_TILE 0xD791
#define TAG_STELLAR_INDICATOR_TILE 0xD792
#define TAG_TERA_TRIGGER_TILE 0xD793
#define TAG_FIRE_INDICATOR_TILE 0xD786
#define TAG_WATER_INDICATOR_TILE 0xD787
#define TAG_GRASS_INDICATOR_TILE 0xD788
#define TAG_ELECTRIC_INDICATOR_TILE 0xD789
#define TAG_PSYCHIC_INDICATOR_TILE 0xD78A
#define TAG_ICE_INDICATOR_TILE 0xD78B
#define TAG_DRAGON_INDICATOR_TILE 0xD78C
#define TAG_DARK_INDICATOR_TILE 0xD78D
#define TAG_FAIRY_INDICATOR_TILE 0xD78E
#define TAG_STELLAR_INDICATOR_TILE 0xD78F
#define TAG_TERA_TRIGGER_TILE 0xD790
#define TAG_MEGA_TRIGGER_PAL 0xD777
#define TAG_GIMMICK_TRIGGER_PAL 0xD777
#define TAG_MEGA_INDICATOR_PAL 0xD778
#define TAG_MISC_INDICATOR_PAL 0xD779 // Alpha, Omega, and Dynamax 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
#define TAG_DYNAMAX_TRIGGER_PAL 0xD77D
#define TAG_TERA_INDICATOR_PAL 0xD77E
#define TAG_TERA_TRIGGER_PAL 0xD77F
#define TAG_TERA_INDICATOR_PAL 0xD77A
enum
{
@ -116,18 +109,6 @@ void InitBattlerHealthboxCoords(u8 battler);
void GetBattlerHealthboxCoords(u8 battler, s16 *x, s16 *y);
void UpdateHpTextInHealthbox(u32 healthboxSpriteId, u32 maxOrCurrent, s16 currHp, s16 maxHp);
void SwapHpBarsWithHpText(void);
void ChangeMegaTriggerSprite(u8 spriteId, u8 animId);
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);
void MegaIndicator_SetVisibilities(u32 healthboxId, bool32 invisible);
u8 CreatePartyStatusSummarySprites(u8 battler, struct HpAndStatus *partyInfo, bool8 skipPlayer, bool8 isBattleStart);
void Task_HidePartyStatusSummary(u8 taskId);
void UpdateHealthboxAttribute(u8 healthboxSpriteId, struct Pokemon *mon, u8 elementId);
@ -136,7 +117,6 @@ u8 GetScaledHPFraction(s16 hp, s16 maxhp, u8 scale);
u8 GetHPBarLevel(s16 hp, s16 maxhp);
void CreateAbilityPopUp(u8 battlerId, u32 ability, bool32 isDoubleBattle);
void DestroyAbilityPopUp(u8 battlerId);
void HideTriggerSprites(void);
bool32 CanThrowLastUsedBall(void);
void TryHideLastUsedBall(void);
void TryRestoreLastUsedBall(void);

View File

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

View File

@ -84,6 +84,7 @@ extern const u8 BattleScript_DmgHazardsOnBattlerScripting[];
extern const u8 BattleScript_DmgHazardsOnFaintedBattler[];
extern const u8 BattleScript_PerishSongTakesLife[];
extern const u8 BattleScript_PerishSongCountGoesDown[];
extern const u8 BattleScript_AllStatsUpZMove[];
extern const u8 BattleScript_AllStatsUp[];
extern const u8 BattleScript_RapidSpinAway[];
extern const u8 BattleScript_WrapFree[];
@ -268,6 +269,7 @@ extern const u8 BattleScript_WonderRoomEnds[];
extern const u8 BattleScript_MagicRoomEnds[];
extern const u8 BattleScript_TerrainEnds[];
extern const u8 BattleScript_TerrainEnds_Ret[];
extern const u8 BattleScript_GrassyTerrainEnds[];
extern const u8 BattleScript_MudSportEnds[];
extern const u8 BattleScript_WaterSportEnds[];
extern const u8 BattleScript_SturdiedMsg[];
@ -838,5 +840,6 @@ extern const u8 BattleScript_EffectShedTail[];
extern const u8 BattleScript_EffectUpperHand[];
extern const u8 BattleScript_EffectTidyUp[];
extern const u8 BattleScript_EffectSpicyExtract[];
extern const u8 BattleScript_DamageToQuarterTargetHP[];
#endif // GUARD_BATTLE_SCRIPTS_H

View File

@ -1,31 +1,14 @@
#ifndef GUARD_BATTLE_TERASTAL_H
#define GUARD_BATTLE_TERASTAL_H
void PrepareBattlerForTera(u32 battler);
void ActivateTera(u32 battler);
void ApplyBattlerVisualsForTeraAnim(u32 battler);
bool32 CanTerastallize(u32 battler);
u32 GetBattlerTeraType(u32 battler);
bool32 IsTerastallized(u32 battler);
void ExpendTypeStellarBoost(u32 battler, u32 type);
bool32 IsTypeStellarBoosted(u32 battler, u32 type);
uq4_12_t GetTeraMultiplier(u32 battler, u32 type);
u16 GetTeraTypeRGB(u32 type);
void ChangeTeraTriggerSprite(u8 spriteId, u8 animId);
void CreateTeraTriggerSprite(u8 battler, u8 palId);
bool32 IsTeraTriggerSpriteActive(void);
void HideTeraTriggerSprite(void);
void DestroyTeraTriggerSprite(void);
void TeraIndicator_LoadSpriteGfx(void);
bool32 TeraIndicator_ShouldBeInvisible(u32 battler);
u8 TeraIndicator_GetSpriteId(u32 healthboxSpriteId);
void TeraIndicator_SetVisibilities(u32 healthboxId, bool32 invisible);
void TeraIndicator_UpdateOamPriorities(u32 healthboxId, u32 oamPriority);
void TeraIndicator_UpdateLevel(u32 healthboxId, u32 level);
void TeraIndicator_CreateSprite(u32 battler, u32 healthboxSpriteId);
void TeraIndicator_DestroySprite(u32 healthboxSpriteId);
void TeraIndicator_UpdateType(u32 battler, u32 healthboxSpriteId);
#endif

View File

@ -196,11 +196,13 @@ s32 GetStealthHazardDamage(u8 hazardType, u32 battler);
s32 GetStealthHazardDamageByTypesAndHP(u8 hazardType, u8 type1, u8 type2, u32 maxHp);
bool32 CanMegaEvolve(u32 battler);
bool32 CanUltraBurst(u32 battler);
void ActivateMegaEvolution(u32 battler);
void ActivateUltraBurst(u32 battler);
bool32 IsBattlerMegaEvolved(u32 battler);
bool32 IsBattlerPrimalReverted(u32 battler);
bool32 IsBattlerUltraBursted(u32 battler);
u16 GetBattleFormChangeTargetSpecies(u32 battler, u16 method);
bool32 TryBattleFormChange(u32 battler, u16 method);
bool32 TryBattleFormChange(u32 battler, u32 method);
bool32 DoBattlersShareType(u32 battler1, u32 battler2);
bool32 CanBattlerGetOrLoseItem(u32 battler, u16 itemId);
u32 GetIllusionMonSpecies(u32 battler);
@ -247,10 +249,10 @@ bool32 MoveHasChargeTurnAdditionalEffect(u32 move);
bool32 CanTargetPartner(u32 battlerAtk, u32 battlerDef);
bool32 TargetFullyImmuneToCurrMove(u32 BattlerAtk, u32 battlerDef);
bool32 CanSleep(u32 battler);
bool32 CanBePoisoned(u32 battlerAttacker, u32 battlerTarget);
bool32 CanBeBurned(u32 battler);
bool32 CanBeParalyzed(u32 battler);
bool32 CanBeSlept(u32 battler, u32 ability);
bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 defAbility);
bool32 CanBeBurned(u32 battler, u32 ability);
bool32 CanBeParalyzed(u32 battler, u32 ability);
bool32 CanBeFrozen(u32 battler);
bool32 CanGetFrostbite(u32 battler);
bool32 CanBeConfused(u32 battler);

View File

@ -13,19 +13,17 @@ struct SignatureZMove
u16 zmove;
};
bool8 IsZMove(u16 move);
void QueueZMove(u8 battler, u16 baseMove);
bool32 IsViableZMove(u8 battler, u16 move);
bool32 TryChangeZIndicator(u8 battler, u8 moveIndex);
void CreateZMoveTriggerSprite(u8, bool8);
void HideZMoveTriggerSprite(void);
bool32 IsZMoveTriggerSpriteActive(void);
void DestroyZMoveTriggerSprite(void);
u16 GetTypeBasedZMove(u16 move, u8 battler);
bool32 IsZMove(u32 move);
bool32 CanUseZMove(u32 battler);
u32 GetUsableZMove(u32 battler, u32 move);
void ActivateZMove(u32 battler);
bool32 IsViableZMove(u32 battler, u32 move);
bool32 TryChangeZTrigger(u32 battler, u32 moveIndex);
u32 GetTypeBasedZMove(u32 move);
u32 GetSignatureZMove(u32 move, u32 species, u32 item);
bool32 MoveSelectionDisplayZMove(u16 zmove, u32 battler);
void SetZEffect(void);
bool32 IsZMoveUsable(u8 battler, u16 moveIndex);
void GetUsableZMoves(u8 battler, u16 *moves);
u16 GetZMovePower(u16 move);
void AssignUsableZMoves(u32 battler, u16 *moves);
u32 GetZMovePower(u32 move);
#endif // GUARD_BATTLE_Z_MOVE_H

View File

@ -147,6 +147,9 @@
#define B_INTREPID_SWORD GEN_LATEST // In Gen9+, Intrepid Sword raises Attack by one stage only once per Battle.
#define B_DAUNTLESS_SHIELD GEN_LATEST // In Gen9+, Dauntless Shield raises Defense by one stage only once per Battle.
#define B_DISGUISE_HP_LOSS GEN_LATEST // In Gen8+, when a Disguised Mimikyu's Disguise is busted, upon changing to its Busted Form it loses HP equal to 1/8 of its maximum HP.
#define B_ABILITY_TRIGGER_CHANCE GEN_LATEST // In Gen3, Shed Skin, Cute Charm, Flame Body, Static and Poison Point have a 1/3 chance to trigger. In Gen 4+ it's 30%.
// In Gen3, Effect Spore has a 10% chance to sleep, poison or paralyze, with an equal chance.
// In Gen4, it's 30%. In Gen5+ it has 11% to sleep, 9% chance to poison and 10% chance to paralyze.
// Item settings
#define B_HP_BERRIES GEN_LATEST // In Gen4+, berries which restore HP activate immediately after HP drops to half. In Gen3, the effect occurs at the end of the turn.
@ -220,6 +223,7 @@
#define B_EXPANDED_MOVE_NAMES TRUE // If set to FALSE, move names are decreased from 16 characters to 12 characters.
#define B_WAIT_TIME_MULTIPLIER 16 // This determines how long text pauses in battle last. Vanilla is 16. Lower values result in faster battles.
#define B_QUICK_MOVE_CURSOR_TO_RUN FALSE // If set to TRUE, pushing B in the battle options against a wild encounter will move the cursor to the run option
#define B_MOVE_DESCRIPTION_BUTTON L_BUTTON // If set to a button other than B_LAST_USED_BALL_BUTTON, pressing this button will open the move description menu
// Catching settings
#define B_SEMI_INVULNERABLE_CATCH GEN_LATEST // In Gen4+, you cannot throw a ball against a Pokemon that is in a semi-invulnerable state (dig/fly/etc)

View File

@ -1,5 +1,5 @@
#ifndef GUARD_CONFIG_H
#define GUARD_CONFIG_H
#ifndef GUARD_CONFIG_GENERAL_H
#define GUARD_CONFIG_GENERAL_H
// In the Generation 3 games, Asserts were used in various debug builds.
// Ruby/Sapphire and Emerald do not have these asserts while Fire Red
@ -53,7 +53,7 @@
#define POKEMON_EXPANSION
#define ITEM_EXPANSION
// Generation constants used in configs to define behavior
// Generation constants used in configs to define behavior.
#define GEN_1 0
#define GEN_2 1
#define GEN_3 2
@ -63,6 +63,7 @@
#define GEN_7 6
#define GEN_8 7
#define GEN_9 8
// Changing GEN_LATEST's value to a different Generation will change every default setting that uses it at once.
#define GEN_LATEST GEN_9
// General settings
@ -79,4 +80,4 @@
#define UNITS UNITS_IMPERIAL
#define CHAR_DEC_SEPARATOR CHAR_PERIOD // CHAR_PERIOD is used as a decimal separator only in the UK and the US. The rest of the world uses CHAR_COMMA.
#endif // GUARD_CONFIG_H
#endif // GUARD_CONFIG_GENERAL_H

View File

@ -82,8 +82,11 @@
#define OW_POPUP_BW_COLOR_WHITE 1 // White pop-up from W2
// Configuration
#define OW_POPUP_BW_COLOR OW_POPUP_BW_COLOR_BLACK // B2W2 use different colors for their map pop-ups.
#define OW_POPUP_BW_COLOR OW_POPUP_BW_COLOR_BLACK // B2W2 use different colors for their map pop-ups.
#define OW_POPUP_BW_TIME_MODE OW_POPUP_BW_TIME_NONE // Determines what type of time is shown.
#define OW_POPUP_BW_ALPHA_BLEND FALSE // Enables alpha blending/transparency for the pop-ups. Mainly intended to be used with the black color option.
// Pokémon Center
#define OW_IGNORE_EGGS_ON_HEAL GEN_LATEST // In Gen 4+, the nurse in the Pokémon Center does not heal Eggs on healing machine.
#endif // GUARD_CONFIG_OVERWORLD_H

View File

@ -39,15 +39,17 @@
#define P_ARCEUS_UNIQUE_FORM_ICONS GEN_LATEST // Since Gen 9, Arceus additionally changes its icon to reflect its current form.
// Other settings
#define P_CUSTOM_GENDER_DIFF_ICONS TRUE // If TRUE, will give more Pokémon custom icons for their female forms, i.e. Hippopotas and Hippowdon
#define P_FOOTPRINTS TRUE // If TRUE, Pokémon will have footprints (as was the case up to Gen 5 and in BDSP). Disabling this saves some ROM space.
#define P_CRIES_ENABLED TRUE // If TRUE, Pokémon will have cries. Disabling this saves around a LOT of ROM space (over 25%!), but instead we recommend disabling individual unused Pokémon families in include/config/species_enabled.h.
#define P_LEGENDARY_PERFECT_IVS GEN_LATEST // Since Gen 6, Legendaries, Mythicals and Ultra Beasts found in the wild or given through gifts have at least 3 perfect IVs.
#define P_EV_CAP GEN_LATEST // Since Gen 6, the max EVs per stat is 252 instead of 255.
#define P_SHOW_TERA_TYPE GEN_8 // Since Gen 9, the Tera Type is shown on the summary screen.
#define P_TM_LITERACY GEN_LATEST // Since Gen 6, TM illiterate Pokémon can learn TMs that teach moves that are in their level-up learnsets.
#define P_EGG_CYCLE_LENGTH GEN_LATEST // Since Gen 8, egg cycles take half as many steps as before.
#define P_TWO_FRAME_FRONT_SPRITES TRUE // In Pokémon Emerald, Pokémon front sprites always consist of two frames. This config can revert it to only use the first frame, as is the case in the other Gen 3 games.
#define P_CUSTOM_GENDER_DIFF_ICONS TRUE // If TRUE, will give more Pokémon custom icons for their female forms, i.e. Hippopotas and Hippowdon
#define P_FOOTPRINTS TRUE // If TRUE, Pokémon will have footprints (as was the case up to Gen 5 and in BDSP). Disabling this saves some ROM space.
#define P_CRIES_ENABLED TRUE // If TRUE, Pokémon will have cries. Disabling this saves around a LOT of ROM space (over 25%!), but instead we recommend disabling individual unused Pokémon families in include/config/species_enabled.h.
#define P_LEGENDARY_PERFECT_IVS GEN_LATEST // Since Gen 6, Legendaries, Mythicals and Ultra Beasts found in the wild or given through gifts have at least 3 perfect IVs.
#define P_EV_CAP GEN_LATEST // Since Gen 6, the max EVs per stat is 252 instead of 255.
#define P_SHOW_TERA_TYPE GEN_8 // Since Gen 9, the Tera Type is shown on the summary screen.
#define P_TM_LITERACY GEN_LATEST // Since Gen 6, TM illiterate Pokémon can learn TMs that teach moves that are in their level-up learnsets.
#define P_EGG_CYCLE_LENGTH GEN_LATEST // Since Gen 8, egg cycles take half as many steps as before.
#define P_TWO_FRAME_FRONT_SPRITES TRUE // In Pokémon Emerald, Pokémon front sprites always consist of two frames. This config can revert it to only use the first frame, as is the case in the other Gen 3 games.
#define P_ONLY_OBTAINABLE_SHINIES FALSE // If TRUE, Pokémon encountered in the Battle Pyramid won't be shiny.
#define P_NO_SHINIES_WITHOUT_POKEBALLS FALSE // If TRUE, Pokémon encountered when the player is out of Poké Balls won't be shiny
// Learnset helper toggles
#define P_LEARNSET_HELPER_TEACHABLE TRUE // If TRUE, teachable_learnsets.h will be populated by tools/learnset_helpers/teachable.py using the included JSON files based on available TMs and tutors.

File diff suppressed because it is too large Load Diff

View File

@ -39,14 +39,14 @@
#define AI_FLAG_NEGATE_UNAWARE (1 << 10) // AI is NOT aware of negating effects like wonder room, mold breaker, etc
#define AI_FLAG_WILL_SUICIDE (1 << 11) // AI will use explosion / self destruct / final gambit / etc
// New, Trainer Strategy Flags
#define AI_FLAG_HELP_PARTNER (1 << 12) // AI can try to help partner. If not set, will tend not to target partner
#define AI_FLAG_PREFER_STATUS_MOVES (1 << 13) // AI gets a score bonus for status moves. Should be combined with AI_FLAG_CHECK_BAD_MOVE to prevent using only status moves
#define AI_FLAG_STALL (1 << 14) // AI stalls battle and prefers secondary damage/trapping/etc. TODO not finished
#define AI_FLAG_SMART_SWITCHING (1 << 15) // AI includes a lot more switching checks. Automatically includes AI_FLAG_SMART_MON_CHOICES.
#define AI_FLAG_ACE_POKEMON (1 << 16) // AI has an Ace Pokemon. The last Pokemon in the party will not be used until it's the last one remaining.
#define AI_FLAG_OMNISCIENT (1 << 17) // AI has full knowledge of player moves, abilities, hold items
#define AI_FLAG_SMART_MON_CHOICES (1 << 18) // AI will make smarter decisions when choosing which mon to send out mid-battle and after a KO, which are separate decisions. Automatically included by AI_FLAG_SMART_SWITCHING.
#define AI_FLAG_CONSERVATIVE (1 << 19) // AI assumes all moves will low roll damage
#define AI_FLAG_PREFER_STATUS_MOVES (1 << 12) // AI gets a score bonus for status moves. Should be combined with AI_FLAG_CHECK_BAD_MOVE to prevent using only status moves
#define AI_FLAG_STALL (1 << 13) // AI stalls battle and prefers secondary damage/trapping/etc. TODO not finished
#define AI_FLAG_SMART_SWITCHING (1 << 14) // AI includes a lot more switching checks. Automatically includes AI_FLAG_SMART_MON_CHOICES.
#define AI_FLAG_ACE_POKEMON (1 << 15) // AI has an Ace Pokemon. The last Pokemon in the party will not be used until it's the last one remaining.
#define AI_FLAG_OMNISCIENT (1 << 16) // AI has full knowledge of player moves, abilities, hold items
#define AI_FLAG_SMART_MON_CHOICES (1 << 17) // AI will make smarter decisions when choosing which mon to send out mid-battle and after a KO, which are separate decisions. Automatically included by AI_FLAG_SMART_SWITCHING.
#define AI_FLAG_CONSERVATIVE (1 << 18) // AI assumes all moves will low roll damage
#define AI_FLAG_SEQUENCE_SWITCHING (1 << 19) // AI switches in mons in exactly party order, and never switches mid-battle
#define AI_FLAG_COUNT 20

View File

@ -352,6 +352,7 @@ enum {
EFFECT_TERA_BLAST,
EFFECT_TERA_STARSTORM,
EFFECT_DRAGON_DARTS,
EFFECT_GUARDIAN_OF_ALOLA,
NUM_BATTLE_MOVE_EFFECTS,
};

View File

@ -63,8 +63,8 @@
#define BS_EFFECT_BATTLER 2
#define BS_FAINTED 3
#define BS_ATTACKER_WITH_PARTNER 4 // for Cmd_updatestatusicon
#define BS_FAINTED_LINK_MULTIPLE_1 5 // for openpartyscreen
#define BS_FAINTED_LINK_MULTIPLE_2 6 // for openpartyscreen
#define BS_FAINTED_MULTIPLE_1 5 // for openpartyscreen
#define BS_FAINTED_MULTIPLE_2 6 // for openpartyscreen
#define BS_BATTLER_0 7
#define BS_ATTACKER_SIDE 8 // for Cmd_jumpifability
#define BS_TARGET_SIDE 9 // for Cmd_jumpifability

View File

@ -129,4 +129,9 @@
// param1: tera type
#define FORM_CHANGE_BATTLE_TERASTALLIZATION 22
// Form change that activates at midnight after a certain amount of days has passed.
// Adding this form change will automatically make the countdown start as soon the Pokémon changes into a species other than the one specified for this form change.
// param1: amount of days
#define FORM_CHANGE_DAYS_PASSED 23
#endif // GUARD_CONSTANTS_FORM_CHANGE_TYPES_H

View File

@ -1,6 +1,7 @@
#ifndef GUARD_CONSTANTS_GLOBAL_H
#define GUARD_CONSTANTS_GLOBAL_H
#include "config/general.h"
#include "config/battle.h"
#include "config/debug.h"
#include "config/item.h"

View File

@ -1629,7 +1629,7 @@
#define SPECIES_URSHIFU_RAPID_STRIKE_STYLE_GIGANTAMAX 1522
#define SPECIES_MIMIKYU_TOTEM_BUSTED 1523
#define SPECIES_EGG SPECIES_MIMIKYU_TOTEM_BUSTED + 1
#define SPECIES_EGG (SPECIES_MIMIKYU_TOTEM_BUSTED + 1)
#define NUM_SPECIES SPECIES_EGG
@ -1694,7 +1694,7 @@
#define SPECIES_MACHAMP_GMAX SPECIES_MACHAMP_GIGANTAMAX
#define SPECIES_MAGEARNA_ORIGINAL SPECIES_MAGEARNA_ORIGINAL_COLOR
#define SPECIES_MAROWAK_ALOLA SPECIES_MAROWAK_ALOLAN
#define SPECIES_MAROWAX_ALOLA_TOTEM SPECIES_MAROWAK_ALOLAN_TOTEM
#define SPECIES_MAROWAK_ALOLA_TOTEM SPECIES_MAROWAK_ALOLAN_TOTEM
#define SPECIES_MAUSHOLD_FOUR SPECIES_MAUSHOLD_FAMILY_OF_FOUR
#define SPECIES_MELMETAL_GMAX SPECIES_MELMETAL_GIGANTAMAX
#define SPECIES_MEOWTH_ALOLA SPECIES_MEOWTH_ALOLAN

View File

@ -70,11 +70,11 @@ struct TrainerMon
u8 nature:5;
bool8 gender:2;
bool8 isShiny:1;
u8 useGimmick:4;
u8 dynamaxLevel:4;
u8 teraType:5;
bool8 gigantamaxFactor:1;
bool8 shouldDynamax:1;
bool8 shouldTerastal:1;
u8 padding:2;
};
#define TRAINER_PARTY(partyArray) partyArray, .partySize = ARRAY_COUNT(partyArray)

View File

@ -3,7 +3,7 @@
#include <string.h>
#include <limits.h>
#include "config.h" // we need to define config before gba headers as print stuff needs the functions nulled before defines.
#include "config/general.h" // we need to define config before gba headers as print stuff needs the functions nulled before defines.
#include "gba/gba.h"
#include "fpmath.h"
#include "metaprogram.h"
@ -604,14 +604,15 @@ struct Roamer
/*0x08*/ u16 species;
/*0x0A*/ u16 hp;
/*0x0C*/ u8 level;
/*0x0D*/ u16 status;
/*0x0F*/ u8 cool;
/*0x10*/ u8 beauty;
/*0x11*/ u8 cute;
/*0x12*/ u8 smart;
/*0x0D*/ u8 statusA;
/*0x0E*/ u8 cool;
/*0x0F*/ u8 beauty;
/*0x10*/ u8 cute;
/*0x11*/ u8 smart;
/*0x12*/ u8 tough;
/*0x13*/ bool8 active;
/*0x14*/ u8 tough;
/*0x15*/ u8 filler[0x7];
/*0x14*/ u8 statusB; // Stores frostbite
/*0x14*/ u8 filler[0x7];
};
struct RamScriptData

View File

@ -44,6 +44,7 @@ u8 *CopyItemNameHandlePlural(u16 itemId, u8 *dst, u32 quantity);
bool8 IsBagPocketNonEmpty(u8 pocket);
bool8 CheckBagHasItem(u16 itemId, u16 count);
bool8 HasAtLeastOneBerry(void);
bool8 HasAtLeastOnePokeBall(void);
bool8 CheckBagHasSpace(u16 itemId, u16 count);
u32 GetFreeSpaceForItemInBag(u16 itemId);
bool8 AddBagItem(u16 itemId, u16 count);

View File

@ -26,6 +26,7 @@ enum {
MON_DATA_IS_SHINY,
MON_DATA_HIDDEN_NATURE,
MON_DATA_HP_LOST,
MON_DATA_DAYS_SINCE_FORM_CHANGE,
MON_DATA_ENCRYPT_SEPARATOR,
MON_DATA_NICKNAME,
MON_DATA_NICKNAME10,
@ -246,7 +247,8 @@ struct BoxPokemon
u8 hasSpecies:1;
u8 isEgg:1;
u8 blockBoxRS:1; // Unused, but Pokémon Box Ruby & Sapphire will refuse to deposit a Pokémon with this flag set.
u8 unused_13:4;
u8 daysSinceFormChange:3; // 7 days.
u8 unused_13:1;
u8 otName[PLAYER_NAME_LENGTH];
u8 markings:4;
u8 compressedStatus:4;
@ -877,5 +879,7 @@ void HealPokemon(struct Pokemon *mon);
void HealBoxPokemon(struct BoxPokemon *boxMon);
const u8 *GetMoveName(u16 moveId);
const u8 *GetMoveAnimationScript(u16 moveId);
void UpdateDaysPassedSinceFormChange(u16 days);
void TrySetDayLimitToFormChange(struct Pokemon *mon);
#endif // GUARD_POKEMON_H

View File

@ -75,5 +75,6 @@ void SetWaldaPhrase(const u8 *src);
bool32 IsWaldaPhraseEmpty(void);
void EnterPokeStorage(u8 boxOption);
u32 CountPartyNonEggMons(void);
#endif // GUARD_POKEMON_STORAGE_SYSTEM_H

View File

@ -144,13 +144,16 @@ static inline void Shuffle(void *data, size_t n, size_t size)
* probability. The array must be known at compile-time (e.g. a global
* const array).
*
* RandomPercentage(tag, t) returns FALSE with probability (1-t)/100,
* RandomPercentage(tag, t) returns FALSE with probability 1-t/100,
* and TRUE with probability t/100.
*
* RandomWeighted(tag, w0, w1, ... wN) returns a number from 0 to N
* inclusive. The return value is proportional to the weights, e.g.
* RandomWeighted(..., 1, 1) returns 50% 0s and 50% 1s.
* RandomWeighted(..., 2, 1) returns 2/3 0s and 1/3 1s. */
* RandomWeighted(..., 2, 1) returns 2/3 0s and 1/3 1s.
*
* RandomChance(tag, successes, total) returns FALSE with probability
* 1-successes/total, and TRUE with probability successes/total. */
enum RandomTag
{
@ -158,9 +161,11 @@ enum RandomTag
RNG_ACCURACY,
RNG_CONFUSION,
RNG_CRITICAL_HIT,
RNG_CURSED_BODY,
RNG_CUTE_CHARM,
RNG_DAMAGE_MODIFIER,
RNG_DIRE_CLAW,
RNG_EFFECT_SPORE,
RNG_FLAME_BODY,
RNG_FORCE_RANDOM_SWITCH,
RNG_FROZEN,
@ -175,14 +180,17 @@ enum RandomTag
RNG_METRONOME,
RNG_PARALYSIS,
RNG_POISON_POINT,
RNG_POISON_TOUCH,
RNG_RAMPAGE_TURNS,
RNG_SECONDARY_EFFECT,
RNG_SECONDARY_EFFECT_2,
RNG_SECONDARY_EFFECT_3,
RNG_SHED_SKIN,
RNG_SLEEP_TURNS,
RNG_SPEED_TIE,
RNG_STATIC,
RNG_STENCH,
RNG_TOXIC_CHAIN,
RNG_TRI_ATTACK,
RNG_QUICK_DRAW,
RNG_QUICK_CLAW,
@ -202,6 +210,8 @@ enum RandomTag
RandomWeightedArray(tag, sum, ARRAY_COUNT(weights), weights); \
})
#define RandomChance(tag, successes, total) (RandomWeighted(tag, total - successes, successes))
#define RandomPercentage(tag, t) \
({ \
u32 r; \

View File

@ -326,14 +326,14 @@
* The inference process is naive, if your test contains anything that
* modifies the speed of a battler you should specify them explicitly.
*
* MOVE(battler, move | moveSlot:, [megaEvolve:], [hit:], [criticalHit:], [target:], [allowed:], [WITH_RNG(tag, value])
* MOVE(battler, move | moveSlot:, [gimmick:], [hit:], [criticalHit:], [target:], [allowed:], [WITH_RNG(tag, value])
* Used when the battler chooses Fight. Either the move ID or move slot
* must be specified. megaEvolve: TRUE causes the battler to Mega Evolve
* if able, hit: FALSE causes the move to miss, criticalHit: TRUE causes
* the move to land a critical hit, target: is used in double battles to
* choose the target (when necessary), and allowed: FALSE is used to
* reject an illegal move e.g. a Disabled one. WITH_RNG allows the move
* to specify an explicit outcome for an RNG tag.
* must be specified. gimmick: GIMMICK_MEGA causes the battler to Mega
* Evolve if able, hit: FALSE causes the move to miss, criticalHit: TRUE
* causes the move to land a critical hit, target: is used in double
* battles to choose the target (when necessary), and allowed: FALSE is
* used to reject an illegal move e.g. a Disabled one. WITH_RNG allows
* the move to specify an explicit outcome for an RNG tag.
* MOVE(playerLeft, MOVE_TACKLE, target: opponentRight);
* If the battler does not have an explicit Moves specified the moveset
* will be populated based on the MOVEs it uses.
@ -507,7 +507,7 @@
// or loop.
#define BATTLE_TEST_STACK_SIZE 1024
#define MAX_TURNS 16
#define MAX_QUEUED_EVENTS 25
#define MAX_QUEUED_EVENTS 30
#define MAX_EXPECTED_ACTIONS 10
enum { BATTLE_TEST_SINGLES, BATTLE_TEST_DOUBLES, BATTLE_TEST_WILD, BATTLE_TEST_AI_SINGLES, BATTLE_TEST_AI_DOUBLES };
@ -662,6 +662,7 @@ struct BattleTestData
u8 gender;
u8 nature;
u16 forcedAbilities[NUM_BATTLE_SIDES][PARTY_SIZE];
u8 chosenGimmick[NUM_BATTLE_SIDES][PARTY_SIZE];
u8 currentMonIndexes[MAX_BATTLERS_COUNT];
u8 turnState;
@ -695,6 +696,7 @@ struct BattleTestData
struct BattleTestRunnerState
{
u8 battlersCount;
bool8 forceMoveAnim;
u16 parametersCount; // Valid only in BattleTest_Setup.
u16 parameters;
u16 runParameter;
@ -940,15 +942,8 @@ struct MoveContext
u16 explicitCriticalHit:1;
u16 secondaryEffect:1;
u16 explicitSecondaryEffect:1;
u16 megaEvolve:1;
u16 explicitMegaEvolve:1;
u16 ultraBurst:1;
u16 explicitUltraBurst:1;
// TODO: u8 zMove:1;
u16 dynamax:1;
u16 explicitDynamax:1;
u16 tera:1;
u16 explicitTera:1;
u16 gimmick:4;
u16 explicitGimmick:1;
u16 allowed:1;
u16 explicitAllowed:1;
u16 notExpected:1; // Has effect only with EXPECT_MOVE
@ -991,6 +986,8 @@ void SendOut(u32 sourceLine, struct BattlePokemon *, u32 partyIndex);
#define NONE_OF for (OpenQueueGroup(__LINE__, QUEUE_GROUP_NONE_OF); gBattleTestRunnerState->data.queueGroupType != QUEUE_GROUP_NONE; CloseQueueGroup(__LINE__))
#define NOT NONE_OF
#define FORCE_MOVE_ANIM(set) gBattleTestRunnerState->forceMoveAnim = (set)
#define ABILITY_POPUP(battler, ...) QueueAbility(__LINE__, battler, (struct AbilityEventContext) { __VA_ARGS__ })
#define ANIMATION(type, id, ...) QueueAnimation(__LINE__, type, id, (struct AnimationEventContext) { __VA_ARGS__ })
#define HP_BAR(battler, ...) QueueHP(__LINE__, battler, (struct HPEventContext) { R_APPEND_TRUE(__VA_ARGS__) })

View File

@ -24,6 +24,7 @@ void TestRunner_Battle_InvalidNoHPMon(u32 battlerId, u32 partyIndex);
void TestRunner_Battle_CheckBattleRecordActionType(u32 battlerId, u32 recordIndex, u32 actionType);
u32 TestRunner_Battle_GetForcedAbility(u32 side, u32 partyIndex);
u32 TestRunner_Battle_GetChosenGimmick(u32 side, u32 partyIndex);
#else
@ -45,6 +46,8 @@ u32 TestRunner_Battle_GetForcedAbility(u32 side, u32 partyIndex);
#define TestRunner_Battle_GetForcedAbility(...) (u32)0
#define TestRunner_Battle_GetChosenGimmick(...) (u32)0
#endif
#endif

View File

@ -8,6 +8,7 @@ bool8 UpdateVsSeekerStepCounter(void);
void MapResetTrainerRematches(u16 mapGroup, u16 mapNum);
void ClearRematchMovementByTrainerId(void);
u16 GetRematchTrainerIdVSSeeker(u16 trainerId);
bool32 IsVsSeekerEnabled(void);
#define VSSEEKER_RECHARGE_STEPS 100

View File

@ -70,7 +70,7 @@ static s32 (*const sBattleAiFuncTable[])(u32, u32, u32, s32) =
[9] = AI_PowerfulStatus, // AI_FLAG_POWERFUL_STATUS
[10] = NULL, // AI_FLAG_NEGATE_UNAWARE
[11] = NULL, // AI_FLAG_WILL_SUICIDE
[12] = NULL, // AI_FLAG_HELP_PARTNER
[12] = NULL, // Unused
[13] = NULL, // Unused
[14] = NULL, // Unused
[15] = NULL, // Unused
@ -399,23 +399,6 @@ static void SetBattlerAiData(u32 battler, struct AiLogicData *aiData)
aiData->speedStats[battler] = GetBattlerTotalSpeedStatArgs(battler, ability, holdEffect);
}
static void SetBattlerAiGimmickData(u32 battler, struct AiLogicData *aiData)
{
bool32 isSecondTrainer = (GetBattlerPosition(battler) == B_POSITION_OPPONENT_RIGHT) && (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && !BATTLE_TWO_VS_ONE_OPPONENT;
u16 trainerId = isSecondTrainer ? gTrainerBattleOpponent_B : gTrainerBattleOpponent_A;
const struct TrainerMon *party = GetTrainerPartyFromId(trainerId);
if (party != NULL)
{
aiData->shouldDynamax[battler] = CanDynamax(battler) && (party[isSecondTrainer ? gBattlerPartyIndexes[battler] - MULTI_PARTY_SIZE : gBattlerPartyIndexes[battler]].shouldDynamax);
aiData->shouldTerastal[battler] = CanTerastallize(battler) && (party[isSecondTrainer ? gBattlerPartyIndexes[battler] - MULTI_PARTY_SIZE : gBattlerPartyIndexes[battler]].shouldTerastal);
}
else
{
aiData->shouldDynamax[battler] = FALSE;
aiData->shouldTerastal[battler] = FALSE;
}
}
static u32 Ai_SetMoveAccuracy(struct AiLogicData *aiData, u32 battlerAtk, u32 battlerDef, u32 move)
{
u32 accuracy;
@ -495,7 +478,6 @@ void SetAiLogicDataForTurn(struct AiLogicData *aiData)
continue;
SetBattlerAiData(battlerAtk, aiData);
SetBattlerAiGimmickData(battlerAtk, aiData);
SetBattlerAiMovesData(aiData, battlerAtk, battlersCount);
}
}
@ -529,7 +511,8 @@ static bool32 AI_ShouldSwitchIfBadMoves(u32 battler, bool32 doubleBattle)
if (CountUsablePartyMons(battler) > 0
&& !IsBattlerTrapped(battler, TRUE)
&& !(gBattleTypeFlags & (BATTLE_TYPE_ARENA | BATTLE_TYPE_PALACE))
&& AI_THINKING_STRUCT->aiFlags[battler] & (AI_FLAG_CHECK_VIABILITY | AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_PREFER_BATON_PASS))
&& AI_THINKING_STRUCT->aiFlags[battler] & (AI_FLAG_CHECK_VIABILITY | AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_PREFER_BATON_PASS)
&& !(AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SEQUENCE_SWITCHING))
{
// Consider switching if all moves are worthless to use.
if (GetTotalBaseStat(gBattleMons[battler].species) >= 310 // Mon is not weak.
@ -1458,7 +1441,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-10);
else if (aiData->abilities[battlerDef] == ABILITY_SUCTION_CUPS)
ADJUST_SCORE(-10);
else if (IsDynamaxed(battlerDef))
else if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
ADJUST_SCORE(-10);
break;
case EFFECT_TOXIC_THREAD:
@ -1491,7 +1474,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
return 0;
if (!ShouldTryOHKO(battlerAtk, battlerDef, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], move))
ADJUST_SCORE(-10);
else if (IsDynamaxed(battlerDef))
else if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
ADJUST_SCORE(-10);
break;
case EFFECT_MIST:
@ -1530,7 +1513,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-3);
break;
case EFFECT_DISABLE:
if (IsDynamaxed(battlerDef))
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
ADJUST_SCORE(-10);
else if (gDisableStructs[battlerDef].disableTimer == 0
&& (B_MENTAL_HERB < GEN_5 || aiData->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB)
@ -1552,7 +1535,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
}
break;
case EFFECT_ENCORE:
if (IsDynamaxed(battlerDef))
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
ADJUST_SCORE(-10);
else if (gDisableStructs[battlerDef].encoreTimer == 0
&& (B_MENTAL_HERB < GEN_5 || aiData->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB)
@ -1768,7 +1751,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
}
break;
case EFFECT_TORMENT:
if (IsDynamaxed(battlerDef))
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
ADJUST_SCORE(-10);
else if (gBattleMons[battlerDef].status2 & STATUS2_TORMENT
|| DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove))
@ -1893,7 +1876,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-10);
break;
case EFFECT_REST:
if (!AI_CanSleep(battlerAtk, aiData->abilities[battlerAtk]))
if (!CanBeSlept(battlerAtk, aiData->abilities[battlerAtk]))
ADJUST_SCORE(-10);
//fallthrough
case EFFECT_RESTORE_HP:
@ -1974,7 +1957,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_DESTINY_BOND:
if (gBattleMons[battlerDef].status2 & STATUS2_DESTINY_BOND)
ADJUST_SCORE(-10);
else if (IsDynamaxed(battlerDef))
else if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
ADJUST_SCORE(-10);
break;
case EFFECT_HEAL_BELL:
@ -2128,7 +2111,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-10);
break;
case EFFECT_NATURE_POWER:
return AI_CheckBadMove(battlerAtk, battlerDef, GetNaturePowerMove(), score);
return AI_CheckBadMove(battlerAtk, battlerDef, GetNaturePowerMove(battlerAtk), score);
case EFFECT_TAUNT:
if (gDisableStructs[battlerDef].tauntTimer > 0
|| DoesPartnerHaveSameMoveEffect(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove))
@ -2172,7 +2155,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|| gAbilitiesInfo[aiData->abilities[battlerDef]].cantBeSwapped
|| aiData->holdEffects[battlerDef] == HOLD_EFFECT_ABILITY_SHIELD)
ADJUST_SCORE(-10);
else if (IsDynamaxed(battlerDef))
else if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
ADJUST_SCORE(-10);
break;
case EFFECT_WORRY_SEED:
@ -2192,7 +2175,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|| gAbilitiesInfo[aiData->abilities[battlerDef]].cantBeOverwritten
|| aiData->holdEffects[battlerAtk] == HOLD_EFFECT_ABILITY_SHIELD)
ADJUST_SCORE(-10);
else if (IsDynamaxed(battlerDef))
else if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
ADJUST_SCORE(-10);
break;
case EFFECT_SIMPLE_BEAM:
@ -2448,11 +2431,11 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
switch (move)
{
case MOVE_TRICK_OR_TREAT:
if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove) || IsTerastallized(battlerDef))
if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GHOST) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove) || GetActiveGimmick(battlerDef) == GIMMICK_TERA)
ADJUST_SCORE(-10);
break;
case MOVE_FORESTS_CURSE:
if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove) || IsTerastallized(battlerDef))
if (IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS) || PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove) || GetActiveGimmick(battlerDef) == GIMMICK_TERA)
ADJUST_SCORE(-10);
break;
}
@ -2520,7 +2503,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
{
ADJUST_SCORE(-10);
}
else if (IsDynamaxed(battlerDef))
else if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
ADJUST_SCORE(-10);
else if (isDoubleBattle)
{
@ -2672,6 +2655,22 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
return 0; // cannot even select
} // move effect checks
// Choice items
if (HOLD_EFFECT_CHOICE(aiData->holdEffects[battlerAtk]) && gBattleMons[battlerAtk].ability != ABILITY_KLUTZ)
{
// Don't use user-target moves ie. Swords Dance, with exceptions
if ((moveTarget & MOVE_TARGET_USER)
&& moveEffect != EFFECT_DESTINY_BOND && moveEffect != EFFECT_WISH && moveEffect != EFFECT_HEALING_WISH
&& !(moveEffect == EFFECT_AURORA_VEIL && (AI_GetWeather(aiData) & (B_WEATHER_SNOW | B_WEATHER_HAIL))))
ADJUST_SCORE(-30);
// Don't use a status move if the mon is the last one in the party, has no good switchin, or is trapped
else if (GetBattleMoveCategory(move) == DAMAGE_CATEGORY_STATUS
&& (CountUsablePartyMons(battlerAtk) < 1
|| AI_DATA->mostSuitableMonId[battlerAtk] == PARTY_SIZE
|| IsBattlerTrapped(battlerAtk, TRUE)))
ADJUST_SCORE(-30);
}
if (score < 0)
score = 0;
@ -3013,8 +3012,8 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
break;
case EFFECT_SOAK:
if (atkPartnerAbility == ABILITY_WONDER_GUARD
&& IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_WATER)
&& !IsTerastallized(battlerAtkPartner))
&& !IS_BATTLER_OF_TYPE(battlerAtkPartner, TYPE_WATER)
&& GetActiveGimmick(battlerAtkPartner) != GIMMICK_TERA)
{
RETURN_SCORE_PLUS(WEAK_EFFECT);
}
@ -3236,7 +3235,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
u32 i;
// The AI should understand that while Dynamaxed, status moves function like Protect.
if (IsDynamaxed(battlerAtk) && gMovesInfo[move].category == DAMAGE_CATEGORY_STATUS)
if (GetActiveGimmick(battlerAtk) == GIMMICK_DYNAMAX && gMovesInfo[move].category == DAMAGE_CATEGORY_STATUS)
moveEffect = EFFECT_PROTECT;
// check status move preference
@ -3431,7 +3430,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
if ((gMovesInfo[move].soundMove && aiData->abilities[battlerDef] == ABILITY_SOUNDPROOF)
|| aiData->abilities[battlerDef] == ABILITY_SUCTION_CUPS)
break;
else if (IsDynamaxed(battlerDef))
else if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
break;
score += AI_TryToClearStats(battlerAtk, battlerDef, isDoubleBattle);
break;
@ -3499,7 +3498,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
}
break;
case EFFECT_REST:
if (!(AI_CanSleep(battlerAtk, aiData->abilities[battlerAtk])))
if (!(CanBeSlept(battlerAtk, aiData->abilities[battlerAtk])))
{
break;
}
@ -3516,7 +3515,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
}
break;
case EFFECT_OHKO:
if (IsDynamaxed(battlerDef))
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
break;
else if (gStatuses3[battlerAtk] & STATUS3_ALWAYS_HITS)
ADJUST_SCORE(BEST_EFFECT);
@ -3613,7 +3612,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(BEST_EFFECT);
break;
case EFFECT_DISABLE:
if (IsDynamaxed(battlerDef))
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
break;
else if (gDisableStructs[battlerDef].disableTimer == 0
&& (gLastMoves[battlerDef] != MOVE_NONE)
@ -3626,7 +3625,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
}
break;
case EFFECT_ENCORE:
if (IsDynamaxed(battlerDef))
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
break;
else if (gDisableStructs[battlerDef].encoreTimer == 0
&& (B_MENTAL_HERB < GEN_5 || aiData->holdEffects[battlerDef] != HOLD_EFFECT_MENTAL_HERB)
@ -3645,7 +3644,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(GOOD_EFFECT);
break;
case EFFECT_DESTINY_BOND:
if (IsDynamaxed(battlerDef))
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
break;
else if (AI_IsFaster(battlerAtk, battlerDef, move) && CanTargetFaintAi(battlerDef, battlerAtk))
ADJUST_SCORE(GOOD_EFFECT);
@ -3758,7 +3757,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(GOOD_EFFECT);
break;
case EFFECT_SANDSTORM:
if (ShouldSetSandstorm(battlerAtk, aiData->holdEffects[battlerAtk], aiData->holdEffects[battlerAtk]))
if (ShouldSetSandstorm(battlerAtk, aiData->abilities[battlerAtk], aiData->holdEffects[battlerAtk]))
{
ADJUST_SCORE(DECENT_EFFECT);
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_SMOOTH_ROCK)
@ -3979,7 +3978,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_FLAME_ORB:
if (!ShouldBurnSelf(battlerAtk, aiData->abilities[battlerAtk]) && AI_CanBeBurned(battlerAtk, aiData->abilities[battlerDef]))
if (!ShouldBurnSelf(battlerAtk, aiData->abilities[battlerAtk]) && CanBeBurned(battlerAtk, aiData->abilities[battlerDef]))
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_BLACK_SLUDGE:
@ -4012,7 +4011,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
}
break;
case HOLD_EFFECT_EJECT_BUTTON:
//if (!IsRaidBattle() && IsDynamaxed(battlerDef) && gNewBS->dynamaxData.timer[battlerDef] > 1 &&
//if (!IsRaidBattle() && GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX && gNewBS->dynamaxData.timer[battlerDef] > 1 &&
if (HasDamagingMove(battlerAtk)
|| (isDoubleBattle && IsBattlerAlive(BATTLE_PARTNER(battlerAtk)) && HasDamagingMove(BATTLE_PARTNER(battlerAtk))))
ADJUST_SCORE(DECENT_EFFECT); // Force 'em out next turn
@ -4094,7 +4093,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(DECENT_EFFECT);
break;
case EFFECT_SKILL_SWAP:
if (IsDynamaxed(battlerDef))
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
break;
else if (gAbilitiesInfo[aiData->abilities[battlerDef]].aiRating > gAbilitiesInfo[aiData->abilities[battlerAtk]].aiRating)
ADJUST_SCORE(DECENT_EFFECT);
@ -4106,7 +4105,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(DECENT_EFFECT);
break;
case EFFECT_ENTRAINMENT:
if (IsDynamaxed(battlerDef))
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
break;
else if ((IsAbilityOfRating(aiData->abilities[battlerDef], 5) || gAbilitiesInfo[aiData->abilities[battlerAtk]].aiRating <= 0)
&& (aiData->abilities[battlerDef] != aiData->abilities[battlerAtk] && !(gStatuses3[battlerDef] & STATUS3_GASTRO_ACID)))
@ -4735,17 +4734,17 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
if (gMovesInfo[move].power)
{
if (GetNoOfHitsToKOBattler(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex) == 0)
ADJUST_SCORE(-20);
ADJUST_AND_RETURN_SCORE(NO_DAMAGE_OR_FAILS); // No point in checking the move further so return early
else
{
if ((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_RISKY) && GetBestDmgMoveFromBattler(battlerAtk, battlerDef) == move)
ADJUST_SCORE(1);
ADJUST_SCORE(BEST_DAMAGE_MOVE);
else
score += AI_CompareDamagingMoves(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex);
ADJUST_SCORE(AI_CompareDamagingMoves(battlerAtk, battlerDef, AI_THINKING_STRUCT->movesetIndex));
}
}
score += AI_CalcMoveEffectScore(battlerAtk, battlerDef, move);
ADJUST_SCORE(AI_CalcMoveEffectScore(battlerAtk, battlerDef, move));
return score;
}

View File

@ -485,7 +485,7 @@ static bool32 ShouldSwitchIfGameStatePrompt(u32 battler, bool32 emitResult)
{
//Yawn
if (gStatuses3[battler] & STATUS3_YAWN
&& AI_CanSleep(battler, monAbility)
&& CanBeSlept(battler, monAbility)
&& gBattleMons[battler].hp > gBattleMons[battler].maxHP / 3)
{
switchMon = TRUE;
@ -919,6 +919,24 @@ static bool32 ShouldSwitchIfEncored(u32 battler, bool32 emitResult)
return FALSE;
}
static bool32 ShouldSwitchIfBadChoiceLock(u32 battler, bool32 emitResult)
{
u32 holdEffect = GetBattlerHoldEffect(battler, FALSE);
if (HOLD_EFFECT_CHOICE(holdEffect) && gBattleMons[battler].ability != ABILITY_KLUTZ)
{
if (gMovesInfo[gLastUsedMove].category == DAMAGE_CATEGORY_STATUS)
{
gBattleStruct->AI_monToSwitchIntoId[battler] = PARTY_SIZE;
if (emitResult)
BtlController_EmitTwoReturnValues(battler, 1, B_ACTION_SWITCH, 0);
return TRUE;
}
}
return FALSE;
}
// AI should switch if it's become setup fodder and has something better to switch to
static bool32 AreAttackingStatsLowered(u32 battler, bool32 emitResult)
{
@ -941,7 +959,8 @@ static bool32 AreAttackingStatsLowered(u32 battler, bool32 emitResult)
if (AI_DATA->mostSuitableMonId[battler] != PARTY_SIZE && (Random() & 1))
{
gBattleStruct->AI_monToSwitchIntoId[battler] = PARTY_SIZE;
BtlController_EmitTwoReturnValues(battler, 1, B_ACTION_SWITCH, 0);
if (emitResult)
BtlController_EmitTwoReturnValues(battler, 1, B_ACTION_SWITCH, 0);
return TRUE;
}
}
@ -949,7 +968,8 @@ static bool32 AreAttackingStatsLowered(u32 battler, bool32 emitResult)
else if (attackingStage < DEFAULT_STAT_STAGE - 2)
{
gBattleStruct->AI_monToSwitchIntoId[battler] = PARTY_SIZE;
BtlController_EmitTwoReturnValues(battler, 1, B_ACTION_SWITCH, 0);
if (emitResult)
BtlController_EmitTwoReturnValues(battler, 1, B_ACTION_SWITCH, 0);
return TRUE;
}
}
@ -1000,6 +1020,10 @@ bool32 ShouldSwitch(u32 battler, bool32 emitResult)
if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
return FALSE;
// Sequence Switching AI never switches mid-battle
if (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SEQUENCE_SWITCHING)
return FALSE;
availableToSwitch = 0;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
@ -1076,6 +1100,8 @@ bool32 ShouldSwitch(u32 battler, bool32 emitResult)
return TRUE;
if (ShouldSwitchIfEncored(battler, emitResult))
return TRUE;
if (ShouldSwitchIfBadChoiceLock(battler, emitResult))
return TRUE;
if (AreAttackingStatsLowered(battler, emitResult))
return TRUE;
@ -1312,7 +1338,7 @@ static bool32 IsMonGrounded(u16 heldItemEffect, u32 ability, u8 type1, u8 type2)
static u32 GetSwitchinHazardsDamage(u32 battler, struct BattlePokemon *battleMon)
{
u8 defType1 = battleMon->type1, defType2 = battleMon->type2, tSpikesLayers;
u16 heldItemEffect = gItemsInfo[battleMon->item].holdEffect;
u16 heldItemEffect = ItemId_GetHoldEffect(battleMon->item);
u32 maxHP = battleMon->maxHP, ability = battleMon->ability, status = battleMon->status1;
u32 spikesDamage = 0, tSpikesDamage = 0, hazardDamage = 0;
u32 hazardFlags = gSideStatuses[GetBattlerSide(battler)] & (SIDE_STATUS_SPIKES | SIDE_STATUS_STEALTH_ROCK | SIDE_STATUS_STICKY_WEB | SIDE_STATUS_TOXIC_SPIKES | SIDE_STATUS_SAFEGUARD);
@ -1336,8 +1362,6 @@ static u32 GetSwitchinHazardsDamage(u32 battler, struct BattlePokemon *battleMon
hazardDamage += spikesDamage;
}
// Toxic Spikes
// TODO: CanBePoisoned compatibility to avoid duplicate code
if ((hazardFlags & SIDE_STATUS_TOXIC_SPIKES) && (defType1 != TYPE_POISON && defType2 != TYPE_POISON
&& defType1 != TYPE_STEEL && defType2 != TYPE_STEEL
&& ability != ABILITY_IMMUNITY && ability != ABILITY_POISON_HEAL && ability != ABILITY_COMATOSE
@ -1372,7 +1396,7 @@ static u32 GetSwitchinHazardsDamage(u32 battler, struct BattlePokemon *battleMon
static s32 GetSwitchinWeatherImpact(void)
{
s32 weatherImpact = 0, maxHP = AI_DATA->switchinCandidate.battleMon.maxHP, ability = AI_DATA->switchinCandidate.battleMon.ability;
u32 holdEffect = gItemsInfo[AI_DATA->switchinCandidate.battleMon.item].holdEffect;
u32 holdEffect = ItemId_GetHoldEffect(AI_DATA->switchinCandidate.battleMon.item);
if (WEATHER_HAS_EFFECT)
{
@ -1436,7 +1460,7 @@ static s32 GetSwitchinWeatherImpact(void)
static u32 GetSwitchinRecurringHealing(void)
{
u32 recurringHealing = 0, maxHP = AI_DATA->switchinCandidate.battleMon.maxHP, ability = AI_DATA->switchinCandidate.battleMon.ability;
u32 holdEffect = gItemsInfo[AI_DATA->switchinCandidate.battleMon.item].holdEffect;
u32 holdEffect = ItemId_GetHoldEffect(AI_DATA->switchinCandidate.battleMon.item);
// Items
if (ability != ABILITY_KLUTZ)
@ -1470,7 +1494,7 @@ static u32 GetSwitchinRecurringHealing(void)
static u32 GetSwitchinRecurringDamage(void)
{
u32 passiveDamage = 0, maxHP = AI_DATA->switchinCandidate.battleMon.maxHP, ability = AI_DATA->switchinCandidate.battleMon.ability;
u32 holdEffect = gItemsInfo[AI_DATA->switchinCandidate.battleMon.item].holdEffect;
u32 holdEffect = ItemId_GetHoldEffect(AI_DATA->switchinCandidate.battleMon.item);
// Items
if (ability != ABILITY_MAGIC_GUARD && ability != ABILITY_KLUTZ)
@ -1502,7 +1526,7 @@ static u32 GetSwitchinStatusDamage(u32 battler)
{
u8 defType1 = AI_DATA->switchinCandidate.battleMon.type1, defType2 = AI_DATA->switchinCandidate.battleMon.type2;
u8 tSpikesLayers = gSideTimers[GetBattlerSide(battler)].toxicSpikesAmount;
u16 heldItemEffect = gItemsInfo[AI_DATA->switchinCandidate.battleMon.item].holdEffect;
u16 heldItemEffect = ItemId_GetHoldEffect(AI_DATA->switchinCandidate.battleMon.item);
u32 status = AI_DATA->switchinCandidate.battleMon.status1, ability = AI_DATA->switchinCandidate.battleMon.ability, maxHP = AI_DATA->switchinCandidate.battleMon.maxHP;
u32 statusDamage = 0;
@ -1580,8 +1604,8 @@ static u32 GetSwitchinHitsToKO(s32 damageTaken, u32 battler)
u32 recurringHealing = GetSwitchinRecurringHealing();
u32 statusDamage = GetSwitchinStatusDamage(battler);
u32 hitsToKO = 0, singleUseItemHeal = 0;
u16 maxHP = AI_DATA->switchinCandidate.battleMon.maxHP, item = AI_DATA->switchinCandidate.battleMon.item, heldItemEffect = gItemsInfo[item].holdEffect;
u8 weatherDuration = gWishFutureKnock.weatherDuration, holdEffectParam = gItemsInfo[item].holdEffectParam;
u16 maxHP = AI_DATA->switchinCandidate.battleMon.maxHP, item = AI_DATA->switchinCandidate.battleMon.item, heldItemEffect = ItemId_GetHoldEffect(item);
u8 weatherDuration = gWishFutureKnock.weatherDuration, holdEffectParam = ItemId_GetHoldEffectParam(item);
u32 opposingBattler = GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerPosition(battler)));
u32 opposingAbility = gBattleMons[opposingBattler].ability;
bool32 usedSingleUseHealingItem = FALSE;
@ -1765,7 +1789,7 @@ static bool32 CanAbilityTrapOpponent(u16 ability, u32 opponent)
// the Most Damage code will prioritize switching into whatever mon deals the most damage, which is generally not as good as having a good Type Matchup
// Everything runs in the same loop to minimize computation time. This makes it harder to read, but hopefully the comments can guide you!
static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId, u32 battler, u32 opposingBattler, u8 battlerIn1, u8 battlerIn2, bool32 isSwitchAfterKO)
static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId, u32 battler, u32 opposingBattler, u32 battlerIn1, u32 battlerIn2, bool32 isSwitchAfterKO)
{
int revengeKillerId = PARTY_SIZE, slowRevengeKillerId = PARTY_SIZE, fastThreatenId = PARTY_SIZE, slowThreatenId = PARTY_SIZE, damageMonId = PARTY_SIZE;
int batonPassId = PARTY_SIZE, typeMatchupId = PARTY_SIZE, typeMatchupEffectiveId = PARTY_SIZE, defensiveMonId = PARTY_SIZE, aceMonId = PARTY_SIZE, trapperId = PARTY_SIZE;
@ -1803,7 +1827,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
InitializeSwitchinCandidate(&party[i]);
// While not really invalid per say, not really wise to switch into this mon
// While not really invalid per se, not really wise to switch into this mon
if (AI_DATA->switchinCandidate.battleMon.ability == ABILITY_TRUANT && IsTruantMonVulnerable(battler, opposingBattler))
continue;
@ -1883,7 +1907,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
// If AI mon outspeeds and doesn't die to hazards
if ((((aiMonSpeed > playerMonSpeed && !(gFieldStatuses & STATUS_FIELD_TRICK_ROOM)) || aiMovePriority > 0) // Outspeed if not Trick Room
|| ((gFieldStatuses & STATUS_FIELD_TRICK_ROOM) // Trick Room
&& (aiMonSpeed < playerMonSpeed || (gItemsInfo[AI_DATA->switchinCandidate.battleMon.item].holdEffect == HOLD_EFFECT_ROOM_SERVICE && aiMonSpeed * 2 / 3 < playerMonSpeed)))) // Trick Room speeds
&& (aiMonSpeed < playerMonSpeed || (ItemId_GetHoldEffect(AI_DATA->switchinCandidate.battleMon.item) == HOLD_EFFECT_ROOM_SERVICE && aiMonSpeed * 2 / 3 < playerMonSpeed)))) // Trick Room speeds
&& AI_DATA->switchinCandidate.battleMon.hp > GetSwitchinHazardsDamage(battler, &AI_DATA->switchinCandidate.battleMon)) // Hazards
{
// We have a revenge killer
@ -1908,7 +1932,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
// If AI mon outspeeds
if (((aiMonSpeed > playerMonSpeed && !(gFieldStatuses & STATUS_FIELD_TRICK_ROOM)) || aiMovePriority > 0) // Outspeed if not Trick Room
|| (((gFieldStatuses & STATUS_FIELD_TRICK_ROOM) && gFieldTimers.trickRoomTimer > 1) // Trick Room has at least 2 turns left
&& (aiMonSpeed < playerMonSpeed || (gItemsInfo[AI_DATA->switchinCandidate.battleMon.item].holdEffect == HOLD_EFFECT_ROOM_SERVICE && aiMonSpeed * 2/ 3 < playerMonSpeed)))) // Trick Room speeds
&& (aiMonSpeed < playerMonSpeed || (ItemId_GetHoldEffect(AI_DATA->switchinCandidate.battleMon.item) == HOLD_EFFECT_ROOM_SERVICE && aiMonSpeed * 2/ 3 < playerMonSpeed)))) // Trick Room speeds
{
// If AI mon can't be OHKO'd
if (hitsToKOAI > hitsToKOAIThreshold)
@ -1949,53 +1973,59 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
// Different switching priorities depending on switching mid battle vs switching after a KO
if (isSwitchAfterKO)
{
// Return Trapper > GetBestMonRevengeKiller > GetBestMonTypeMatchup > GetBestMonBatonPass > GetBestMonDmg
if (trapperId != PARTY_SIZE)
return trapperId;
else if (revengeKillerId != PARTY_SIZE)
return revengeKillerId;
else if (slowRevengeKillerId != PARTY_SIZE)
return slowRevengeKillerId;
else if (fastThreatenId != PARTY_SIZE)
return fastThreatenId;
else if (slowThreatenId != PARTY_SIZE)
return slowThreatenId;
else if (typeMatchupEffectiveId != PARTY_SIZE)
return typeMatchupEffectiveId;
else if (typeMatchupId != PARTY_SIZE)
return typeMatchupId;
else if (batonPassId != PARTY_SIZE)
return batonPassId;
else if (damageMonId != PARTY_SIZE)
return damageMonId;
// Return Trapper > Revenge Killer > Type Matchup > Baton Pass > Best Damage
if (trapperId != PARTY_SIZE) return trapperId;
else if (revengeKillerId != PARTY_SIZE) return revengeKillerId;
else if (slowRevengeKillerId != PARTY_SIZE) return slowRevengeKillerId;
else if (fastThreatenId != PARTY_SIZE) return fastThreatenId;
else if (slowThreatenId != PARTY_SIZE) return slowThreatenId;
else if (typeMatchupEffectiveId != PARTY_SIZE) return typeMatchupEffectiveId;
else if (typeMatchupId != PARTY_SIZE) return typeMatchupId;
else if (batonPassId != PARTY_SIZE) return batonPassId;
else if (damageMonId != PARTY_SIZE) return damageMonId;
}
else
{
// Return Trapper > GetBestMonTypeMatchup > GetBestMonDefensive > GetBestMonBatonPass
if (trapperId != PARTY_SIZE)
return trapperId;
else if (typeMatchupEffectiveId != PARTY_SIZE)
return typeMatchupEffectiveId;
else if (typeMatchupId != PARTY_SIZE)
return typeMatchupId;
else if (defensiveMonId != PARTY_SIZE)
return defensiveMonId;
else if (batonPassId != PARTY_SIZE)
return batonPassId;
// Return Trapper > Type Matchup > Best Defensive > Baton Pass
if (trapperId != PARTY_SIZE) return trapperId;
else if (typeMatchupEffectiveId != PARTY_SIZE) return typeMatchupEffectiveId;
else if (typeMatchupId != PARTY_SIZE) return typeMatchupId;
else if (defensiveMonId != PARTY_SIZE) return defensiveMonId;
else if (batonPassId != PARTY_SIZE) return batonPassId;
// If ace mon is the last available Pokemon and U-Turn/Volt Switch was used - switch to the mon.
else if (aceMonId != PARTY_SIZE
&& (gMovesInfo[gLastUsedMove].effect == EFFECT_HIT_ESCAPE || gMovesInfo[gLastUsedMove].effect == EFFECT_PARTING_SHOT))
&& (gMovesInfo[gLastUsedMove].effect == EFFECT_HIT_ESCAPE || gMovesInfo[gLastUsedMove].effect == EFFECT_PARTING_SHOT || gMovesInfo[gLastUsedMove].effect == EFFECT_BATON_PASS))
return aceMonId;
}
return PARTY_SIZE;
}
u8 GetMostSuitableMonToSwitchInto(u32 battler, bool32 switchAfterMonKOd)
static u32 GetNextMonInParty(struct Pokemon *party, int firstId, int lastId, u32 battlerIn1, u32 battlerIn2)
{
u32 i;
// Iterate through mons
for (i = firstId; i < lastId; i++)
{
// Check mon validity
if (!IsValidForBattle(&party[i])
|| gBattlerPartyIndexes[battlerIn1] == i
|| gBattlerPartyIndexes[battlerIn2] == i
|| i == gBattleStruct->monToSwitchIntoId[battlerIn1]
|| i == gBattleStruct->monToSwitchIntoId[battlerIn2])
{
continue;
}
return i;
}
return PARTY_SIZE;
}
u32 GetMostSuitableMonToSwitchInto(u32 battler, bool32 switchAfterMonKOd)
{
u32 opposingBattler = 0;
u32 bestMonId = PARTY_SIZE;
u8 battlerIn1 = 0, battlerIn2 = 0;
u32 battlerIn1 = 0, battlerIn2 = 0;
s32 firstId = 0;
s32 lastId = 0; // + 1
struct Pokemon *party;
@ -2031,6 +2061,12 @@ u8 GetMostSuitableMonToSwitchInto(u32 battler, bool32 switchAfterMonKOd)
else
party = gEnemyParty;
if (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SEQUENCE_SWITCHING)
{
bestMonId = GetNextMonInParty(party, firstId, lastId, battlerIn1, battlerIn2);
return bestMonId;
}
// Split ideal mon decision between after previous mon KO'd (prioritize offensive options) and after switching active mon out (prioritize defensive options), and expand the scope of both.
// Only use better mon selection if AI_FLAG_SMART_MON_CHOICES is set for the trainer.
if (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SMART_MON_CHOICES && !(gBattleTypeFlags & BATTLE_TYPE_DOUBLE)) // Double Battles aren't included in AI_FLAG_SMART_MON_CHOICE. Defaults to regular switch in logic
@ -2052,7 +2088,7 @@ u8 GetMostSuitableMonToSwitchInto(u32 battler, bool32 switchAfterMonKOd)
|| gBattlerPartyIndexes[battlerIn2] == i
|| i == gBattleStruct->monToSwitchIntoId[battlerIn1]
|| i == gBattleStruct->monToSwitchIntoId[battlerIn2]
|| (GetMonAbility(&party[i]) == ABILITY_TRUANT && IsTruantMonVulnerable(battler, opposingBattler))) // While not really invalid per say, not really wise to switch into this mon.)
|| (GetMonAbility(&party[i]) == ABILITY_TRUANT && IsTruantMonVulnerable(battler, opposingBattler))) // While not really invalid per se, not really wise to switch into this mon.)
{
invalidMons |= gBitTable[i];
}

View File

@ -468,7 +468,7 @@ bool32 IsDamageMoveUnusable(u32 move, u32 battlerAtk, u32 battlerDef)
break;
case EFFECT_LOW_KICK:
case EFFECT_HEAT_CRASH:
if (IsDynamaxed(battlerDef))
if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
return TRUE;
break;
case EFFECT_FAIL_IF_NOT_ARG_TYPE:
@ -508,35 +508,28 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
s32 moveType;
uq4_12_t effectivenessMultiplier;
bool32 isDamageMoveUnusable = FALSE;
bool32 toggledDynamax = FALSE;
bool32 toggledTera = FALSE;
bool32 toggledGimmick = FALSE;
struct AiLogicData *aiData = AI_DATA;
gBattleStruct->aiCalcInProgress = TRUE;
// Temporarily enable Z-Moves for damage calcs
if (considerZPower && IsViableZMove(battlerAtk, move))
// Temporarily enable gimmicks for damage calcs if planned
if (gBattleStruct->gimmick.usableGimmick[battlerAtk] && GetActiveGimmick(battlerAtk) == GIMMICK_NONE
&& !(gBattleStruct->gimmick.usableGimmick[battlerAtk] == GIMMICK_Z_MOVE && !considerZPower))
{
gBattleStruct->zmove.baseMoves[battlerAtk] = move;
gBattleStruct->zmove.active = TRUE;
// Set Z-Move variables if needed
if (gBattleStruct->gimmick.usableGimmick[battlerAtk] == GIMMICK_Z_MOVE && IsViableZMove(battlerAtk, move))
gBattleStruct->zmove.baseMoves[battlerAtk] = move;
toggledGimmick = TRUE;
SetActiveGimmick(battlerAtk, gBattleStruct->gimmick.usableGimmick[battlerAtk]);
}
else if (gMovesInfo[move].effect == EFFECT_PHOTON_GEYSER)
if (gMovesInfo[move].effect == EFFECT_PHOTON_GEYSER)
gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == DAMAGE_CATEGORY_PHYSICAL);
else if (move == MOVE_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] == DAMAGE_CATEGORY_SPECIAL)
else if (move == MOVE_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] == DAMAGE_CATEGORY_PHYSICAL)
gBattleStruct->swapDamageCategory = TRUE;
else if (gMovesInfo[move].effect == EFFECT_NATURE_POWER)
move = GetNaturePowerMove();
// Temporarily enable other gimmicks for damage calcs if planned
if (AI_DATA->shouldDynamax[battlerAtk])
{
toggledDynamax = TRUE;
gBattleStruct->dynamax.dynamaxed[battlerAtk] = TRUE;
}
if (AI_DATA->shouldTerastal[battlerAtk])
{
toggledTera = TRUE;
gBattleStruct->tera.isTerastallized[GetBattlerSide(battlerAtk)] |= gBitTable[gBattlerPartyIndexes[battlerAtk]];
}
move = GetNaturePowerMove(battlerAtk);
gBattleStruct->dynamicMoveType = 0;
@ -608,7 +601,7 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
simDamage.minimum = LowestRollDmg(nonCritDmg);
}
if (!gBattleStruct->zmove.active)
if (GetActiveGimmick(battlerAtk) != GIMMICK_Z_MOVE)
{
// Handle dynamic move damage
switch (gMovesInfo[move].effect)
@ -697,14 +690,12 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
// convert multiper to AI_EFFECTIVENESS_xX
*typeEffectiveness = AI_GetEffectiveness(effectivenessMultiplier);
// Undo temporary settings
gBattleStruct->aiCalcInProgress = FALSE;
gBattleStruct->swapDamageCategory = FALSE;
gBattleStruct->zmove.active = FALSE;
gBattleStruct->zmove.baseMoves[battlerAtk] = MOVE_NONE;
if (toggledDynamax)
gBattleStruct->dynamax.dynamaxed[battlerAtk] = FALSE;
if (toggledTera)
gBattleStruct->tera.isTerastallized[GetBattlerSide(battlerAtk)] &= ~(gBitTable[gBattlerPartyIndexes[battlerAtk]]);
if (toggledGimmick)
SetActiveGimmick(battlerAtk, GIMMICK_NONE);
return simDamage;
}
@ -2740,48 +2731,18 @@ bool32 IsBattlerIncapacitated(u32 battler, u32 ability)
return FALSE;
}
bool32 AI_CanSleep(u32 battler, u32 ability)
{
if (ability == ABILITY_INSOMNIA
|| ability == ABILITY_VITAL_SPIRIT
|| ability == ABILITY_COMATOSE
|| gBattleMons[battler].status1 & STATUS1_ANY
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD
|| (gFieldStatuses & (STATUS_FIELD_MISTY_TERRAIN | STATUS_FIELD_ELECTRIC_TERRAIN))
|| IsAbilityStatusProtected(battler))
return FALSE;
return TRUE;
}
bool32 AI_CanPutToSleep(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove)
{
if (!AI_CanSleep(battlerDef, defAbility)
if (!CanBeSlept(battlerDef, defAbility)
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
|| PartnerMoveEffectIsStatusSameTarget(BATTLE_PARTNER(battlerAtk), battlerDef, partnerMove)) // shouldn't try to sleep mon that partner is trying to make sleep
return FALSE;
return TRUE;
}
static bool32 AI_CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 move)
{
u32 ability = AI_DATA->abilities[battlerDef];
if (!(CanPoisonType(battlerAtk, battlerDef))
|| gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD
|| gBattleMons[battlerDef].status1 & STATUS1_ANY
|| ability == ABILITY_IMMUNITY
|| ability == ABILITY_COMATOSE
|| AI_IsAbilityOnSide(battlerDef, ABILITY_PASTEL_VEIL)
|| gBattleMons[battlerDef].status1 & STATUS1_ANY
|| IsAbilityStatusProtected(battlerDef)
|| AI_IsTerrainAffected(battlerDef, STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
return TRUE;
}
bool32 ShouldPoisonSelf(u32 battler, u32 ability)
{
if (AI_CanBePoisoned(battler, battler, 0) && (
if (CanBePoisoned(battler, battler, GetBattlerAbility(battler)) && (
ability == ABILITY_MARVEL_SCALE
|| ability == ABILITY_POISON_HEAL
|| ability == ABILITY_QUICK_FEET
@ -2796,7 +2757,7 @@ bool32 ShouldPoisonSelf(u32 battler, u32 ability)
bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove)
{
if (!AI_CanBePoisoned(battlerAtk, battlerDef, move)
if (!CanBePoisoned(battlerAtk, battlerDef, GetBattlerAbility(battlerDef))
|| AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == AI_EFFECTIVENESS_x0
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
|| PartnerMoveEffectIsStatusSameTarget(BATTLE_PARTNER(battlerAtk), battlerDef, partnerMove))
@ -2809,20 +2770,9 @@ bool32 AI_CanPoison(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u3
return TRUE;
}
static bool32 AI_CanBeParalyzed(u32 battler, u32 ability)
{
if (ability == ABILITY_LIMBER
|| ability == ABILITY_COMATOSE
|| IS_BATTLER_OF_TYPE(battler, TYPE_ELECTRIC)
|| gBattleMons[battler].status1 & STATUS1_ANY
|| IsAbilityStatusProtected(battler))
return FALSE;
return TRUE;
}
bool32 AI_CanParalyze(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 move, u32 partnerMove)
{
if (!AI_CanBeParalyzed(battlerDef, defAbility)
if (!CanBeParalyzed(battlerDef, defAbility)
|| AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == AI_EFFECTIVENESS_x0
|| gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_SAFEGUARD
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
@ -2856,19 +2806,6 @@ bool32 AI_CanConfuse(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battler
return TRUE;
}
bool32 AI_CanBeBurned(u32 battler, u32 ability)
{
if (ability == ABILITY_WATER_VEIL
|| ability == ABILITY_WATER_BUBBLE
|| ability == ABILITY_COMATOSE
|| IS_BATTLER_OF_TYPE(battler, TYPE_FIRE)
|| gBattleMons[battler].status1 & STATUS1_ANY
|| IsAbilityStatusProtected(battler)
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD)
return FALSE;
return TRUE;
}
bool32 AI_CanGetFrostbite(u32 battler, u32 ability)
{
if (ability == ABILITY_MAGMA_ARMOR
@ -2883,7 +2820,7 @@ bool32 AI_CanGetFrostbite(u32 battler, u32 ability)
bool32 ShouldBurnSelf(u32 battler, u32 ability)
{
if (AI_CanBeBurned(battler, ability) && (
if (CanBeBurned(battler, ability) && (
ability == ABILITY_QUICK_FEET
|| ability == ABILITY_HEATPROOF
|| ability == ABILITY_MAGIC_GUARD
@ -2897,7 +2834,7 @@ bool32 ShouldBurnSelf(u32 battler, u32 ability)
bool32 AI_CanBurn(u32 battlerAtk, u32 battlerDef, u32 defAbility, u32 battlerAtkPartner, u32 move, u32 partnerMove)
{
if (!AI_CanBeBurned(battlerDef, defAbility)
if (!CanBeBurned(battlerDef, defAbility)
|| AI_DATA->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex] == AI_EFFECTIVENESS_x0
|| DoesSubstituteBlockMove(battlerAtk, battlerDef, move)
|| PartnerMoveEffectIsStatusSameTarget(battlerAtkPartner, battlerDef, partnerMove))
@ -3812,24 +3749,28 @@ bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, u32 chosenMove)
{
// simple logic. just upgrades chosen move to z move if possible, unless regular move would kill opponent
if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && battlerDef == BATTLE_PARTNER(battlerAtk))
return FALSE; //don't use z move on partner
if (gBattleStruct->zmove.used[battlerAtk])
return FALSE; //cant use z move twice
return FALSE; // don't use z move on partner
if (HasTrainerUsedGimmick(battlerAtk, GIMMICK_Z_MOVE))
return FALSE; // can't use z move twice
if (IsViableZMove(battlerAtk, chosenMove))
{
u8 effectiveness;
u32 zMove = GetUsableZMove(battlerAtk, chosenMove);
struct SimulatedDamage dmg;
if (gBattleMons[battlerDef].ability == ABILITY_DISGUISE
&& !gMovesInfo[zMove].ignoresTargetAbility
&& (gBattleMons[battlerDef].species == SPECIES_MIMIKYU_DISGUISED || gBattleMons[battlerDef].species == SPECIES_MIMIKYU_TOTEM_DISGUISED))
return FALSE; // Don't waste a Z-Move busting disguise
if (gBattleMons[battlerDef].ability == ABILITY_ICE_FACE && gBattleMons[battlerDef].species == SPECIES_EISCUE_ICE_FACE && IS_MOVE_PHYSICAL(chosenMove))
if (gBattleMons[battlerDef].ability == ABILITY_ICE_FACE
&& !gMovesInfo[zMove].ignoresTargetAbility
&& gBattleMons[battlerDef].species == SPECIES_EISCUE_ICE_FACE && IS_MOVE_PHYSICAL(chosenMove))
return FALSE; // Don't waste a Z-Move busting Ice Face
if (IS_MOVE_STATUS(chosenMove) && !IS_MOVE_STATUS(gBattleStruct->zmove.chosenZMove))
if (IS_MOVE_STATUS(chosenMove) && !IS_MOVE_STATUS(zMove))
return FALSE;
else if (!IS_MOVE_STATUS(chosenMove) && IS_MOVE_STATUS(gBattleStruct->zmove.chosenZMove))
else if (!IS_MOVE_STATUS(chosenMove) && IS_MOVE_STATUS(zMove))
return FALSE;
dmg = AI_CalcDamageSaveBattlers(chosenMove, battlerAtk, battlerDef, &effectiveness, FALSE, DMG_ROLL_DEFAULT);

View File

@ -18,6 +18,7 @@
#include "sprite.h"
#include "task.h"
#include "test_runner.h"
#include "test/battle.h"
#include "constants/battle_anim.h"
#include "constants/moves.h"
@ -239,6 +240,9 @@ void LaunchBattleAnimation(u32 animType, u32 animId)
TestRunner_Battle_RecordAnimation(animType, animId);
// Play Transform and Ally Switch even in Headless as these move animations also change mon data.
if (gTestRunnerHeadless
#if TESTING // Because gBattleTestRunnerState is not seen outside of test env.
&& !gBattleTestRunnerState->forceMoveAnim
#endif // TESTING
&& !(animType == ANIM_TYPE_MOVE && (animId == MOVE_TRANSFORM || animId == MOVE_ALLY_SWITCH)))
{
gAnimScriptCallback = Nop;
@ -446,7 +450,7 @@ static u8 GetBattleAnimMoveTargets(u8 battlerArgIndex, u8 *targets)
u32 i;
u32 ignoredTgt = gBattlerAttacker;
u32 target = GetBattlerMoveTargetType(gBattleAnimAttacker, gAnimMoveIndex);
switch (battlerAnimId)
{
case ANIM_ATTACKER:
@ -458,7 +462,7 @@ static u8 GetBattleAnimMoveTargets(u8 battlerArgIndex, u8 *targets)
ignoredTgt = gBattlerAttacker;
break;
}
switch (target)
{
case MOVE_TARGET_FOES_AND_ALLY:

View File

@ -6557,8 +6557,8 @@ static void ReloadBattlerSprites(u32 battler, struct Pokemon *party)
BattleLoadMonSpriteGfx(mon, battler);
CreateBattlerSprite(battler);
UpdateHealthboxAttribute(gHealthboxSpriteIds[battler], mon, HEALTHBOX_ALL);
// If battler is mega evolved / primal reversed, hide the sprite until the move animation finishes.
MegaIndicator_SetVisibilities(gHealthboxSpriteIds[battler], TRUE);
// If battler has an indicator for a gimmick, hide the sprite until the move animation finishes.
UpdateIndicatorVisibilityAndType(gHealthboxSpriteIds[battler], TRUE);
// Try to recreate shadow sprite
if (gBattleSpritesDataPtr->healthBoxesData[battler].shadowSpriteId < MAX_SPRITES)

View File

@ -552,22 +552,17 @@ static void OpponentHandleChooseMove(u32 battler)
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
}
if (ShouldUseZMove(battler, gBattlerTarget, chosenMove))
QueueZMove(battler, chosenMove);
// If opponent can Mega Evolve, do it.
if (CanMegaEvolve(battler))
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8));
// If opponent can Ultra Burst, do it.
else if (CanUltraBurst(battler))
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_ULTRA_BURST) | (gBattlerTarget << 8));
// If opponent can Dynamax and is allowed in the partydata, do it.
else if (CanDynamax(battler) && AI_DATA->shouldDynamax[battler])
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_DYNAMAX) | (gBattlerTarget << 8));
// If opponent can Terastal and is allowed in the partydata, do it.
else if (CanTerastallize(battler) && AI_DATA->shouldTerastal[battler])
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_TERASTAL) | (gBattlerTarget << 8));
// If opponent can and should use a gimmick (considering trainer data), do it
if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE
&& !(gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_Z_MOVE
&& !ShouldUseZMove(battler, gBattlerTarget, moveInfo->moves[chosenMoveId])))
{
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_GIMMICK) | (gBattlerTarget << 8));
}
else
{
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8));
}
}
break;
}

View File

@ -9,6 +9,7 @@
#include "battle_setup.h"
#include "battle_tv.h"
#include "battle_z_move.h"
#include "battle_gimmick.h"
#include "bg.h"
#include "data.h"
#include "item.h"
@ -162,11 +163,6 @@ static void (*const sPlayerBufferCommands[CONTROLLER_CMDS_COUNT])(u32 battler) =
[CONTROLLER_TERMINATOR_NOP] = BtlController_TerminatorNop
};
static EWRAM_DATA bool8 sDescriptionSubmenu = 0;
static EWRAM_DATA bool8 sAckBallUseBtn = FALSE;
static EWRAM_DATA bool8 sBallSwapped = FALSE;
void SetControllerToPlayer(u32 battler)
{
gBattlerControllerEndFuncs[battler] = PlayerBufferExecCompleted;
@ -264,61 +260,63 @@ static void HandleInputChooseAction(u32 battler)
else
gPlayerDpadHoldFrames = 0;
#if B_LAST_USED_BALL == TRUE && B_LAST_USED_BALL_CYCLE == TRUE
if (!gLastUsedBallMenuPresent)
if (B_LAST_USED_BALL == TRUE && B_LAST_USED_BALL_CYCLE == TRUE)
{
sAckBallUseBtn = FALSE;
if (!gLastUsedBallMenuPresent)
{
gBattleStruct->ackBallUseBtn = FALSE;
}
else if (JOY_NEW(B_LAST_USED_BALL_BUTTON))
{
gBattleStruct->ackBallUseBtn = TRUE;
gBattleStruct->ballSwapped = FALSE;
ArrowsChangeColorLastBallCycle(TRUE);
}
if (gBattleStruct->ackBallUseBtn)
{
if (JOY_HELD(B_LAST_USED_BALL_BUTTON) && (JOY_NEW(DPAD_DOWN) || JOY_NEW(DPAD_RIGHT)))
{
bool32 sameBall = FALSE;
u32 nextBall = GetNextBall(gBallToDisplay);
gBattleStruct->ballSwapped = TRUE;
if (gBallToDisplay == nextBall)
sameBall = TRUE;
else
gBallToDisplay = nextBall;
SwapBallToDisplay(sameBall);
PlaySE(SE_SELECT);
}
else if (JOY_HELD(B_LAST_USED_BALL_BUTTON) && (JOY_NEW(DPAD_UP) || JOY_NEW(DPAD_LEFT)))
{
bool32 sameBall = FALSE;
u32 prevBall = GetPrevBall(gBallToDisplay);
gBattleStruct->ballSwapped = TRUE;
if (gBallToDisplay == prevBall)
sameBall = TRUE;
else
gBallToDisplay = prevBall;
SwapBallToDisplay(sameBall);
PlaySE(SE_SELECT);
}
else if (JOY_NEW(B_BUTTON) || (!JOY_HELD(B_LAST_USED_BALL_BUTTON) && gBattleStruct->ballSwapped))
{
gBattleStruct->ackBallUseBtn = FALSE;
gBattleStruct->ballSwapped = FALSE;
ArrowsChangeColorLastBallCycle(FALSE);
}
else if (!JOY_HELD(B_LAST_USED_BALL_BUTTON) && CanThrowLastUsedBall())
{
gBattleStruct->ackBallUseBtn = FALSE;
PlaySE(SE_SELECT);
ArrowsChangeColorLastBallCycle(FALSE);
TryHideLastUsedBall();
BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_THROW_BALL, 0);
PlayerBufferExecCompleted(battler);
}
return;
}
}
else if (JOY_NEW(B_LAST_USED_BALL_BUTTON))
{
sAckBallUseBtn = TRUE;
sBallSwapped = FALSE;
ArrowsChangeColorLastBallCycle(TRUE);
}
if (sAckBallUseBtn)
{
if (JOY_HELD(B_LAST_USED_BALL_BUTTON) && (JOY_NEW(DPAD_DOWN) || JOY_NEW(DPAD_RIGHT)))
{
bool8 sameBall = FALSE;
u16 nextBall = GetNextBall(gBallToDisplay);
sBallSwapped = TRUE;
if (gBallToDisplay == nextBall)
sameBall = TRUE;
else
gBallToDisplay = nextBall;
SwapBallToDisplay(sameBall);
PlaySE(SE_SELECT);
}
else if (JOY_HELD(B_LAST_USED_BALL_BUTTON) && (JOY_NEW(DPAD_UP) || JOY_NEW(DPAD_LEFT)))
{
bool8 sameBall = FALSE;
u16 prevBall = GetPrevBall(gBallToDisplay);
sBallSwapped = TRUE;
if (gBallToDisplay == prevBall)
sameBall = TRUE;
else
gBallToDisplay = prevBall;
SwapBallToDisplay(sameBall);
PlaySE(SE_SELECT);
}
else if (JOY_NEW(B_BUTTON) || (!JOY_HELD(B_LAST_USED_BALL_BUTTON) && sBallSwapped))
{
sAckBallUseBtn = FALSE;
sBallSwapped = FALSE;
ArrowsChangeColorLastBallCycle(FALSE);
}
else if (!JOY_HELD(B_LAST_USED_BALL_BUTTON) && CanThrowLastUsedBall())
{
sAckBallUseBtn = FALSE;
PlaySE(SE_SELECT);
ArrowsChangeColorLastBallCycle(FALSE);
TryHideLastUsedBall();
BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_THROW_BALL, 0);
PlayerBufferExecCompleted(battler);
}
return;
}
#endif
if (JOY_NEW(A_BUTTON))
{
@ -413,22 +411,19 @@ static void HandleInputChooseAction(u32 battler)
{
SwapHpBarsWithHpText();
}
#if DEBUG_BATTLE_MENU == TRUE
else if (JOY_NEW(SELECT_BUTTON))
else if (DEBUG_BATTLE_MENU == TRUE && JOY_NEW(SELECT_BUTTON))
{
BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_DEBUG, 0);
PlayerBufferExecCompleted(battler);
}
#endif
#if B_LAST_USED_BALL == TRUE && B_LAST_USED_BALL_CYCLE == FALSE
else if (JOY_NEW(B_LAST_USED_BALL_BUTTON) && CanThrowLastUsedBall())
else if (B_LAST_USED_BALL == TRUE && B_LAST_USED_BALL_CYCLE == FALSE
&& JOY_NEW(B_LAST_USED_BALL_BUTTON) && CanThrowLastUsedBall())
{
PlaySE(SE_SELECT);
TryHideLastUsedBall();
BtlController_EmitTwoReturnValues(battler, BUFFER_B, B_ACTION_THROW_BALL, 0);
PlayerBufferExecCompleted(battler);
}
#endif
}
static void HandleInputChooseTarget(u32 battler)
@ -454,19 +449,13 @@ static void HandleInputChooseTarget(u32 battler)
{
PlaySE(SE_SELECT);
gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = SpriteCB_HideAsMoveTarget;
if (gBattleStruct->mega.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_MEGA_EVOLUTION | (gMultiUsePlayerCursor << 8));
else if (gBattleStruct->burst.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_ULTRA_BURST | (gMultiUsePlayerCursor << 8));
else if (gBattleStruct->dynamax.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_DYNAMAX | (gMultiUsePlayerCursor << 8));
else if (gBattleStruct->tera.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_TERASTAL | (gMultiUsePlayerCursor << 8));
if (gBattleStruct->gimmick.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_GIMMICK | (gMultiUsePlayerCursor << 8));
else
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | (gMultiUsePlayerCursor << 8));
EndBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX);
TryHideLastUsedBall();
HideTriggerSprites();
HideGimmickTriggerSprite();
PlayerBufferExecCompleted(battler);
}
else if (JOY_NEW(B_BUTTON) || gPlayerDpadHoldFrames > 59)
@ -618,17 +607,11 @@ static void HandleInputShowEntireFieldTargets(u32 battler)
{
PlaySE(SE_SELECT);
HideAllTargets();
if (gBattleStruct->mega.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_MEGA_EVOLUTION | (gMultiUsePlayerCursor << 8));
else if (gBattleStruct->burst.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_ULTRA_BURST | (gMultiUsePlayerCursor << 8));
else if (gBattleStruct->dynamax.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_DYNAMAX | (gMultiUsePlayerCursor << 8));
else if (gBattleStruct->tera.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_TERASTAL | (gMultiUsePlayerCursor << 8));
if (gBattleStruct->gimmick.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_GIMMICK | (gMultiUsePlayerCursor << 8));
else
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | (gMultiUsePlayerCursor << 8));
HideTriggerSprites();
HideGimmickTriggerSprite();
PlayerBufferExecCompleted(battler);
}
else if (JOY_NEW(B_BUTTON) || gPlayerDpadHoldFrames > 59)
@ -652,17 +635,11 @@ static void HandleInputShowTargets(u32 battler)
{
PlaySE(SE_SELECT);
HideShownTargets(battler);
if (gBattleStruct->mega.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_MEGA_EVOLUTION | (gMultiUsePlayerCursor << 8));
else if (gBattleStruct->burst.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_ULTRA_BURST | (gMultiUsePlayerCursor << 8));
else if (gBattleStruct->dynamax.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_DYNAMAX | (gMultiUsePlayerCursor << 8));
else if (gBattleStruct->tera.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_TERASTAL | (gMultiUsePlayerCursor << 8));
if (gBattleStruct->gimmick.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_GIMMICK | (gMultiUsePlayerCursor << 8));
else
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | (gMultiUsePlayerCursor << 8));
HideTriggerSprites();
HideGimmickTriggerSprite();
TryHideLastUsedBall();
PlayerBufferExecCompleted(battler);
}
@ -696,7 +673,7 @@ static void HandleInputChooseMove(u32 battler)
else
gPlayerDpadHoldFrames = 0;
if (JOY_NEW(A_BUTTON) && !sDescriptionSubmenu)
if (JOY_NEW(A_BUTTON) && !gBattleStruct->descriptionSubmenu)
{
PlaySE(SE_SELECT);
@ -704,16 +681,13 @@ static void HandleInputChooseMove(u32 battler)
if (gBattleStruct->zmove.viewing)
{
u16 chosenMove = moveInfo->moves[gMoveSelectionCursor[battler]];
QueueZMove(battler, chosenMove);
gBattleStruct->zmove.viewing = FALSE;
if (gMovesInfo[moveInfo->moves[gMoveSelectionCursor[battler]]].category != DAMAGE_CATEGORY_STATUS)
moveTarget = MOVE_TARGET_SELECTED; //damaging z moves always have selected target
}
// Status moves turn into Max Guard when Dynamaxed, targets user.
if ((IsDynamaxed(battler) || gBattleStruct->dynamax.playerSelect))
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX || IsGimmickSelected(battler, GIMMICK_DYNAMAX))
moveTarget = gMovesInfo[GetMaxMove(battler, moveInfo->moves[gMoveSelectionCursor[battler]])].target;
if (moveTarget & MOVE_TARGET_USER)
@ -769,17 +743,11 @@ static void HandleInputChooseMove(u32 battler)
{
case 0:
default:
if (gBattleStruct->mega.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_MEGA_EVOLUTION | (gMultiUsePlayerCursor << 8));
else if (gBattleStruct->burst.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_ULTRA_BURST | (gMultiUsePlayerCursor << 8));
else if (gBattleStruct->dynamax.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_DYNAMAX | (gMultiUsePlayerCursor << 8));
else if (gBattleStruct->tera.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_TERASTAL | (gMultiUsePlayerCursor << 8));
if (gBattleStruct->gimmick.playerSelect)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | RET_GIMMICK | (gMultiUsePlayerCursor << 8));
else
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, gMoveSelectionCursor[battler] | (gMultiUsePlayerCursor << 8));
HideTriggerSprites();
HideGimmickTriggerSprite();
TryHideLastUsedBall();
PlayerBufferExecCompleted(battler);
break;
@ -803,22 +771,18 @@ static void HandleInputChooseMove(u32 battler)
break;
}
}
else if ((JOY_NEW(B_BUTTON) || gPlayerDpadHoldFrames > 59) && !sDescriptionSubmenu)
else if ((JOY_NEW(B_BUTTON) || gPlayerDpadHoldFrames > 59) && !gBattleStruct->descriptionSubmenu)
{
PlaySE(SE_SELECT);
gBattleStruct->gimmick.playerSelect = FALSE;
if (gBattleStruct->zmove.viewing)
{
ReloadMoveNames(battler);
}
else
{
gBattleStruct->mega.playerSelect = FALSE;
gBattleStruct->burst.playerSelect = FALSE;
gBattleStruct->dynamax.playerSelect = FALSE;
gBattleStruct->tera.playerSelect = FALSE;
gBattleStruct->zmove.viable = FALSE;
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, 0xFFFF);
HideTriggerSprites();
HideGimmickTriggerSprite();
PlayerBufferExecCompleted(battler);
}
}
@ -832,9 +796,9 @@ static void HandleInputChooseMove(u32 battler)
MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0);
MoveSelectionDisplayPpNumber(battler);
MoveSelectionDisplayMoveType(battler);
if (sDescriptionSubmenu)
if (gBattleStruct->descriptionSubmenu)
MoveSelectionDisplayMoveDescription(battler);
TryChangeZIndicator(battler, gMoveSelectionCursor[battler]);
TryChangeZTrigger(battler, gMoveSelectionCursor[battler]);
}
}
else if (JOY_NEW(DPAD_RIGHT) && !gBattleStruct->zmove.viewing)
@ -848,9 +812,9 @@ static void HandleInputChooseMove(u32 battler)
MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0);
MoveSelectionDisplayPpNumber(battler);
MoveSelectionDisplayMoveType(battler);
if (sDescriptionSubmenu)
if (gBattleStruct->descriptionSubmenu)
MoveSelectionDisplayMoveDescription(battler);
TryChangeZIndicator(battler, gMoveSelectionCursor[battler]);
TryChangeZTrigger(battler, gMoveSelectionCursor[battler]);
}
}
else if (JOY_NEW(DPAD_UP) && !gBattleStruct->zmove.viewing)
@ -863,9 +827,9 @@ static void HandleInputChooseMove(u32 battler)
MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0);
MoveSelectionDisplayPpNumber(battler);
MoveSelectionDisplayMoveType(battler);
if (sDescriptionSubmenu)
if (gBattleStruct->descriptionSubmenu)
MoveSelectionDisplayMoveDescription(battler);
TryChangeZIndicator(battler, gMoveSelectionCursor[battler]);
TryChangeZTrigger(battler, gMoveSelectionCursor[battler]);
}
}
else if (JOY_NEW(DPAD_DOWN) && !gBattleStruct->zmove.viewing)
@ -879,12 +843,12 @@ static void HandleInputChooseMove(u32 battler)
MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0);
MoveSelectionDisplayPpNumber(battler);
MoveSelectionDisplayMoveType(battler);
if (sDescriptionSubmenu)
if (gBattleStruct->descriptionSubmenu)
MoveSelectionDisplayMoveDescription(battler);
TryChangeZIndicator(battler, gMoveSelectionCursor[battler]);
TryChangeZTrigger(battler, gMoveSelectionCursor[battler]);
}
}
else if (JOY_NEW(SELECT_BUTTON) && !gBattleStruct->zmove.viewing && !sDescriptionSubmenu)
else if (JOY_NEW(SELECT_BUTTON) && !gBattleStruct->zmove.viewing && !gBattleStruct->descriptionSubmenu)
{
if (gNumberOfMovesToChoose > 1 && !(gBattleTypeFlags & BATTLE_TYPE_LINK))
{
@ -900,11 +864,11 @@ static void HandleInputChooseMove(u32 battler)
gBattlerControllerFuncs[battler] = HandleMoveSwitching;
}
}
else if (sDescriptionSubmenu)
else if (gBattleStruct->descriptionSubmenu)
{
if (JOY_NEW(L_BUTTON) || JOY_NEW(A_BUTTON) || JOY_NEW(B_BUTTON))
if (JOY_NEW(B_MOVE_DESCRIPTION_BUTTON) || JOY_NEW(A_BUTTON) || JOY_NEW(B_BUTTON))
{
sDescriptionSubmenu = FALSE;
gBattleStruct->descriptionSubmenu = FALSE;
if (gCategoryIconSpriteId != 0xFF)
{
DestroySprite(&gSprites[gCategoryIconSpriteId]);
@ -919,48 +883,18 @@ static void HandleInputChooseMove(u32 battler)
MoveSelectionDisplayMoveType(battler);
}
}
else if (JOY_NEW(L_BUTTON))
else if (JOY_NEW(B_MOVE_DESCRIPTION_BUTTON) && B_MOVE_DESCRIPTION_BUTTON != B_LAST_USED_BALL_BUTTON)
{
sDescriptionSubmenu = TRUE;
gBattleStruct->descriptionSubmenu = TRUE;
MoveSelectionDisplayMoveDescription(battler);
}
else if (JOY_NEW(START_BUTTON))
{
if (CanMegaEvolve(battler))
if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE)
{
gBattleStruct->mega.playerSelect ^= 1;
ChangeMegaTriggerSprite(gBattleStruct->mega.triggerSpriteId, gBattleStruct->mega.playerSelect);
PlaySE(SE_SELECT);
}
else if (CanUltraBurst(battler))
{
gBattleStruct->burst.playerSelect ^= 1;
ChangeBurstTriggerSprite(gBattleStruct->burst.triggerSpriteId, gBattleStruct->burst.playerSelect);
PlaySE(SE_SELECT);
}
else if (gBattleStruct->zmove.viable)
{
// show z move name / info
//TODO: brighten z move symbol
PlaySE(SE_SELECT);
if (!gBattleStruct->zmove.viewing)
MoveSelectionDisplayZMove(gBattleStruct->zmove.chosenZMove, battler);
else
ReloadMoveNames(battler);
}
else if (CanDynamax(battler))
{
gBattleStruct->dynamax.playerSelect ^= 1;
MoveSelectionDisplayMoveNames(battler);
MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0);
ChangeDynamaxTriggerSprite(gBattleStruct->dynamax.triggerSpriteId, gBattleStruct->dynamax.playerSelect);
PlaySE(SE_SELECT);
}
else if (CanTerastallize(battler))
{
gBattleStruct->tera.playerSelect ^= 1;
ChangeTeraTriggerSprite(gBattleStruct->tera.triggerSpriteId, gBattleStruct->tera.playerSelect);
MoveSelectionDisplayMoveType(battler); // For Tera Blast / Tera Starstorm
gBattleStruct->gimmick.playerSelect ^= 1;
ReloadMoveNames(battler);
ChangeGimmickTriggerSprite(gBattleStruct->gimmick.triggerSpriteId, gBattleStruct->gimmick.playerSelect);
PlaySE(SE_SELECT);
}
}
@ -968,16 +902,20 @@ static void HandleInputChooseMove(u32 battler)
static void ReloadMoveNames(u32 battler)
{
gBattleStruct->mega.playerSelect = FALSE;
gBattleStruct->burst.playerSelect = FALSE;
gBattleStruct->dynamax.playerSelect = FALSE;
gBattleStruct->tera.playerSelect = FALSE;
gBattleStruct->zmove.viewing = FALSE;
MoveSelectionDestroyCursorAt(battler);
MoveSelectionDisplayMoveNames(battler);
MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0);
MoveSelectionDisplayPpNumber(battler);
MoveSelectionDisplayMoveType(battler);
if (gBattleStruct->zmove.viable && !gBattleStruct->zmove.viewing)
{
struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]);
MoveSelectionDisplayZMove(GetUsableZMove(battler, moveInfo->moves[gMoveSelectionCursor[battler]]), battler);
}
else
{
gBattleStruct->zmove.viewing = FALSE;
MoveSelectionDestroyCursorAt(battler);
MoveSelectionDisplayMoveNames(battler);
MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0);
MoveSelectionDisplayPpNumber(battler);
MoveSelectionDisplayMoveType(battler);
}
}
static u32 UNUSED HandleMoveInputUnused(u32 battler)
@ -1130,7 +1068,7 @@ static void HandleMoveSwitching(u32 battler)
MoveSelectionDisplayPpString(battler);
MoveSelectionDisplayPpNumber(battler);
MoveSelectionDisplayMoveType(battler);
GetUsableZMoves(battler, moveInfo->moves);
AssignUsableZMoves(battler, moveInfo->moves);
}
else if (JOY_NEW(B_BUTTON | SELECT_BUTTON))
{
@ -1501,7 +1439,7 @@ static void Task_GiveExpToMon(u8 taskId)
CalculateMonStats(mon);
// Reapply Dynamax HP multiplier after stats are recalculated.
if (IsDynamaxed(battler) && monId == gBattlerPartyIndexes[battler])
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX && monId == gBattlerPartyIndexes[battler])
{
ApplyDynamaxHPMultiplier(battler, mon);
gBattleMons[battler].hp = gBattleStruct->dynamax.levelUpHP;
@ -1585,7 +1523,7 @@ static void Task_GiveExpWithExpBar(u8 taskId)
CalculateMonStats(&gPlayerParty[monId]);
// Reapply Dynamax HP multiplier after stats are recalculated.
if (IsDynamaxed(battler) && monId == gBattlerPartyIndexes[battler])
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX && monId == gBattlerPartyIndexes[battler])
{
ApplyDynamaxHPMultiplier(battler, &gPlayerParty[monId]);
gBattleMons[battler].hp = gBattleStruct->dynamax.levelUpHP;
@ -1738,8 +1676,7 @@ static void MoveSelectionDisplayMoveNames(u32 battler)
for (i = 0; i < MAX_MON_MOVES; i++)
{
MoveSelectionDestroyCursorAt(i);
if ((gBattleStruct->dynamax.playerSelect && CanDynamax(battler))
|| IsDynamaxed(battler))
if (IsGimmickSelected(battler, GIMMICK_DYNAMAX) || GetActiveGimmick(battler) == GIMMICK_DYNAMAX)
StringCopy(gDisplayedStringBattle, GetMoveName(GetMaxMove(battler, moveInfo->moves[i])));
else
StringCopy(gDisplayedStringBattle, GetMoveName(moveInfo->moves[i]));
@ -1786,7 +1723,7 @@ static void MoveSelectionDisplayMoveType(u32 battler)
if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_TERA_BLAST)
{
if (gBattleStruct->tera.playerSelect || IsTerastallized(battler))
if (IsGimmickSelected(battler, GIMMICK_TERA) || GetActiveGimmick(battler) == GIMMICK_TERA)
type = GetBattlerTeraType(battler);
}
else if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_IVY_CUDGEL)
@ -1798,10 +1735,16 @@ static void MoveSelectionDisplayMoveType(u32 battler)
|| speciesId == SPECIES_OGERPON_CORNERSTONE_MASK || speciesId == SPECIES_OGERPON_CORNERSTONE_MASK_TERA)
type = gBattleMons[battler].type2;
}
// Max Guard is a Normal-type move
else if (gMovesInfo[moveInfo->moves[gMoveSelectionCursor[battler]]].category == DAMAGE_CATEGORY_STATUS
&& (GetActiveGimmick(battler) == GIMMICK_DYNAMAX || IsGimmickSelected(battler, GIMMICK_DYNAMAX)))
{
type = TYPE_NORMAL;
}
else if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_TERA_STARSTORM)
{
if (gBattleMons[battler].species == SPECIES_TERAPAGOS_STELLAR
|| (gBattleStruct->tera.playerSelect && gBattleMons[battler].species == SPECIES_TERAPAGOS_TERASTAL))
|| (IsGimmickSelected(battler, GIMMICK_TERA) && gBattleMons[battler].species == SPECIES_TERAPAGOS_TERASTAL))
type = TYPE_STELLAR;
}
@ -1817,7 +1760,7 @@ static void MoveSelectionDisplayMoveDescription(u32 battler)
u16 pwr = gMovesInfo[move].power;
u16 acc = gMovesInfo[move].accuracy;
u8 cat = gMovesInfo[move].category;
u8 pwr_num[3], acc_num[3];
u8 cat_desc[7] = _("CAT: ");
u8 pwr_desc[7] = _("PWR: ");
@ -2155,32 +2098,16 @@ static void PlayerHandleChooseMove(u32 battler)
struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]);
InitMoveSelectionsVarsAndStrings(battler);
gBattleStruct->mega.playerSelect = FALSE;
gBattleStruct->burst.playerSelect = FALSE;
gBattleStruct->dynamax.playerSelect = FALSE;
gBattleStruct->tera.playerSelect = FALSE;
if (!IsMegaTriggerSpriteActive())
gBattleStruct->mega.triggerSpriteId = 0xFF;
if (CanMegaEvolve(battler))
CreateMegaTriggerSprite(battler, 0);
if (!IsBurstTriggerSpriteActive())
gBattleStruct->burst.triggerSpriteId = 0xFF;
if (CanUltraBurst(battler))
CreateBurstTriggerSprite(battler, 0);
if (!IsDynamaxTriggerSpriteActive())
gBattleStruct->dynamax.triggerSpriteId = 0xFF;
if (CanDynamax(battler))
CreateDynamaxTriggerSprite(battler, 0);
if (!IsZMoveTriggerSpriteActive())
gBattleStruct->zmove.triggerSpriteId = 0xFF;
if (!IsTeraTriggerSpriteActive())
gBattleStruct->tera.triggerSpriteId = 0xFF;
if (CanTerastallize(battler))
CreateTeraTriggerSprite(battler, 0);
gBattleStruct->gimmick.playerSelect = FALSE;
AssignUsableZMoves(battler, moveInfo->moves);
gBattleStruct->zmove.viable = (gBattleStruct->zmove.possibleZMoves[battler] & gBitTable[gMoveSelectionCursor[battler]]) != 0;
if (!IsGimmickTriggerSpriteActive())
gBattleStruct->gimmick.triggerSpriteId = 0xFF;
if (!(gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_Z_MOVE && !gBattleStruct->zmove.viable))
CreateGimmickTriggerSprite(battler);
GetUsableZMoves(battler, moveInfo->moves);
gBattleStruct->zmove.viable = IsZMoveUsable(battler, gMoveSelectionCursor[battler]);
CreateZMoveTriggerSprite(battler, gBattleStruct->zmove.viable);
gBattlerControllerFuncs[battler] = HandleChooseMoveAfterDma3;
}
}

View File

@ -368,17 +368,17 @@ static void PlayerPartnerHandleChooseMove(u32 battler)
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])
gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
}
if (ShouldUseZMove(battler, gBattlerTarget, moveInfo->moves[chosenMoveId]))
QueueZMove(battler, moveInfo->moves[chosenMoveId]);
// If partner can mega evolve, do it.
if (CanMegaEvolve(battler))
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_MEGA_EVOLUTION) | (gBattlerTarget << 8));
else if (CanUltraBurst(battler))
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_ULTRA_BURST) | (gBattlerTarget << 8));
// If partner can and should use a gimmick (considering trainer data), do it
if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_NONE
&& !(gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_Z_MOVE
&& !ShouldUseZMove(battler, gBattlerTarget, moveInfo->moves[chosenMoveId])))
{
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (RET_GIMMICK) | (gBattlerTarget << 8));
}
else
{
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (gBattlerTarget << 8));
}
}
PlayerPartnerBufferExecCompleted(battler);

View File

@ -517,7 +517,7 @@ static void RecordedPlayerHandleIntroTrainerBallThrow(u32 battler)
else
trainerPicId = gSaveBlock2Ptr->playerGender + TRAINER_BACK_PIC_BRENDAN;
trainerPal = gTrainerSprites[trainerPicId].palette.data;
trainerPal = gTrainerBacksprites[trainerPicId].palette.data;
BtlController_HandleIntroTrainerBallThrow(battler, 0xD6F9, trainerPal, 24, Intro_TryShinyAnimShowHealthbox);
}

View File

@ -5,6 +5,7 @@
#include "battle_interface.h"
#include "battle_scripts.h"
#include "battle_script_commands.h"
#include "battle_gimmick.h"
#include "data.h"
#include "event_data.h"
#include "graphics.h"
@ -22,7 +23,7 @@
#include "constants/items.h"
#include "constants/moves.h"
static u8 GetMaxPowerTier(u16 move);
static u8 GetMaxPowerTier(u32 move);
struct GMaxMove
{
@ -69,35 +70,21 @@ static const struct GMaxMove sGMaxMoveTable[] =
{SPECIES_URSHIFU_RAPID_STRIKE_STYLE_GIGANTAMAX, TYPE_WATER, MOVE_G_MAX_RAPID_FLOW},
};
// forward declarations
static void SpriteCb_DynamaxTrigger(struct Sprite *);
// Returns whether a battler is Dynamaxed.
bool32 IsDynamaxed(u16 battlerId)
{
if (gBattleStruct->dynamax.dynamaxed[battlerId]
/*|| IsRaidBoss(battlerId)*/)
return TRUE;
return FALSE;
}
// Returns whether a battler can Dynamax.
bool32 CanDynamax(u16 battlerId)
bool32 CanDynamax(u32 battler)
{
u16 species = gBattleMons[battlerId].species;
u16 holdEffect = ItemId_GetHoldEffect(gBattleMons[battlerId].item);
// Check if Dynamax battle flag is set. This needs to be defined in include/config/battle.h
#if B_FLAG_DYNAMAX_BATTLE != 0
if (!FlagGet(B_FLAG_DYNAMAX_BATTLE))
#endif
return FALSE;
u16 species = gBattleMons[battler].species;
u16 holdEffect = GetBattlerHoldEffect(battler, FALSE);
// Check if Player has a Dynamax Band.
if ((GetBattlerPosition(battlerId) == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battlerId) == B_POSITION_PLAYER_RIGHT))
&& !CheckBagHasItem(ITEM_DYNAMAX_BAND, 1))
return FALSE;
if (!TESTING && (GetBattlerPosition(battler) == B_POSITION_PLAYER_LEFT
|| (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT)))
{
if (!CheckBagHasItem(ITEM_DYNAMAX_BAND, 1))
return FALSE;
if (B_FLAG_DYNAMAX_BATTLE == 0 || (B_FLAG_DYNAMAX_BATTLE != 0 && !FlagGet(B_FLAG_DYNAMAX_BATTLE)))
return FALSE;
}
// Check if species isn't allowed to Dynamax.
if (GET_BASE_SPECIES_ID(species) == SPECIES_ZACIAN
@ -105,18 +92,24 @@ bool32 CanDynamax(u16 battlerId)
|| GET_BASE_SPECIES_ID(species) == SPECIES_ETERNATUS)
return FALSE;
// Cannot Dynamax if you can Mega Evolve or use a Z-Move
if (holdEffect == HOLD_EFFECT_MEGA_STONE || holdEffect == HOLD_EFFECT_Z_CRYSTAL)
// Check if Trainer has already Dynamaxed.
if (HasTrainerUsedGimmick(battler, GIMMICK_DYNAMAX))
return FALSE;
// Cannot Dynamax if your side has already or will Dynamax.
if (gBattleStruct->dynamax.alreadyDynamaxed[GetBattlerSide(battlerId)]
|| gBattleStruct->dynamax.dynamaxed[BATTLE_PARTNER(battlerId)]
|| gBattleStruct->dynamax.toDynamax & gBitTable[BATTLE_PARTNER(battlerId)])
// Check if AI battler is intended to Dynamaxed.
if (!ShouldTrainerBattlerUseGimmick(battler, GIMMICK_DYNAMAX))
return FALSE;
// Check if battler has another gimmick active.
if (GetActiveGimmick(battler) != GIMMICK_NONE)
return FALSE;
// Check if battler is holding a Z-Crystal or Mega Stone.
if (!TESTING && (holdEffect == HOLD_EFFECT_Z_CRYSTAL || holdEffect == HOLD_EFFECT_MEGA_STONE)) // tests make this check already
return FALSE;
// TODO: Cannot Dynamax in a Max Raid if you don't have Dynamax Energy.
// if (gBattleTypeFlags & BATTLE_TYPE_RAID && gBattleStruct->raid.dynamaxEnergy != battlerId)
// if (gBattleTypeFlags & BATTLE_TYPE_RAID && gBattleStruct->raid.dynamaxEnergy != battler)
// return FALSE;
// No checks failed, all set!
@ -124,10 +117,10 @@ bool32 CanDynamax(u16 battlerId)
}
// Returns whether a battler is transformed into a Gigantamax form.
bool32 IsGigantamaxed(u16 battlerId)
bool32 IsGigantamaxed(u32 battler)
{
struct Pokemon *mon = &GetSideParty(GetBattlerSide(battlerId))[gBattlerPartyIndexes[battlerId]];
if ((gSpeciesInfo[gBattleMons[battlerId].species].isGigantamax) && GetMonData(mon, MON_DATA_GIGANTAMAX_FACTOR))
struct Pokemon *mon = &GetSideParty(GetBattlerSide(battler))[gBattlerPartyIndexes[battler]];
if ((gSpeciesInfo[gBattleMons[battler].species].isGigantamax) && GetMonData(mon, MON_DATA_GIGANTAMAX_FACTOR))
return TRUE;
return FALSE;
}
@ -148,79 +141,80 @@ void ApplyDynamaxHPMultiplier(u32 battler, struct Pokemon* mon)
}
// Returns the non-Dynamax HP of a Pokemon.
u16 GetNonDynamaxHP(u16 battlerId)
u16 GetNonDynamaxHP(u32 battler)
{
if (!IsDynamaxed(battlerId) || gBattleMons[battlerId].species == SPECIES_SHEDINJA)
return gBattleMons[battlerId].hp;
if (GetActiveGimmick(battler) != GIMMICK_DYNAMAX || gBattleMons[battler].species == SPECIES_SHEDINJA)
return gBattleMons[battler].hp;
else
{
u16 mult = UQ_4_12(1.0/1.5); // placeholder
u16 hp = UQ_4_12_TO_INT((gBattleMons[battlerId].hp * mult) + UQ_4_12_ROUND);
u16 hp = UQ_4_12_TO_INT((gBattleMons[battler].hp * mult) + UQ_4_12_ROUND);
return hp;
}
}
// Returns the non-Dynamax Max HP of a Pokemon.
u16 GetNonDynamaxMaxHP(u32 battlerId)
u16 GetNonDynamaxMaxHP(u32 battler)
{
if (!IsDynamaxed(battlerId) || gBattleMons[battlerId].species == SPECIES_SHEDINJA)
return gBattleMons[battlerId].maxHP;
if (GetActiveGimmick(battler) != GIMMICK_DYNAMAX || gBattleMons[battler].species == SPECIES_SHEDINJA)
return gBattleMons[battler].maxHP;
else
{
u16 mult = UQ_4_12(1.0/1.5); // placeholder
u16 maxHP = UQ_4_12_TO_INT((gBattleMons[battlerId].maxHP * mult) + UQ_4_12_ROUND);
u16 maxHP = UQ_4_12_TO_INT((gBattleMons[battler].maxHP * mult) + UQ_4_12_ROUND);
return maxHP;
}
}
// Sets flags used for Dynamaxing and checks Gigantamax forms.
void PrepareBattlerForDynamax(u16 battlerId)
void ActivateDynamax(u32 battler)
{
u8 side = GetBattlerSide(battlerId);
gBattleStruct->dynamax.alreadyDynamaxed[side] = TRUE;
gBattleStruct->dynamax.dynamaxed[battlerId] = TRUE;
gBattleStruct->dynamax.dynamaxTurns[battlerId] = DYNAMAX_TURNS_COUNT;
// Set appropriate use flags.
SetActiveGimmick(battler, GIMMICK_DYNAMAX);
SetGimmickAsActivated(battler, GIMMICK_DYNAMAX);
gBattleStruct->dynamax.dynamaxTurns[battler] = DYNAMAX_TURNS_COUNT;
// Substitute is removed upon Dynamaxing.
gBattleMons[battlerId].status2 &= ~STATUS2_SUBSTITUTE;
ClearBehindSubstituteBit(battlerId);
gBattleMons[battler].status2 &= ~STATUS2_SUBSTITUTE;
ClearBehindSubstituteBit(battler);
// Choiced Moves are reset upon Dynamaxing.
gBattleStruct->choicedMove[battlerId] = MOVE_NONE;
gBattleStruct->choicedMove[battler] = MOVE_NONE;
// Try Gigantamax form change.
if (!(gBattleMons[battlerId].status2 & STATUS2_TRANSFORMED)) // Ditto cannot Gigantamax.
TryBattleFormChange(battlerId, FORM_CHANGE_BATTLE_GIGANTAMAX);
if (!(gBattleMons[battler].status2 & STATUS2_TRANSFORMED)) // Ditto cannot Gigantamax.
TryBattleFormChange(battler, FORM_CHANGE_BATTLE_GIGANTAMAX);
BattleScriptExecute(BattleScript_DynamaxBegins);
}
// Unsets the flags used for Dynamaxing and reverts max HP if needed.
void UndoDynamax(u16 battlerId)
void UndoDynamax(u32 battler)
{
u8 side = GetBattlerSide(battlerId);
u8 monId = gBattlerPartyIndexes[battlerId];
u8 side = GetBattlerSide(battler);
u8 monId = gBattlerPartyIndexes[battler];
// Revert HP if battler is still Dynamaxed.
if (IsDynamaxed(battlerId))
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX)
{
struct Pokemon *mon = (side == B_SIDE_PLAYER) ? &gPlayerParty[monId] : &gEnemyParty[monId];
u16 mult = UQ_4_12(1.0/1.5); // placeholder
gBattleMons[battlerId].hp = UQ_4_12_TO_INT((GetMonData(mon, MON_DATA_HP) * mult + 1) + UQ_4_12_ROUND); // round up
SetMonData(mon, MON_DATA_HP, &gBattleMons[battlerId].hp);
gBattleMons[battler].hp = UQ_4_12_TO_INT((GetMonData(mon, MON_DATA_HP) * mult + 1) + UQ_4_12_ROUND); // round up
SetMonData(mon, MON_DATA_HP, &gBattleMons[battler].hp);
CalculateMonStats(mon);
}
// Makes sure there are no Dynamax flags set, including on switch / faint.
gBattleStruct->dynamax.dynamaxed[battlerId] = FALSE;
gBattleStruct->dynamax.dynamaxTurns[battlerId] = 0;
SetActiveGimmick(battler, GIMMICK_NONE);
gBattleStruct->dynamax.dynamaxTurns[battler] = 0;
// Undo form change if needed.
if (IsGigantamaxed(battlerId))
TryBattleFormChange(battlerId, FORM_CHANGE_END_BATTLE);
if (IsGigantamaxed(battler))
TryBattleFormChange(battler, FORM_CHANGE_END_BATTLE);
}
// Certain moves are blocked by Max Guard that normally ignore protection.
bool32 IsMoveBlockedByMaxGuard(u16 move)
bool32 IsMoveBlockedByMaxGuard(u32 move)
{
switch (move)
{
@ -239,7 +233,7 @@ bool32 IsMoveBlockedByMaxGuard(u16 move)
}
// Weight-based moves (and some other moves in Raids) are blocked by Dynamax.
bool32 IsMoveBlockedByDynamax(u16 move)
bool32 IsMoveBlockedByDynamax(u32 move)
{
// TODO: Certain moves are banned in raids.
switch (gMovesInfo[move].effect)
@ -251,24 +245,15 @@ bool32 IsMoveBlockedByDynamax(u16 move)
return FALSE;
}
// Returns whether a move should be converted into a Max Move.
bool32 ShouldUseMaxMove(u16 battlerId, u16 baseMove)
{
// TODO: Raid bosses do not always use Max Moves.
// if (IsRaidBoss(battlerId))
// return !IsRaidBossUsingRegularMove(battlerId, baseMove);
return IsDynamaxed(battlerId) || gBattleStruct->dynamax.toDynamax & gBitTable[battlerId];
}
static u16 GetTypeBasedMaxMove(u16 battlerId, u16 type)
static u16 GetTypeBasedMaxMove(u32 battler, u32 type)
{
// Gigantamax check
u32 i;
u16 species = gBattleMons[battlerId].species;
u16 targetSpecies = SPECIES_NONE;
u32 species = gBattleMons[battler].species;
u32 targetSpecies = SPECIES_NONE;
if (!gSpeciesInfo[species].isGigantamax)
targetSpecies = GetBattleFormChangeTargetSpecies(battlerId, FORM_CHANGE_BATTLE_GIGANTAMAX);
targetSpecies = GetBattleFormChangeTargetSpecies(battler, FORM_CHANGE_BATTLE_GIGANTAMAX);
if (targetSpecies != SPECIES_NONE)
species = targetSpecies;
@ -289,9 +274,9 @@ static u16 GetTypeBasedMaxMove(u16 battlerId, u16 type)
}
// Returns the appropriate Max Move or G-Max Move for a battler to use.
u16 GetMaxMove(u16 battlerId, u16 baseMove)
u16 GetMaxMove(u32 battler, u32 baseMove)
{
u16 move = baseMove;
u32 move = baseMove;
if (baseMove == MOVE_NONE) // for move display
{
return MOVE_NONE;
@ -306,13 +291,11 @@ u16 GetMaxMove(u16 battlerId, u16 baseMove)
}
else if (gBattleStruct->dynamicMoveType)
{
move = GetTypeBasedMaxMove(battlerId, gBattleStruct->dynamicMoveType & DYNAMIC_TYPE_MASK);
gBattleStruct->dynamax.categories[battlerId] = gMovesInfo[baseMove].category;
move = GetTypeBasedMaxMove(battler, gBattleStruct->dynamicMoveType & DYNAMIC_TYPE_MASK);
}
else
{
move = GetTypeBasedMaxMove(battlerId, gMovesInfo[baseMove].type);
gBattleStruct->dynamax.categories[battlerId] = gMovesInfo[baseMove].category;
move = GetTypeBasedMaxMove(battler, gMovesInfo[baseMove].type);
}
return move;
@ -332,7 +315,7 @@ enum
};
// Gets the base power of a Max Move.
u8 GetMaxMovePower(u16 move)
u8 GetMaxMovePower(u32 move)
{
u8 tier;
// G-Max Drum Solo, G-Max Hydrosnipe, and G-Max Fireball always have 160 base power.
@ -383,7 +366,7 @@ u8 GetMaxMovePower(u16 move)
}
}
static u8 GetMaxPowerTier(u16 move)
static u8 GetMaxPowerTier(u32 move)
{
if (gMovesInfo[move].strikeCount >= 2 && gMovesInfo[move].strikeCount <= 5)
{
@ -459,7 +442,7 @@ static u8 GetMaxPowerTier(u16 move)
}
// Returns whether a move is a Max Move or not.
bool32 IsMaxMove(u16 move)
bool32 IsMaxMove(u32 move)
{
return move >= FIRST_MAX_MOVE && move <= LAST_MAX_MOVE;
}
@ -485,7 +468,7 @@ void ChooseDamageNonTypesString(u8 type)
}
// Returns the status effect that should be applied by a G-Max Move.
static u32 GetMaxMoveStatusEffect(u16 move)
static u32 GetMaxMoveStatusEffect(u32 move)
{
u8 maxEffect = gMovesInfo[move].argument;
switch (maxEffect)
@ -524,7 +507,7 @@ static u32 GetMaxMoveStatusEffect(u16 move)
void BS_UpdateDynamax(void)
{
NATIVE_ARGS();
u16 battler = gBattleScripting.battler;
u32 battler = gBattleScripting.battler;
struct Pokemon *mon = &GetSideParty(GetBattlerSide(battler))[gBattlerPartyIndexes[battler]];
if (!IsGigantamaxed(battler)) // RecalcBattlerStats will get called on form change.
@ -776,7 +759,7 @@ void BS_SetMaxMoveEffect(void)
{
static const u8 sSnoozeEffects[] = {TRUE, FALSE};
if (!(gStatuses3[gBattlerTarget] & STATUS3_YAWN)
&& CanSleep(gBattlerTarget)
&& CanBeSlept(gBattlerTarget, GetBattlerAbility(gBattlerTarget))
&& RandomElement(RNG_G_MAX_SNOOZE, sSnoozeEffects)) // 50% chance of success
{
gStatuses3[gBattlerTarget] |= STATUS3_YAWN_TURN(2);
@ -882,7 +865,7 @@ void BS_TrySetStatus1(void)
switch (status1)
{
case STATUS1_POISON:
if (CanBePoisoned(gBattlerAttacker, gBattlerTarget))
if (CanBePoisoned(gBattlerAttacker, gBattlerTarget, GetBattlerAbility(gBattlerTarget)))
{
gBattleMons[gBattlerTarget].status1 |= STATUS1_POISON;
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
@ -890,7 +873,7 @@ void BS_TrySetStatus1(void)
}
break;
case STATUS1_PARALYSIS:
if (CanBeParalyzed(gBattlerTarget))
if (CanBeParalyzed(gBattlerTarget, GetBattlerAbility(gBattlerTarget)))
{
gBattleMons[gBattlerTarget].status1 |= STATUS1_PARALYSIS;
gBattleCommunication[MULTISTRING_CHOOSER] = 3;
@ -898,7 +881,7 @@ void BS_TrySetStatus1(void)
}
break;
case STATUS1_SLEEP:
if (CanSleep(gBattlerTarget))
if (CanBeSlept(gBattlerTarget, GetBattlerAbility(gBattlerTarget)))
{
if (B_SLEEP_TURNS >= GEN_5)
gBattleMons[gBattlerTarget].status1 |= STATUS1_SLEEP_TURN((Random() % 3) + 2);
@ -1049,201 +1032,8 @@ void BS_TryRecycleBerry(void)
void BS_JumpIfDynamaxed(void)
{
NATIVE_ARGS(const u8 *jumpInstr);
if (IsDynamaxed(gBattlerTarget))
if ((GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX))
gBattlescriptCurrInstr = cmd->jumpInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
}
// DYNAMAX TRIGGER:
static const u8 ALIGNED(4) sDynamaxTriggerGfx[] = INCBIN_U8("graphics/battle_interface/dynamax_trigger.4bpp");
static const u16 sDynamaxTriggerPal[] = INCBIN_U16("graphics/battle_interface/dynamax_trigger.gbapal");
static const struct SpriteSheet sSpriteSheet_DynamaxTrigger =
{
sDynamaxTriggerGfx, sizeof(sDynamaxTriggerGfx), TAG_DYNAMAX_TRIGGER_TILE
};
static const struct SpritePalette sSpritePalette_DynamaxTrigger =
{
sDynamaxTriggerPal, TAG_DYNAMAX_TRIGGER_PAL
};
static const struct OamData sOamData_DynamaxTrigger =
{
.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_DynamaxTriggerOff[] =
{
ANIMCMD_FRAME(0, 0),
ANIMCMD_END
};
static const union AnimCmd sSpriteAnim_DynamaxTriggerOn[] =
{
ANIMCMD_FRAME(16, 0),
ANIMCMD_END
};
static const union AnimCmd *const sSpriteAnimTable_DynamaxTrigger[] =
{
sSpriteAnim_DynamaxTriggerOff,
sSpriteAnim_DynamaxTriggerOn,
};
static const struct SpriteTemplate sSpriteTemplate_DynamaxTrigger =
{
.tileTag = TAG_DYNAMAX_TRIGGER_TILE,
.paletteTag = TAG_DYNAMAX_TRIGGER_PAL,
.oam = &sOamData_DynamaxTrigger,
.anims = sSpriteAnimTable_DynamaxTrigger,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_DynamaxTrigger
};
// Dynamax Evolution Trigger icon functions.
void ChangeDynamaxTriggerSprite(u8 spriteId, u8 animId)
{
StartSpriteAnim(&gSprites[spriteId], animId);
}
#define SINGLES_DYNAMAX_TRIGGER_POS_X_OPTIMAL (30)
#define SINGLES_DYNAMAX_TRIGGER_POS_X_PRIORITY (31)
#define SINGLES_DYNAMAX_TRIGGER_POS_X_SLIDE (15)
#define SINGLES_DYNAMAX_TRIGGER_POS_Y_DIFF (-11)
#define DOUBLES_DYNAMAX_TRIGGER_POS_X_OPTIMAL (30)
#define DOUBLES_DYNAMAX_TRIGGER_POS_X_PRIORITY (31)
#define DOUBLES_DYNAMAX_TRIGGER_POS_X_SLIDE (15)
#define DOUBLES_DYNAMAX_TRIGGER_POS_Y_DIFF (-4)
#define tBattler data[0]
#define tHide data[1]
void CreateDynamaxTriggerSprite(u8 battlerId, u8 palId)
{
LoadSpritePalette(&sSpritePalette_DynamaxTrigger);
if (GetSpriteTileStartByTag(TAG_DYNAMAX_TRIGGER_TILE) == 0xFFFF)
LoadSpriteSheet(&sSpriteSheet_DynamaxTrigger);
if (gBattleStruct->dynamax.triggerSpriteId == 0xFF)
{
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
gBattleStruct->dynamax.triggerSpriteId = CreateSprite(&sSpriteTemplate_DynamaxTrigger,
gSprites[gHealthboxSpriteIds[battlerId]].x - DOUBLES_DYNAMAX_TRIGGER_POS_X_SLIDE,
gSprites[gHealthboxSpriteIds[battlerId]].y - DOUBLES_DYNAMAX_TRIGGER_POS_Y_DIFF, 0);
else
gBattleStruct->dynamax.triggerSpriteId = CreateSprite(&sSpriteTemplate_DynamaxTrigger,
gSprites[gHealthboxSpriteIds[battlerId]].x - SINGLES_DYNAMAX_TRIGGER_POS_X_SLIDE,
gSprites[gHealthboxSpriteIds[battlerId]].y - SINGLES_DYNAMAX_TRIGGER_POS_Y_DIFF, 0);
}
gSprites[gBattleStruct->dynamax.triggerSpriteId].tBattler = battlerId;
gSprites[gBattleStruct->dynamax.triggerSpriteId].tHide = FALSE;
ChangeDynamaxTriggerSprite(gBattleStruct->dynamax.triggerSpriteId, palId);
}
static void SpriteCb_DynamaxTrigger(struct Sprite *sprite)
{
s32 xSlide, xPriority, xOptimal;
s32 yDiff;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
xSlide = DOUBLES_DYNAMAX_TRIGGER_POS_X_SLIDE;
xPriority = DOUBLES_DYNAMAX_TRIGGER_POS_X_PRIORITY;
xOptimal = DOUBLES_DYNAMAX_TRIGGER_POS_X_OPTIMAL;
yDiff = DOUBLES_DYNAMAX_TRIGGER_POS_Y_DIFF;
}
else
{
xSlide = SINGLES_DYNAMAX_TRIGGER_POS_X_SLIDE;
xPriority = SINGLES_DYNAMAX_TRIGGER_POS_X_PRIORITY;
xOptimal = SINGLES_DYNAMAX_TRIGGER_POS_X_OPTIMAL;
yDiff = SINGLES_DYNAMAX_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)
DestroyDynamaxTriggerSprite();
}
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 IsDynamaxTriggerSpriteActive(void)
{
if (GetSpriteTileStartByTag(TAG_DYNAMAX_TRIGGER_TILE) == 0xFFFF)
return FALSE;
else if (IndexOfSpritePaletteTag(TAG_DYNAMAX_TRIGGER_PAL) != 0xFF)
return TRUE;
else
return FALSE;
}
void HideDynamaxTriggerSprite(void)
{
if (gBattleStruct->dynamax.triggerSpriteId >= MAX_SPRITES)
return;
ChangeDynamaxTriggerSprite(gBattleStruct->dynamax.triggerSpriteId, 0);
gSprites[gBattleStruct->dynamax.triggerSpriteId].tHide = TRUE;
}
void DestroyDynamaxTriggerSprite(void)
{
FreeSpritePaletteByTag(TAG_DYNAMAX_TRIGGER_PAL);
FreeSpriteTilesByTag(TAG_DYNAMAX_TRIGGER_TILE);
if (gBattleStruct->dynamax.triggerSpriteId != 0xFF)
DestroySprite(&gSprites[gBattleStruct->dynamax.triggerSpriteId]);
gBattleStruct->dynamax.triggerSpriteId = 0xFF;
}
#undef tBattler
#undef tHide
// data fields for healthboxMain
// oam.affineParam holds healthboxRight spriteId
#define hMain_DynamaxIndicatorId data[3]
#define hMain_HealthBarSpriteId data[5]
#define hMain_Battler data[6]
#define hMain_Data7 data[7]
// data fields for healthboxRight
#define hOther_HealthBoxSpriteId data[5]
// data fields for healthbar
#define hBar_HealthBoxSpriteId data[5]

View File

@ -626,7 +626,7 @@ void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battler)
}
// dynamax tint
if (IsDynamaxed(battler))
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX)
{
// Calyrex and its forms have a blue dynamax aura instead of red.
if (GET_BASE_SPECIES_ID(species) == SPECIES_CALYREX)
@ -637,7 +637,7 @@ void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battler)
}
// Terastallization's tint
if (IsTerastallized(battler))
if (GetActiveGimmick(battler) == GIMMICK_TERA)
{
BlendPalette(paletteOffset, 16, 8, GetTeraTypeRGB(GetBattlerTeraType(battler)));
CpuCopy32(gPlttBufferFaded + paletteOffset, gPlttBufferUnfaded + paletteOffset, PLTT_SIZEOF(16));
@ -706,8 +706,7 @@ bool8 BattleLoadAllHealthBoxesGfx(u8 state)
{
LoadSpritePalette(&sSpritePalettes_HealthBoxHealthBar[0]);
LoadSpritePalette(&sSpritePalettes_HealthBoxHealthBar[1]);
MegaIndicator_LoadSpritesGfx();
TeraIndicator_LoadSpriteGfx();
LoadIndicatorSpritesGfx();
CategoryIcons_LoadSpritesGfx();
}
else if (!IsDoubleBattle())

395
src/battle_gimmick.c Normal file
View File

@ -0,0 +1,395 @@
#include "global.h"
#include "battle.h"
#include "battle_anim.h"
#include "battle_controllers.h"
#include "battle_interface.h"
#include "battle_gimmick.h"
#include "battle_z_move.h"
#include "battle_setup.h"
#include "battle_util.h"
#include "item.h"
#include "palette.h"
#include "pokemon.h"
#include "sprite.h"
#include "util.h"
#include "test_runner.h"
#include "data/gimmicks.h"
// Populates gBattleStruct->gimmick.usableGimmick for each battler.
void AssignUsableGimmicks(void)
{
u32 battler, gimmick;
for (battler = 0; battler < gBattlersCount; ++battler)
{
gBattleStruct->gimmick.usableGimmick[battler] = GIMMICK_NONE;
for (gimmick = 0; gimmick < GIMMICKS_COUNT; ++gimmick)
{
if (CanActivateGimmick(battler, gimmick))
{
gBattleStruct->gimmick.usableGimmick[battler] = gimmick;
break;
}
}
}
}
// Returns whether a battler is able to use a gimmick. Checks consumption and gimmick specific functions.
bool32 CanActivateGimmick(u32 battler, enum Gimmick gimmick)
{
return gGimmicksInfo[gimmick].CanActivate != NULL && gGimmicksInfo[gimmick].CanActivate(battler);
}
// Returns whether the player has a gimmick selected while in the move selection menu.
bool32 IsGimmickSelected(u32 battler, enum Gimmick gimmick)
{
// There's no player select in tests, but some gimmicks need to test choice before they are fully activated.
if (TESTING)
return (gBattleStruct->gimmick.toActivate & gBitTable[battler]) && gBattleStruct->gimmick.usableGimmick[battler] == gimmick;
else
return gBattleStruct->gimmick.usableGimmick[battler] == gimmick && gBattleStruct->gimmick.playerSelect;
}
// Sets a battler as having a gimmick active using their party index.
void SetActiveGimmick(u32 battler, enum Gimmick gimmick)
{
gBattleStruct->gimmick.activeGimmick[GetBattlerSide(battler)][gBattlerPartyIndexes[battler]] = gimmick;
}
// Returns a battler's active gimmick, if any.
enum Gimmick GetActiveGimmick(u32 battler)
{
return gBattleStruct->gimmick.activeGimmick[GetBattlerSide(battler)][gBattlerPartyIndexes[battler]];
}
// Returns whether a trainer mon is intended to use an unrestrictive gimmick via .useGimmick (i.e Tera).
bool32 ShouldTrainerBattlerUseGimmick(u32 battler, enum Gimmick gimmick)
{
// There are no trainer party settings in battles, but the AI needs to know which gimmick to use.
if (TESTING)
{
return gimmick == TestRunner_Battle_GetChosenGimmick(GetBattlerSide(battler), gBattlerPartyIndexes[battler]);
}
// The player can bypass these checks because they can choose through the controller.
else if (GetBattlerSide(battler) == B_SIDE_PLAYER
&& !((gBattleTypeFlags & BATTLE_TYPE_MULTI) && battler == B_POSITION_PLAYER_RIGHT))
{
return TRUE;
}
// Check the trainer party data to see if a gimmick is intended.
else
{
bool32 isSecondTrainer = (GetBattlerPosition(battler) == B_POSITION_OPPONENT_RIGHT) && (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && !BATTLE_TWO_VS_ONE_OPPONENT;
u16 trainerId = isSecondTrainer ? gTrainerBattleOpponent_B : gTrainerBattleOpponent_A;
const struct TrainerMon *mon = &GetTrainerPartyFromId(trainerId)[isSecondTrainer ? gBattlerPartyIndexes[battler] - MULTI_PARTY_SIZE : gBattlerPartyIndexes[battler]];
return mon->useGimmick == gimmick;
}
}
// Returns whether a trainer has used a gimmick during a battle.
bool32 HasTrainerUsedGimmick(u32 battler, enum Gimmick gimmick)
{
// Check whether partner battler has used gimmick or plans to during turn.
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
&& IsPartnerMonFromSameTrainer(battler)
&& (gBattleStruct->gimmick.activated[BATTLE_PARTNER(battler)][gimmick]
|| ((gBattleStruct->gimmick.toActivate & gBitTable[BATTLE_PARTNER(battler)]
&& gBattleStruct->gimmick.usableGimmick[BATTLE_PARTNER(battler)] == gimmick))))
{
return TRUE;
}
// Otherwise, return whether current battler has used gimmick.
else
{
return gBattleStruct->gimmick.activated[battler][gimmick];
}
}
// Sets a gimmick as used by a trainer with checks for Multi Battles.
void SetGimmickAsActivated(u32 battler, enum Gimmick gimmick)
{
gBattleStruct->gimmick.activated[battler][gimmick] = TRUE;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && IsPartnerMonFromSameTrainer(battler))
gBattleStruct->gimmick.activated[BATTLE_PARTNER(battler)][gimmick] = TRUE;
}
#define SINGLES_GIMMICK_TRIGGER_POS_X_OPTIMAL (30)
#define SINGLES_GIMMICK_TRIGGER_POS_X_PRIORITY (31)
#define SINGLES_GIMMICK_TRIGGER_POS_X_SLIDE (15)
#define SINGLES_GIMMICK_TRIGGER_POS_Y_DIFF (-11)
#define DOUBLES_GIMMICK_TRIGGER_POS_X_OPTIMAL (30)
#define DOUBLES_GIMMICK_TRIGGER_POS_X_PRIORITY (31)
#define DOUBLES_GIMMICK_TRIGGER_POS_X_SLIDE (15)
#define DOUBLES_GIMMICK_TRIGGER_POS_Y_DIFF (-4)
#define tBattler data[0]
#define tHide data[1]
void ChangeGimmickTriggerSprite(u32 spriteId, u32 animId)
{
StartSpriteAnim(&gSprites[spriteId], animId);
}
void CreateGimmickTriggerSprite(u32 battler)
{
const struct GimmickInfo * gimmick = &gGimmicksInfo[gBattleStruct->gimmick.usableGimmick[battler]];
// Exit if there shouldn't be a sprite produced.
if (GetBattlerSide(battler) == B_SIDE_OPPONENT
|| gBattleStruct->gimmick.usableGimmick[battler] == GIMMICK_NONE
|| gimmick->triggerSheet == NULL)
{
return;
}
LoadSpritePalette(gimmick->triggerPal);
if (GetSpriteTileStartByTag(TAG_GIMMICK_TRIGGER_TILE) == 0xFFFF)
LoadSpriteSheet(gimmick->triggerSheet);
if (gBattleStruct->gimmick.triggerSpriteId == 0xFF)
{
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
gBattleStruct->gimmick.triggerSpriteId = CreateSprite(gimmick->triggerTemplate,
gSprites[gHealthboxSpriteIds[battler]].x - DOUBLES_GIMMICK_TRIGGER_POS_X_SLIDE,
gSprites[gHealthboxSpriteIds[battler]].y - DOUBLES_GIMMICK_TRIGGER_POS_Y_DIFF, 0);
else
gBattleStruct->gimmick.triggerSpriteId = CreateSprite(gimmick->triggerTemplate,
gSprites[gHealthboxSpriteIds[battler]].x - SINGLES_GIMMICK_TRIGGER_POS_X_SLIDE,
gSprites[gHealthboxSpriteIds[battler]].y - SINGLES_GIMMICK_TRIGGER_POS_Y_DIFF, 0);
}
gSprites[gBattleStruct->gimmick.triggerSpriteId].tBattler = battler;
gSprites[gBattleStruct->gimmick.triggerSpriteId].tHide = FALSE;
ChangeGimmickTriggerSprite(gBattleStruct->gimmick.triggerSpriteId, 0);
}
bool32 IsGimmickTriggerSpriteActive(void)
{
if (GetSpriteTileStartByTag(TAG_GIMMICK_TRIGGER_TILE) == 0xFFFF)
return FALSE;
else if (IndexOfSpritePaletteTag(TAG_GIMMICK_TRIGGER_PAL) != 0xFF)
return TRUE;
else
return FALSE;
}
void HideGimmickTriggerSprite(void)
{
if (gBattleStruct->gimmick.triggerSpriteId != 0xFF)
{
ChangeGimmickTriggerSprite(gBattleStruct->gimmick.triggerSpriteId, 0);
gSprites[gBattleStruct->gimmick.triggerSpriteId].tHide = TRUE;
}
}
void DestroyGimmickTriggerSprite(void)
{
FreeSpritePaletteByTag(TAG_GIMMICK_TRIGGER_PAL);
FreeSpriteTilesByTag(TAG_GIMMICK_TRIGGER_TILE);
if (gBattleStruct->gimmick.triggerSpriteId != 0xFF)
DestroySprite(&gSprites[gBattleStruct->gimmick.triggerSpriteId]);
gBattleStruct->gimmick.triggerSpriteId = 0xFF;
}
static void SpriteCb_GimmickTrigger(struct Sprite *sprite)
{
s32 xSlide, xPriority, xOptimal;
s32 yDiff;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
xSlide = DOUBLES_GIMMICK_TRIGGER_POS_X_SLIDE;
xPriority = DOUBLES_GIMMICK_TRIGGER_POS_X_PRIORITY;
xOptimal = DOUBLES_GIMMICK_TRIGGER_POS_X_OPTIMAL;
yDiff = DOUBLES_GIMMICK_TRIGGER_POS_Y_DIFF;
}
else
{
xSlide = SINGLES_GIMMICK_TRIGGER_POS_X_SLIDE;
xPriority = SINGLES_GIMMICK_TRIGGER_POS_X_PRIORITY;
xOptimal = SINGLES_GIMMICK_TRIGGER_POS_X_OPTIMAL;
yDiff = SINGLES_GIMMICK_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)
DestroyGimmickTriggerSprite();
}
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;
}
}
#undef tBattler
#undef tHide
// for sprite data fields
#define tBattler data[0]
#define tPosX data[2]
#define tLevelXDelta data[3] // X position depends whether level has 3, 2 or 1 digit
// data fields for healthboxMain
// oam.affineParam holds healthboxRight spriteId
#define hMain_Battler data[6]
void LoadIndicatorSpritesGfx(void)
{
u32 gimmick;
for (gimmick = 0; gimmick < GIMMICKS_COUNT; ++gimmick)
{
if (gimmick == GIMMICK_TERA) // special case
LoadSpriteSheets(sTeraIndicatorSpriteSheets);
else if (gGimmicksInfo[gimmick].indicatorSheet != NULL)
LoadSpriteSheet(gGimmicksInfo[gimmick].indicatorSheet);
if (gGimmicksInfo[gimmick].indicatorPal != NULL)
LoadSpritePalette(gGimmicksInfo[gimmick].indicatorPal);
}
// Primal reversion graphics aren't loaded as part of gimmick data
LoadSpriteSheet(&sSpriteSheet_AlphaIndicator);
LoadSpriteSheet(&sSpriteSheet_OmegaIndicator);
}
static void SpriteCb_GimmickIndicator(struct Sprite *sprite)
{
u32 battler = sprite->tBattler;
sprite->x = gSprites[gHealthboxSpriteIds[battler]].x + sprite->tPosX + sprite->tLevelXDelta;
sprite->x2 = gSprites[gHealthboxSpriteIds[battler]].x2;
sprite->y2 = gSprites[gHealthboxSpriteIds[battler]].y2;
}
static inline u32 GetIndicatorSpriteId(u32 healthboxId)
{
return gBattleStruct->gimmick.indicatorSpriteId[gSprites[healthboxId].hMain_Battler];
}
u32 GetIndicatorTileTag(u32 battler)
{
u32 gimmick = GetActiveGimmick(battler);
if (IsBattlerPrimalReverted(battler))
{
if (gBattleMons[battler].species == SPECIES_GROUDON_PRIMAL)
return TAG_OMEGA_INDICATOR_TILE;
else
return TAG_ALPHA_INDICATOR_TILE;
}
else if (gimmick == GIMMICK_TERA) // special case
{
return sTeraIndicatorSpriteSheets[GetBattlerTeraType(battler)].tag;
}
else if (gGimmicksInfo[gimmick].indicatorSheet != NULL)
{
return gGimmicksInfo[gimmick].indicatorSheet->tag;
}
else
{
return TAG_NONE;
}
}
u32 GetIndicatorPalTag(u32 battler)
{
u32 gimmick = GetActiveGimmick(battler);
if (IsBattlerPrimalReverted(battler))
return TAG_MISC_INDICATOR_PAL;
else if (gGimmicksInfo[gimmick].indicatorPal != NULL)
return gGimmicksInfo[gimmick].indicatorPal->tag;
else
return TAG_NONE;
}
void UpdateIndicatorVisibilityAndType(u32 healthboxId, bool32 invisible)
{
u32 battler = gSprites[healthboxId].hMain_Battler;
u32 tileTag = GetIndicatorTileTag(battler);
u32 palTag = GetIndicatorPalTag(battler);
struct Sprite *sprite = &gSprites[GetIndicatorSpriteId(healthboxId)];
if (GetIndicatorSpriteId(healthboxId) == 0) // safari zone means the player doesn't have an indicator sprite id
return;
if (tileTag != TAG_NONE && palTag != TAG_NONE)
{
sprite->oam.tileNum = GetSpriteTileStartByTag(tileTag);
sprite->oam.paletteNum = IndexOfSpritePaletteTag(palTag);
sprite->invisible = invisible;
}
else // in case of error
{
sprite->invisible = TRUE;
}
}
void UpdateIndicatorOamPriority(u32 healthboxId, u32 oamPriority)
{
gSprites[GetIndicatorSpriteId(healthboxId)].oam.priority = oamPriority;
}
void UpdateIndicatorLevelData(u32 healthboxId, u32 level)
{
s32 xDelta = 0;
if (level >= 100)
xDelta -= 4;
else if (level < 10)
xDelta += 5;
gSprites[GetIndicatorSpriteId(healthboxId)].tLevelXDelta = xDelta;
}
static const s8 sIndicatorPositions[][2] =
{
[B_POSITION_PLAYER_LEFT] = {53, -9},
[B_POSITION_OPPONENT_LEFT] = {44, -9},
[B_POSITION_PLAYER_RIGHT] = {52, -9},
[B_POSITION_OPPONENT_RIGHT] = {44, -9},
};
void CreateIndicatorSprite(u32 battler)
{
u32 position, spriteId;
s16 xHealthbox = 0, x = 0, y = 0;
position = GetBattlerPosition(battler);
GetBattlerHealthboxCoords(battler, &xHealthbox, &y);
x = sIndicatorPositions[position][0];
y += sIndicatorPositions[position][1];
spriteId = CreateSpriteAtEnd(&sSpriteTemplate_GimmickIndicator, 0, y, 0);
gBattleStruct->gimmick.indicatorSpriteId[battler] = spriteId;
gSprites[spriteId].tBattler = battler;
gSprites[spriteId].tPosX = x;
gSprites[spriteId].invisible = FALSE;
}
#undef tBattler
#undef tPosX
#undef tLevelXDelta
#undef hMain_Battler

View File

@ -195,13 +195,6 @@ static void SpriteCB_StatusSummaryBalls_Enter(struct Sprite *);
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_UpdateLevel(u32 healthboxId, u32 level);
static void MegaIndicator_CreateSprite(u32 battlerId, u32 healthboxSpriteId);
static void MegaIndicator_UpdateOamPriority(u32 healthboxId, u32 oamPriority);
static void SpriteCb_MegaIndicator(struct Sprite *);
static u8 GetStatusIconForBattlerId(u8, u8);
static s32 CalcNewBarValue(s32, s32, s32, s32 *, u8, u16);
static u8 GetScaledExpFraction(s32, s32, s32, u8);
@ -620,122 +613,6 @@ static const struct WindowTemplate sHealthboxWindowTemplate = {
.baseBlock = 0
};
static const u8 ALIGNED(4) sMegaTriggerGfx[] = INCBIN_U8("graphics/battle_interface/mega_trigger.4bpp");
static const u16 sMegaTriggerPal[] = INCBIN_U16("graphics/battle_interface/mega_trigger.gbapal");
static const struct SpriteSheet sSpriteSheet_MegaTrigger =
{
sMegaTriggerGfx, sizeof(sMegaTriggerGfx), TAG_MEGA_TRIGGER_TILE
};
static const struct SpritePalette sSpritePalette_MegaTrigger =
{
sMegaTriggerPal, TAG_MEGA_TRIGGER_PAL
};
static const struct OamData sOamData_MegaTrigger =
{
.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_MegaTriggerOff[] =
{
ANIMCMD_FRAME(0, 0),
ANIMCMD_END
};
static const union AnimCmd sSpriteAnim_MegaTriggerOn[] =
{
ANIMCMD_FRAME(16, 0),
ANIMCMD_END
};
static const union AnimCmd *const sSpriteAnimTable_MegaTrigger[] =
{
sSpriteAnim_MegaTriggerOff,
sSpriteAnim_MegaTriggerOn,
};
static const struct SpriteTemplate sSpriteTemplate_MegaTrigger =
{
.tileTag = TAG_MEGA_TRIGGER_TILE,
.paletteTag = TAG_MEGA_TRIGGER_PAL,
.oam = &sOamData_MegaTrigger,
.anims = sSpriteAnimTable_MegaTrigger,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.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.
@ -743,7 +620,6 @@ static const struct SpriteTemplate sSpriteTemplate_BurstTrigger =
// data fields for healthboxMain
// oam.affineParam holds healthboxRight spriteId
#define hMain_MegaIndicatorId data[3]
#define hMain_HealthBarSpriteId data[5]
#define hMain_Battler data[6]
#define hMain_Data7 data[7]
@ -852,11 +728,7 @@ u8 CreateBattlerHealthboxSprites(u8 battlerId)
healthBarSpritePtr->hBar_Data6 = data6;
healthBarSpritePtr->invisible = TRUE;
// Create mega indicator sprite.
MegaIndicator_CreateSprite(battlerId, healthboxLeftSpriteId);
// Create tera indicator sprites.
TeraIndicator_CreateSprite(battlerId, healthboxLeftSpriteId);
CreateIndicatorSprite(battlerId);
gBattleStruct->ballSpriteIds[0] = MAX_SPRITES;
gBattleStruct->ballSpriteIds[1] = MAX_SPRITES;
@ -940,8 +812,7 @@ void SetHealthboxSpriteInvisible(u8 healthboxSpriteId)
gSprites[healthboxSpriteId].invisible = TRUE;
gSprites[gSprites[healthboxSpriteId].hMain_HealthBarSpriteId].invisible = TRUE;
gSprites[gSprites[healthboxSpriteId].oam.affineParam].invisible = TRUE;
MegaIndicator_SetVisibilities(healthboxSpriteId, TRUE);
TeraIndicator_SetVisibilities(healthboxSpriteId, TRUE);
UpdateIndicatorVisibilityAndType(healthboxSpriteId, TRUE);
}
void SetHealthboxSpriteVisible(u8 healthboxSpriteId)
@ -949,8 +820,7 @@ void SetHealthboxSpriteVisible(u8 healthboxSpriteId)
gSprites[healthboxSpriteId].invisible = FALSE;
gSprites[gSprites[healthboxSpriteId].hMain_HealthBarSpriteId].invisible = FALSE;
gSprites[gSprites[healthboxSpriteId].oam.affineParam].invisible = FALSE;
MegaIndicator_SetVisibilities(healthboxSpriteId, FALSE);
TeraIndicator_SetVisibilities(healthboxSpriteId, FALSE);
UpdateIndicatorVisibilityAndType(healthboxSpriteId, FALSE);
}
static void UpdateSpritePos(u8 spriteId, s16 x, s16 y)
@ -976,8 +846,8 @@ static void TryToggleHealboxVisibility(u32 priority, u32 healthboxLeftSpriteId,
gSprites[healthboxLeftSpriteId].invisible = invisible;
gSprites[healthboxRightSpriteId].invisible = invisible;
gSprites[healthbarSpriteId].invisible = invisible;
MegaIndicator_SetVisibilities(healthboxLeftSpriteId, invisible);
TeraIndicator_SetVisibilities(healthboxLeftSpriteId, invisible);
UpdateIndicatorVisibilityAndType(healthboxLeftSpriteId, invisible);
}
void UpdateOamPriorityInAllHealthboxes(u8 priority, bool32 hideHPBoxes)
@ -994,8 +864,7 @@ void UpdateOamPriorityInAllHealthboxes(u8 priority, bool32 hideHPBoxes)
gSprites[healthboxRightSpriteId].oam.priority = priority;
gSprites[healthbarSpriteId].oam.priority = priority;
MegaIndicator_UpdateOamPriority(healthboxLeftSpriteId, priority);
TeraIndicator_UpdateOamPriorities(healthboxLeftSpriteId, priority);
UpdateIndicatorOamPriority(healthboxLeftSpriteId, priority);
if (B_HIDE_HEALTHBOX_IN_ANIMS == TRUE && hideHPBoxes && IsBattlerAlive(i))
TryToggleHealboxVisibility(priority, healthboxLeftSpriteId, healthboxRightSpriteId, healthbarSpriteId);
@ -1050,20 +919,13 @@ static void UpdateLvlInHealthbox(u8 healthboxSpriteId, u8 lvl)
u8 *objVram;
u8 battler = gSprites[healthboxSpriteId].hMain_Battler;
// Don't print Lv char if mon is mega evolved or primal reverted or Dynamaxed.
if (IsBattlerMegaEvolved(battler) || IsBattlerPrimalReverted(battler) || IsDynamaxed(battler))
// Don't print Lv char if mon has a gimmick with an indicator active.
if (GetIndicatorTileTag(battler) != TAG_NONE)
{
objVram = ConvertIntToDecimalStringN(text, lvl, STR_CONV_MODE_LEFT_ALIGN, 3);
xPos = 5 * (3 - (objVram - (text + 2))) - 1;
MegaIndicator_UpdateLevel(healthboxSpriteId, lvl);
MegaIndicator_SetVisibilities(healthboxSpriteId, FALSE);
}
else if (IsTerastallized(battler))
{
objVram = ConvertIntToDecimalStringN(text, lvl, STR_CONV_MODE_LEFT_ALIGN, 3);
xPos = 5 * (3 - (objVram - (text + 2))) - 1;
TeraIndicator_UpdateLevel(healthboxSpriteId, lvl);
TeraIndicator_SetVisibilities(healthboxSpriteId, FALSE);
UpdateIndicatorLevelData(healthboxSpriteId, lvl);
UpdateIndicatorVisibilityAndType(healthboxSpriteId, FALSE);
}
else
{
@ -1072,7 +934,7 @@ static void UpdateLvlInHealthbox(u8 healthboxSpriteId, u8 lvl)
objVram = ConvertIntToDecimalStringN(text + 2, lvl, STR_CONV_MODE_LEFT_ALIGN, 3);
xPos = 5 * (3 - (objVram - (text + 2)));
MegaIndicator_SetVisibilities(healthboxSpriteId, TRUE);
UpdateIndicatorVisibilityAndType(healthboxSpriteId, TRUE);
}
windowTileData = AddTextPrinterAndCreateWindowOnHealthbox(text, xPos, 3, 2, &windowId);
@ -1373,435 +1235,6 @@ void SwapHpBarsWithHpText(void)
}
}
// Mega Evolution Trigger icon functions.
void ChangeMegaTriggerSprite(u8 spriteId, u8 animId)
{
StartSpriteAnim(&gSprites[spriteId], animId);
}
#define SINGLES_MEGA_TRIGGER_POS_X_OPTIMAL (30)
#define SINGLES_MEGA_TRIGGER_POS_X_PRIORITY (31)
#define SINGLES_MEGA_TRIGGER_POS_X_SLIDE (15)
#define SINGLES_MEGA_TRIGGER_POS_Y_DIFF (-11)
#define DOUBLES_MEGA_TRIGGER_POS_X_OPTIMAL (30)
#define DOUBLES_MEGA_TRIGGER_POS_X_PRIORITY (31)
#define DOUBLES_MEGA_TRIGGER_POS_X_SLIDE (15)
#define DOUBLES_MEGA_TRIGGER_POS_Y_DIFF (-4)
#define tBattler data[0]
#define tHide data[1]
void CreateMegaTriggerSprite(u8 battlerId, u8 palId)
{
LoadSpritePalette(&sSpritePalette_MegaTrigger);
if (GetSpriteTileStartByTag(TAG_MEGA_TRIGGER_TILE) == 0xFFFF)
LoadSpriteSheet(&sSpriteSheet_MegaTrigger);
if (gBattleStruct->mega.triggerSpriteId == 0xFF)
{
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
gBattleStruct->mega.triggerSpriteId = CreateSprite(&sSpriteTemplate_MegaTrigger,
gSprites[gHealthboxSpriteIds[battlerId]].x - DOUBLES_MEGA_TRIGGER_POS_X_SLIDE,
gSprites[gHealthboxSpriteIds[battlerId]].y - DOUBLES_MEGA_TRIGGER_POS_Y_DIFF, 0);
else
gBattleStruct->mega.triggerSpriteId = CreateSprite(&sSpriteTemplate_MegaTrigger,
gSprites[gHealthboxSpriteIds[battlerId]].x - SINGLES_MEGA_TRIGGER_POS_X_SLIDE,
gSprites[gHealthboxSpriteIds[battlerId]].y - SINGLES_MEGA_TRIGGER_POS_Y_DIFF, 0);
}
gSprites[gBattleStruct->mega.triggerSpriteId].tBattler = battlerId;
gSprites[gBattleStruct->mega.triggerSpriteId].tHide = FALSE;
ChangeMegaTriggerSprite(gBattleStruct->mega.triggerSpriteId, palId);
}
static void SpriteCb_MegaTrigger(struct Sprite *sprite)
{
s32 xSlide, xPriority, xOptimal;
s32 yDiff;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
xSlide = DOUBLES_MEGA_TRIGGER_POS_X_SLIDE;
xPriority = DOUBLES_MEGA_TRIGGER_POS_X_PRIORITY;
xOptimal = DOUBLES_MEGA_TRIGGER_POS_X_OPTIMAL;
yDiff = DOUBLES_MEGA_TRIGGER_POS_Y_DIFF;
}
else
{
xSlide = SINGLES_MEGA_TRIGGER_POS_X_SLIDE;
xPriority = SINGLES_MEGA_TRIGGER_POS_X_PRIORITY;
xOptimal = SINGLES_MEGA_TRIGGER_POS_X_OPTIMAL;
yDiff = SINGLES_MEGA_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)
DestroyMegaTriggerSprite();
}
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 IsMegaTriggerSpriteActive(void)
{
if (GetSpriteTileStartByTag(TAG_MEGA_TRIGGER_TILE) == 0xFFFF)
return FALSE;
else if (IndexOfSpritePaletteTag(TAG_MEGA_TRIGGER_PAL) != 0xFF)
return TRUE;
else
return FALSE;
}
void HideMegaTriggerSprite(void)
{
if (gBattleStruct->mega.triggerSpriteId >= MAX_SPRITES)
return;
ChangeMegaTriggerSprite(gBattleStruct->mega.triggerSpriteId, 0);
gSprites[gBattleStruct->mega.triggerSpriteId].tHide = TRUE;
}
void HideTriggerSprites(void)
{
HideMegaTriggerSprite();
HideBurstTriggerSprite();
HideZMoveTriggerSprite();
HideDynamaxTriggerSprite();
HideTeraTriggerSprite();
}
void DestroyMegaTriggerSprite(void)
{
FreeSpritePaletteByTag(TAG_MEGA_TRIGGER_PAL);
FreeSpriteTilesByTag(TAG_MEGA_TRIGGER_TILE);
if (gBattleStruct->mega.triggerSpriteId != 0xFF)
DestroySprite(&gSprites[gBattleStruct->mega.triggerSpriteId]);
gBattleStruct->mega.triggerSpriteId = 0xFF;
}
#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
{
INDICATOR_MEGA,
INDICATOR_ALPHA,
INDICATOR_OMEGA,
INDICATOR_DYNAMAX,
INDICATOR_COUNT,
};
static const u8 ALIGNED(4) sMegaIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/mega_indicator.4bpp");
static const u16 sMegaIndicatorPal[] = INCBIN_U16("graphics/battle_interface/mega_indicator.gbapal");
static const u8 ALIGNED(4) sAlphaIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/alpha_indicator.4bpp");
static const u8 ALIGNED(4) sOmegaIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/omega_indicator.4bpp");
static const u16 sAlphaOmegaIndicatorPal[] = INCBIN_U16("graphics/battle_interface/misc_indicator.gbapal");
static const u8 ALIGNED(4) sDynamaxIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/dynamax_indicator.4bpp");
static const u16 sDynamaxIndicatorPal[] = INCBIN_U16("graphics/battle_interface/misc_indicator.gbapal");
static const struct SpriteSheet sMegaIndicator_SpriteSheets[] =
{
[INDICATOR_MEGA] = {sMegaIndicatorGfx, sizeof(sMegaIndicatorGfx), TAG_MEGA_INDICATOR_TILE},
[INDICATOR_ALPHA] = {sAlphaIndicatorGfx, sizeof(sAlphaIndicatorGfx), TAG_ALPHA_INDICATOR_TILE},
[INDICATOR_OMEGA] = {sOmegaIndicatorGfx, sizeof(sOmegaIndicatorGfx), TAG_OMEGA_INDICATOR_TILE},
[INDICATOR_DYNAMAX] = {sDynamaxIndicatorGfx, sizeof(sDynamaxIndicatorGfx), TAG_DYNAMAX_INDICATOR_TILE},
[INDICATOR_COUNT] = {0}
};
static const struct SpritePalette sMegaIndicator_SpritePalettes[] =
{
[INDICATOR_MEGA] = {sMegaIndicatorPal, TAG_MEGA_INDICATOR_PAL},
[INDICATOR_ALPHA] = {sAlphaOmegaIndicatorPal, TAG_MISC_INDICATOR_PAL},
[INDICATOR_OMEGA] = {sAlphaOmegaIndicatorPal, TAG_MISC_INDICATOR_PAL},
[INDICATOR_DYNAMAX] = {sDynamaxIndicatorPal, TAG_MISC_INDICATOR_PAL},
[INDICATOR_COUNT] = {0}
};
static const struct OamData sOamData_MegaIndicator =
{
.shape = SPRITE_SHAPE(16x16),
.size = SPRITE_SIZE(16x16),
.priority = 1,
};
static const struct SpriteTemplate sSpriteTemplate_MegaIndicator =
{
.tileTag = TAG_MEGA_INDICATOR_TILE,
.paletteTag = TAG_MEGA_INDICATOR_PAL,
.oam = &sOamData_MegaIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_MegaIndicator,
};
static const u16 sMegaIndicatorTags[][2] =
{
[INDICATOR_MEGA] = {TAG_MEGA_INDICATOR_TILE, TAG_MEGA_INDICATOR_PAL},
[INDICATOR_ALPHA] = {TAG_ALPHA_INDICATOR_TILE, TAG_MISC_INDICATOR_PAL},
[INDICATOR_OMEGA] = {TAG_OMEGA_INDICATOR_TILE, TAG_MISC_INDICATOR_PAL},
[INDICATOR_DYNAMAX] = {TAG_DYNAMAX_INDICATOR_TILE, TAG_MISC_INDICATOR_PAL},
};
static const s8 sIndicatorPositions[][2] =
{
[B_POSITION_PLAYER_LEFT] = {52, -9},
[B_POSITION_OPPONENT_LEFT] = {44, -9},
[B_POSITION_PLAYER_RIGHT] = {52, -9},
[B_POSITION_OPPONENT_RIGHT] = {44, -9},
};
// for sprite data fields
#define tBattler data[0]
#define tType data[1] // Indicator type: mega, alpha, omega
#define tPosX data[2]
#define tLevelXDelta data[3] // X position depends whether level has 3, 2 or 1 digit
void MegaIndicator_LoadSpritesGfx(void)
{
LoadSpriteSheets(sMegaIndicator_SpriteSheets);
LoadSpritePalettes(sMegaIndicator_SpritePalettes);
}
static bool32 MegaIndicator_ShouldBeInvisible(u32 battlerId, struct Sprite *sprite)
{
bool32 megaEvolved = IsBattlerMegaEvolved(battlerId);
bool32 primalReverted = IsBattlerPrimalReverted(battlerId);
bool32 dynamaxed = IsDynamaxed(battlerId);
if (!megaEvolved && !primalReverted && !dynamaxed)
return TRUE;
if (megaEvolved)
sprite->tType = INDICATOR_MEGA;
else if (primalReverted && gBattleMons[battlerId].species == SPECIES_KYOGRE_PRIMAL)
sprite->tType = INDICATOR_ALPHA;
else if (primalReverted && gBattleMons[battlerId].species == SPECIES_GROUDON_PRIMAL)
sprite->tType = INDICATOR_OMEGA;
else if (dynamaxed)
sprite->tType = INDICATOR_DYNAMAX;
sprite->oam.tileNum = GetSpriteTileStartByTag(sMegaIndicatorTags[sprite->tType][0]);
sprite->oam.paletteNum = IndexOfSpritePaletteTag(sMegaIndicatorTags[sprite->tType][1]);
return FALSE;
}
static u8 *MegaIndicator_GetSpriteId(u32 healthboxSpriteId)
{
u8 *spriteId = (u8 *)(&gSprites[healthboxSpriteId].hMain_MegaIndicatorId);
return spriteId;
}
void MegaIndicator_SetVisibilities(u32 healthboxId, bool32 invisible)
{
u8 *spriteId = MegaIndicator_GetSpriteId(healthboxId);
u32 battlerId = gSprites[healthboxId].hMain_Battler;
if (GetSafariZoneFlag())
return;
if (invisible == TRUE)
gSprites[*spriteId].invisible = TRUE;
else // Try visible.
gSprites[*spriteId].invisible = MegaIndicator_ShouldBeInvisible(battlerId, &gSprites[*spriteId]);
}
static void MegaIndicator_UpdateOamPriority(u32 healthboxId, u32 oamPriority)
{
u8 *spriteId = MegaIndicator_GetSpriteId(healthboxId);
gSprites[*spriteId].oam.priority = oamPriority;
}
static void MegaIndicator_UpdateLevel(u32 healthboxId, u32 level)
{
s16 xDelta = 0;
u8 *spriteId = MegaIndicator_GetSpriteId(healthboxId);
if (level >= 100)
xDelta -= 4;
else if (level < 10)
xDelta += 5;
gSprites[*spriteId].tLevelXDelta = xDelta;
}
static void MegaIndicator_CreateSprite(u32 battlerId, u32 healthboxSpriteId)
{
struct SpriteTemplate sprTemplate;
u32 position;
u8 *spriteId;
s16 xHealthbox = 0, y = 0;
s32 x = 0;
position = GetBattlerPosition(battlerId);
GetBattlerHealthboxCoords(battlerId, &xHealthbox, &y);
x = sIndicatorPositions[position][0];
y += sIndicatorPositions[position][1];
spriteId = MegaIndicator_GetSpriteId(healthboxSpriteId);
sprTemplate = sSpriteTemplate_MegaIndicator;
sprTemplate.tileTag = sMegaIndicatorTags[INDICATOR_MEGA][0];
sprTemplate.paletteTag = sMegaIndicatorTags[INDICATOR_MEGA][1];
*spriteId = CreateSpriteAtEnd(&sprTemplate, 0, y, 0);
gSprites[*spriteId].tType = INDICATOR_MEGA;
gSprites[*spriteId].tBattler = battlerId;
gSprites[*spriteId].tPosX = x;
gSprites[*spriteId].invisible = TRUE;
}
static void SpriteCb_MegaIndicator(struct Sprite *sprite)
{
u32 battlerId = sprite->tBattler;
sprite->x = gSprites[gHealthboxSpriteIds[battlerId]].x + sprite->tPosX + sprite->tLevelXDelta;
sprite->x2 = gSprites[gHealthboxSpriteIds[battlerId]].x2;
sprite->y2 = gSprites[gHealthboxSpriteIds[battlerId]].y2;
}
#undef tBattler
#undef tType
#undef tPosX
@ -2545,10 +1978,6 @@ void UpdateHealthboxAttribute(u8 healthboxSpriteId, struct Pokemon *mon, u8 elem
u32 battlerId = gSprites[healthboxSpriteId].hMain_Battler;
s32 maxHp = GetMonData(mon, MON_DATA_MAX_HP);
s32 currHp = GetMonData(mon, MON_DATA_HP);
// This fixes a bug that should likely never happen involving switching between two Teras.
if (elementId == HEALTHBOX_ALL)
TeraIndicator_UpdateType(battlerId, healthboxSpriteId);
if (GetBattlerSide(battlerId) == B_SIDE_PLAYER)
{

View File

@ -13,6 +13,8 @@
#include "battle_setup.h"
#include "battle_tower.h"
#include "battle_util.h"
#include "battle_z_move.h"
#include "battle_gimmick.h"
#include "berry.h"
#include "bg.h"
#include "data.h"
@ -3097,9 +3099,6 @@ static void BattleStartClearSetData(void)
gBattleStruct->arenaLostPlayerMons = 0;
gBattleStruct->arenaLostOpponentMons = 0;
gBattleStruct->mega.triggerSpriteId = 0xFF;
gBattleStruct->burst.triggerSpriteId = 0xFF;
for (i = 0; i < ARRAY_COUNT(gSideTimers); i++)
{
gSideTimers[i].stickyWebBattlerId = 0xFF;
@ -3111,13 +3110,16 @@ static void BattleStartClearSetData(void)
{
gBattleStruct->usedHeldItems[i][B_SIDE_PLAYER] = 0;
gBattleStruct->usedHeldItems[i][B_SIDE_OPPONENT] = 0;
gBattleStruct->itemLost[i].originalItem = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM);
gBattleStruct->itemLost[B_SIDE_PLAYER][i].originalItem = GetMonData(&gPlayerParty[i], MON_DATA_HELD_ITEM);
gBattleStruct->itemLost[B_SIDE_OPPONENT][i].originalItem = GetMonData(&gEnemyParty[i], MON_DATA_HELD_ITEM);
gPartyCriticalHits[i] = 0;
gBattleStruct->allowedToChangeFormInWeather[i][B_SIDE_PLAYER] = FALSE;
gBattleStruct->allowedToChangeFormInWeather[i][B_SIDE_OPPONENT] = FALSE;
}
gBattleStruct->swapDamageCategory = FALSE; // Photon Geyser, Shell Side Arm, Light That Burns the Sky
gBattleStruct->categoryOverride = FALSE; // used for Z-Moves and Max Moves
gSelectedMonPartyId = PARTY_SIZE; // Revival Blessing
gCategoryIconSpriteId = 0xFF;
}
@ -3247,9 +3249,6 @@ void SwitchInClearSetData(u32 battler)
// Reset G-Max Chi Strike boosts.
gBattleStruct->bonusCritStages[battler] = 0;
// Reset Dynamax flags.
UndoDynamax(battler);
gBattleStruct->overwrittenAbilities[battler] = ABILITY_NONE;
// Clear selected party ID so Revival Blessing doesn't get confused.
@ -3416,10 +3415,6 @@ const u8* FaintClearSetData(u32 battler)
}
}
// Clear Z-Move data
gBattleStruct->zmove.active = FALSE;
gBattleStruct->zmove.toBeUsed[battler] = MOVE_NONE;
gBattleStruct->zmove.effect = EFFECT_HIT;
// Clear Dynamax data
UndoDynamax(battler);
@ -3881,6 +3876,7 @@ static void TryDoEventsBeforeFirstTurn(void)
SpecialStatusesClear();
*(&gBattleStruct->absentBattlerFlags) = gAbsentBattlerFlags;
BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG);
AssignUsableGimmicks();
gBattleMainFunc = HandleTurnActionSelectionState;
ResetSentPokesToOpponentValue();
@ -3999,6 +3995,7 @@ void BattleTurnPassed(void)
*(&gBattleStruct->absentBattlerFlags) = gAbsentBattlerFlags;
BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG);
AssignUsableGimmicks();
SetShellSideArmCategory();
SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers
gBattleMainFunc = HandleTurnActionSelectionState;
@ -4226,7 +4223,6 @@ static void HandleTurnActionSelectionState(void)
struct ChooseMoveStruct moveInfo;
moveInfo.zmove = gBattleStruct->zmove;
moveInfo.mega = gBattleStruct->mega;
moveInfo.species = gBattleMons[battler].species;
moveInfo.monType1 = gBattleMons[battler].type1;
moveInfo.monType2 = gBattleMons[battler].type2;
@ -4351,12 +4347,7 @@ static void HandleTurnActionSelectionState(void)
RecordedBattle_ClearBattlerAction(GetBattlerAtPosition(BATTLE_PARTNER(GetBattlerPosition(battler))), 3);
}
gBattleStruct->mega.toEvolve &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]);
gBattleStruct->burst.toBurst &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]);
gBattleStruct->dynamax.toDynamax &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]);
gBattleStruct->tera.toTera &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]);
gBattleStruct->dynamax.usingMaxMove[BATTLE_PARTNER(GetBattlerPosition(battler))] = FALSE;
gBattleStruct->zmove.toBeUsed[BATTLE_PARTNER(GetBattlerPosition(battler))] = MOVE_NONE;
gBattleStruct->gimmick.toActivate &= ~(gBitTable[BATTLE_PARTNER(GetBattlerPosition(battler))]);
BtlController_EmitEndBounceEffect(battler, BUFFER_A);
MarkBattlerForControllerExec(battler);
return;
@ -4444,25 +4435,18 @@ static void HandleTurnActionSelectionState(void)
}
// Get the chosen move position (and thus the chosen move) and target from the returned buffer.
gBattleStruct->chosenMovePositions[battler] = gBattleResources->bufferB[battler][2] & ~(RET_MEGA_EVOLUTION | RET_ULTRA_BURST | RET_DYNAMAX | RET_TERASTAL);
gBattleStruct->chosenMovePositions[battler] = gBattleResources->bufferB[battler][2] & ~RET_GIMMICK;
gChosenMoveByBattler[battler] = gBattleMons[battler].moves[gBattleStruct->chosenMovePositions[battler]];
gBattleStruct->moveTarget[battler] = gBattleResources->bufferB[battler][3];
// Check to see if any gimmicks need to be prepared.
if (gBattleResources->bufferB[battler][2] & RET_MEGA_EVOLUTION)
gBattleStruct->mega.toEvolve |= gBitTable[battler];
else if (gBattleResources->bufferB[battler][2] & RET_ULTRA_BURST)
gBattleStruct->burst.toBurst |= gBitTable[battler];
else if (gBattleResources->bufferB[battler][2] & RET_DYNAMAX)
gBattleStruct->dynamax.toDynamax |= gBitTable[battler];
else if (gBattleResources->bufferB[battler][2] & RET_TERASTAL)
gBattleStruct->tera.toTera |= gBitTable[battler];
if (gBattleResources->bufferB[battler][2] & RET_GIMMICK)
gBattleStruct->gimmick.toActivate |= gBitTable[battler];
// Max Move check
if (ShouldUseMaxMove(battler, gChosenMoveByBattler[battler]))
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX || IsGimmickSelected(battler, GIMMICK_DYNAMAX))
{
gBattleStruct->dynamax.baseMove[battler] = gBattleMons[battler].moves[gBattleStruct->chosenMovePositions[battler]];
gBattleStruct->dynamax.usingMaxMove[battler] = TRUE;
gBattleStruct->dynamax.baseMoves[battler] = gBattleMons[battler].moves[gBattleStruct->chosenMovePositions[battler]];
}
gBattleCommunication[battler]++;
@ -4738,7 +4722,7 @@ u32 GetBattlerTotalSpeedStatArgs(u32 battler, u32 ability, u32 holdEffect)
speed /= 2;
else if (holdEffect == HOLD_EFFECT_IRON_BALL)
speed /= 2;
else if (holdEffect == HOLD_EFFECT_CHOICE_SCARF && !IsDynamaxed(battler))
else if (holdEffect == HOLD_EFFECT_CHOICE_SCARF && GetActiveGimmick(battler) != GIMMICK_DYNAMAX)
speed = (speed * 150) / 100;
else if (holdEffect == HOLD_EFFECT_QUICK_POWDER && gBattleMons[battler].species == SPECIES_DITTO && !(gBattleMons[battler].status2 & STATUS2_TRANSFORMED))
speed *= 2;
@ -4784,13 +4768,13 @@ s8 GetMovePriority(u32 battler, u16 move)
s8 priority;
u16 ability = GetBattlerAbility(battler);
if (gBattleStruct->zmove.toBeUsed[battler] && gMovesInfo[move].power != 0)
move = gBattleStruct->zmove.toBeUsed[battler];
if (GetActiveGimmick(battler) == GIMMICK_Z_MOVE && gMovesInfo[move].power != 0)
move = GetUsableZMove(battler, move);
priority = gMovesInfo[move].priority;
// Max Guard check
if (gBattleStruct->dynamax.usingMaxMove[battler] && gMovesInfo[move].category == DAMAGE_CATEGORY_STATUS)
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX && gMovesInfo[move].category == DAMAGE_CATEGORY_STATUS)
return gMovesInfo[MOVE_MAX_GUARD].priority;
if (ability == ABILITY_GALE_WINGS
@ -4804,7 +4788,7 @@ s8 GetMovePriority(u32 battler, u16 move)
gProtectStructs[battler].pranksterElevated = 1;
priority++;
}
else if (gMovesInfo[move].effect == EFFECT_GRASSY_GLIDE && gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && IsBattlerGrounded(battler) && !IsDynamaxed(battler) && !(gBattleStruct->dynamax.toDynamax & gBitTable[battler]))
else if (gMovesInfo[move].effect == EFFECT_GRASSY_GLIDE && gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN && IsBattlerGrounded(battler) && GetActiveGimmick(gBattlerAttacker) != GIMMICK_DYNAMAX && !IsGimmickSelected(battler, GIMMICK_DYNAMAX))
{
priority++;
}
@ -5069,9 +5053,7 @@ static void PopulateArrayWithBattlers(u8 *battlers)
static bool32 TryDoGimmicksBeforeMoves(void)
{
if (!(gHitMarker & HITMARKER_RUN)
&& (gBattleStruct->mega.toEvolve || gBattleStruct->burst.toBurst
|| gBattleStruct->dynamax.toDynamax || gBattleStruct->tera.toTera))
if (!(gHitMarker & HITMARKER_RUN) && gBattleStruct->gimmick.toActivate)
{
u32 i, battler;
u8 order[MAX_BATTLERS_COUNT];
@ -5080,52 +5062,16 @@ static bool32 TryDoGimmicksBeforeMoves(void)
SortBattlersBySpeed(order, FALSE);
for (i = 0; i < gBattlersCount; i++)
{
// Tera Check
if (gBattleStruct->tera.toTera & gBitTable[order[i]])
// Search through each battler and activate their gimmick if they have one prepared.
if ((gBattleStruct->gimmick.toActivate & gBitTable[order[i]]) && !(gProtectStructs[order[i]].noValidMoves))
{
gBattlerAttacker = order[i];
gBattleScripting.battler = gBattlerAttacker;
gBattleStruct->tera.toTera &= ~(gBitTable[gBattlerAttacker]);
PrepareBattlerForTera(gBattlerAttacker);
PREPARE_TYPE_BUFFER(gBattleTextBuff1, GetBattlerTeraType(gBattlerAttacker));
if (TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_TERASTALLIZATION))
BattleScriptExecute(BattleScript_TeraFormChange);
else
BattleScriptExecute(BattleScript_Terastallization);
return TRUE;
}
// Dynamax Check
if (gBattleStruct->dynamax.toDynamax & gBitTable[order[i]])
{
gBattlerAttacker = order[i];
gBattleScripting.battler = gBattlerAttacker;
gBattleStruct->dynamax.toDynamax &= ~(gBitTable[gBattlerAttacker]);
PrepareBattlerForDynamax(gBattlerAttacker);
BattleScriptExecute(BattleScript_DynamaxBegins);
return TRUE;
}
// Mega Evo Check
if (gBattleStruct->mega.toEvolve & gBitTable[order[i]]
&& !(gProtectStructs[order[i]].noValidMoves))
{
gBattlerAttacker = order[i];
gBattleStruct->mega.toEvolve &= ~(gBitTable[gBattlerAttacker]);
gLastUsedItem = gBattleMons[gBattlerAttacker].item;
if (GetBattleFormChangeTargetSpecies(gBattlerAttacker, FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE) != SPECIES_NONE)
BattleScriptExecute(BattleScript_WishMegaEvolution);
else
BattleScriptExecute(BattleScript_MegaEvolution);
return TRUE;
}
// Ultra Burst Check
if (gBattleStruct->burst.toBurst & gBitTable[order[i]]
&& !(gProtectStructs[order[i]].noValidMoves))
{
battler = gBattlerAttacker = order[i];
gBattleStruct->burst.toBurst &= ~(gBitTable[battler]);
gLastUsedItem = gBattleMons[battler].item;
BattleScriptExecute(BattleScript_UltraBurst);
return TRUE;
battler = gBattlerAttacker = gBattleScripting.battler = order[i];
gBattleStruct->gimmick.toActivate &= ~(gBitTable[battler]);
if (gGimmicksInfo[gBattleStruct->gimmick.usableGimmick[battler]].ActivateGimmick != NULL)
{
gGimmicksInfo[gBattleStruct->gimmick.usableGimmick[battler]].ActivateGimmick(battler);
return TRUE;
}
}
}
}
@ -5512,10 +5458,10 @@ static void HandleEndTurn_FinishBattle(void)
TryRestoreHeldItems();
// Undo Dynamax HP multiplier before recalculating stats.
for (i = 0; i < gBattlersCount; ++i)
for (battler = 0; battler < gBattlersCount; ++battler)
{
if (IsDynamaxed(i))
UndoDynamax(i);
if (GetActiveGimmick(battler) == GIMMICK_DYNAMAX)
UndoDynamax(battler);
}
for (i = 0; i < PARTY_SIZE; i++)
@ -5588,8 +5534,9 @@ static void FreeResetData_ReturnToOvOrDoEvolutions(void)
}
FreeAllWindowBuffers();
if (!(gBattleTypeFlags & BATTLE_TYPE_LINK))
if (gBattleStruct != NULL && !(gBattleTypeFlags & BATTLE_TYPE_LINK))
{
ZeroEnemyPartyMons();
FreeMonSpritesGfx();
FreeBattleResources();
FreeBattleSpritesData();
@ -5699,7 +5646,7 @@ bool32 TrySetAteType(u32 move, u32 battlerAtk, u32 attackerAbility)
switch (gMovesInfo[move].effect)
{
case EFFECT_TERA_BLAST:
if (IsTerastallized(battlerAtk))
if (GetActiveGimmick(battlerAtk) == GIMMICK_TERA)
return FALSE;
break;
case EFFECT_TERA_STARSTORM:
@ -5733,7 +5680,7 @@ bool32 TrySetAteType(u32 move, u32 battlerAtk, u32 attackerAbility)
break;
}
if (ateType != TYPE_NONE)
if (ateType != TYPE_NONE && GetActiveGimmick(battlerAtk) != GIMMICK_Z_MOVE)
{
gBattleStruct->dynamicMoveType = ateType | F_DYNAMIC_TYPE_SET;
return TRUE;
@ -5792,7 +5739,7 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk)
}
else if (gMovesInfo[move].effect == EFFECT_REVELATION_DANCE)
{
if (IsTerastallized(battlerAtk) && GetBattlerTeraType(battlerAtk) != TYPE_STELLAR)
if (GetActiveGimmick(battlerAtk) == GIMMICK_TERA && GetBattlerTeraType(battlerAtk) != TYPE_STELLAR)
gBattleStruct->dynamicMoveType = GetBattlerTeraType(battlerAtk);
else if (gBattleMons[battlerAtk].type1 != TYPE_MYSTERY)
gBattleStruct->dynamicMoveType = gBattleMons[battlerAtk].type1 | F_DYNAMIC_TYPE_SET;
@ -5836,7 +5783,7 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk)
gBattleStruct->dynamicMoveType = TYPE_NORMAL | F_DYNAMIC_TYPE_SET;
}
}
else if (gMovesInfo[move].effect == EFFECT_TERA_BLAST && IsTerastallized(battlerAtk))
else if (gMovesInfo[move].effect == EFFECT_TERA_BLAST && GetActiveGimmick(battlerAtk) == GIMMICK_TERA)
{
gBattleStruct->dynamicMoveType = GetBattlerTeraType(battlerAtk) | F_DYNAMIC_TYPE_SET;
}
@ -5846,18 +5793,20 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk)
}
attackerAbility = GetBattlerAbility(battlerAtk);
if (gMovesInfo[move].type == TYPE_NORMAL && TrySetAteType(move, battlerAtk, attackerAbility))
if (gMovesInfo[move].type == TYPE_NORMAL
&& TrySetAteType(move, battlerAtk, attackerAbility)
&& GetActiveGimmick(battlerAtk) != GIMMICK_DYNAMAX)
{
if (!IsDynamaxed(battlerAtk))
gBattleStruct->ateBoost[battlerAtk] = 1;
}
else if (gMovesInfo[move].type != TYPE_NORMAL
&& gMovesInfo[move].effect != EFFECT_HIDDEN_POWER
&& gMovesInfo[move].effect != EFFECT_WEATHER_BALL
&& attackerAbility == ABILITY_NORMALIZE)
&& attackerAbility == ABILITY_NORMALIZE
&& GetActiveGimmick(battlerAtk) != GIMMICK_Z_MOVE)
{
gBattleStruct->dynamicMoveType = TYPE_NORMAL | F_DYNAMIC_TYPE_SET;
if (!IsDynamaxed(battlerAtk))
if (GetActiveGimmick(battlerAtk) != GIMMICK_DYNAMAX)
gBattleStruct->ateBoost[battlerAtk] = 1;
}
else if (gMovesInfo[move].soundMove && attackerAbility == ABILITY_LIQUID_VOICE)

View File

@ -3967,7 +3967,10 @@ void BattlePutTextOnWindow(const u8 *text, u8 windowId)
// We cannot check the actual width of the window because
// B_WIN_MOVE_NAME_1 and B_WIN_MOVE_NAME_3 are 16 wide for
// Z-move details.
printerTemplate.fontId = GetFontIdToFit(text, printerTemplate.fontId, printerTemplate.letterSpacing, 8 * TILE_WIDTH);
if (gBattleStruct->zmove.viewing && windowId == B_WIN_MOVE_NAME_1)
printerTemplate.fontId = GetFontIdToFit(text, printerTemplate.fontId, printerTemplate.letterSpacing, 16 * TILE_WIDTH);
else
printerTemplate.fontId = GetFontIdToFit(text, printerTemplate.fontId, printerTemplate.letterSpacing, 8 * TILE_WIDTH);
}
if (printerTemplate.x == 0xFF)
@ -4023,7 +4026,7 @@ void SetPpNumbersPaletteInMoveSelection(u32 battler)
var = GetCurrentPpToMaxPpState(chooseMoveStruct->currentPp[gMoveSelectionCursor[battler]],
chooseMoveStruct->maxPp[gMoveSelectionCursor[battler]]);
else
var = GetCurrentPpToMaxPpState(chooseMoveStruct->currentPp[gMoveSelectionCursor[battler]], gMovesInfo[gMoveSelectionCursor[battler]].pp);
var = 3;
gPlttBufferUnfaded[BG_PLTT_ID(5) + 12] = palPtr[(var * 2) + 0];
gPlttBufferUnfaded[BG_PLTT_ID(5) + 11] = palPtr[(var * 2) + 1];

View File

@ -1163,7 +1163,7 @@ bool32 ProteanTryChangeType(u32 battler, u32 ability, u32 move, u32 moveType)
&& (gBattleMons[battler].type1 != moveType || gBattleMons[battler].type2 != moveType
|| (gBattleMons[battler].type3 != moveType && gBattleMons[battler].type3 != TYPE_MYSTERY))
&& move != MOVE_STRUGGLE
&& !IsTerastallized(battler))
&& GetActiveGimmick(battler) != GIMMICK_TERA)
{
SET_BATTLER_TYPE(battler, moveType);
return TRUE;
@ -1198,7 +1198,7 @@ static void Cmd_attackcanceler(void)
GET_MOVE_TYPE(gCurrentMove, moveType);
// Weight-based moves are blocked by Dynamax.
if (IsDynamaxed(gBattlerTarget) && IsMoveBlockedByDynamax(gCurrentMove))
if ((GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX) && IsMoveBlockedByDynamax(gCurrentMove))
{
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_MoveBlockedByDynamax;
@ -1243,7 +1243,7 @@ static void Cmd_attackcanceler(void)
&& GetBattlerAbility(gBattlerAttacker) == ABILITY_PARENTAL_BOND
&& IsMoveAffectedByParentalBond(gCurrentMove, gBattlerAttacker)
&& !(gAbsentBattlerFlags & gBitTable[gBattlerTarget])
&& gBattleStruct->zmove.toBeUsed[gBattlerAttacker] == MOVE_NONE)
&& GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE)
{
gSpecialStatuses[gBattlerAttacker].parentalBondState = PARENTAL_BOND_1ST_HIT;
gMultiHitCounter = 2;
@ -1364,7 +1364,8 @@ static void Cmd_attackcanceler(void)
}
// Z-moves and Max Moves bypass protection, but deal reduced damage (factored in AccumulateOtherModifiers)
if ((gBattleStruct->zmove.active || IsMaxMove(gCurrentMove))
if ((IsZMove(gCurrentMove)
|| IsMaxMove(gCurrentMove))
&& IS_BATTLER_PROTECTED(gBattlerTarget))
{
BattleScriptPush(cmd->nextInstr);
@ -1513,7 +1514,7 @@ static bool32 AccuracyCalcHelper(u16 move)
return TRUE;
}
if (gBattleStruct->zmove.active && !(gStatuses3[gBattlerTarget] & STATUS3_SEMI_INVULNERABLE))
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !(gStatuses3[gBattlerTarget] & STATUS3_SEMI_INVULNERABLE))
{
JumpIfMoveFailed(7, move);
return TRUE;
@ -1943,7 +1944,7 @@ static void Cmd_critcalc(void)
else if (critChance == -2)
gIsCriticalHit = TRUE;
else
gIsCriticalHit = RandomWeighted(RNG_CRITICAL_HIT, sCriticalHitOdds[critChance] - 1, 1);
gIsCriticalHit = RandomChance(RNG_CRITICAL_HIT, 1, sCriticalHitOdds[critChance]);
// Counter for EVO_CRITICAL_HITS.
partySlot = gBattlerPartyIndexes[gBattlerAttacker];
@ -1961,7 +1962,7 @@ static void Cmd_damagecalc(void)
u8 moveType;
GET_MOVE_TYPE(gCurrentMove, moveType);
if (gBattleStruct->shellSideArmCategory[gBattlerAttacker][gBattlerTarget] == DAMAGE_CATEGORY_SPECIAL && gCurrentMove == MOVE_SHELL_SIDE_ARM)
if (gBattleStruct->shellSideArmCategory[gBattlerAttacker][gBattlerTarget] == DAMAGE_CATEGORY_PHYSICAL && gCurrentMove == MOVE_SHELL_SIDE_ARM)
gBattleStruct->swapDamageCategory = TRUE;
gBattleMoveDamage = CalculateMoveDamage(gCurrentMove, gBattlerAttacker, gBattlerTarget, moveType, 0, gIsCriticalHit, TRUE, TRUE);
gBattlescriptCurrInstr = cmd->nextInstr;
@ -2859,7 +2860,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
if (i != gBattlersCount)
break;
if (!CanSleep(gEffectBattler))
if (!CanBeSlept(gEffectBattler, GetBattlerAbility(gEffectBattler)))
break;
cancelMultiTurnMovesResult = CancelMultiTurnMoves(gEffectBattler);
@ -2898,7 +2899,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_STATUS_HAD_NO_EFFECT;
RESET_RETURN
}
if (!CanBePoisoned(gBattleScripting.battler, gEffectBattler))
if (!CanBePoisoned(gBattleScripting.battler, gEffectBattler, GetBattlerAbility(gEffectBattler)))
break;
statusChanged = TRUE;
@ -2942,7 +2943,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
break;
}
if (!CanBeBurned(gEffectBattler))
if (!CanBeBurned(gEffectBattler, GetBattlerAbility(gEffectBattler)))
break;
statusChanged = TRUE;
@ -3007,7 +3008,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
}
if (!CanParalyzeType(gBattleScripting.battler, gEffectBattler))
break;
if (!CanBeParalyzed(gEffectBattler))
if (!CanBeParalyzed(gEffectBattler, GetBattlerAbility(gEffectBattler)))
break;
statusChanged = TRUE;
@ -3045,7 +3046,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
}
if (gBattleMons[gEffectBattler].status1)
break;
if (CanBePoisoned(gBattleScripting.battler, gEffectBattler))
if (CanBePoisoned(gBattleScripting.battler, gEffectBattler, GetBattlerAbility(gEffectBattler)))
{
// It's redundant, because at this point we know the status1 value is 0.
gBattleMons[gEffectBattler].status1 &= ~STATUS1_TOXIC_POISON;
@ -3178,7 +3179,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
}
}
else if (GetBattlerTurnOrderNum(gEffectBattler) > gCurrentTurnActionNumber
&& !IsDynamaxed(gEffectBattler))
&& !(GetActiveGimmick(gEffectBattler) == GIMMICK_DYNAMAX))
{
gBattleMons[gEffectBattler].status2 |= sStatusFlagsForMoveEffects[gBattleScripting.moveEffect];
gBattlescriptCurrInstr++;
@ -3296,7 +3297,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
if (NoAliveMonsForEitherParty()
|| ChangeStatBuffs(SET_STAT_BUFF_VALUE(1),
gBattleScripting.moveEffect - MOVE_EFFECT_ATK_PLUS_1 + 1,
affectsUser | STAT_CHANGE_UPDATE_MOVE_EFFECT, 0))
affectsUser | STAT_CHANGE_UPDATE_MOVE_EFFECT, 0) == STAT_CHANGE_DIDNT_WORK)
{
gBattlescriptCurrInstr++;
}
@ -3326,7 +3327,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
if (ChangeStatBuffs(SET_STAT_BUFF_VALUE(1) | STAT_BUFF_NEGATIVE,
gBattleScripting.moveEffect - MOVE_EFFECT_ATK_MINUS_1 + 1,
flags, gBattlescriptCurrInstr + 1))
flags, gBattlescriptCurrInstr + 1) == STAT_CHANGE_DIDNT_WORK)
{
if (!mirrorArmorReflected)
gBattlescriptCurrInstr++;
@ -3349,7 +3350,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
if (NoAliveMonsForEitherParty()
|| ChangeStatBuffs(SET_STAT_BUFF_VALUE(2),
gBattleScripting.moveEffect - MOVE_EFFECT_ATK_PLUS_2 + 1,
affectsUser | STAT_CHANGE_UPDATE_MOVE_EFFECT, 0))
affectsUser | STAT_CHANGE_UPDATE_MOVE_EFFECT, 0) == STAT_CHANGE_DIDNT_WORK)
{
gBattlescriptCurrInstr++;
}
@ -3376,7 +3377,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
flags |= STAT_CHANGE_ALLOW_PTR;
if (ChangeStatBuffs(SET_STAT_BUFF_VALUE(2) | STAT_BUFF_NEGATIVE,
gBattleScripting.moveEffect - MOVE_EFFECT_ATK_MINUS_2 + 1,
flags | STAT_CHANGE_UPDATE_MOVE_EFFECT, gBattlescriptCurrInstr + 1))
flags | STAT_CHANGE_UPDATE_MOVE_EFFECT, gBattlescriptCurrInstr + 1) == STAT_CHANGE_DIDNT_WORK)
{
if (!mirrorArmorReflected)
gBattlescriptCurrInstr++;
@ -3409,7 +3410,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
}
else if (GetBattlerAbility(gBattlerTarget) == ABILITY_STICKY_HOLD)
{
BattleScriptPushCursor();
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_NoItemSteal;
gLastUsedAbility = gBattleMons[gBattlerTarget].ability;
@ -3532,6 +3533,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
case MOVE_EFFECT_SPECTRAL_THIEF:
if (!NoAliveMonsForEitherParty())
{
bool32 contrary = (GetBattlerAbility(gBattlerAttacker) == ABILITY_CONTRARY);
gBattleStruct->stolenStats[0] = 0; // Stats to steal.
gBattleScripting.animArg1 = 0;
for (i = STAT_ATK; i < NUM_BATTLE_STATS; i++)
@ -3553,16 +3555,16 @@ void SetMoveEffect(bool32 primary, bool32 certain)
if (gBattleScripting.animArg1 == 0)
{
if (byTwo)
gBattleScripting.animArg1 = STAT_ANIM_PLUS2 + i;
gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MINUS2 : STAT_ANIM_PLUS2) + i;
else
gBattleScripting.animArg1 = STAT_ANIM_PLUS1 + i;
gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MINUS1 : STAT_ANIM_PLUS1) + i;
}
else
{
if (byTwo)
gBattleScripting.animArg1 = STAT_ANIM_MULTIPLE_PLUS2;
gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MULTIPLE_MINUS2 : STAT_ANIM_MULTIPLE_PLUS2);
else
gBattleScripting.animArg1 = STAT_ANIM_MULTIPLE_PLUS1;
gBattleScripting.animArg1 = (contrary ? STAT_ANIM_MULTIPLE_MINUS1 : STAT_ANIM_MULTIPLE_PLUS1);
}
}
}
@ -3800,7 +3802,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
}
break;
case MOVE_EFFECT_TERA_BLAST:
if (IsTerastallized(gEffectBattler)
if (GetActiveGimmick(gEffectBattler) == GIMMICK_TERA
&& GetBattlerTeraType(gEffectBattler) == TYPE_STELLAR
&& !NoAliveMonsForEitherParty())
{
@ -3977,7 +3979,7 @@ static void Cmd_tryfaintmon(void)
gSideTimers[B_SIDE_OPPONENT].retaliateTimer = 2;
}
if ((gHitMarker & HITMARKER_DESTINYBOND) && IsBattlerAlive(gBattlerAttacker)
&& !IsDynamaxed(gBattlerAttacker))
&& !(GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX))
{
gHitMarker &= ~HITMARKER_DESTINYBOND;
BattleScriptPush(gBattlescriptCurrInstr);
@ -4589,7 +4591,7 @@ bool32 NoAliveMonsForEitherParty(void)
return (NoAliveMonsForPlayer() || NoAliveMonsForOpponent());
}
// For battles that aren't BATTLE_TYPE_LINK or BATTLE_TYPE_RECORDED_LINK, the only thing this
// For battles that aren't BATTLE_TYPE_LINK or BATTLE_TYPE_RECORDED_LINK or trainer battles, the only thing this
// command does is check whether the player has won/lost by totaling each team's HP. It then
// sets gBattleOutcome accordingly, if necessary.
static void Cmd_checkteamslost(void)
@ -4605,10 +4607,12 @@ static void Cmd_checkteamslost(void)
if (NoAliveMonsForOpponent())
gBattleOutcome |= B_OUTCOME_WON;
// For link battles that haven't ended, count number of empty battler spots
// In link multi battles, jump to pointer if more than 1 spot empty
// Fair switching - everyone has to switch in most at the same time, without knowing which pokemon the other trainer selected.
// In vanilla Emerald this was only used for link battles, in expansion it's also used for regular trainer battles.
// For battles that haven't ended, count number of empty battler spots
// In multi battles, jump to pointer if more than 1 spot empty
// In non-multi battles, jump to pointer if 1 spot is missing on both sides
if (gBattleOutcome == 0 && (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK)))
if (gBattleOutcome == 0 && (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK | BATTLE_TYPE_TRAINER)))
{
s32 i, emptyPlayerSpots, emptyOpponentSpots;
@ -5389,7 +5393,6 @@ static void Cmd_moveend(void)
bool32 effect = FALSE;
u32 moveType = 0;
u32 holdEffectAtk = 0;
u16 *choicedMoveAtk = NULL;
u32 endMode, endState;
u32 originallyUsedMove;
@ -5402,7 +5405,6 @@ static void Cmd_moveend(void)
endState = cmd->endState;
holdEffectAtk = GetBattlerHoldEffect(gBattlerAttacker, TRUE);
choicedMoveAtk = &gBattleStruct->choicedMove[gBattlerAttacker];
GET_MOVE_TYPE(gCurrentMove, moveType);
do
@ -5483,7 +5485,7 @@ static void Cmd_moveend(void)
}
// Not strictly a protect effect, but works the same way
else if (gProtectStructs[gBattlerTarget].beakBlastCharge
&& CanBeBurned(gBattlerAttacker)
&& CanBeBurned(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker))
&& !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
{
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
@ -5612,29 +5614,34 @@ static void Cmd_moveend(void)
gBattleScripting.moveendState++;
break;
case MOVEEND_CHOICE_MOVE: // update choice band move
if (gHitMarker & HITMARKER_OBEYS
&& (HOLD_EFFECT_CHOICE(holdEffectAtk) || GetBattlerAbility(gBattlerAttacker) == ABILITY_GORILLA_TACTICS)
&& gChosenMove != MOVE_STRUGGLE
&& (*choicedMoveAtk == MOVE_NONE || *choicedMoveAtk == MOVE_UNAVAILABLE))
{
if ((gMovesInfo[gChosenMove].effect == EFFECT_BATON_PASS
|| gMovesInfo[gChosenMove].effect == EFFECT_HEALING_WISH)
&& !(gMoveResultFlags & MOVE_RESULT_FAILED))
u16 *choicedMoveAtk = &gBattleStruct->choicedMove[gBattlerAttacker];
if (gHitMarker & HITMARKER_OBEYS
&& (HOLD_EFFECT_CHOICE(holdEffectAtk) || GetBattlerAbility(gBattlerAttacker) == ABILITY_GORILLA_TACTICS)
&& gChosenMove != MOVE_STRUGGLE
&& (*choicedMoveAtk == MOVE_NONE || *choicedMoveAtk == MOVE_UNAVAILABLE))
{
gBattleScripting.moveendState++;
break;
if ((gMovesInfo[gChosenMove].effect == EFFECT_BATON_PASS
|| gMovesInfo[gChosenMove].effect == EFFECT_HEALING_WISH)
&& !(gMoveResultFlags & MOVE_RESULT_FAILED))
{
gBattleScripting.moveendState++;
break;
}
*choicedMoveAtk = gChosenMove;
}
*choicedMoveAtk = gChosenMove;
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (gBattleMons[gBattlerAttacker].moves[i] == *choicedMoveAtk)
break;
}
if (i == MAX_MON_MOVES)
{
*choicedMoveAtk = MOVE_NONE;
}
gBattleScripting.moveendState++;
break;
}
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (gBattleMons[gBattlerAttacker].moves[i] == *choicedMoveAtk)
break;
}
if (i == MAX_MON_MOVES)
*choicedMoveAtk = MOVE_NONE;
gBattleScripting.moveendState++;
break;
case MOVEEND_CHANGED_ITEMS: // changed held items
for (i = 0; i < gBattlersCount; i++)
{
@ -5854,7 +5861,7 @@ static void Cmd_moveend(void)
gLastPrintedMoves[gBattlerAttacker] = gChosenMove;
gLastUsedMove = gCurrentMove;
if (IsMaxMove(gCurrentMove))
gBattleStruct->dynamax.lastUsedBaseMove = gBattleStruct->dynamax.baseMove[gBattlerAttacker];
gBattleStruct->dynamax.lastUsedBaseMove = gBattleStruct->dynamax.baseMoves[gBattlerAttacker];
}
}
if (!(gAbsentBattlerFlags & gBitTable[gBattlerAttacker])
@ -6387,17 +6394,16 @@ static void Cmd_moveend(void)
gSpecialStatuses[gBattlerAttacker].preventLifeOrbDamage = 0;
gSpecialStatuses[gBattlerTarget].berryReduced = FALSE;
gBattleScripting.moveEffect = 0;
// clear attacker z move data
gBattleStruct->zmove.active = FALSE;
gBattleStruct->zmove.toBeUsed[gBattlerAttacker] = MOVE_NONE;
gBattleStruct->zmove.effect = EFFECT_HIT;
gBattleStruct->hitSwitchTargetFailed = FALSE;
gBattleStruct->isAtkCancelerForCalledMove = FALSE;
gBattleStruct->swapDamageCategory = FALSE;
gBattleStruct->categoryOverride = FALSE;
gBattleStruct->bouncedMoveIsUsed = FALSE;
gBattleStruct->enduredDamage = 0;
gBattleStruct->additionalEffectsCounter = 0;
gBattleStruct->poisonPuppeteerConfusion = FALSE;
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE)
SetActiveGimmick(gBattlerAttacker, GIMMICK_NONE);
gBattleStruct->distortedTypeMatchups = 0;
gBattleScripting.moveendState++;
break;
@ -6758,7 +6764,7 @@ static void Cmd_openpartyscreen(void)
u32 i, battler = 0;
const u8 *failInstr = cmd->failInstr;
if (cmd->battler == BS_FAINTED_LINK_MULTIPLE_1)
if (cmd->battler == BS_FAINTED_MULTIPLE_1)
{
if ((gBattleTypeFlags & BATTLE_TYPE_MULTI) || !(gBattleTypeFlags & BATTLE_TYPE_DOUBLE))
{
@ -6913,7 +6919,7 @@ static void Cmd_openpartyscreen(void)
}
gBattlescriptCurrInstr = cmd->nextInstr;
}
else if (cmd->battler == BS_FAINTED_LINK_MULTIPLE_2)
else if (cmd->battler == BS_FAINTED_MULTIPLE_2)
{
if (!(gBattleTypeFlags & BATTLE_TYPE_MULTI))
{
@ -7131,13 +7137,8 @@ bool32 DoSwitchInAbilities(u32 battler)
|| AbilityBattleEffects(ABILITYEFFECT_TRACE2, 0, 0, 0, 0));
}
static void Cmd_switchineffects(void)
static void UpdateSentMonFlags(u32 battler)
{
CMD_ARGS(u8 battler);
s32 i;
u32 battler = GetBattlerForBattleScript(cmd->battler);
UpdateSentPokesToOpponentValue(battler);
gHitMarker &= ~HITMARKER_FAINTED(battler);
@ -7145,7 +7146,11 @@ static void Cmd_switchineffects(void)
if (!BattlerHasAi(battler))
gBattleStruct->appearedInBattle |= gBitTable[gBattlerPartyIndexes[battler]];
}
static bool32 DoSwitchInEffectsForBattler(u32 battler)
{
u32 i;
// Neutralizing Gas announces itself before hazards
if (gBattleMons[battler].ability == ABILITY_NEUTRALIZING_GAS && gSpecialStatuses[battler].announceNeutralizingGas == 0)
{
@ -7263,7 +7268,7 @@ static void Cmd_switchineffects(void)
BattleScriptPushCursor();
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_Z_HP_TRAP;
gBattlescriptCurrInstr = BattleScript_HealReplacementZMove;
return;
return TRUE;
}
else
{
@ -7278,9 +7283,9 @@ static void Cmd_switchineffects(void)
gDisableStructs[battler].truantSwitchInHack = 0;
if (DoSwitchInAbilities(battler) || ItemBattleEffects(ITEMEFFECT_ON_SWITCH_IN, battler, FALSE))
return;
return TRUE;
else if (AbilityBattleEffects(ABILITYEFFECT_OPPORTUNIST, battler, 0, 0, 0))
return;
return TRUE;
gDisableStructs[battler].stickyWebDone = FALSE;
gDisableStructs[battler].spikesDone = FALSE;
@ -7296,22 +7301,68 @@ static void Cmd_switchineffects(void)
gBattleStruct->hpOnSwitchout[GetBattlerSide(i)] = gBattleMons[i].hp;
}
if (cmd->battler == BS_FAINTED_LINK_MULTIPLE_1)
{
u32 hitmarkerFaintBits = gHitMarker >> 28;
gBattlerFainted++;
while (1)
{
if (hitmarkerFaintBits & gBitTable[gBattlerFainted] && !(gAbsentBattlerFlags & gBitTable[gBattlerFainted]))
break;
if (gBattlerFainted >= gBattlersCount)
break;
gBattlerFainted++;
}
}
gBattleStruct->forcedSwitch &= ~(gBitTable[battler]);
return FALSE;
}
return TRUE; // Effect's script plays.
}
static void Cmd_switchineffects(void)
{
CMD_ARGS(u8 battler);
u32 i, battler = GetBattlerForBattleScript(cmd->battler);
switch (cmd->battler)
{
// Multiple mons fainted and are being switched-in. Their abilities/hazards will play according to speed ties.
case BS_FAINTED_MULTIPLE_1: // Saves the battlers.
gBattleStruct->multipleSwitchInBattlers |= gBitTable[battler];
UpdateSentMonFlags(battler);
// Increment fainted battler.
do
{
gBattlerFainted++;
if (gBattlerFainted >= gBattlersCount)
break;
if (gHitMarker & HITMARKER_FAINTED(gBattlerFainted) && !(gAbsentBattlerFlags & gBitTable[gBattlerFainted]))
break;
} while (1);
gBattlescriptCurrInstr = cmd->nextInstr;
return;
case BS_FAINTED_MULTIPLE_2: // Plays hazards/abilities.
switch (gBattleStruct->multipleSwitchInState)
{
case 0: // Sort battlers by speed
for (i = 0; i < gBattlersCount; i++)
gBattleStruct->multipleSwitchInSortedBattlers[i] = i;
SortBattlersBySpeed(gBattleStruct->multipleSwitchInSortedBattlers, FALSE);
gBattleStruct->multipleSwitchInState++;
gBattleStruct->multipleSwitchInCursor = 0;
// Loop through all available battlers
case 1:
for (; gBattleStruct->multipleSwitchInCursor < gBattlersCount; gBattleStruct->multipleSwitchInCursor++)
{
gBattlerFainted = gBattleStruct->multipleSwitchInSortedBattlers[gBattleStruct->multipleSwitchInCursor];
if (gBattleStruct->multipleSwitchInBattlers & gBitTable[gBattlerFainted])
{
if (DoSwitchInEffectsForBattler(gBattlerFainted))
return;
}
}
// All battlers done, end
gBattleStruct->multipleSwitchInBattlers = 0;
gBattleStruct->multipleSwitchInState = 0;
gBattlescriptCurrInstr = cmd->nextInstr;
}
break;
default:
UpdateSentMonFlags(battler);
if (!DoSwitchInEffectsForBattler(battler))
gBattlescriptCurrInstr = cmd->nextInstr;
break;
}
}
@ -7958,7 +8009,7 @@ static void BestowItem(u32 battlerAtk, u32 battlerDef)
// Called by Cmd_removeitem. itemId represents the item that was removed, not being given.
static bool32 TrySymbiosis(u32 battler, u32 itemId)
{
if (!gBattleStruct->itemLost[gBattlerPartyIndexes[battler]].stolen
if (!gBattleStruct->itemLost[B_SIDE_PLAYER][gBattlerPartyIndexes[battler]].stolen
&& gBattleStruct->changedItems[battler] == ITEM_NONE
&& GetBattlerHoldEffect(battler, TRUE) != HOLD_EFFECT_EJECT_BUTTON
&& GetBattlerHoldEffect(battler, TRUE) != HOLD_EFFECT_EJECT_PACK
@ -8767,7 +8818,6 @@ static void HandleScriptMegaPrimalBurst(u32 caseId, u32 battler, u32 type)
{
struct Pokemon *party = GetBattlerParty(battler);
struct Pokemon *mon = &party[gBattlerPartyIndexes[battler]];
u32 position = GetBattlerPosition(battler);
u32 side = GetBattlerSide(battler);
// Change species.
@ -8795,9 +8845,9 @@ static void HandleScriptMegaPrimalBurst(u32 caseId, u32 battler, u32 type)
if (side == B_SIDE_OPPONENT)
SetBattlerShadowSpriteCallback(battler, gBattleMons[battler].species);
if (type == HANDLE_TYPE_MEGA_EVOLUTION)
gBattleStruct->mega.alreadyEvolved[position] = TRUE;
SetGimmickAsActivated(battler, GIMMICK_MEGA);
if (type == HANDLE_TYPE_ULTRA_BURST)
gBattleStruct->burst.alreadyBursted[position] = TRUE;
SetGimmickAsActivated(battler, GIMMICK_ULTRA_BURST);
}
}
@ -9660,7 +9710,7 @@ static void Cmd_various(void)
else
{
if (gBattleMons[gBattlerTarget].ability == gBattleMons[gBattlerAttacker].ability
|| IsDynamaxed(gBattlerTarget))
|| (GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX))
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -9699,7 +9749,15 @@ static void Cmd_various(void)
gBattlescriptCurrInstr = cmd->failInstr;
else
{
gCalledMove = move;
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IS_MOVE_STATUS(move))
{
gBattleStruct->zmove.baseMoves[gBattlerAttacker] = move;
gCalledMove = GetTypeBasedZMove(move);
}
else
{
gCalledMove = move;
}
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE);
gStatuses3[gBattlerAttacker] |= STATUS3_ME_FIRST;
@ -9735,7 +9793,7 @@ static void Cmd_various(void)
VARIOUS_ARGS(const u8 *failInstr);
if ((GetBattlerType(gBattlerTarget, 0, FALSE) == gMovesInfo[gCurrentMove].type
&& GetBattlerType(gBattlerTarget, 1, FALSE) == gMovesInfo[gCurrentMove].type)
|| IsTerastallized(gBattlerTarget))
|| GetActiveGimmick(gBattlerTarget) == GIMMICK_TERA)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -9832,7 +9890,9 @@ static void Cmd_various(void)
if (move == MOVE_NONE || move == MOVE_UNAVAILABLE || MoveHasAdditionalEffectSelf(move, MOVE_EFFECT_RECHARGE)
|| gMovesInfo[move].instructBanned
|| gBattleMoveEffects[gMovesInfo[move].effect].twoTurnEffect
|| IsDynamaxed(gBattlerTarget))
|| (GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX)
|| IsZMove(move)
|| IsMaxMove(move))
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -9902,16 +9962,17 @@ static void Cmd_various(void)
case VARIOUS_PSYCHO_SHIFT:
{
VARIOUS_ARGS(const u8 *failInstr);
u32 targetAbility = GetBattlerAbility(gBattlerTarget);
// Psycho shift works
if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget))
if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget, targetAbility))
gBattleCommunication[MULTISTRING_CHOOSER] = 0;
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_TOXIC_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget))
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_TOXIC_POISON) && CanBePoisoned(gBattlerAttacker, gBattlerTarget, targetAbility))
gBattleCommunication[MULTISTRING_CHOOSER] = 1;
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_BURN) && CanBeBurned(gBattlerTarget))
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_BURN) && CanBeBurned(gBattlerTarget, targetAbility))
gBattleCommunication[MULTISTRING_CHOOSER] = 2;
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && CanBeParalyzed(gBattlerTarget))
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_PARALYSIS) && CanBeParalyzed(gBattlerTarget, targetAbility))
gBattleCommunication[MULTISTRING_CHOOSER] = 3;
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) && CanSleep(gBattlerTarget))
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP) && CanBeSlept(gBattlerTarget, targetAbility))
gBattleCommunication[MULTISTRING_CHOOSER] = 4;
else if ((gBattleMons[gBattlerAttacker].status1 & STATUS1_FROSTBITE) && CanBeFrozen(gBattlerTarget))
gBattleCommunication[MULTISTRING_CHOOSER] = 5;
@ -10068,7 +10129,7 @@ static void Cmd_various(void)
case VARIOUS_TRY_THIRD_TYPE:
{
VARIOUS_ARGS(const u8 *failInstr);
if (IS_BATTLER_OF_TYPE(battler, gMovesInfo[gCurrentMove].argument) || IsTerastallized(battler))
if (IS_BATTLER_OF_TYPE(battler, gMovesInfo[gCurrentMove].argument) || GetActiveGimmick(battler) == GIMMICK_TERA)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -11061,10 +11122,10 @@ static void SetMoveForMirrorMove(u32 move)
{
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
// Edge case, we used Z Mirror Move, got the stat boost and now need to use the Z-move
if (gBattleStruct->zmove.toBeUsed[gBattlerAttacker] && !IS_MOVE_STATUS(move))
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IS_MOVE_STATUS(move))
{
gCurrentMove = gBattleStruct->zmove.chosenZMove = GetTypeBasedZMove(move, gBattlerAttacker);
QueueZMove(gBattlerAttacker, move);
gBattleStruct->zmove.baseMoves[gBattlerAttacker] = move;
gCurrentMove = GetTypeBasedZMove(move);
}
else
{
@ -12104,7 +12165,7 @@ static void Cmd_tryconversiontypechange(void)
u8 moveChecked = 0;
u8 moveType = 0;
if (IsTerastallized(gBattlerAttacker))
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA)
{
gBattlescriptCurrInstr = cmd->failInstr;
return;
@ -12248,7 +12309,7 @@ static void Cmd_tryKO(void)
u16 targetAbility = GetBattlerAbility(gBattlerTarget);
// Dynamaxed Pokemon cannot be hit by OHKO moves.
if (IsDynamaxed(gBattlerTarget))
if ((GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX))
{
gMoveResultFlags |= MOVE_RESULT_MISSED;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_KO_UNAFFECTED;
@ -12796,11 +12857,11 @@ static void Cmd_trysetencore(void)
s32 i;
if (IsMaxMove(gLastMoves[gBattlerTarget]) && !IsDynamaxed(gBattlerTarget))
if (IsMaxMove(gLastMoves[gBattlerTarget]) && !(GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX))
{
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (gBattleMons[gBattlerTarget].moves[i] == gBattleStruct->dynamax.baseMove[gBattlerTarget])
if (gBattleMons[gBattlerTarget].moves[i] == gBattleStruct->dynamax.baseMoves[gBattlerTarget])
break;
}
}
@ -12825,7 +12886,11 @@ static void Cmd_trysetencore(void)
{
gDisableStructs[gBattlerTarget].encoredMove = gBattleMons[gBattlerTarget].moves[i];
gDisableStructs[gBattlerTarget].encoredMovePos = i;
gDisableStructs[gBattlerTarget].encoreTimer = 3;
// Encore always lasts 3 turns, but we need to account for a scenario where Encore changes the move during the same turn.
if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget))
gDisableStructs[gBattlerTarget].encoreTimer = 4;
else
gDisableStructs[gBattlerTarget].encoreTimer = 3;
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
@ -12875,7 +12940,7 @@ static void Cmd_settypetorandomresistance(void)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else if (IsTerastallized(gBattlerAttacker))
else if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -13014,7 +13079,15 @@ static void Cmd_trychoosesleeptalkmove(void)
movePosition = MOD(Random(), MAX_MON_MOVES);
} while ((gBitTable[movePosition] & unusableMovesBits));
gCalledMove = gBattleMons[gBattlerAttacker].moves[movePosition];
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IS_MOVE_STATUS(gBattleMons[gBattlerAttacker].moves[movePosition]))
{
gBattleStruct->zmove.baseMoves[gBattlerAttacker] = gBattleMons[gBattlerAttacker].moves[movePosition];
gCalledMove = GetTypeBasedZMove(gBattleMons[gBattlerAttacker].moves[movePosition]);
}
else
{
gCalledMove = gBattleMons[gBattlerAttacker].moves[movePosition];
}
gCurrMovePos = movePosition;
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE);
@ -13083,7 +13156,7 @@ static void Cmd_tryspiteppreduce(void)
{
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (gBattleStruct->dynamax.baseMove[gBattlerTarget] == gBattleMons[gBattlerTarget].moves[i])
if (gBattleStruct->dynamax.baseMoves[gBattlerTarget] == gBattleMons[gBattlerTarget].moves[i])
break;
}
}
@ -13359,7 +13432,16 @@ static void Cmd_handlefurycutter(void)
}
else
{
if (gDisableStructs[gBattlerAttacker].furyCutterCounter != 5
u32 max;
if (B_UPDATED_MOVE_DATA >= GEN_6)
max = 3;
else if (B_UPDATED_MOVE_DATA == GEN_5)
max = 4;
else
max = 5;
if (gDisableStructs[gBattlerAttacker].furyCutterCounter < max
&& gSpecialStatuses[gBattlerAttacker].parentalBondState != PARENTAL_BOND_1ST_HIT) // Don't increment counter on first hit
gDisableStructs[gBattlerAttacker].furyCutterCounter++;
@ -13543,7 +13625,7 @@ static void Cmd_jumpifnopursuitswitchdmg(void)
gActionsByTurnOrder[i] = B_ACTION_TRY_FINISH;
}
gCurrentMove = gChosenMoveByBattler[gBattlerTarget];
gCurrentMove = gChosenMove = gChosenMoveByBattler[gBattlerTarget];
gCurrMovePos = gChosenMovePos = *(gBattleStruct->chosenMovePositions + gBattlerTarget);
gBattlescriptCurrInstr = cmd->nextInstr;
gBattleScripting.animTurn = 1;
@ -13936,25 +14018,33 @@ static void Cmd_callterrainattack(void)
CMD_ARGS();
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
gCurrentMove = GetNaturePowerMove();
gCurrentMove = GetNaturePowerMove(gBattlerAttacker);
gBattlerTarget = GetMoveTarget(gCurrentMove, NO_TARGET_OVERRIDE);
BattleScriptPush(GET_MOVE_BATTLESCRIPT(gCurrentMove));
gBattlescriptCurrInstr = cmd->nextInstr;
}
u16 GetNaturePowerMove(void)
u32 GetNaturePowerMove(u32 battler)
{
u32 move = sNaturePowerMoves[gBattleTerrain];
if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)
return MOVE_MOONBLAST;
move = MOVE_MOONBLAST;
else if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)
return MOVE_THUNDERBOLT;
move = MOVE_THUNDERBOLT;
else if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN)
return MOVE_ENERGY_BALL;
move = MOVE_ENERGY_BALL;
else if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN)
return MOVE_PSYCHIC;
move = MOVE_PSYCHIC;
else if (sNaturePowerMoves[gBattleTerrain] == MOVE_NONE)
return MOVE_TRI_ATTACK;
return sNaturePowerMoves[gBattleTerrain];
move = MOVE_TRI_ATTACK;
if (GetActiveGimmick(battler) == GIMMICK_Z_MOVE)
{
gBattleStruct->zmove.baseMoves[gBattlerAttacker] = move;
move = GetTypeBasedZMove(move);
}
return move;
}
// Refresh
@ -13980,7 +14070,7 @@ static void Cmd_settorment(void)
CMD_ARGS(const u8 *failInstr);
if (gBattleMons[gBattlerTarget].status2 & STATUS2_TORMENT
|| IsDynamaxed(gBattlerTarget))
|| (GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX))
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -14369,7 +14459,7 @@ static void Cmd_tryswapabilities(void)
}
else
{
if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT || IsDynamaxed(gBattlerTarget))
if (gMoveResultFlags & MOVE_RESULT_NO_EFFECT || (GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX))
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -14847,7 +14937,7 @@ static void Cmd_settypetoterrain(void)
break;
}
if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, terrainType) && !IsTerastallized(gBattlerAttacker))
if (!IS_BATTLER_OF_TYPE(gBattlerAttacker, terrainType) && GetActiveGimmick(gBattlerAttacker) != GIMMICK_TERA)
{
SET_BATTLER_TYPE(gBattlerAttacker, terrainType);
PREPARE_TYPE_BUFFER(gBattleTextBuff1, terrainType);
@ -15265,6 +15355,13 @@ static void Cmd_givecaughtmon(void)
{
CMD_ARGS();
if (B_RESTORE_HELD_BATTLE_ITEMS >= GEN_9)
{
u16 lostItem = gBattleStruct->itemLost[B_SIDE_OPPONENT][gBattlerPartyIndexes[GetCatchingBattler()]].originalItem;
if (lostItem != ITEM_NONE && ItemId_GetPocket(lostItem) != POCKET_BERRIES)
SetMonData(&gEnemyParty[gBattlerPartyIndexes[GetCatchingBattler()]], MON_DATA_HELD_ITEM, &lostItem); // Restore non-berry items
}
if (GiveMonToPlayer(&gEnemyParty[gBattlerPartyIndexes[GetCatchingBattler()]]) != MON_GIVEN_TO_PARTY)
{
if (!ShouldShowBoxWasFullMessage())
@ -16437,9 +16534,9 @@ void BS_SetRemoveTerrain(void)
}
else
{
u16 atkHoldEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE);
u32 atkHoldEffect = GetBattlerHoldEffect(gBattlerAttacker, TRUE);
gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY;
gFieldStatuses &= ~(STATUS_FIELD_TERRAIN_ANY | STATUS_FIELD_TERRAIN_PERMANENT);
gFieldStatuses |= statusFlag;
gFieldTimers.terrainTimer = (atkHoldEffect == HOLD_EFFECT_TERRAIN_EXTENDER) ? 8 : 5;
gBattlescriptCurrInstr = cmd->nextInstr;
@ -16469,7 +16566,7 @@ void BS_TryReflectType(void)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else if (IsTerastallized(gBattlerAttacker))
else if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
@ -16537,7 +16634,8 @@ void BS_TryRelicSong(void)
{
NATIVE_ARGS();
if (GetBattlerAbility(gBattlerAttacker) != ABILITY_SHEER_FORCE && !(gBattleMons[gBattlerAttacker].status2 & STATUS2_TRANSFORMED))
if (GetBattlerAbility(gBattlerAttacker) != ABILITY_SHEER_FORCE && !(gBattleMons[gBattlerAttacker].status2 & STATUS2_TRANSFORMED)
&& (gBattleMons[gBattlerAttacker].species == SPECIES_MELOETTA_ARIA || gBattleMons[gBattlerAttacker].species == SPECIES_MELOETTA_PIROUETTE))
{
if (gBattleMons[gBattlerAttacker].species == SPECIES_MELOETTA_ARIA)
gBattleMons[gBattlerAttacker].species = SPECIES_MELOETTA_PIROUETTE;
@ -16548,7 +16646,9 @@ void BS_TryRelicSong(void)
gBattlescriptCurrInstr = BattleScript_AttackerFormChangeMoveEffect;
}
else
{
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
void BS_SetPledge(void)
@ -16697,9 +16797,8 @@ void BS_TryTrainerSlideDynamaxMsg(void)
NATIVE_ARGS();
s32 shouldSlide;
if ((shouldSlide = ShouldDoTrainerSlide(gBattlerAttacker, TRAINER_SLIDE_DYNAMAX)))
if ((shouldSlide = ShouldDoTrainerSlide(gBattleScripting.battler, TRAINER_SLIDE_DYNAMAX)))
{
gBattleScripting.battler = gBattlerAttacker;
BattleScriptPush(cmd->nextInstr);
gBattlescriptCurrInstr = (shouldSlide == 1 ? BattleScript_TrainerASlideMsgRet : BattleScript_TrainerBSlideMsgRet);
}
@ -16740,10 +16839,19 @@ void BS_TryCopycat(void)
}
else
{
if (IsMaxMove(gLastUsedMove))
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE && !IS_MOVE_STATUS(gLastUsedMove))
{
gBattleStruct->zmove.baseMoves[gBattlerAttacker] = gLastUsedMove;
gCalledMove = GetTypeBasedZMove(gLastUsedMove);
}
else if (IsMaxMove(gLastUsedMove))
{
gCalledMove = gBattleStruct->dynamax.lastUsedBaseMove;
}
else
{
gCalledMove = gLastUsedMove;
}
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE);
@ -16831,8 +16939,8 @@ void BS_AllySwitchFailChance(void)
void BS_SetPhotonGeyserCategory(void)
{
NATIVE_ARGS();
if (!((gMovesInfo[gCurrentMove].effect == EFFECT_TERA_BLAST && !IsTerastallized(gBattlerAttacker))
|| (gMovesInfo[gCurrentMove].effect == EFFECT_TERA_STARSTORM && !(IsTerastallized(gBattlerAttacker) && gBattleMons[gBattlerAttacker].species == SPECIES_TERAPAGOS_STELLAR))))
if (!((gMovesInfo[gCurrentMove].effect == EFFECT_TERA_BLAST && GetActiveGimmick(gBattlerAttacker) != GIMMICK_TERA)
|| (gMovesInfo[gCurrentMove].effect == EFFECT_TERA_STARSTORM && GetActiveGimmick(gBattlerAttacker) != GIMMICK_TERA && gBattleMons[gBattlerAttacker].species == SPECIES_TERAPAGOS_STELLAR)))
gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == DAMAGE_CATEGORY_PHYSICAL);
gBattlescriptCurrInstr = cmd->nextInstr;
}
@ -17033,3 +17141,12 @@ void BS_ApplyTerastallization(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_DamageToQuarterTargetHP(void)
{
NATIVE_ARGS();
gBattleMoveDamage = (3 * GetNonDynamaxHP(gBattlerTarget)) / 4;
if (gBattleMoveDamage == 0)
gBattleMoveDamage = 1;
gBattlescriptCurrInstr = cmd->nextInstr;
}

View File

@ -1938,15 +1938,16 @@ static bool32 HasAtLeastFiveBadges(void)
void IncrementRematchStepCounter(void)
{
#if FREE_MATCH_CALL == FALSE
if (HasAtLeastFiveBadges()
&& (I_VS_SEEKER_CHARGING != 0)
&& (!CheckBagHasItem(ITEM_VS_SEEKER, 1)))
{
if (gSaveBlock1Ptr->trainerRematchStepCounter >= STEP_COUNTER_MAX)
gSaveBlock1Ptr->trainerRematchStepCounter = STEP_COUNTER_MAX;
else
gSaveBlock1Ptr->trainerRematchStepCounter++;
}
if (!HasAtLeastFiveBadges())
return;
if (IsVsSeekerEnabled())
return;
if (gSaveBlock1Ptr->trainerRematchStepCounter >= STEP_COUNTER_MAX)
gSaveBlock1Ptr->trainerRematchStepCounter = STEP_COUNTER_MAX;
else
gSaveBlock1Ptr->trainerRematchStepCounter++;
#endif //FREE_MATCH_CALL
}

View File

@ -4,6 +4,8 @@
#include "battle_controllers.h"
#include "battle_interface.h"
#include "battle_terastal.h"
#include "battle_gimmick.h"
#include "battle_scripts.h"
#include "event_data.h"
#include "item.h"
#include "palette.h"
@ -16,14 +18,13 @@
#include "constants/rgb.h"
// Sets flags and variables upon a battler's Terastallization.
void PrepareBattlerForTera(u32 battler)
void ActivateTera(u32 battler)
{
u32 side = GetBattlerSide(battler);
u32 index = gBattlerPartyIndexes[battler];
// Update TeraData fields.
gBattleStruct->tera.isTerastallized[side] |= gBitTable[index];
gBattleStruct->tera.alreadyTerastallized[battler] = TRUE;
// Set appropriate flags.
SetActiveGimmick(battler, GIMMICK_TERA);
SetGimmickAsActivated(battler, GIMMICK_TERA);
// Remove Tera Orb charge.
if (B_FLAG_TERA_ORB_CHARGED != 0
@ -33,6 +34,13 @@ void PrepareBattlerForTera(u32 battler)
{
FlagClear(B_FLAG_TERA_ORB_CHARGED);
}
// Execute battle script.
PREPARE_TYPE_BUFFER(gBattleTextBuff1, GetBattlerTeraType(battler));
if (TryBattleFormChange(gBattlerAttacker, FORM_CHANGE_BATTLE_TERASTALLIZATION))
BattleScriptExecute(BattleScript_TeraFormChange);
else
BattleScriptExecute(BattleScript_Terastallization);
}
// Applies palette blend and enables UI indicator after animation has played
@ -56,33 +64,32 @@ bool32 CanTerastallize(u32 battler)
u32 holdEffect = GetBattlerHoldEffect(battler, FALSE);
// Check if Player has Tera Orb and has charge.
if (!CheckBagHasItem(ITEM_TERA_ORB, 1)
|| !((B_FLAG_TERA_ORB_NO_COST != 0 && FlagGet(B_FLAG_TERA_ORB_NO_COST))
|| (B_FLAG_TERA_ORB_CHARGED != 0 && FlagGet(B_FLAG_TERA_ORB_CHARGED)
&& ((battler == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && battler == B_POSITION_PLAYER_RIGHT))))))
{
if (!TESTING && !CheckBagHasItem(ITEM_TERA_ORB, 1))
return FALSE;
if (!TESTING
&& !(B_FLAG_TERA_ORB_NO_COST != 0 && FlagGet(B_FLAG_TERA_ORB_NO_COST))
&& (battler == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && battler == B_POSITION_PLAYER_RIGHT)))
{
if (B_FLAG_TERA_ORB_CHARGED != 0 && !FlagGet(B_FLAG_TERA_ORB_CHARGED))
return FALSE;
}
// Check if Trainer has already Terastallized.
if (gBattleStruct->tera.alreadyTerastallized[battler])
{
if (HasTrainerUsedGimmick(battler, GIMMICK_TERA))
return FALSE;
}
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
&& IsPartnerMonFromSameTrainer(battler)
&& (gBattleStruct->tera.alreadyTerastallized[BATTLE_PARTNER(battler)]
|| (gBattleStruct->tera.toTera & gBitTable[BATTLE_PARTNER(battler)])))
{
// Check if AI battler is intended to Terastallize.
if (!ShouldTrainerBattlerUseGimmick(battler, GIMMICK_TERA))
return FALSE;
// Check if battler has another gimmick active.
if (GetActiveGimmick(battler) != GIMMICK_NONE)
return FALSE;
}
// Check if battler is holding a Z-Crystal or Mega Stone.
if (holdEffect == HOLD_EFFECT_Z_CRYSTAL || holdEffect == HOLD_EFFECT_MEGA_STONE)
{
if (!TESTING && (holdEffect == HOLD_EFFECT_Z_CRYSTAL || holdEffect == HOLD_EFFECT_MEGA_STONE)) // tests make this check already
return FALSE;
}
// Every check passed!
return TRUE;
@ -94,25 +101,18 @@ u32 GetBattlerTeraType(u32 battler)
return GetMonData(&GetBattlerParty(battler)[gBattlerPartyIndexes[battler]], MON_DATA_TERA_TYPE);
}
// Returns whether a battler is terastallized.
bool32 IsTerastallized(u32 battler)
{
return gBattleStruct->tera.isTerastallized[GetBattlerSide(battler)] & gBitTable[gBattlerPartyIndexes[battler]];
}
// Uses up a type's Stellar boost.
void ExpendTypeStellarBoost(u32 battler, u32 type)
{
if (type < 32) // avoid OOB access
gBattleStruct->tera.stellarBoostFlags[GetBattlerSide(battler)] |= gBitTable[type];
gBattleStruct->stellarBoostFlags[GetBattlerSide(battler)] |= gBitTable[type];
}
// Checks whether a type's Stellar boost has been expended.
bool32 IsTypeStellarBoosted(u32 battler, u32 type)
{
if (type < 32) // avoid OOB access
return !(gBattleStruct->tera.stellarBoostFlags[GetBattlerSide(battler)] & gBitTable[type]);
return !(gBattleStruct->stellarBoostFlags[GetBattlerSide(battler)] & gBitTable[type]);
else
return FALSE;
}
@ -125,7 +125,7 @@ uq4_12_t GetTeraMultiplier(u32 battler, u32 type)
bool32 hasAdaptability = (GetBattlerAbility(battler) == ABILITY_ADAPTABILITY);
// Safety check.
if (!IsTerastallized(battler))
if (GetActiveGimmick(battler) != GIMMICK_TERA)
return UQ_4_12(1.0);
// Stellar-type checks.
@ -172,604 +172,3 @@ u16 GetTeraTypeRGB(u32 type)
{
return gTypesInfo[type].teraTypeRGBValue;
}
// TERASTAL TRIGGER:
static const u8 ALIGNED(4) sTeraTriggerGfx[] = INCBIN_U8("graphics/battle_interface/tera_trigger.4bpp");
static const u16 sTeraTriggerPal[] = INCBIN_U16("graphics/battle_interface/tera_trigger.gbapal");
static const struct SpriteSheet sSpriteSheet_TeraTrigger =
{
sTeraTriggerGfx, sizeof(sTeraTriggerGfx), TAG_TERA_TRIGGER_TILE
};
static const struct SpritePalette sSpritePalette_TeraTrigger =
{
sTeraTriggerPal, TAG_TERA_TRIGGER_PAL
};
static const struct OamData sOamData_TeraTrigger =
{
.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_TeraTriggerOff[] =
{
ANIMCMD_FRAME(0, 0),
ANIMCMD_END
};
static const union AnimCmd sSpriteAnim_TeraTriggerOn[] =
{
ANIMCMD_FRAME(16, 0),
ANIMCMD_END
};
static const union AnimCmd *const sSpriteAnimTable_TeraTrigger[] =
{
sSpriteAnim_TeraTriggerOff,
sSpriteAnim_TeraTriggerOn,
};
static void SpriteCb_TeraTrigger(struct Sprite *sprite);
static const struct SpriteTemplate sSpriteTemplate_TeraTrigger =
{
.tileTag = TAG_TERA_TRIGGER_TILE,
.paletteTag = TAG_TERA_TRIGGER_PAL,
.oam = &sOamData_TeraTrigger,
.anims = sSpriteAnimTable_TeraTrigger,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraTrigger
};
// Tera Evolution Trigger icon functions.
void ChangeTeraTriggerSprite(u8 spriteId, u8 animId)
{
StartSpriteAnim(&gSprites[spriteId], animId);
}
#define SINGLES_TERA_TRIGGER_POS_X_OPTIMAL (30)
#define SINGLES_TERA_TRIGGER_POS_X_PRIORITY (31)
#define SINGLES_TERA_TRIGGER_POS_X_SLIDE (15)
#define SINGLES_TERA_TRIGGER_POS_Y_DIFF (-11)
#define DOUBLES_TERA_TRIGGER_POS_X_OPTIMAL (30)
#define DOUBLES_TERA_TRIGGER_POS_X_PRIORITY (31)
#define DOUBLES_TERA_TRIGGER_POS_X_SLIDE (15)
#define DOUBLES_TERA_TRIGGER_POS_Y_DIFF (-4)
#define tBattler data[0]
#define tHide data[1]
void CreateTeraTriggerSprite(u8 battler, u8 palId)
{
LoadSpritePalette(&sSpritePalette_TeraTrigger);
if (GetSpriteTileStartByTag(TAG_TERA_TRIGGER_TILE) == 0xFFFF)
{
LoadSpriteSheet(&sSpriteSheet_TeraTrigger);
}
if (gBattleStruct->tera.triggerSpriteId == 0xFF)
{
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
gBattleStruct->tera.triggerSpriteId = CreateSprite(&sSpriteTemplate_TeraTrigger,
gSprites[gHealthboxSpriteIds[battler]].x - DOUBLES_TERA_TRIGGER_POS_X_SLIDE,
gSprites[gHealthboxSpriteIds[battler]].y - DOUBLES_TERA_TRIGGER_POS_Y_DIFF, 0);
else
gBattleStruct->tera.triggerSpriteId = CreateSprite(&sSpriteTemplate_TeraTrigger,
gSprites[gHealthboxSpriteIds[battler]].x - SINGLES_TERA_TRIGGER_POS_X_SLIDE,
gSprites[gHealthboxSpriteIds[battler]].y - SINGLES_TERA_TRIGGER_POS_Y_DIFF, 0);
}
gSprites[gBattleStruct->tera.triggerSpriteId].tBattler = battler;
gSprites[gBattleStruct->tera.triggerSpriteId].tHide = FALSE;
ChangeTeraTriggerSprite(gBattleStruct->tera.triggerSpriteId, palId);
}
static void SpriteCb_TeraTrigger(struct Sprite *sprite)
{
s32 xSlide, xPriority, xOptimal;
s32 yDiff;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
xSlide = DOUBLES_TERA_TRIGGER_POS_X_SLIDE;
xPriority = DOUBLES_TERA_TRIGGER_POS_X_PRIORITY;
xOptimal = DOUBLES_TERA_TRIGGER_POS_X_OPTIMAL;
yDiff = DOUBLES_TERA_TRIGGER_POS_Y_DIFF;
}
else
{
xSlide = SINGLES_TERA_TRIGGER_POS_X_SLIDE;
xPriority = SINGLES_TERA_TRIGGER_POS_X_PRIORITY;
xOptimal = SINGLES_TERA_TRIGGER_POS_X_OPTIMAL;
yDiff = SINGLES_TERA_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)
DestroyTeraTriggerSprite();
}
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 IsTeraTriggerSpriteActive(void)
{
if (GetSpriteTileStartByTag(TAG_TERA_TRIGGER_TILE) == 0xFFFF)
return FALSE;
else if (IndexOfSpritePaletteTag(TAG_TERA_TRIGGER_PAL) != 0xFF)
return TRUE;
else
return FALSE;
}
void HideTeraTriggerSprite(void)
{
if (gBattleStruct->tera.triggerSpriteId != 0xFF)
{
ChangeTeraTriggerSprite(gBattleStruct->tera.triggerSpriteId, 0);
gSprites[gBattleStruct->tera.triggerSpriteId].tHide = TRUE;
}
}
void DestroyTeraTriggerSprite(void)
{
FreeSpritePaletteByTag(TAG_TERA_TRIGGER_PAL);
FreeSpriteTilesByTag(TAG_TERA_TRIGGER_TILE);
if (gBattleStruct->tera.triggerSpriteId != 0xFF)
DestroySprite(&gSprites[gBattleStruct->tera.triggerSpriteId]);
gBattleStruct->tera.triggerSpriteId = 0xFF;
}
#undef tBattler
#undef tHide
// TERA INDICATOR:
static const u16 sTeraIndicatorPal[] = INCBIN_U16("graphics/battle_interface/tera_indicator.gbapal");
static const u8 ALIGNED(4) sNormalIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/normal_indicator.4bpp");
static const u8 ALIGNED(4) sFightingIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/fighting_indicator.4bpp");
static const u8 ALIGNED(4) sFlyingIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/flying_indicator.4bpp");
static const u8 ALIGNED(4) sPoisonIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/poison_indicator.4bpp");
static const u8 ALIGNED(4) sGroundIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/ground_indicator.4bpp");
static const u8 ALIGNED(4) sRockIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/rock_indicator.4bpp");
static const u8 ALIGNED(4) sBugIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/bug_indicator.4bpp");
static const u8 ALIGNED(4) sGhostIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/ghost_indicator.4bpp");
static const u8 ALIGNED(4) sSteelIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/steel_indicator.4bpp");
static const u8 ALIGNED(4) sFireIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/fire_indicator.4bpp");
static const u8 ALIGNED(4) sWaterIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/water_indicator.4bpp");
static const u8 ALIGNED(4) sGrassIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/grass_indicator.4bpp");
static const u8 ALIGNED(4) sElectricIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/electric_indicator.4bpp");
static const u8 ALIGNED(4) sPsychicIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/psychic_indicator.4bpp");
static const u8 ALIGNED(4) sIceIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/ice_indicator.4bpp");
static const u8 ALIGNED(4) sDragonIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/dragon_indicator.4bpp");
static const u8 ALIGNED(4) sDarkIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/dark_indicator.4bpp");
static const u8 ALIGNED(4) sFairyIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/fairy_indicator.4bpp");
static const u8 ALIGNED(4) sStellarIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/stellar_indicator.4bpp");
static void SpriteCb_TeraIndicator(struct Sprite *sprite);
static const s8 sIndicatorPositions[][2] =
{
[B_POSITION_PLAYER_LEFT] = {53, -9},
[B_POSITION_OPPONENT_LEFT] = {44, -9},
[B_POSITION_PLAYER_RIGHT] = {52, -9},
[B_POSITION_OPPONENT_RIGHT] = {44, -9},
};
static const struct SpritePalette sSpritePalette_TeraIndicator =
{
sTeraIndicatorPal, TAG_TERA_INDICATOR_PAL
};
static const struct OamData sOamData_TeraIndicator =
{
.shape = SPRITE_SHAPE(16x16),
.size = SPRITE_SIZE(16x16),
.priority = 1,
};
static const struct SpriteTemplate sSpriteTemplate_NormalIndicator =
{
.tileTag = TAG_NORMAL_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_FightingIndicator =
{
.tileTag = TAG_FIGHTING_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_FlyingIndicator =
{
.tileTag = TAG_FLYING_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_PoisonIndicator =
{
.tileTag = TAG_POISON_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_GroundIndicator =
{
.tileTag = TAG_GROUND_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_RockIndicator =
{
.tileTag = TAG_ROCK_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_BugIndicator =
{
.tileTag = TAG_BUG_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_GhostIndicator =
{
.tileTag = TAG_GHOST_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_SteelIndicator =
{
.tileTag = TAG_STEEL_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_FireIndicator =
{
.tileTag = TAG_FIRE_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_WaterIndicator =
{
.tileTag = TAG_WATER_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_GrassIndicator =
{
.tileTag = TAG_GRASS_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_ElectricIndicator =
{
.tileTag = TAG_ELECTRIC_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_PsychicIndicator =
{
.tileTag = TAG_PSYCHIC_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_IceIndicator =
{
.tileTag = TAG_ICE_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_DragonIndicator =
{
.tileTag = TAG_DRAGON_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_DarkIndicator =
{
.tileTag = TAG_DARK_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_FairyIndicator =
{
.tileTag = TAG_FAIRY_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteTemplate sSpriteTemplate_StellarIndicator =
{
.tileTag = TAG_STELLAR_INDICATOR_TILE,
.paletteTag = TAG_TERA_INDICATOR_PAL,
.oam = &sOamData_TeraIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_TeraIndicator,
};
static const struct SpriteSheet sTeraIndicatorSpriteSheets[NUMBER_OF_MON_TYPES + 1] =
{
{sNormalIndicatorGfx, sizeof(sNormalIndicatorGfx), TAG_NORMAL_INDICATOR_TILE}, // TYPE_NONE
{sNormalIndicatorGfx, sizeof(sNormalIndicatorGfx), TAG_NORMAL_INDICATOR_TILE},
{sFightingIndicatorGfx, sizeof(sFightingIndicatorGfx), TAG_FIGHTING_INDICATOR_TILE},
{sFlyingIndicatorGfx, sizeof(sFlyingIndicatorGfx), TAG_FLYING_INDICATOR_TILE},
{sPoisonIndicatorGfx, sizeof(sPoisonIndicatorGfx), TAG_POISON_INDICATOR_TILE},
{sGroundIndicatorGfx, sizeof(sGroundIndicatorGfx), TAG_GROUND_INDICATOR_TILE},
{sRockIndicatorGfx, sizeof(sRockIndicatorGfx), TAG_ROCK_INDICATOR_TILE},
{sBugIndicatorGfx, sizeof(sBugIndicatorGfx), TAG_BUG_INDICATOR_TILE},
{sGhostIndicatorGfx, sizeof(sGhostIndicatorGfx), TAG_GHOST_INDICATOR_TILE},
{sSteelIndicatorGfx, sizeof(sSteelIndicatorGfx), TAG_STEEL_INDICATOR_TILE},
{sNormalIndicatorGfx, sizeof(sNormalIndicatorGfx), TAG_NORMAL_INDICATOR_TILE}, // TYPE_MYSTERY
{sFireIndicatorGfx, sizeof(sFireIndicatorGfx), TAG_FIRE_INDICATOR_TILE},
{sWaterIndicatorGfx, sizeof(sWaterIndicatorGfx), TAG_WATER_INDICATOR_TILE},
{sGrassIndicatorGfx, sizeof(sGrassIndicatorGfx), TAG_GRASS_INDICATOR_TILE},
{sElectricIndicatorGfx, sizeof(sElectricIndicatorGfx), TAG_ELECTRIC_INDICATOR_TILE},
{sPsychicIndicatorGfx, sizeof(sPsychicIndicatorGfx), TAG_PSYCHIC_INDICATOR_TILE},
{sIceIndicatorGfx, sizeof(sIceIndicatorGfx), TAG_ICE_INDICATOR_TILE},
{sDragonIndicatorGfx, sizeof(sDragonIndicatorGfx), TAG_DRAGON_INDICATOR_TILE},
{sDarkIndicatorGfx, sizeof(sDarkIndicatorGfx), TAG_DARK_INDICATOR_TILE},
{sFairyIndicatorGfx, sizeof(sFairyIndicatorGfx), TAG_FAIRY_INDICATOR_TILE},
{sStellarIndicatorGfx, sizeof(sStellarIndicatorGfx), TAG_STELLAR_INDICATOR_TILE},
{0}
};
static const struct SpriteTemplate * const sTeraIndicatorSpriteTemplates[NUMBER_OF_MON_TYPES] =
{
[TYPE_NONE] = &sSpriteTemplate_NormalIndicator, // just in case
[TYPE_NORMAL] = &sSpriteTemplate_NormalIndicator,
[TYPE_FIGHTING] = &sSpriteTemplate_FightingIndicator,
[TYPE_FLYING] = &sSpriteTemplate_FlyingIndicator,
[TYPE_POISON] = &sSpriteTemplate_PoisonIndicator,
[TYPE_GROUND] = &sSpriteTemplate_GroundIndicator,
[TYPE_ROCK] = &sSpriteTemplate_RockIndicator,
[TYPE_BUG] = &sSpriteTemplate_BugIndicator,
[TYPE_GHOST] = &sSpriteTemplate_GhostIndicator,
[TYPE_STEEL] = &sSpriteTemplate_SteelIndicator,
[TYPE_MYSTERY] = &sSpriteTemplate_NormalIndicator, // just in case
[TYPE_FIRE] = &sSpriteTemplate_FireIndicator,
[TYPE_WATER] = &sSpriteTemplate_WaterIndicator,
[TYPE_GRASS] = &sSpriteTemplate_GrassIndicator,
[TYPE_ELECTRIC] = &sSpriteTemplate_ElectricIndicator,
[TYPE_PSYCHIC] = &sSpriteTemplate_PsychicIndicator,
[TYPE_ICE] = &sSpriteTemplate_IceIndicator,
[TYPE_DRAGON] = &sSpriteTemplate_DragonIndicator,
[TYPE_DARK] = &sSpriteTemplate_DarkIndicator,
[TYPE_FAIRY] = &sSpriteTemplate_FairyIndicator,
[TYPE_STELLAR] = &sSpriteTemplate_StellarIndicator,
};
// for sprite data fields
#define tBattler data[0]
#define tType data[1] // Indicator type: tera
#define tPosX data[2]
#define tLevelXDelta data[3] // X position depends whether level has 3, 2 or 1 digit
// data fields for healthboxMain
// oam.affineParam holds healthboxRight spriteId
#define hMain_TeraIndicatorId data[3]
#define hMain_HealthBarSpriteId data[5]
#define hMain_Battler data[6]
#define hMain_Data7 data[7]
// data fields for healthboxRight
#define hOther_HealthBoxSpriteId data[5]
// data fields for healthbar
#define hBar_HealthBoxSpriteId data[5]
void TeraIndicator_LoadSpriteGfx(void)
{
LoadSpriteSheets(sTeraIndicatorSpriteSheets);
LoadSpritePalette(&sSpritePalette_TeraIndicator);
}
bool32 TeraIndicator_ShouldBeInvisible(u32 battler)
{
return !IsTerastallized(battler);
}
u8 TeraIndicator_GetSpriteId(u32 healthboxSpriteId)
{
return gBattleStruct->tera.indicatorSpriteId[gSprites[healthboxSpriteId].hMain_Battler];
}
void TeraIndicator_SetVisibilities(u32 healthboxId, bool32 invisible)
{
u8 spriteId = TeraIndicator_GetSpriteId(healthboxId);
u32 battler = gSprites[healthboxId].hMain_Battler;
if (GetSafariZoneFlag())
return;
if (invisible == TRUE)
gSprites[spriteId].invisible = TRUE;
else // Try visible.
gSprites[spriteId].invisible = TeraIndicator_ShouldBeInvisible(battler);
}
void TeraIndicator_UpdateOamPriorities(u32 healthboxId, u32 oamPriority)
{
u8 spriteId = TeraIndicator_GetSpriteId(healthboxId);
gSprites[spriteId].oam.priority = oamPriority;
}
void TeraIndicator_UpdateLevel(u32 healthboxId, u32 level)
{
s16 xDelta = 0;
u8 spriteId = TeraIndicator_GetSpriteId(healthboxId);
if (level >= 100)
xDelta -= 4;
else if (level < 10)
xDelta += 5;
gSprites[spriteId].tLevelXDelta = xDelta;
}
void TeraIndicator_CreateSprite(u32 battler, u32 healthboxSpriteId)
{
u32 position;
u8 spriteId;
s16 xHealthbox = 0, y = 0;
s32 x = 0;
u32 type = GetBattlerTeraType(battler);
position = GetBattlerPosition(battler);
GetBattlerHealthboxCoords(battler, &xHealthbox, &y);
x = sIndicatorPositions[position][0];
y += sIndicatorPositions[position][1];
spriteId = gBattleStruct->tera.indicatorSpriteId[battler] = CreateSpriteAtEnd(sTeraIndicatorSpriteTemplates[type], 0, y, 0);
gSprites[spriteId].tBattler = battler;
gSprites[spriteId].tPosX = x;
gSprites[spriteId].invisible = TRUE;
}
void TeraIndicator_DestroySprite(u32 healthboxSpriteId)
{
u8 spriteId = TeraIndicator_GetSpriteId(healthboxSpriteId);
DestroySprite(&gSprites[spriteId]);
}
void TeraIndicator_UpdateType(u32 battler, u32 healthboxSpriteId)
{
TeraIndicator_DestroySprite(healthboxSpriteId);
TeraIndicator_CreateSprite(battler, healthboxSpriteId);
}
static void SpriteCb_TeraIndicator(struct Sprite *sprite)
{
u32 battler = sprite->tBattler;
sprite->x = gSprites[gHealthboxSpriteIds[battler]].x + sprite->tPosX + sprite->tLevelXDelta;
sprite->x2 = gSprites[gHealthboxSpriteIds[battler]].x2;
sprite->y2 = gSprites[gHealthboxSpriteIds[battler]].y2;
}
#undef tBattler
#undef tType
#undef tPosX
#undef tLevelXDelta

View File

@ -212,7 +212,6 @@ static bool8 AngledWipes_TryEnd(struct Task *);
static bool8 AngledWipes_StartNext(struct Task *);
static bool8 ShredSplit_Init(struct Task *);
static bool8 ShredSplit_Main(struct Task *);
static bool8 ShredSplit_BrokenCheck(struct Task *);
static bool8 ShredSplit_End(struct Task *);
static bool8 Blackhole_Init(struct Task *);
static bool8 Blackhole_Vibrate(struct Task *);
@ -561,7 +560,6 @@ static const TransitionStateFunc sShredSplit_Funcs[] =
{
ShredSplit_Init,
ShredSplit_Main,
ShredSplit_BrokenCheck,
ShredSplit_End
};
@ -2928,29 +2926,6 @@ static bool8 ShredSplit_Main(struct Task *task)
return FALSE;
}
// This function never increments the state counter, because the loop condition
// is always false, resulting in the game being stuck in an infinite loop.
// It's possible this transition is only partially
// done and the second part was left out.
// In any case removing or bypassing this state allows the transition to finish.
static bool8 ShredSplit_BrokenCheck(struct Task *task)
{
u16 i;
bool32 done = TRUE;
u16 checkVar2 = 0xFF10;
for (i = 0; i < DISPLAY_HEIGHT; i++)
{
if (gScanlineEffectRegBuffers[1][i] != DISPLAY_WIDTH && gScanlineEffectRegBuffers[1][i] != checkVar2)
done = FALSE;
}
if (done == TRUE)
task->tState++;
return FALSE;
}
static bool8 ShredSplit_End(struct Task *task)
{
DmaStop(0);

File diff suppressed because it is too large Load Diff

View File

@ -45,12 +45,8 @@
#define STAT_STAGE(battler, stat) (gBattleMons[battler].statStages[stat - 1])
// Function Declarations
static void SpriteCB_ZMoveTrigger(struct Sprite *sprite);
static u16 GetSignatureZMove(u16 move, u16 species, u16 item);
static void ZMoveSelectionDisplayPpNumber(u32 battler);
static void ZMoveSelectionDisplayPower(u16 move, u16 zMove);
static void ShowZMoveTriggerSprite(u8 battleId);
static bool32 AreStatsMaxed(u8 battler, u8 n);
static void ZMoveSelectionDisplayMoveType(u16 zMove, u32 battler);
// Const Data
@ -108,260 +104,130 @@ static const u8 sText_RecoverHP[] = _("Recover HP");
static const u8 sText_HealAllyHP[] = _("Heal Replacement HP");
static const u8 sText_PowerColon[] = _("Power: ");
static const u32 sZMoveTriggerGfx[] = INCBIN_U32("graphics/battle_interface/z_move_trigger.4bpp.lz");
static const u16 sZMoveTriggerPal[] = INCBIN_U16("graphics/battle_interface/z_move_trigger.gbapal");
static const struct CompressedSpriteSheet sSpriteSheet_ZMoveTrigger = {
sZMoveTriggerGfx, (32 * 32) / 2, TAG_ZMOVE_TRIGGER_TILE
};
static const struct SpritePalette sSpritePalette_ZMoveTrigger = {
sZMoveTriggerPal, TAG_ZMOVE_TRIGGER_PAL
};
static const struct OamData sOamData_ZMoveTrigger =
{
.affineMode = ST_OAM_AFFINE_OFF,
.objMode = ST_OAM_OBJ_NORMAL,
.shape = SPRITE_SHAPE(32x32),
.size = SPRITE_SIZE(32x32),
.priority = 1,
};
static const struct SpriteTemplate sSpriteTemplate_ZMoveTrigger =
{
.tileTag = TAG_ZMOVE_TRIGGER_TILE,
.paletteTag = TAG_ZMOVE_TRIGGER_PAL,
.oam = &sOamData_ZMoveTrigger,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCB_ZMoveTrigger
};
// Functions
bool8 IsZMove(u16 move)
bool32 IsZMove(u32 move)
{
return move >= FIRST_Z_MOVE && move <= LAST_Z_MOVE;
}
void QueueZMove(u8 battler, u16 baseMove)
bool32 CanUseZMove(u32 battler)
{
gBattleStruct->zmove.toBeUsed[battler] = gBattleStruct->zmove.chosenZMove;
gBattleStruct->zmove.baseMoves[battler] = baseMove;
if (gBattleStruct->zmove.chosenZMove == MOVE_LIGHT_THAT_BURNS_THE_SKY)
gBattleStruct->zmove.categories[battler] = GetCategoryBasedOnStats(battler);
else
gBattleStruct->zmove.categories[battler] = gMovesInfo[baseMove].category;
u32 holdEffect = GetBattlerHoldEffect(battler, FALSE);
// Check if Player has Z-Power Ring.
if (!TESTING && (battler == B_POSITION_PLAYER_LEFT
|| (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && battler == B_POSITION_PLAYER_RIGHT))
&& !CheckBagHasItem(ITEM_Z_POWER_RING, 1))
return FALSE;
// Add '| BATTLE_TYPE_FRONTIER' to below if issues occur
if (gBattleTypeFlags & (BATTLE_TYPE_SAFARI | BATTLE_TYPE_WALLY_TUTORIAL))
return FALSE;
// Check if Trainer has already used a Z-Move.
if (HasTrainerUsedGimmick(battler, GIMMICK_Z_MOVE))
return FALSE;
// Check if battler has another gimmick active.
if (GetActiveGimmick(battler) != GIMMICK_NONE && GetActiveGimmick(battler) != GIMMICK_ULTRA_BURST)
return FALSE;
// Check if battler isn't holding a Z-Crystal.
if (holdEffect != HOLD_EFFECT_Z_CRYSTAL)
return FALSE;
// All checks passed!
return TRUE;
}
bool32 IsViableZMove(u8 battler, u16 move)
u32 GetUsableZMove(u32 battler, u32 move)
{
u32 item = gBattleMons[battler].item;
u32 holdEffect = GetBattlerHoldEffect(battler, FALSE);
if (holdEffect == HOLD_EFFECT_Z_CRYSTAL)
{
u16 zMove = GetSignatureZMove(move, gBattleMons[battler].species, item);
if (zMove != MOVE_NONE)
return zMove; // Signature z move exists
if (move != MOVE_NONE && zMove != MOVE_Z_STATUS && gMovesInfo[move].type == ItemId_GetSecondaryId(item))
return GetTypeBasedZMove(move);
}
return MOVE_NONE;
}
void ActivateZMove(u32 battler)
{
gBattleStruct->zmove.baseMoves[battler] = gBattleMons[battler].moves[gBattleStruct->chosenMovePositions[battler]];
SetActiveGimmick(battler, GIMMICK_Z_MOVE);
}
bool32 IsViableZMove(u32 battler, u32 move)
{
u32 item;
u16 holdEffect;
u32 holdEffect = GetBattlerHoldEffect(battler, FALSE);
int moveSlotIndex;
item = gBattleMons[battler].item;
if (gBattleStruct->gimmick.usableGimmick[battler] != GIMMICK_Z_MOVE)
return FALSE;
for (moveSlotIndex = 0; moveSlotIndex < MAX_MON_MOVES; moveSlotIndex++)
{
if (gBattleMons[battler].moves[moveSlotIndex] == move && gBattleMons[battler].pp[moveSlotIndex] == 0)
return FALSE;
}
if (gBattleStruct->zmove.used[battler])
// Check if Player has Z-Power Ring.
if ((battler == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && battler == B_POSITION_PLAYER_RIGHT))
&& !CheckBagHasItem(ITEM_Z_POWER_RING, 1))
{
return FALSE;
}
// Add '| BATTLE_TYPE_FRONTIER' to below if issues occur
if (gBattleTypeFlags & (BATTLE_TYPE_SAFARI | BATTLE_TYPE_WALLY_TUTORIAL))
return FALSE;
if ((GetBattlerPosition(battler) == B_POSITION_PLAYER_LEFT || (!(gBattleTypeFlags & BATTLE_TYPE_MULTI) && GetBattlerPosition(battler) == B_POSITION_PLAYER_RIGHT)) && !CheckBagHasItem(ITEM_Z_POWER_RING, 1))
return FALSE;
if (item == ITEM_ENIGMA_BERRY_E_READER)
return FALSE; // HoldEffect = gEnigmaBerries[battler].holdEffect;
else
holdEffect = ItemId_GetHoldEffect(item);
// Check for signature Z-Move or type-based Z-Move.
if (holdEffect == HOLD_EFFECT_Z_CRYSTAL)
{
u16 zMove = GetSignatureZMove(move, gBattleMons[battler].species, item);
if (zMove != MOVE_NONE)
{
gBattleStruct->zmove.chosenZMove = zMove; // Signature z move exists
return TRUE;
}
if (move != MOVE_NONE && zMove != MOVE_Z_STATUS && gMovesInfo[move].type == ItemId_GetSecondaryId(item))
{
gBattleStruct->zmove.chosenZMove = GetTypeBasedZMove(move, battler);
if (move != MOVE_NONE && gMovesInfo[move].type == ItemId_GetSecondaryId(item))
return TRUE;
}
}
return FALSE;
}
void GetUsableZMoves(u8 battler, u16 *moves)
void AssignUsableZMoves(u32 battler, u16 *moves)
{
u32 i;
gBattleStruct->zmove.possibleZMoves[battler] = 0;
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (moves[i] != MOVE_NONE && IsViableZMove(battler, moves[i]))
gBattleStruct->zmove.possibleZMoves[battler] |= (1 << i);
gBattleStruct->zmove.possibleZMoves[battler] |= gBitTable[i];
}
}
bool32 IsZMoveUsable(u8 battler, u16 moveIndex)
bool32 TryChangeZTrigger(u32 battler, u32 moveIndex)
{
if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE) && IsPartnerMonFromSameTrainer(battler) && gBattleStruct->zmove.toBeUsed[BATTLE_PARTNER(battler)] != MOVE_NONE)
return FALSE; // Player's other mon has a z move queued up already
if (gBattleStruct->zmove.possibleZMoves[battler] & (1 << moveIndex))
return TRUE;
return FALSE;
}
bool32 TryChangeZIndicator(u8 battler, u8 moveIndex)
{
bool32 viableZMove = IsZMoveUsable(battler, moveIndex);
bool32 viableZMove = (gBattleStruct->zmove.possibleZMoves[battler] & gBitTable[moveIndex]) != 0;
if (gBattleStruct->zmove.viable && !viableZMove)
HideZMoveTriggerSprite(); // Was a viable z move, now is not -> slide out
HideGimmickTriggerSprite(); // Was a viable z move, now is not -> slide out
else if (!gBattleStruct->zmove.viable && viableZMove)
ShowZMoveTriggerSprite(battler); // Was not a viable z move, now is -> slide back in
CreateGimmickTriggerSprite(battler); // Was not a viable z move, now is -> slide back in
gBattleStruct->zmove.viable = viableZMove;
return viableZMove;
}
#define SINGLES_Z_TRIGGER_POS_X_OPTIMAL (29)
#define SINGLES_Z_TRIGGER_POS_X_PRIORITY (29)
#define SINGLES_Z_TRIGGER_POS_X_SLIDE (15)
#define SINGLES_Z_TRIGGER_POS_Y_DIFF (-10)
#define DOUBLES_Z_TRIGGER_POS_X_OPTIMAL SINGLES_Z_TRIGGER_POS_X_OPTIMAL
#define DOUBLES_Z_TRIGGER_POS_X_PRIORITY SINGLES_Z_TRIGGER_POS_X_PRIORITY
#define DOUBLES_Z_TRIGGER_POS_X_SLIDE SINGLES_Z_TRIGGER_POS_X_SLIDE
#define DOUBLES_Z_TRIGGER_POS_Y_DIFF (-4)
#define tBattler data[0]
#define tHide data[1]
void CreateZMoveTriggerSprite(u8 battler, bool8 viable)
{
s16 x, y;
LoadSpritePalette(&sSpritePalette_ZMoveTrigger);
if (GetSpriteTileStartByTag(TAG_ZMOVE_TRIGGER_TILE) == 0xFFFF)
LoadCompressedSpriteSheetUsingHeap(&sSpriteSheet_ZMoveTrigger);
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
x = gSprites[gHealthboxSpriteIds[battler]].x - DOUBLES_Z_TRIGGER_POS_X_SLIDE;
y = gSprites[gHealthboxSpriteIds[battler]].y - DOUBLES_Z_TRIGGER_POS_Y_DIFF;
}
else
{
x = gSprites[gHealthboxSpriteIds[battler]].x - SINGLES_Z_TRIGGER_POS_X_SLIDE;
y = gSprites[gHealthboxSpriteIds[battler]].y - SINGLES_Z_TRIGGER_POS_Y_DIFF;
}
if (gBattleStruct->zmove.triggerSpriteId == 0xFF)
gBattleStruct->zmove.triggerSpriteId = CreateSprite(&sSpriteTemplate_ZMoveTrigger, x, y, 0);
gSprites[gBattleStruct->zmove.triggerSpriteId].tBattler = battler;
gSprites[gBattleStruct->zmove.triggerSpriteId].tHide = (viable == TRUE) ? FALSE : TRUE;
}
static void SpriteCB_ZMoveTrigger(struct Sprite *sprite)
{
s32 xSlide, xPriority, xOptimal;
s32 yDiff;
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
{
xSlide = DOUBLES_Z_TRIGGER_POS_X_SLIDE;
xPriority = DOUBLES_Z_TRIGGER_POS_X_PRIORITY;
xOptimal = DOUBLES_Z_TRIGGER_POS_X_OPTIMAL;
yDiff = DOUBLES_Z_TRIGGER_POS_Y_DIFF;
}
else
{
xSlide = SINGLES_Z_TRIGGER_POS_X_SLIDE;
xPriority = SINGLES_Z_TRIGGER_POS_X_PRIORITY;
xOptimal = SINGLES_Z_TRIGGER_POS_X_OPTIMAL;
yDiff = SINGLES_Z_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)
DestroyZMoveTriggerSprite();
}
else
{
if (sprite->x != gSprites[gHealthboxSpriteIds[sprite->tBattler]].x - xOptimal)
{
sprite->x--;
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 IsZMoveTriggerSpriteActive(void)
{
if (GetSpriteTileStartByTag(TAG_ZMOVE_TRIGGER_TILE) == 0xFFFF)
return FALSE;
else if (IndexOfSpritePaletteTag(TAG_ZMOVE_TRIGGER_PAL) != 0xFF)
return TRUE;
else
return FALSE;
}
void HideZMoveTriggerSprite(void)
{
struct Sprite *sprite;
gBattleStruct->zmove.viable = FALSE;
if (gBattleStruct->zmove.triggerSpriteId >= MAX_SPRITES)
return;
sprite = &gSprites[gBattleStruct->zmove.triggerSpriteId];
sprite->tHide = TRUE;
}
static void ShowZMoveTriggerSprite(u8 battler)
{
gBattleStruct->zmove.viable = TRUE;
CreateZMoveTriggerSprite(battler, TRUE);
}
void DestroyZMoveTriggerSprite(void)
{
FreeSpritePaletteByTag(TAG_ZMOVE_TRIGGER_PAL);
FreeSpriteTilesByTag(TAG_ZMOVE_TRIGGER_TILE);
if (gBattleStruct->zmove.triggerSpriteId != 0xFF)
DestroySprite(&gSprites[gBattleStruct->zmove.triggerSpriteId]);
gBattleStruct->zmove.triggerSpriteId = 0xFF;
}
static u16 GetSignatureZMove(u16 move, u16 species, u16 item)
u32 GetSignatureZMove(u32 move, u32 species, u32 item)
{
u32 i;
@ -375,13 +241,17 @@ static u16 GetSignatureZMove(u16 move, u16 species, u16 item)
return MOVE_NONE;
}
u16 GetTypeBasedZMove(u16 move, u8 battler)
u32 GetTypeBasedZMove(u32 move)
{
u8 moveType = gMovesInfo[move].type;
u32 moveType = gMovesInfo[move].type;
if (moveType >= NUMBER_OF_MON_TYPES)
moveType = TYPE_MYSTERY;
// Z-Weather Ball changes types, however Revelation Dance, -ate ability affected moves, and Hidden Power do not
if (gBattleStruct->dynamicMoveType && gMovesInfo[move].effect == EFFECT_WEATHER_BALL)
moveType = gBattleStruct->dynamicMoveType & DYNAMIC_TYPE_MASK;
// Get Z-Move from type
if (gTypesInfo[moveType].zMove == MOVE_NONE) // failsafe
return gTypesInfo[0].zMove;
@ -544,7 +414,7 @@ static void ZMoveSelectionDisplayPpNumber(u32 battler)
static void ZMoveSelectionDisplayMoveType(u16 zMove, u32 battler)
{
u8 *txtPtr;
u8 *txtPtr, *end;
u8 zMoveType;
GET_MOVE_TYPE(zMove, zMoveType);
@ -554,7 +424,8 @@ static void ZMoveSelectionDisplayMoveType(u16 zMove, u32 battler)
*(txtPtr)++ = EXT_CTRL_CODE_FONT;
*(txtPtr)++ = FONT_NORMAL;
StringCopy(txtPtr, gTypesInfo[zMoveType].name);
end = StringCopy(txtPtr, gTypesInfo[zMoveType].name);
PrependFontIdToFit(txtPtr, end, FONT_NORMAL, WindowWidthPx(B_WIN_MOVE_TYPE) - 25);
BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MOVE_TYPE);
}
@ -564,20 +435,20 @@ static void ZMoveSelectionDisplayMoveType(u16 zMove, u32 battler)
void SetZEffect(void)
{
u32 i;
u32 effect = gMovesInfo[gBattleStruct->zmove.baseMoves[gBattlerAttacker]].zMove.effect;
gBattleStruct->zmove.zStatusActive = TRUE;
if (gBattleStruct->zmove.effect == Z_EFFECT_CURSE)
if (effect == Z_EFFECT_CURSE)
{
if (IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST))
gBattleStruct->zmove.effect = Z_EFFECT_RECOVER_HP;
effect = Z_EFFECT_RECOVER_HP;
else
gBattleStruct->zmove.effect = Z_EFFECT_ATK_UP_1;
effect = Z_EFFECT_ATK_UP_1;
}
gBattleScripting.savedStatChanger = gBattleScripting.statChanger; // Save used move's stat changer (e.g. for Z-Growl)
gBattleScripting.battler = gBattlerAttacker;
switch (gBattleStruct->zmove.effect)
switch (effect)
{
case Z_EFFECT_RESET_STATS:
for (i = 0; i < NUM_BATTLE_STATS - 1; i++)
@ -590,22 +461,27 @@ void SetZEffect(void)
gBattlescriptCurrInstr = BattleScript_ZEffectPrintString;
break;
case Z_EFFECT_ALL_STATS_UP_1:
if (!AreStatsMaxed(gBattlerAttacker, STAT_SPDEF))
{
bool32 canBoost = FALSE;
for (i = STAT_ATK; i < NUM_STATS; i++) // Doesn't increase Acc or Evsn
{
for (i = 0; i < STAT_ACC - 1; i++) // Doesn't increase Acc or Evsn
if (STAT_STAGE(gBattlerAttacker, i) < 12)
{
if (gBattleMons[gBattlerAttacker].statStages[i] < 12)
++gBattleMons[gBattlerAttacker].statStages[i];
canBoost = TRUE;
}
}
if (canBoost)
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_Z_ALL_STATS_UP;
BattleScriptPush(gBattlescriptCurrInstr + Z_EFFECT_BS_LENGTH);
gBattlescriptCurrInstr = BattleScript_ZEffectPrintString;
gBattlescriptCurrInstr = BattleScript_AllStatsUpZMove;
}
else
{
gBattlescriptCurrInstr += Z_EFFECT_BS_LENGTH;
}
break;
}
case Z_EFFECT_BOOST_CRITS:
if (!(gBattleMons[gBattlerAttacker].status2 & STATUS2_FOCUS_ENERGY_ANY))
{
@ -646,17 +522,17 @@ void SetZEffect(void)
gBattlescriptCurrInstr = BattleScript_ZEffectPrintString;
break;
case Z_EFFECT_ATK_UP_1 ... Z_EFFECT_EVSN_UP_1:
SET_STATCHANGER(gBattleStruct->zmove.effect - Z_EFFECT_ATK_UP_1 + 1, 1, FALSE);
SET_STATCHANGER(effect - Z_EFFECT_ATK_UP_1 + 1, 1, FALSE);
BattleScriptPush(gBattlescriptCurrInstr + Z_EFFECT_BS_LENGTH);
gBattlescriptCurrInstr = BattleScript_StatUpZMove;
break;
case Z_EFFECT_ATK_UP_2 ... Z_EFFECT_EVSN_UP_2:
SET_STATCHANGER(gBattleStruct->zmove.effect - Z_EFFECT_ATK_UP_2 + 1, 2, FALSE);
SET_STATCHANGER(effect - Z_EFFECT_ATK_UP_2 + 1, 2, FALSE);
BattleScriptPush(gBattlescriptCurrInstr + Z_EFFECT_BS_LENGTH);
gBattlescriptCurrInstr = BattleScript_StatUpZMove;
break;
case Z_EFFECT_ATK_UP_3 ... Z_EFFECT_EVSN_UP_3:
SET_STATCHANGER(gBattleStruct->zmove.effect - Z_EFFECT_ATK_UP_3 + 1, 3, FALSE);
SET_STATCHANGER(effect - Z_EFFECT_ATK_UP_3 + 1, 3, FALSE);
BattleScriptPush(gBattlescriptCurrInstr + Z_EFFECT_BS_LENGTH);
gBattlescriptCurrInstr = BattleScript_StatUpZMove;
break;
@ -664,22 +540,9 @@ void SetZEffect(void)
gBattlescriptCurrInstr += Z_EFFECT_BS_LENGTH;
break;
}
gBattleStruct->zmove.zStatusActive = FALSE;
}
static bool32 AreStatsMaxed(u8 battler, u8 n)
{
u32 i;
for (i = STAT_ATK; i <= n; i++)
{
if (STAT_STAGE(battler, i) < MAX_STAT_STAGE)
return FALSE;
}
return TRUE;
}
u16 GetZMovePower(u16 move)
u32 GetZMovePower(u32 move)
{
if (gMovesInfo[move].category == DAMAGE_CATEGORY_STATUS)
return 0;

View File

@ -54,6 +54,7 @@ static void UpdatePerDay(struct Time *localTime)
UpdateFrontierGambler(daysSince);
SetShoalItemFlag(daysSince);
SetRandomLotteryNumber(daysSince);
UpdateDaysPassedSinceFormChange(daysSince);
*days = localTime->days;
}
}

View File

@ -2548,7 +2548,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
#else
.name = _("SuprswtSyrup"),
#endif
.description = COMPOUND_STRING("Lowers the foe's Speed."),
.description = COMPOUND_STRING("Lowers the foe's Evasion."),
.aiRating = 5,
},

View File

@ -2243,4 +2243,11 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
.battleScript = BattleScript_EffectHit,
.battleTvScore = 0, // TODO: Assign points
},
[EFFECT_GUARDIAN_OF_ALOLA] =
{
.battleScript = BattleScript_DamageToQuarterTargetHP,
.battleTvScore = 0, // TODO: Assign points
},
};

54
src/data/gimmicks.h Normal file
View File

@ -0,0 +1,54 @@
#include "graphics/gimmicks.h"
// Gimmick data
const struct GimmickInfo gGimmicksInfo[GIMMICKS_COUNT] =
{
[GIMMICK_NONE] = {0},
[GIMMICK_MEGA] =
{
.triggerSheet = &sSpriteSheet_MegaTrigger,
.triggerPal = &sSpritePalette_MegaTrigger,
.triggerTemplate = &sSpriteTemplate_GimmickTrigger,
.indicatorSheet = &sSpriteSheet_MegaIndicator,
.indicatorPal = &sSpritePalette_MegaIndicator,
.CanActivate = CanMegaEvolve,
.ActivateGimmick = ActivateMegaEvolution,
},
[GIMMICK_Z_MOVE] =
{
.triggerSheet = &sSpriteSheet_ZMoveTrigger,
.triggerPal = &sSpritePalette_ZMoveTrigger,
.triggerTemplate = &sSpriteTemplate_GimmickTrigger,
.CanActivate = CanUseZMove,
.ActivateGimmick = ActivateZMove,
},
[GIMMICK_ULTRA_BURST] =
{
.triggerSheet = &sSpriteSheet_BurstTrigger,
.triggerPal = &sSpritePalette_BurstTrigger,
.triggerTemplate = &sSpriteTemplate_GimmickTrigger,
.CanActivate = CanUltraBurst,
.ActivateGimmick = ActivateUltraBurst,
},
[GIMMICK_DYNAMAX] =
{
.triggerSheet = &sSpriteSheet_DynamaxTrigger,
.triggerPal = &sSpritePalette_DynamaxTrigger,
.triggerTemplate = &sSpriteTemplate_GimmickTrigger,
.indicatorSheet = &sSpriteSheet_DynamaxIndicator,
.indicatorPal = &sSpritePalette_MiscIndicator,
.CanActivate = CanDynamax,
.ActivateGimmick = ActivateDynamax,
},
[GIMMICK_TERA] =
{
.triggerSheet = &sSpriteSheet_TeraTrigger,
.triggerPal = &sSpritePalette_TeraTrigger,
.triggerTemplate = &sSpriteTemplate_GimmickTrigger,
.indicatorSheet = NULL, // handled separately
.indicatorPal = &sSpritePalette_TeraIndicator,
.CanActivate = CanTerastallize,
.ActivateGimmick = ActivateTera,
}
};

View File

@ -0,0 +1,152 @@
// trigger data
static const u8 ALIGNED(4) sMegaTriggerGfx[] = INCBIN_U8("graphics/battle_interface/mega_trigger.4bpp");
static const u8 ALIGNED(4) sZMoveTriggerGfx[] = INCBIN_U8("graphics/battle_interface/z_move_trigger.4bpp");
static const u8 ALIGNED(4) sBurstTriggerGfx[] = INCBIN_U8("graphics/battle_interface/burst_trigger.4bpp");
static const u8 ALIGNED(4) sDynamaxTriggerGfx[] = INCBIN_U8("graphics/battle_interface/dynamax_trigger.4bpp");
static const u8 ALIGNED(4) sTeraTriggerGfx[] = INCBIN_U8("graphics/battle_interface/tera_trigger.4bpp");
static const u16 sMegaTriggerPal[] = INCBIN_U16("graphics/battle_interface/mega_trigger.gbapal");
static const u16 sZMoveTriggerPal[] = INCBIN_U16("graphics/battle_interface/z_move_trigger.gbapal");
static const u16 sBurstTriggerPal[] = INCBIN_U16("graphics/battle_interface/burst_trigger.gbapal");
static const u16 sDynamaxTriggerPal[] = INCBIN_U16("graphics/battle_interface/dynamax_trigger.gbapal");
static const u16 sTeraTriggerPal[] = INCBIN_U16("graphics/battle_interface/tera_trigger.gbapal");
static const struct SpriteSheet sSpriteSheet_MegaTrigger = {sMegaTriggerGfx, sizeof(sMegaTriggerGfx), TAG_GIMMICK_TRIGGER_TILE};
static const struct SpriteSheet sSpriteSheet_ZMoveTrigger = {sZMoveTriggerGfx, sizeof(sZMoveTriggerGfx), TAG_GIMMICK_TRIGGER_TILE};
static const struct SpriteSheet sSpriteSheet_BurstTrigger = {sBurstTriggerGfx, sizeof(sBurstTriggerGfx), TAG_GIMMICK_TRIGGER_TILE};
static const struct SpriteSheet sSpriteSheet_DynamaxTrigger = {sDynamaxTriggerGfx, sizeof(sDynamaxTriggerGfx), TAG_GIMMICK_TRIGGER_TILE};
static const struct SpriteSheet sSpriteSheet_TeraTrigger = {sTeraTriggerGfx, sizeof(sTeraTriggerGfx), TAG_GIMMICK_TRIGGER_TILE};
static const struct SpritePalette sSpritePalette_MegaTrigger = {sMegaTriggerPal, TAG_GIMMICK_TRIGGER_TILE};
static const struct SpritePalette sSpritePalette_ZMoveTrigger = {sZMoveTriggerPal, TAG_GIMMICK_TRIGGER_PAL};
static const struct SpritePalette sSpritePalette_BurstTrigger = {sBurstTriggerPal, TAG_GIMMICK_TRIGGER_TILE};
static const struct SpritePalette sSpritePalette_DynamaxTrigger = {sDynamaxTriggerPal, TAG_GIMMICK_TRIGGER_PAL};
static const struct SpritePalette sSpritePalette_TeraTrigger = {sTeraTriggerPal, TAG_GIMMICK_TRIGGER_TILE};
static const struct OamData sOamData_GimmickTrigger =
{
.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_GimmickTriggerOff[] =
{
ANIMCMD_FRAME(0, 0),
ANIMCMD_END
};
static const union AnimCmd sSpriteAnim_GimmickTriggerOn[] =
{
ANIMCMD_FRAME(16, 0),
ANIMCMD_END
};
static const union AnimCmd *const sSpriteAnimTable_GimmickTrigger[] =
{
sSpriteAnim_GimmickTriggerOff,
sSpriteAnim_GimmickTriggerOn,
};
static void SpriteCb_GimmickTrigger(struct Sprite *sprite);
static const struct SpriteTemplate sSpriteTemplate_GimmickTrigger =
{
.tileTag = TAG_GIMMICK_TRIGGER_TILE,
.paletteTag = TAG_GIMMICK_TRIGGER_PAL,
.oam = &sOamData_GimmickTrigger,
.anims = sSpriteAnimTable_GimmickTrigger,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_GimmickTrigger,
};
// indicator data
static const u8 ALIGNED(4) sMegaIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/mega_indicator.4bpp");
static const u8 ALIGNED(4) sAlphaIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/alpha_indicator.4bpp");
static const u8 ALIGNED(4) sOmegaIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/omega_indicator.4bpp");
static const u8 ALIGNED(4) sDynamaxIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/dynamax_indicator.4bpp");
static const u8 ALIGNED(4) sNormalIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/normal_indicator.4bpp");
static const u8 ALIGNED(4) sFightingIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/fighting_indicator.4bpp");
static const u8 ALIGNED(4) sFlyingIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/flying_indicator.4bpp");
static const u8 ALIGNED(4) sPoisonIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/poison_indicator.4bpp");
static const u8 ALIGNED(4) sGroundIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/ground_indicator.4bpp");
static const u8 ALIGNED(4) sRockIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/rock_indicator.4bpp");
static const u8 ALIGNED(4) sBugIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/bug_indicator.4bpp");
static const u8 ALIGNED(4) sGhostIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/ghost_indicator.4bpp");
static const u8 ALIGNED(4) sSteelIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/steel_indicator.4bpp");
static const u8 ALIGNED(4) sFireIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/fire_indicator.4bpp");
static const u8 ALIGNED(4) sWaterIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/water_indicator.4bpp");
static const u8 ALIGNED(4) sGrassIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/grass_indicator.4bpp");
static const u8 ALIGNED(4) sElectricIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/electric_indicator.4bpp");
static const u8 ALIGNED(4) sPsychicIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/psychic_indicator.4bpp");
static const u8 ALIGNED(4) sIceIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/ice_indicator.4bpp");
static const u8 ALIGNED(4) sDragonIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/dragon_indicator.4bpp");
static const u8 ALIGNED(4) sDarkIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/dark_indicator.4bpp");
static const u8 ALIGNED(4) sFairyIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/fairy_indicator.4bpp");
static const u8 ALIGNED(4) sStellarIndicatorGfx[] = INCBIN_U8("graphics/battle_interface/stellar_indicator.4bpp");
static const u16 sMiscIndicatorPal[] = INCBIN_U16("graphics/battle_interface/misc_indicator.gbapal"); // has room for more colors
static const u16 sMegaIndicatorPal[] = INCBIN_U16("graphics/battle_interface/mega_indicator.gbapal");
static const u16 sTeraIndicatorPal[] = INCBIN_U16("graphics/battle_interface/tera_indicator.gbapal");
static const struct SpriteSheet sSpriteSheet_MegaIndicator = {sMegaIndicatorGfx, sizeof(sMegaIndicatorGfx), TAG_MEGA_INDICATOR_TILE};
static const struct SpriteSheet sSpriteSheet_AlphaIndicator = {sAlphaIndicatorGfx, sizeof(sAlphaIndicatorGfx), TAG_ALPHA_INDICATOR_TILE};
static const struct SpriteSheet sSpriteSheet_OmegaIndicator = {sOmegaIndicatorGfx, sizeof(sOmegaIndicatorGfx), TAG_OMEGA_INDICATOR_TILE};
static const struct SpriteSheet sSpriteSheet_DynamaxIndicator = {sDynamaxIndicatorGfx, sizeof(sDynamaxIndicatorGfx), TAG_DYNAMAX_INDICATOR_TILE};
static const struct SpriteSheet sTeraIndicatorSpriteSheets[NUMBER_OF_MON_TYPES + 1] =
{
{sNormalIndicatorGfx, sizeof(sNormalIndicatorGfx), TAG_NORMAL_INDICATOR_TILE},
{sFightingIndicatorGfx, sizeof(sFightingIndicatorGfx), TAG_FIGHTING_INDICATOR_TILE},
{sFlyingIndicatorGfx, sizeof(sFlyingIndicatorGfx), TAG_FLYING_INDICATOR_TILE},
{sPoisonIndicatorGfx, sizeof(sPoisonIndicatorGfx), TAG_POISON_INDICATOR_TILE},
{sGroundIndicatorGfx, sizeof(sGroundIndicatorGfx), TAG_GROUND_INDICATOR_TILE},
{sRockIndicatorGfx, sizeof(sRockIndicatorGfx), TAG_ROCK_INDICATOR_TILE},
{sBugIndicatorGfx, sizeof(sBugIndicatorGfx), TAG_BUG_INDICATOR_TILE},
{sGhostIndicatorGfx, sizeof(sGhostIndicatorGfx), TAG_GHOST_INDICATOR_TILE},
{sSteelIndicatorGfx, sizeof(sSteelIndicatorGfx), TAG_STEEL_INDICATOR_TILE},
{sNormalIndicatorGfx, sizeof(sNormalIndicatorGfx), TAG_NORMAL_INDICATOR_TILE}, // TYPE_MYSTERY
{sFireIndicatorGfx, sizeof(sFireIndicatorGfx), TAG_FIRE_INDICATOR_TILE},
{sWaterIndicatorGfx, sizeof(sWaterIndicatorGfx), TAG_WATER_INDICATOR_TILE},
{sGrassIndicatorGfx, sizeof(sGrassIndicatorGfx), TAG_GRASS_INDICATOR_TILE},
{sElectricIndicatorGfx, sizeof(sElectricIndicatorGfx), TAG_ELECTRIC_INDICATOR_TILE},
{sPsychicIndicatorGfx, sizeof(sPsychicIndicatorGfx), TAG_PSYCHIC_INDICATOR_TILE},
{sIceIndicatorGfx, sizeof(sIceIndicatorGfx), TAG_ICE_INDICATOR_TILE},
{sDragonIndicatorGfx, sizeof(sDragonIndicatorGfx), TAG_DRAGON_INDICATOR_TILE},
{sDarkIndicatorGfx, sizeof(sDarkIndicatorGfx), TAG_DARK_INDICATOR_TILE},
{sFairyIndicatorGfx, sizeof(sFairyIndicatorGfx), TAG_FAIRY_INDICATOR_TILE},
{sStellarIndicatorGfx, sizeof(sStellarIndicatorGfx), TAG_STELLAR_INDICATOR_TILE},
{0}
};
static const struct SpritePalette sSpritePalette_MiscIndicator = {sMiscIndicatorPal, TAG_MISC_INDICATOR_PAL};
static const struct SpritePalette sSpritePalette_MegaIndicator = {sMegaIndicatorPal, TAG_MEGA_INDICATOR_PAL};
static const struct SpritePalette sSpritePalette_TeraIndicator = {sTeraIndicatorPal, TAG_TERA_INDICATOR_PAL};
static const struct OamData sOamData_GimmickIndicator =
{
.shape = SPRITE_SHAPE(16x16),
.size = SPRITE_SIZE(16x16),
.priority = 1,
};
static void SpriteCb_GimmickIndicator(struct Sprite *sprite);
static const struct SpriteTemplate sSpriteTemplate_GimmickIndicator =
{
.tileTag = TAG_NORMAL_INDICATOR_TILE, // updated dynamically
.paletteTag = TAG_TERA_INDICATOR_PAL, // updated dynamically
.oam = &sOamData_GimmickIndicator,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCb_GimmickIndicator,
};

View File

@ -15646,7 +15646,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
[MOVE_FLORAL_HEALING] =
{
.name = HANDLE_EXPANDED_MOVE_NAME("FloralHealng", "Floral Healng"),
.name = HANDLE_EXPANDED_MOVE_NAME("FloralHealng", "Floral Healing"),
.description = COMPOUND_STRING(
"Restores an ally's HP.\n"
"Heals more on grass."),
@ -18422,7 +18422,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
[MOVE_JUNGLE_HEALING] =
{
.name = HANDLE_EXPANDED_MOVE_NAME("JungleHealng", "Jungle Healng"),
.name = HANDLE_EXPANDED_MOVE_NAME("JungleHealng", "Jungle Healing"),
.description = COMPOUND_STRING(
"Heals HP and status of\n"
"itself and allies in battle."),
@ -21042,7 +21042,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
},
[MOVE_OCEANIC_OPERETTA] =
{
.name = COMPOUND_STRING("Oceaning Operetta"),
.name = COMPOUND_STRING("Oceanic Operetta"),
.description = sNullDescription,
.effect = EFFECT_HIT,
.power = 195,
@ -21106,9 +21106,9 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
},
[MOVE_GUARDIAN_OF_ALOLA] =
{
.name = COMPOUND_STRING("Guardian Of Alola"),
.name = COMPOUND_STRING("Guardian of Alola"),
.description = sNullDescription,
.effect = EFFECT_SUPER_FANG,
.effect = EFFECT_GUARDIAN_OF_ALOLA,
.power = 1,
.type = TYPE_FAIRY,
.accuracy = 0,
@ -21152,7 +21152,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
{
.name = COMPOUND_STRING("Light That Burns The Sky"),
.description = sNullDescription,
.effect = EFFECT_HIT,
.effect = EFFECT_PHOTON_GEYSER,
.power = 200,
.type = TYPE_PSYCHIC,
.accuracy = 0,

View File

@ -779,6 +779,14 @@ static const struct FormChange sGreninjaBattleBondFormChangeTable[] = {
};
#endif //P_FAMILY_FROAKIE
#if P_FAMILY_FURFROU
static const struct FormChange sFurfrouFormChangeTable[] = {
{FORM_CHANGE_WITHDRAW, SPECIES_FURFROU_NATURAL},
{FORM_CHANGE_DAYS_PASSED, SPECIES_FURFROU_NATURAL, 5},
{FORM_CHANGE_TERMINATOR},
};
#endif //P_FAMILY_FURFROU
#if P_FAMILY_HONEDGE
static const struct FormChange sAegislashFormChangeTable[] = {
{FORM_CHANGE_BATTLE_SWITCH, SPECIES_AEGISLASH_SHIELD},
@ -841,8 +849,9 @@ static const struct FormChange sDiancieFormChangeTable[] = {
#if P_FAMILY_HOOPA
static const struct FormChange sHoopaFormChangeTable[] = {
{FORM_CHANGE_ITEM_USE, SPECIES_HOOPA_UNBOUND, ITEM_PRISON_BOTTLE, SPECIES_HOOPA_CONFINED},
{FORM_CHANGE_WITHDRAW, SPECIES_HOOPA_CONFINED},
{FORM_CHANGE_ITEM_USE, SPECIES_HOOPA_UNBOUND, ITEM_PRISON_BOTTLE, SPECIES_HOOPA_CONFINED},
{FORM_CHANGE_WITHDRAW, SPECIES_HOOPA_CONFINED},
{FORM_CHANGE_DAYS_PASSED, SPECIES_HOOPA_CONFINED, 3},
{FORM_CHANGE_TERMINATOR},
};
#endif //P_FAMILY_HOOPA

View File

@ -76,11 +76,7 @@ const struct SpeciesInfo gSpeciesInfo[] =
.categoryName = _("Unknown"),
.height = 0,
.weight = 0,
.description = COMPOUND_STRING(
"This is a newly discovered Pokémon.\n"
"It is currently under investigation.\n"
"No detailed information is available\n"
"at this time."),
.description = gFallbackPokedexText,
.pokemonScale = 256,
.pokemonOffset = 0,
.trainerScale = 256,

View File

@ -2156,6 +2156,7 @@ const struct SpeciesInfo gSpeciesInfoGen6[] =
.teachableLearnset = sFurfrouTeachableLearnset, \
.eggMoveLearnset = sFurfrouEggMoveLearnset, \
.formSpeciesIdTable = sFurfrouFormSpeciesIdTable, \
.formChangeTable = sFurfrouFormChangeTable, \
}
[SPECIES_FURFROU_NATURAL] = FURFROU_MISC_INFO(Natural, FALSE, 48, 3, 56, 0, 0),

View File

@ -4143,6 +4143,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] =
.baseSpAttack = 60, \
.baseSpDefense = 100, \
.weight = 400, \
.description = gMiniorMeteorPokedexText, \
.frontPic = gMonFrontPic_MiniorMeteor, \
.frontPicSize = MON_COORDS_SIZE(48, 40), \
.frontPicYOffset = 14, \

View File

@ -1,3 +1,10 @@
// fallback
const u8 gFallbackPokedexText[] = _(
"This is a newly discovered Pokémon.\n"
"It is currently under investigation.\n"
"No detailed information is available\n"
"at this time.");
// Gen 1 families
const u8 gRaticateAlolanPokedexText[] = _(
"It forms a group of Rattata, which it \n"

View File

@ -1039,12 +1039,14 @@ static u16 DetermineEggSpeciesAndParentSlots(struct DayCare *daycare, u8 *parent
eggSpecies = SPECIES_ILLUMISE;
else if (eggSpecies == SPECIES_MANAPHY)
eggSpecies = SPECIES_PHIONE;
else if (eggSpecies == SPECIES_SINISTEA_ANTIQUE)
eggSpecies = SPECIES_SINISTEA_PHONY;
else if (GET_BASE_SPECIES_ID(eggSpecies) == SPECIES_ROTOM)
eggSpecies = SPECIES_ROTOM;
else if (GET_BASE_SPECIES_ID(eggSpecies) == SPECIES_FURFROU)
eggSpecies = SPECIES_FURFROU;
else if (eggSpecies == SPECIES_SINISTEA_ANTIQUE)
eggSpecies = SPECIES_SINISTEA_PHONY;
else if (eggSpecies == SPECIES_POLTCHAGEIST_ARTISAN)
eggSpecies = SPECIES_POLTCHAGEIST_COUNTERFEIT;
// To avoid single-stage Totem Pokémon to breed more of themselves.
else if (eggSpecies == SPECIES_MIMIKYU_TOTEM_DISGUISED)
eggSpecies = SPECIES_MIMIKYU_DISGUISED;

View File

@ -20,6 +20,7 @@
#include "palette.h"
#include "party_menu.h"
#include "pokemon.h"
#include "pokemon_storage_system.h"
#include "script.h"
#include "sound.h"
#include "sprite.h"
@ -1015,10 +1016,10 @@ void MultiplyPaletteRGBComponents(u16 i, u8 r, u8 g, u8 b)
bool8 FldEff_PokecenterHeal(void)
{
u8 nPokemon;
u32 nPokemon;
struct Task *task;
nPokemon = CalculatePlayerPartyCount();
nPokemon = (OW_IGNORE_EGGS_ON_HEAL <= GEN_3) ? CalculatePlayerPartyCount() : CountPartyNonEggMons();
task = &gTasks[CreateTask(Task_PokecenterHeal, 0xff)];
task->tNumMons = nPokemon;
task->tFirstBallX = 93;

View File

@ -164,6 +164,18 @@ bool8 HasAtLeastOneBerry(void)
return FALSE;
}
bool8 HasAtLeastOnePokeBall(void)
{
u16 i;
for (i = FIRST_BALL; i <= LAST_BALL; i++)
{
if (CheckBagHasItem(i, 1) == TRUE)
return TRUE;
}
return FALSE;
}
bool8 CheckBagHasSpace(u16 itemId, u16 count)
{
if (ItemId_GetPocket(itemId) == POCKET_NONE)

View File

@ -1,7 +1,7 @@
#include <stdarg.h>
#include <stdio.h>
#include "gba/gba.h"
#include "config.h"
#include "config/general.h"
#include "malloc.h"
#include "mini_printf.h"

View File

@ -20,8 +20,8 @@
#include "constants/layouts.h"
#include "constants/region_map_sections.h"
#include "constants/weather.h"
#include "config/general.h"
#include "config/overworld.h"
#include "config.h"
// enums
enum MapPopUp_Themes

View File

@ -35,7 +35,7 @@
#include "mini_printf.h"
#include "gba/types.h"
#include "gba/defines.h"
#include "config.h"
#include "config/general.h"
#include "characters.h"
#include "string_util.h"

View File

@ -6361,6 +6361,7 @@ static void Task_TryItemUseFormChange(u8 taskId)
case 0:
targetSpecies = gTasks[taskId].tTargetSpecies;
SetMonData(mon, MON_DATA_SPECIES, &targetSpecies);
TrySetDayLimitToFormChange(mon);
CalculateMonStats(mon);
gTasks[taskId].tState++;
break;

View File

@ -1387,7 +1387,7 @@ static const struct SearchOptionText sDexSearchColorOptions[] =
{},
};
static const struct SearchOptionText sDexSearchTypeOptions[NUMBER_OF_MON_TYPES] = // + 2 for "None" and terminator, - 2 for Mystery and Stellar
static const struct SearchOptionText sDexSearchTypeOptions[] =
{
{gText_DexEmptyString, gTypesInfo[TYPE_NONE].name},
{gText_DexEmptyString, gTypesInfo[TYPE_NORMAL].name},

View File

@ -1901,7 +1901,7 @@ static const struct SearchOptionText sDexSearchColorOptions[] =
{},
};
static const struct SearchOptionText sDexSearchTypeOptions[NUMBER_OF_MON_TYPES] = // + 2 for "None" and terminator, - 2 for Mystery and Stellar
static const struct SearchOptionText sDexSearchTypeOptions[] =
{
{gText_DexEmptyString, gTypesInfo[TYPE_NONE].name},
{gText_DexEmptyString, gTypesInfo[TYPE_NORMAL].name},

View File

@ -1148,6 +1148,14 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV,
{
isShiny = DexNavTryMakeShinyMon();
}
else if (P_ONLY_OBTAINABLE_SHINIES && InBattlePyramid())
{
isShiny = FALSE;
}
else if (P_NO_SHINIES_WITHOUT_POKEBALLS && !HasAtLeastOnePokeBall())
{
isShiny = FALSE;
}
else
{
u32 totalRerolls = 0;
@ -2861,6 +2869,9 @@ u32 GetBoxMonData3(struct BoxPokemon *boxMon, s32 field, u8 *data)
retVal = nature ^ boxMon->hiddenNatureModifier;
break;
}
case MON_DATA_DAYS_SINCE_FORM_CHANGE:
retVal = boxMon->daysSinceFormChange;
break;
default:
break;
}
@ -3295,6 +3306,9 @@ void SetBoxMonData(struct BoxPokemon *boxMon, s32 field, const void *dataArg)
boxMon->hiddenNatureModifier = nature ^ hiddenNature;
break;
}
case MON_DATA_DAYS_SINCE_FORM_CHANGE:
SET8(boxMon->daysSinceFormChange);
break;
}
}
@ -3397,6 +3411,9 @@ u8 GetMonsStateToDoubles(void)
s32 i;
CalculatePlayerPartyCount();
if (OW_DOUBLE_APPROACH_WITH_ONE_MON)
return PLAYER_HAS_TWO_USABLE_MONS;
if (gPlayerPartyCount == 1)
return gPlayerPartyCount; // PLAYER_HAS_ONE_MON
@ -6528,6 +6545,7 @@ u16 GetFormChangeTargetSpeciesBoxMon(struct BoxPokemon *boxMon, u16 method, u32
break;
case FORM_CHANGE_WITHDRAW:
case FORM_CHANGE_FAINT:
case FORM_CHANGE_DAYS_PASSED:
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_STATUS:
@ -6555,6 +6573,22 @@ u16 GetFormChangeTargetSpeciesBoxMon(struct BoxPokemon *boxMon, u16 method, u32
return targetSpecies;
}
void TrySetDayLimitToFormChange(struct Pokemon *mon)
{
u32 i;
u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
const struct FormChange *formChanges = GetSpeciesFormChanges(species);
for (i = 0; formChanges[i].method != FORM_CHANGE_TERMINATOR; i++)
{
if (formChanges[i].method == FORM_CHANGE_DAYS_PASSED && species != formChanges[i].targetSpecies)
{
SetMonData(mon, MON_DATA_DAYS_SINCE_FORM_CHANGE, &formChanges[i].param1);
break;
}
}
}
bool32 DoesSpeciesHaveFormChangeMethod(u16 species, u16 method)
{
u32 i;
@ -6871,3 +6905,38 @@ const u8 *GetMoveAnimationScript(u16 moveId)
}
return gMovesInfo[moveId].battleAnimScript;
}
void UpdateDaysPassedSinceFormChange(u16 days)
{
u32 i;
for (i = 0; i < PARTY_SIZE; i++)
{
struct Pokemon *mon = &gPlayerParty[i];
u8 daysSinceFormChange;
if (!GetMonData(mon, MON_DATA_SPECIES, 0))
continue;
daysSinceFormChange = GetMonData(mon, MON_DATA_DAYS_SINCE_FORM_CHANGE, 0);
if (daysSinceFormChange == 0)
continue;
if (daysSinceFormChange > days)
daysSinceFormChange -= days;
else
daysSinceFormChange = 0;
SetMonData(mon, MON_DATA_DAYS_SINCE_FORM_CHANGE, &daysSinceFormChange);
if (daysSinceFormChange == 0)
{
u16 targetSpecies = GetFormChangeTargetSpecies(mon, FORM_CHANGE_DAYS_PASSED, 0);
if (targetSpecies != SPECIES_NONE)
{
SetMonData(mon, MON_DATA_SPECIES, &targetSpecies);
CalculateMonStats(mon);
}
}
}
}

View File

@ -1436,9 +1436,9 @@ s16 GetFirstFreeBoxSpot(u8 boxId)
return -1; // all spots are taken
}
u8 CountPartyNonEggMons(void)
u32 CountPartyNonEggMons(void)
{
u16 i, count;
u32 i, count;
for (i = 0, count = 0; i < PARTY_SIZE; i++)
{
@ -6434,23 +6434,10 @@ static void SetPlacedMonData(u8 boxId, u8 position)
static void PurgeMonOrBoxMon(u8 boxId, u8 position)
{
u16 item = ITEM_NONE;
if (boxId == TOTAL_BOXES_COUNT)
{
if (OW_PC_RELEASE_ITEM >= GEN_8)
item = GetMonData(&gPlayerParty[position], MON_DATA_HELD_ITEM);
ZeroMonData(&gPlayerParty[position]);
}
else
{
if (OW_PC_RELEASE_ITEM >= GEN_8)
item = GetBoxMonDataAt(boxId, position, MON_DATA_HELD_ITEM);
ZeroBoxMonAt(boxId, position);
}
if (item != ITEM_NONE)
AddBagItem(item, 1);
}
static void SetShiftedMonData(u8 boxId, u8 position)
@ -6530,6 +6517,7 @@ static bool8 TryHideReleaseMon(void)
static void ReleaseMon(void)
{
u8 boxId;
u16 item = ITEM_NONE;
DestroyReleaseMonIcon();
if (sIsMonBeingMoved)
@ -6539,11 +6527,21 @@ static void ReleaseMon(void)
else
{
if (sCursorArea == CURSOR_AREA_IN_PARTY)
{
boxId = TOTAL_BOXES_COUNT;
if (OW_PC_RELEASE_ITEM >= GEN_8)
item = GetMonData(&gPlayerParty[sCursorPosition], MON_DATA_HELD_ITEM);
}
else
{
boxId = StorageGetCurrentBox();
if (OW_PC_RELEASE_ITEM >= GEN_8)
item = GetBoxMonDataAt(boxId, sCursorPosition, MON_DATA_HELD_ITEM);
}
PurgeMonOrBoxMon(boxId, sCursorPosition);
if (item != ITEM_NONE)
AddBagItem(item, 1);
}
TryRefreshDisplayMon();
}

View File

@ -105,7 +105,8 @@ static void CreateInitialRoamerMon(u8 index, u16 species, u8 level)
ROAMER(index)->personality = GetMonData(&gEnemyParty[0], MON_DATA_PERSONALITY);
ROAMER(index)->species = species;
ROAMER(index)->level = level;
ROAMER(index)->status = 0;
ROAMER(index)->statusA = 0;
ROAMER(index)->statusB = 0;
ROAMER(index)->hp = GetMonData(&gEnemyParty[0], MON_DATA_MAX_HP);
ROAMER(index)->cool = GetMonData(&gEnemyParty[0], MON_DATA_COOL);
ROAMER(index)->beauty = GetMonData(&gEnemyParty[0], MON_DATA_BEAUTY);
@ -238,18 +239,13 @@ bool8 IsRoamerAt(u32 roamerIndex, u8 mapGroup, u8 mapNum)
void CreateRoamerMonInstance(u32 roamerIndex)
{
u32 status;
u32 status = ROAMER(roamerIndex)->statusA + (ROAMER(roamerIndex)->statusB << 8);
struct Pokemon *mon = &gEnemyParty[0];
ZeroEnemyPartyMons();
CreateMonWithIVsPersonality(mon, ROAMER(roamerIndex)->species, ROAMER(roamerIndex)->level, ROAMER(roamerIndex)->ivs, ROAMER(roamerIndex)->personality);
// The roamer's status field is u16, but SetMonData expects status to be u32, so will set the roamer's status
// using the status field and the following 3 bytes (cool, beauty, and cute).
#ifdef BUGFIX
status = ROAMER(roamerIndex)->status;
SetMonData(mon, MON_DATA_STATUS, &status);
#else
SetMonData(mon, MON_DATA_STATUS, &ROAMER->status);
#endif
SetMonData(mon, MON_DATA_HP, &ROAMER(roamerIndex)->hp);
SetMonData(mon, MON_DATA_COOL, &ROAMER(roamerIndex)->cool);
SetMonData(mon, MON_DATA_BEAUTY, &ROAMER(roamerIndex)->beauty);
@ -276,8 +272,11 @@ bool8 TryStartRoamerEncounter(void)
void UpdateRoamerHPStatus(struct Pokemon *mon)
{
u32 status = GetMonData(mon, MON_DATA_STATUS);
ROAMER(gEncounteredRoamerIndex)->hp = GetMonData(mon, MON_DATA_HP);
ROAMER(gEncounteredRoamerIndex)->status = GetMonData(mon, MON_DATA_STATUS);
ROAMER(gEncounteredRoamerIndex)->statusA = status;
ROAMER(gEncounteredRoamerIndex)->statusB = status >> 8;
RoamerMoveToOtherLocationSet(gEncounteredRoamerIndex);
}

View File

@ -4,7 +4,7 @@
#include "gba/gba.h"
#include "siirtc.h"
#include "config.h"
#include "config/general.h"
#define STATUS_INTFE 0x02 // frequency interrupt enable
#define STATUS_INTME 0x08 // per-minute interrupt enable

Some files were not shown because too many files have changed in this diff Show More