merge w upcoming
11
.github/workflows/docs.yml
vendored
@ -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'
|
||||
|
||||
@ -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!)_
|
||||
|
||||
|
||||
2
Makefile
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
|
||||
if [[ -d "$DEVKITARM/bin/" ]]; then
|
||||
OBJDUMP_BIN="$DEVKITARM/bin/arm-none-eabi-objdump"
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#include "config.h"
|
||||
#include "config/general.h"
|
||||
#include "constants/global.h"
|
||||
#include "constants/contest.h"
|
||||
.include "asm/macros.inc"
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#include "config.h"
|
||||
#include "config/general.h"
|
||||
#include "config/battle.h"
|
||||
#include "config/item.h"
|
||||
#include "constants/global.h"
|
||||
|
||||
@ -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::
|
||||
|
||||
|
Before Width: | Height: | Size: 314 B After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 275 B After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 451 B After Width: | Height: | Size: 388 B |
@ -15,5 +15,5 @@ JASC-PAL
|
||||
248 136 136
|
||||
88 104 96
|
||||
184 192 192
|
||||
0 0 0
|
||||
248 248 248
|
||||
0 0 0
|
||||
|
||||
@ -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
|
||||
|
||||
|
Before Width: | Height: | Size: 708 B After Width: | Height: | Size: 620 B |
|
Before Width: | Height: | Size: 666 B After Width: | Height: | Size: 571 B |
@ -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
|
||||
|
||||
|
Before Width: | Height: | Size: 837 B After Width: | Height: | Size: 816 B |
@ -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
|
||||
|
||||
|
Before Width: | Height: | Size: 898 B After Width: | Height: | Size: 820 B |
@ -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,
|
||||
|
||||
@ -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 \
|
||||
{ \
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
};
|
||||
|
||||
|
||||
@ -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
@ -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
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -352,6 +352,7 @@ enum {
|
||||
EFFECT_TERA_BLAST,
|
||||
EFFECT_TERA_STARSTORM,
|
||||
EFFECT_DRAGON_DARTS,
|
||||
EFFECT_GUARDIAN_OF_ALOLA,
|
||||
NUM_BATTLE_MOVE_EFFECTS,
|
||||
};
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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; \
|
||||
|
||||
@ -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__) })
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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];
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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
@ -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
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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];
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -54,6 +54,7 @@ static void UpdatePerDay(struct Time *localTime)
|
||||
UpdateFrontierGambler(daysSince);
|
||||
SetShoalItemFlag(daysSince);
|
||||
SetRandomLotteryNumber(daysSince);
|
||||
UpdateDaysPassedSinceFormChange(daysSince);
|
||||
*days = localTime->days;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,
|
||||
},
|
||||
|
||||
|
||||
@ -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
@ -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,
|
||||
}
|
||||
};
|
||||
152
src/data/graphics/gimmicks.h
Normal 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,
|
||||
};
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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, \
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
12
src/item.c
@ -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)
|
||||
|
||||
@ -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"
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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},
|
||||
|
||||
@ -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},
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
15
src/roamer.c
@ -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);
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||