updated battle engine

This commit is contained in:
cawtds
2024-09-28 21:22:27 +02:00
parent 89e57d753b
commit bca079bcbd
28 changed files with 1149 additions and 988 deletions

View File

@@ -507,9 +507,9 @@
.byte \battler
.endm
.macro trainerslidein battler:req
.macro trainerslidein position:req
.byte 0x53
.byte \battler
.byte \position
.endm
.macro playse song:req
@@ -1674,14 +1674,62 @@
callnative BS_FickleBeamDamageCalculation
.endm
@ various command changed to more readable macros
.macro cancelmultiturnmoves battler:req
various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES
.macro trytarshot failInstr:req
callnative BS_TryTarShot
.4byte \failInstr
.endm
.macro jumpifteainvulnerable battler:req, jumpInstr:req
callnative BS_TeatimeInvul
.byte \battler
.4byte \jumpInstr
.endm
.macro jumpifteanoberry failInstr:req
callnative BS_TeatimeTargets
.4byte \failInstr
.endm
.macro trywindriderpower battler:req, failInstr:req
callnative BS_TryWindRiderPower
.byte \battler
.4byte \failInstr
.endm
.macro activateweatherchangeabilities battler:req
callnative BS_ActivateWeatherChangeAbilities
.byte \battler
.endm
.macro activateterrainchangeabilities battler:req
callnative BS_ActivateTerrainChangeAbilities
.byte \battler
.endm
@ Stores Healing Wish effect.
.macro storehealingwish battler:req
various \battler, VARIOUS_STORE_HEALING_WISH
callnative BS_StoreHealingWish
.byte \battler
.endm
.macro hitswitchtargetfailed
callnative BS_HitSwitchTargetFailed
.endm
.macro tryrevivalblessing, failInstr:req
callnative BS_TryRevivalBlessing
.4byte \failInstr
.endm
.macro jumpifblockedbysoundproof battler:req, failInstr:req
callnative BS_JumpIfBlockedBySoundproof
.byte \battler
.4byte \failInstr
.endm
@ various command changed to more readable macros
.macro cancelmultiturnmoves battler:req
various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES
.endm
.macro setmagiccoattarget battler:req
@@ -2210,11 +2258,6 @@
.4byte \failInstr
.endm
.macro trytarshot battler:req, failInstr:req
various \battler, VARIOUS_TRY_TAR_SHOT
.4byte \failInstr
.endm
.macro cantarshotwork battler:req, failInstr:req
various \battler, VARIOUS_CAN_TAR_SHOT_WORK
.4byte \failInstr
@@ -2230,16 +2273,6 @@
.4byte \failInstr
.endm
.macro jumpifteanoberry jumpInstr:req
various BS_ATTACKER, VARIOUS_TEATIME_TARGETS
.4byte \jumpInstr
.endm
.macro jumpifteainvulnerable battler:req, jumpInstr:req
various \battler, VARIOUS_TEATIME_INVUL
.4byte \jumpInstr
.endm
.macro curecertainstatuses battler:req
various \battler, VARIOUS_CURE_CERTAIN_STATUSES
.endm
@@ -2284,19 +2317,6 @@
.byte \stat
.endm
.macro trywindriderpower battler:req, failInstr:req
various \battler, VARIOUS_TRY_WIND_RIDER_POWER
.4byte \failInstr
.endm
.macro activateweatherchangeabilities battler:req
various \battler, VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES
.endm
.macro activateterrainchangeabilities battler:req
various \battler, VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES
.endm
@ various remaining from pokefirered
.macro getbattlersforrecall
various BS_ATTACKER, VARIOUS_GET_BATTLERS_FOR_RECALL
@@ -2504,15 +2524,6 @@
waitmessage B_WAIT_TIME_LONG
.endm
.macro hitswitchtargetfailed
various 0, VARIOUS_HIT_SWITCH_TARGET_FAILED
.endm
.macro tryrevivalblessing, jumpInstr:req
various 0, VARIOUS_TRY_REVIVAL_BLESSING
.4byte \jumpInstr
.endm
@ Will jump to script pointer if the specified battler has or has not fainted.
.macro jumpiffainted battler:req, value:req, ptr:req
getbattlerfainted \battler

View File

@@ -587,7 +587,7 @@ BattleScript_EffectAttackUpUserAlly_TryAlly:
BattleScript_EffectAttackUpUserAlly_End:
goto BattleScript_MoveEnd
BattleScript_EffectAttackUpUserAlly_TryAlly_:
jumpifability BS_ATTACKER_PARTNER, ABILITY_SOUNDPROOF, BattleScript_EffectAttackUpUserAlly_TryAllyBlocked
jumpifblockedbysoundproof BS_ATTACKER_PARTNER, BattleScript_EffectAttackUpUserAlly_TryAllyBlocked
setstatchanger STAT_ATK, 1, FALSE
statbuffchange STAT_CHANGE_ALLOW_PTR, BattleScript_EffectAttackUpUserAlly_End
jumpifbyte CMP_NOT_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_INCREASE, BattleScript_EffectAttackUpUserAlly_AllyAnim
@@ -981,7 +981,7 @@ BattleScript_EffectTarShot::
printfromtable gStatDownStringIds
waitmessage B_WAIT_TIME_LONG
BattleScript_TryTarShot:
trytarshot BS_TARGET, BattleScript_MoveEnd
trytarshot BattleScript_MoveEnd
printstring STRINGID_PKMNBECAMEWEAKERTOFIRE
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
@@ -1836,6 +1836,8 @@ BattleScript_EffectInstruct::
tryinstruct BattleScript_ButItFailed
attackanimation
waitanimation
copybyte gBattlerAttacker, gBattlerTarget
copybyte gBattlerTarget, gEffectBattler
printstring STRINGID_USEDINSTRUCTEDMOVE
waitmessage B_WAIT_TIME_LONG
setbyte sB_ANIM_TURN, 0
@@ -1915,7 +1917,7 @@ BattleScript_HitSwitchTargetForceRandomSwitchFailed:
goto BattleScript_MoveEnd
BattleScript_EffectToxicThread::
setstatchanger STAT_SPEED, 2, TRUE
setstatchanger STAT_SPEED, 1, TRUE
attackcanceler
jumpifsubstituteblocks BattleScript_FailedFromAtkString
jumpifstat BS_TARGET, CMP_NOT_EQUAL, STAT_SPEED, MIN_STAT_STAGE, BattleScript_ToxicThreadWorks
@@ -3356,51 +3358,17 @@ BattleScript_RoarBlockedByDynamax:
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_MultiHitLoop::
jumpifhasnohp BS_ATTACKER, BattleScript_MultiHitEnd
jumpifhasnohp BS_TARGET, BattleScript_MultiHitPrintStrings
jumpifhalfword CMP_EQUAL, gChosenMove, MOVE_SLEEP_TALK, BattleScript_DoMultiHit
jumpifstatus BS_ATTACKER, STATUS1_SLEEP, BattleScript_MultiHitPrintStrings
BattleScript_DoMultiHit::
movevaluescleanup
copyhword sMOVE_EFFECT, sMULTIHIT_EFFECT
critcalc
damagecalc
jumpifmovehadnoeffect BattleScript_MultiHitNoMoreHits
adjustdamage
attackanimation
waitanimation
effectivenesssound
hitanimation BS_TARGET
waitstate
healthbarupdate BS_TARGET
datahpupdate BS_TARGET
critmessage
waitmessage B_WAIT_TIME_LONG
multihitresultmessage
flushtextbox
addbyte sMULTIHIT_STRING + 4, 1
moveendto MOVEEND_NEXT_TARGET
jumpifbyte CMP_COMMON_BITS, gMoveResultFlags, MOVE_RESULT_FOE_ENDURED, BattleScript_MultiHitPrintStrings
decrementmultihit BattleScript_MultiHitLoop
goto BattleScript_MultiHitPrintStrings
BattleScript_MultiHitNoMoreHits::
pause B_WAIT_TIME_SHORT
BattleScript_MultiHitPrintStrings::
resultmessage
waitmessage B_WAIT_TIME_LONG
jumpifmovehadnoeffect BattleScript_MultiHitEnd
copyarray gBattleTextBuff1, sMULTIHIT_STRING, 6
printstring STRINGID_HITXTIMES
waitmessage B_WAIT_TIME_LONG
return
BattleScript_MultiHitEnd::
setadditionaleffects
tryfaintmon BS_TARGET
moveendcase MOVEEND_SYNCHRONIZE_TARGET
moveendfrom MOVEEND_STATUS_IMMUNITY_ABILITIES
end
BattleScript_ScaleShot::
call BattleScript_MultiHitPrintStrings
goto BattleScript_DefDownSpeedUp
BattleScript_EffectConversion::
attackcanceler
@@ -4358,7 +4326,7 @@ BattleScript_EffectPerishSong::
waitmessage B_WAIT_TIME_LONG
setbyte gBattlerTarget, 0
BattleScript_PerishSongLoop::
jumpifability BS_TARGET, ABILITY_SOUNDPROOF, BattleScript_PerishSongBlocked
jumpifblockedbysoundproof BS_TARGET, BattleScript_PerishSongBlocked
jumpifpranksterblocked BS_TARGET, BattleScript_PerishSongNotAffected
BattleScript_PerishSongLoopIncrement::
addbyte gBattlerTarget, 1
@@ -4801,6 +4769,9 @@ BattleScript_ButItFailed::
resultmessage
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_RestoreAttackerButItFailed:
restoreattacker
goto BattleScript_ButItFailed
BattleScript_NotAffected::
pause B_WAIT_TIME_SHORT
@@ -5680,13 +5651,13 @@ BattleScript_LocalTrainerBattleWon::
BattleScript_LocalTwoTrainersDefeated::
printstring STRINGID_TWOENEMIESDEFEATED
BattleScript_LocalBattleWonLoseTexts::
trainerslidein BS_ATTACKER
trainerslidein BS_OPPONENT1
waitstate
printstring STRINGID_TRAINER1LOSETEXT
jumpifnotbattletype BATTLE_TYPE_TWO_OPPONENTS, BattleScript_LocalBattleWonReward
trainerslideout B_POSITION_OPPONENT_LEFT
trainerslideout BS_OPPONENT1
waitstate
trainerslidein BS_FAINTED
trainerslidein BS_OPPONENT2
waitstate
printstring STRINGID_TRAINER2LOSETEXT
BattleScript_LocalBattleWonReward::
@@ -6444,12 +6415,14 @@ BattleScript_SeedSowerActivates::
return
BattleScript_AngerShellActivates::
saveattacker
copybyte gBattlerAttacker, gBattlerTarget
call BattleScript_AbilityPopUp
jumpifstat BS_TARGET, CMP_LESS_THAN, STAT_ATK, MAX_STAT_STAGE, BattleScript_AngerShellTryDef
jumpifstat BS_TARGET, CMP_LESS_THAN, STAT_SPATK, MAX_STAT_STAGE, BattleScript_AngerShellTryDef
jumpifstat BS_TARGET, CMP_LESS_THAN, STAT_SPEED, MAX_STAT_STAGE, BattleScript_AngerShellTryDef
jumpifstat BS_TARGET, CMP_GREATER_THAN, STAT_DEF, MIN_STAT_STAGE, BattleScript_AngerShellTryDef
jumpifstat BS_TARGET, CMP_EQUAL, STAT_SPDEF, MIN_STAT_STAGE, BattleScript_ButItFailed
jumpifstat BS_TARGET, CMP_EQUAL, STAT_SPDEF, MIN_STAT_STAGE, BattleScript_RestoreAttackerButItFailed
BattleScript_AngerShellTryDef::
setbyte sSTAT_ANIM_PLAYED, FALSE
modifybattlerstatstage BS_ATTACKER, STAT_DEF, DECREASE, 1, BattleScript_AngerShellTrySpDef, ANIM_ON
@@ -6463,6 +6436,7 @@ BattleScript_AngerShellTrySpAtk:
BattleScript_AngerShellTrySpeed:
modifybattlerstatstage BS_ATTACKER, STAT_SPEED, INCREASE, 1, BattleScript_AngerShellRet, ANIM_ON
BattleScript_AngerShellRet:
restoreattacker
return
BattleScript_WindPowerActivates::
@@ -6979,12 +6953,12 @@ BattleScript_WishMegaEvolution::
BattleScript_PrimalReversion::
call BattleScript_PrimalReversionRet
end2
end3
BattleScript_PrimalReversionRestoreAttacker::
call BattleScript_PrimalReversionRet
copybyte gBattlerAttacker, sSAVED_BATTLER
end2
end3
BattleScript_PrimalReversionRet::
flushtextbox
@@ -7693,15 +7667,11 @@ BattleScript_EmergencyExitWildNoPopUp::
BattleScript_TraceActivates::
pause B_WAIT_TIME_SHORT
call BattleScript_AbilityPopUp
call BattleScript_AbilityPopUpScripting
printstring STRINGID_PKMNTRACED
waitmessage B_WAIT_TIME_LONG
settracedability BS_SCRIPTING
switchinabilities BS_SCRIPTING
return
BattleScript_TraceActivatesEnd3::
call BattleScript_TraceActivates
end3
BattleScript_ReceiverActivates::
@@ -9285,6 +9255,16 @@ BattleScript_ZMoveActivateStatus::
copybyte sSTATCHANGER, sSAVED_STAT_CHANGER
return
BattleScript_ZMoveActivatePowder::
flushtextbox
trytrainerslidezmovemsg
savetarget
printstring STRINGID_ZPOWERSURROUNDS
playanimation BS_ATTACKER, B_ANIM_ZMOVE_ACTIVATE, NULL
setzeffect
restoretarget
goto BattleScript_MoveUsedPowder
BattleScript_ZEffectPrintString::
printfromtable gZEffectStringIds
waitmessage B_WAIT_TIME_LONG
@@ -9916,7 +9896,7 @@ BattleScript_BerserkGeneRet_End:
BattleScript_BoosterEnergyEnd2::
playanimation BS_SCRIPTING, B_ANIM_HELD_ITEM_EFFECT, sB_ANIM_ARG1
call BattleScript_AbilityPopUpTarget
call BattleScript_AbilityPopUpScripting
printstring STRINGID_BOOSTERENERGYACTIVATES
waitmessage B_WAIT_TIME_MED
printstring STRINGID_STATWASHEIGHTENED

View File

@@ -705,6 +705,7 @@ struct BattleStruct
u8 blunderPolicy:1; // should blunder policy activate
u8 swapDamageCategory:1; // Photon Geyser, Shell Side Arm, Light That Burns the Sky
u8 bouncedMoveIsUsed:1;
u8 snatchedMoveIsUsed: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
@@ -755,6 +756,8 @@ struct BattleStruct
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
u8 fickleBeamBoosted:1;
u8 obedienceResult:3;
u8 usedMicleBerry;
// pokefirered
u8 field_DA; // battle tower related
@@ -1098,6 +1101,12 @@ static inline u32 GetBattlerSide(u32 battler)
return GetBattlerPosition(battler) & BIT_SIDE;
}
static inline struct Pokemon* GetPartyBattlerData(u32 battler)
{
u32 index = gBattlerPartyIndexes[battler];
return (GetBattlerSide(battler) == B_SIDE_OPPONENT) ? &gEnemyParty[index] : &gPlayerParty[index];
}
static inline struct Pokemon *GetSideParty(u32 side)
{
return (side == B_SIDE_PLAYER) ? gPlayerParty : gEnemyParty;
@@ -1108,5 +1117,10 @@ static inline struct Pokemon *GetBattlerParty(u32 battler)
return GetSideParty(GetBattlerSide(battler));
}
static inline bool32 IsDoubleBattle(void)
{
return gBattleTypeFlags & BATTLE_TYPE_DOUBLE;
}
#endif // GUARD_BATTLE_H

View File

@@ -72,7 +72,6 @@ bool32 CanKnockOffItem(u32 battler, u32 item);
bool32 IsAbilityOfRating(u32 ability, s8 rating);
bool32 AI_IsAbilityOnSide(u32 battlerId, u32 ability);
bool32 AI_MoveMakesContact(u32 ability, u32 holdEffect, u32 move);
u32 AI_GetBattlerMoveTargetType(u32 battlerId, u32 move);
bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, u32 chosenMove);
// stat stage checks
@@ -115,6 +114,7 @@ bool32 HasMoveEffectANDArg(u32 battlerId, u32 effect, u32 argument);
bool32 HasMoveWithAdditionalEffect(u32 battlerId, u32 moveEffect);
bool32 HasMoveWithCriticalHitChance(u32 battlerId);
bool32 HasMoveWithMoveEffectExcept(u32 battlerId, u32 moveEffect, u32 exception);
bool32 HasMoveThatLowersOwnStats(u32 battlerId);
bool32 HasMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef, u32 accCheck, bool32 ignoreStatus, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect);
bool32 HasAnyKnownMove(u32 battlerId);
bool32 IsAromaVeilProtectedMove(u32 move);
@@ -139,6 +139,7 @@ bool32 ShouldFakeOut(u32 battlerAtk, u32 battlerDef, u32 move);
bool32 HasThawingMove(u32 battler);
bool32 IsStatRaisingEffect(u32 effect);
bool32 IsStatLoweringEffect(u32 effect);
bool32 IsSelfStatLoweringEffect(u32 effect);
bool32 IsAttackBoostMoveEffect(u32 effect);
bool32 IsUngroundingEffect(u32 effect);
bool32 IsSemiInvulnerable(u32 battlerDef, u32 move);
@@ -205,5 +206,6 @@ bool32 AI_ShouldCopyStatChanges(u32 battlerAtk, u32 battlerDef);
bool32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData);
void IncreaseTidyUpScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
bool32 AI_ShouldSpicyExtract(u32 battlerAtk, u32 battlerAtkPartner, u32 move, struct AiLogicData *aiData);
void IncreaseSubstituteMoveScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
#endif //GUARD_BATTLE_AI_UTIL_H

View File

@@ -527,7 +527,6 @@ void InitSpritePosToAnimTarget(struct Sprite *sprite, bool8 respectMonPicOffsets
void InitSpritePosToAnimAttacker(struct Sprite *sprite, bool8 respectMonPicOffsets);
u8 GetBattlerAtPosition(u8 position);
bool8 IsBattlerSpritePresent(u8 battlerId);
bool8 IsDoubleBattle(void);
void GetBattleAnimBg1Data(struct BattleAnimBgData *animBgData);
void GetBattleAnimBgData(struct BattleAnimBgData *animBgData, u32 bgId);
void GetBattleAnimBgDataByPriorityRank(struct BattleAnimBgData *animBgData, u8 unused);

View File

@@ -403,6 +403,7 @@ extern const u8 BattleScript_SyrupBombActivates[];
extern const u8 BattleScript_AromaVeilProtectsRet[];
extern const u8 BattleScript_StatUpMsg[];
extern const u8 BattleScript_SymbiosisActivates[];
extern const u8 BattleScript_ScaleShot[];
extern const u8 BattleScript_OverworldStatusStarts[];
extern const u8 BattleScript_OverworldTerrain[];
extern const u8 BattleScript_ImposterActivates[];
@@ -615,7 +616,6 @@ extern const u8 BattleScript_Recoil33End[];
extern const u8 BattleScript_ItemSteal[];
extern const u8 BattleScript_DrizzleActivates[];
extern const u8 BattleScript_SpeedBoostActivates[];
extern const u8 BattleScript_TraceActivatesEnd3[];
extern const u8 BattleScript_RainDishActivates[];
extern const u8 BattleScript_SandstreamActivates[];
extern const u8 BattleScript_ShedSkinActivates[];
@@ -772,6 +772,7 @@ extern const u8 BattleScript_ProteanActivates[];
extern const u8 BattleScript_DazzlingProtected[];
extern const u8 BattleScript_MoveUsedPsychicTerrainPrevents[];
extern const u8 BattleScript_MoveUsedPowder[];
extern const u8 BattleScript_ZMoveActivatePowder[];
extern const u8 BattleScript_SelectingNotAllowedStuffCheeks[];
extern const u8 BattleScript_SelectingNotAllowedStuffCheeksInPalace[];
extern const u8 BattleScript_SelectingNotAllowedBelch[];

View File

@@ -107,6 +107,15 @@ enum
CANCELLER_END2,
};
enum {
OBEYS,
DISOBEYS_LOAFS,
DISOBEYS_HITS_SELF,
DISOBEYS_FALL_ASLEEP,
DISOBEYS_WHILE_ASLEEP,
DISOBEYS_RANDOM_MOVE,
};
extern const struct TypePower gNaturalGiftTable[];
void HandleAction_ThrowBall(void);
@@ -172,7 +181,7 @@ void ClearVariousBattlerFlags(u32 battler);
void HandleAction_RunBattleScript(void);
u32 SetRandomTarget(u32 battler);
u32 GetMoveTarget(u16 move, u8 setTarget);
u8 IsMonDisobedient(void);
u8 GetAttackerObedienceForAction();
u32 GetBattlerHoldEffect(u32 battler, bool32 checkNegating);
u32 GetBattlerHoldEffectIgnoreAbility(u32 battler, bool32 checkNegating);
u32 GetBattlerHoldEffectInternal(u32 battler, bool32 checkNegating, bool32 checkAbility);
@@ -248,7 +257,7 @@ bool32 MoveHasAdditionalEffectSelf(u32 move, u32 moveEffect);
bool32 MoveHasAdditionalEffectSelfArg(u32 move, u32 moveEffect, u32 argument);
bool32 MoveHasChargeTurnAdditionalEffect(u32 move);
bool32 CanTargetPartner(u32 battlerAtk, u32 battlerDef);
bool32 TargetFullyImmuneToCurrMove(u32 BattlerAtk, u32 battlerDef);
bool32 TargetFullyImmuneToCurrMove(u32 battlerAtk, u32 battlerDef);
bool32 CanBeSlept(u32 battler, u32 ability);
bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, u32 defAbility);

View File

@@ -1,5 +1,5 @@
#ifndef GUARD_LEVEL_CAP_H
#define GUARD_LEVEL_CAP_H
#ifndef GUARD_CAPS_H
#define GUARD_CAPS_H
#if B_EXP_CAP_TYPE != EXP_CAP_NONE && B_EXP_CAP_TYPE != EXP_CAP_HARD && B_EXP_CAP_TYPE != EXP_CAP_SOFT
#error "Invalid choice for B_EXP_CAP_TYPE, must be of [EXP_CAP_NONE, EXP_CAP_HARD, EXP_CAP_SOFT]"
@@ -14,7 +14,12 @@
#endif
#endif
#if B_EV_CAP_TYPE != EV_CAP_NONE && B_EV_CAP_TYPE != EV_CAP_FLAG_LIST && B_EV_CAP_TYPE != EV_CAP_VARIABLE && B_EV_CAP_TYPE != EV_CAP_NO_GAIN
#error "Invalid choice for B_EV_CAP_TYPE, must be one of [EV_CAP_NONE, EV_CAP_FLAG_LIST, EV_CAP_VARIABLE, EV_CAP_NO_GAIN]"
#endif
u32 GetCurrentLevelCap(void);
u32 GetSoftLevelCapExpValue(u32 level, u32 expValue);
u32 GetCurrentEVCap(void);
#endif /* GUARD_LEVEL_CAP_H */
#endif /* GUARD_CAPS_H */

View File

@@ -39,7 +39,6 @@
#define B_MULTIPLE_TARGETS_DMG GEN_LATEST // In Gen4+, damage dealt by moves that hit multiple targets at once is reduced to 75%. Before, it was 50%.
// Type settings
#define B_EXPANDED_TYPE_NAMES TRUE // If TRUE, type names are increased from 6 characters to 8 characters.
#define B_GHOSTS_ESCAPE GEN_LATEST // In Gen6+, abilities like Shadow Tag or moves like Mean Look fail on Ghost-type Pokémon. They can also escape any Wild Battle.
#define B_PARALYZE_ELECTRIC GEN_LATEST // In Gen6+, Electric-type Pokémon can't be paralyzed.
#define B_POWDER_GRASS GEN_LATEST // In Gen6+, Grass-type Pokémon are immune to powder and spore moves.
@@ -121,9 +120,12 @@
#define B_SKETCH_BANS GEN_LATEST // In Gen9+, Sketch is unable to copy more moves than in previous generations.
#define B_KNOCK_OFF_REMOVAL GEN_LATEST // In Gen5+, Knock Off removes the foe's item instead of rendering it unusable.
#define B_HEAL_BELL_SOUNDPROOF GEN_LATEST // In Gen5, Heal Bell affects all mons with Soundproof. In Gen6-8 it affects inactive mons, but not battlers. In Gen9 it always affects the user.
#define B_CHARGE GEN_LATEST // In Gen8-, Charge status is lost regardless of the typing of the next move.
#define B_POWDER_RAIN GEN_LATEST // In Gen7+, Powder doesn't damage the user of a Fire type move in heavy rain.
#define B_AFTER_YOU_TURN_ORDER GEN_LATEST // In Gen8+, After You doesn't fail if the turn order wouldn't change after use.
#define B_QUASH_TURN_ORDER GEN_LATEST // In Gen8+, Quash-affected battlers move according to speed order. Before Gen8, Quash-affected battlers move in the order they were affected by Quash.
// Ability settings
#define B_EXPANDED_ABILITY_NAMES TRUE // If TRUE, ability names are increased from 12 characters to 16 characters.
#define B_ABILITY_WEATHER GEN_LATEST // In Gen6+, ability-induced weather lasts 5 turns. Before, it lasted until the battle ended or until it was changed by a move or a different weather-affecting ability.
#define B_GALE_WINGS GEN_LATEST // In Gen7+ requires full HP to trigger.
#define B_STANCE_CHANGE_FAIL GEN_LATEST // In Gen7+, Stance Change fails if the Pokémon is unable to use a move because of confusion, paralysis, etc. In Gen6, it doesn't.
@@ -163,7 +165,7 @@
#define B_RESTORE_HELD_BATTLE_ITEMS GEN_LATEST // In Gen9, all non-berry items are restored after battle.
#define B_SOUL_DEW_BOOST GEN_LATEST // In Gens3-6, Soul Dew boosts Latis' Sp. Atk and Sp. Def. In Gen7+ it boosts the power of their Psychic and Dragon type moves instead.
#define B_NET_BALL_MODIFIER GEN_LATEST // In Gen7+, Net Ball's catch multiplier is x5 instead of x3.
#define B_DIVE_BALL_MODIFIER GEN_LATEST // In Gen4+, Dive Ball's effectiveness increases by when Surfing or Fishing.
#define B_DIVE_BALL_MODIFIER GEN_LATEST // In Gen4+, Dive Ball's effectiveness increases by x3.5 when Surfing or Fishing.
#define B_NEST_BALL_MODIFIER GEN_LATEST // Nest Ball's formula varies depending on the Gen. See Cmd_handleballthrow.
#define B_REPEAT_BALL_MODIFIER GEN_LATEST // In Gen7+, Repeat Ball's catch multiplier is x3.5 instead of x3.
#define B_TIMER_BALL_MODIFIER GEN_LATEST // In Gen5+, Timer Ball's effectiveness increases by x0.3 per turn instead of x0.1
@@ -204,6 +206,9 @@
// Flag and Var settings
#define B_RESET_FLAGS_VARS_AFTER_WHITEOUT TRUE // If TRUE, Overworld_ResetBattleFlagsAndVars will reset battle-related Flags and Vars when the player whites out.
// Ingame partner flag
#define B_SHOW_PARTNER_TARGET FALSE // Shows the battler partner will target.
// Terrain settings
#define B_TERRAIN_BG_CHANGE TRUE // If set to TRUE, terrain moves permanently change the default battle background until the effect fades.
#define B_THUNDERSTORM_TERRAIN TRUE // If TRUE, overworld Thunderstorm generates Rain and Electric Terrain as in Gen 8.
@@ -221,7 +226,6 @@
#define B_SHOW_TARGETS TRUE // If set to TRUE, all available targets, for moves hitting 2 or 3 Pokémon, will be shown before selecting a move.
#define B_SHOW_CATEGORY_ICON TRUE // If set to TRUE, it will show an icon in the summary and move relearner showing the move's category.
#define B_HIDE_HEALTHBOX_IN_ANIMS TRUE // If set to TRUE, hides healthboxes during move animations.
#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
@@ -278,4 +282,7 @@
#define SHOW_TYPES_CAUGHT 2
#define B_SHOW_TYPES SHOW_TYPES_NEVER // When defined as SHOW_TYPES_ALWAYS, after selecting "Fight" in battle, the types of all Pokemon are revealed. Whe defined as SHOW_TYPES_OWN, types are only revealed if the player owns the mon in question.
// Pokémon battle sprite settings
#define B_ENEMY_MON_SHADOW_STYLE GEN_LATEST // In Gen4+, all enemy Pokemon will have a shadow drawn beneath them.
#endif // GUARD_CONFIG_BATTLE_H

View File

@@ -1,7 +1,7 @@
#ifndef GUARD_CONFIG_LEVEL_CAP_H
#define GUARD_CONFIG_LEVEL_CAP_H
#ifndef GUARD_CONFIG_CAPS_H
#define GUARD_CONFIG_CAPS_H
// Constants
// Level Cap Constants
#define EXP_CAP_NONE 0 // Regular behavior, no level caps are applied
#define EXP_CAP_HARD 1 // Pokémon with a level >= the level cap cannot gain any experience
#define EXP_CAP_SOFT 2 // Pokémon with a level >= the level cap will gain reduced experience
@@ -10,7 +10,7 @@
#define LEVEL_CAP_FLAG_LIST 1 // Level cap is chosen according to the first unset flag in `sLevelCapFlagMap`
#define LEVEL_CAP_VARIABLE 2 // Level cap is chosen according to the contents of the event variable specified by B_LEVEL_CAP_VARIABLE
// Configs
// Level Cap Configs
#define B_EXP_CAP_TYPE EXP_CAP_NONE // [EXP_CAP_NONE, EXP_CAP_HARD, EXP_CAP_SOFT] choose the type of level cap to apply
#define B_LEVEL_CAP_TYPE LEVEL_CAP_NONE // [LEVEL_CAP_NONE, LEVEL_CAP_FLAG_LIST, LEVEL_CAP_VARIABLE] choose the method to derive the level cap
#define B_LEVEL_CAP_VARIABLE 0 // event variable used to derive level cap if B_LEVEL_CAP_TYPE is set to LEVEL_CAP_VARIABLE
@@ -18,4 +18,16 @@
#define B_RARE_CANDY_CAP FALSE // If set to true, Rare Candies can't be used to go over the level cap
#define B_LEVEL_CAP_EXP_UP FALSE // If set to true, mons under level cap will receive more experience
#endif /* GUARD_CONFIG_LEVEL_CAP_H */
// EV Cap Constants
#define EV_CAP_NONE 0 // Regular behavior, no EV caps are applied
#define EV_CAP_FLAG_LIST 1 // EV cap is chosen according to the first unset flag in `sEVCapFlagMap`
#define EV_CAP_VARIABLE 2 // EV cap is chosen according to the contents of the event variable specified by B_EV_CAP_VARIABLE
#define EV_CAP_NO_GAIN 3 // No EVs can be gained
// EV Cap Configs
#define B_EV_CAP_TYPE EV_CAP_NONE // [EV_CAP_NONE, EV_CAP_FLAG_LIST, EV_CAP_VARIABLE, EV_CAP_NO_GAIN] choose the type of EV cap to apply#define B_EV_CAP_VARIABLE 12 // event variable used to derive EV cap if B_EV_CAP_TYPE is set to EV_CAP_VARIABLE
#define B_EV_CAP_VARIABLE 8 // event variable used to derive EV cap if B_EV_CAP_TYPE is set to EV_CAP_VARIABLE
#define B_EV_ITEMS_CAP FALSE // If set to true, EV-boosting items can't be used to go over the EV cap
#endif /* GUARD_CONFIG_CAPS_H */

View File

@@ -1,14 +1,8 @@
#ifndef GUARD_CONFIG_TEST_H
#define GUARD_CONFIG_TEST_H
#undef B_EXPANDED_MOVE_NAMES
#define B_EXPANDED_MOVE_NAMES TRUE
#undef I_EXPANDED_ITEM_NAMES
#define I_EXPANDED_ITEM_NAMES TRUE
#undef POKEMON_NAME_LENGTH
#define POKEMON_NAME_LENGTH 12
#undef B_EXPANDED_TYPE_NAMES
#define B_EXPANDED_TYPE_NAMES TRUE
#undef P_MEGA_EVOLUTIONS
#define P_MEGA_EVOLUTIONS TRUE

View File

@@ -25,31 +25,36 @@
#define AI_EFFECTIVENESS_x0 0
// AI Flags. Most run specific functions to update score, new flags are used for internal logic in other scripts
#define AI_FLAG_CHECK_BAD_MOVE (1 << 0)
#define AI_FLAG_TRY_TO_FAINT (1 << 1)
#define AI_FLAG_CHECK_VIABILITY (1 << 2)
#define AI_FLAG_SETUP_FIRST_TURN (1 << 3)
#define AI_FLAG_RISKY (1 << 4)
#define AI_FLAG_PREFER_STRONGEST_MOVE (1 << 5)
#define AI_FLAG_PREFER_BATON_PASS (1 << 6)
#define AI_FLAG_DOUBLE_BATTLE (1 << 7) // Automatically set for double battles, handles AI behaviour with partner
#define AI_FLAG_HP_AWARE (1 << 8)
#define AI_FLAG_POWERFUL_STATUS (1 << 9) // AI prefers moves that set up field effects or side statuses, even if the user can faint the target
// See docs/ai_flags.md for more details.
#define AI_FLAG_CHECK_BAD_MOVE (1 << 0) // AI will avoid using moves that are likely to fail or be ineffective in the current situation.
#define AI_FLAG_TRY_TO_FAINT (1 << 1) // AI will prioritize KOing the player's mon if able.
#define AI_FLAG_CHECK_VIABILITY (1 << 2) // AI damaging moves and move effects to determine the best available move in the current situation.
#define AI_FLAG_FORCE_SETUP_FIRST_TURN (1 << 3) // AI will prioritize using setup moves on the first turn at the expensve of all else. AI_FLAG_CHECK_VIABILITY will instead do this when the AI determines it makes sense.
#define AI_FLAG_RISKY (1 << 4) // AI will generally behave more recklessly, prioritizing damage over accuracy, explosions, etc.
#define AI_FLAG_PREFER_STRONGEST_MOVE (1 << 5) // AI adds score bonus to any move the AI has that either OHKOs or 2HKOs the player.
#define AI_FLAG_PREFER_BATON_PASS (1 << 6) // AI prefers raising its own stats and setting for / using Baton Pass.
#define AI_FLAG_DOUBLE_BATTLE (1 << 7) // Automatically set for double battles, handles AI behaviour with partner.
#define AI_FLAG_HP_AWARE (1 << 8) // AI will favour certain move effects based on how much remaining HP it and the player's mon have.
#define AI_FLAG_POWERFUL_STATUS (1 << 9) // AI prefers moves that set up field effects or side statuses, even if the user can faint the target.
// New, Trainer Handicap Flags
#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
#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_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_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_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_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
// The following options are enough to have a basic/smart trainer. Any other addtion could make the trainer worse/better depending on the flag
#define AI_FLAG_BASIC_TRAINER (AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY)
#define AI_FLAG_SMART_TRAINER (AI_FLAG_BASIC_TRAINER | AI_FLAG_OMNISCIENT | AI_FLAG_SMART_SWITCHING | AI_FLAG_SMART_MON_CHOICES)
// 'other' ai logic flags
#define AI_FLAG_DYNAMIC_FUNC (1 << 28) // Create custom AI functions for specific battles via "setdynamicaifunc" cmd
#define AI_FLAG_ROAMING (1 << 29)

View File

@@ -3,9 +3,9 @@
#include "config/general.h"
#include "config/battle.h"
#include "config/caps.h"
#include "config/debug.h"
#include "config/item.h"
#include "config/level_caps.h"
#include "config/pokemon.h"
#include "config/overworld.h"
@@ -70,7 +70,7 @@
#define CONTEST_CATEGORIES_COUNT 5
// string lengths
#define ITEM_NAME_LENGTH ((I_EXPANDED_ITEM_NAMES == TRUE) ? 20 : 14)
#define ITEM_NAME_LENGTH 20
#define ITEM_NAME_PLURAL_LENGTH ITEM_NAME_LENGTH + 2 // 2 is used for the instance where a word's suffix becomes y->ies
#define POKEMON_NAME_LENGTH 12
#define VANILLA_POKEMON_NAME_LENGTH 10
@@ -78,14 +78,14 @@
#define PLAYER_NAME_LENGTH 7
#define MAIL_WORDS_COUNT 9
#define EASY_CHAT_BATTLE_WORDS_COUNT 6
#define MOVE_NAME_LENGTH ((B_EXPANDED_MOVE_NAMES == TRUE) ? 16 : 12)
#define MOVE_NAME_LENGTH 16
#define NUM_QUESTIONNAIRE_WORDS 4
#define WONDER_CARD_TEXT_LENGTH 40
#define WONDER_NEWS_TEXT_LENGTH 40
#define WONDER_CARD_BODY_TEXT_LINES 4
#define WONDER_NEWS_BODY_TEXT_LINES 10
#define TYPE_NAME_LENGTH ((B_EXPANDED_TYPE_NAMES == TRUE) ? 8 : 6)
#define ABILITY_NAME_LENGTH ((B_EXPANDED_ABILITY_NAMES == TRUE) ? 16 : 12)
#define TYPE_NAME_LENGTH 8
#define ABILITY_NAME_LENGTH 16
#define TRAINER_NAME_LENGTH 10
#define MAX_STAMP_CARD_STAMPS 7

View File

@@ -29,8 +29,7 @@
#define HOLD_EFFECT_EXP_SHARE 25
#define HOLD_EFFECT_QUICK_CLAW 26
#define HOLD_EFFECT_FRIENDSHIP_UP 27
#define HOLD_EFFECT_CURE_ATTRACT 28
#define HOLD_EFFECT_MENTAL_HERB HOLD_EFFECT_CURE_ATTRACT
#define HOLD_EFFECT_MENTAL_HERB 28
#define HOLD_EFFECT_CHOICE_BAND 29
#define HOLD_EFFECT_FLINCH 30
#define HOLD_EFFECT_BUG_POWER 31
@@ -63,14 +62,12 @@
#define HOLD_EFFECT_FIRE_POWER 58
#define HOLD_EFFECT_DRAGON_POWER 59
#define HOLD_EFFECT_NORMAL_POWER 60
#define HOLD_EFFECT_UP_GRADE 61
#define HOLD_EFFECT_UPGRADE HOLD_EFFECT_UP_GRADE
#define HOLD_EFFECT_UPGRADE 61
#define HOLD_EFFECT_SHELL_BELL 62
#define HOLD_EFFECT_LUCKY_PUNCH 63
#define HOLD_EFFECT_METAL_POWDER 64
#define HOLD_EFFECT_THICK_CLUB 65
#define HOLD_EFFECT_STICK 66
#define HOLD_EFFECT_LEEK HOLD_EFFECT_STICK
#define HOLD_EFFECT_LEEK 66
// Gen4 hold effects.
#define HOLD_EFFECT_CHOICE_SCARF 67
@@ -158,12 +155,12 @@
// Gen9 hold effects
#define HOLD_EFFECT_ABILITY_SHIELD 175
#define HOLD_EFFECT_CLEAR_AMULET 176
#define HOLD_EFFECT_MIRROR_HERB 177 // Not implemented.
#define HOLD_EFFECT_MIRROR_HERB 177
#define HOLD_EFFECT_PUNCHING_GLOVE 178
#define HOLD_EFFECT_COVERT_CLOAK 179
#define HOLD_EFFECT_LOADED_DICE 180
#define HOLD_EFFECT_BOOSTER_ENERGY 181 // Not implemented.
#define HOLD_EFFECT_MASK 183
#define HOLD_EFFECT_BOOSTER_ENERGY 181
#define HOLD_EFFECT_OGERPON_MASK 182
// Gen2 hold effect
#define HOLD_EFFECT_BERSERK_GENE 184

View File

@@ -162,7 +162,8 @@ enum RandomTag
RNG_TRACE,
RNG_FICKLE_BEAM,
RNG_AI_ABILITY,
RNG_AI_HASBADODDS,
RNG_AI_SWITCH_HASBADODDS,
RNG_AI_SWITCH_WONDER_GUARD,
RNG_SHELL_SIDE_ARM,
};

View File

@@ -46,7 +46,7 @@ EWRAM_DATA AiScoreFunc sDynamicAiFunc = NULL;
static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
static s32 AI_TryToFaint(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
static s32 AI_SetupFirstTurn(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
static s32 AI_ForceSetupFirstTurn(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
static s32 AI_Risky(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
static s32 AI_PreferStrongestMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
static s32 AI_PreferBatonPass(u32 battlerAtk, u32 battlerDef, u32 move, s32 score);
@@ -64,7 +64,7 @@ static s32 (*const sBattleAiFuncTable[])(u32, u32, u32, s32) =
[0] = AI_CheckBadMove, // AI_FLAG_CHECK_BAD_MOVE
[1] = AI_TryToFaint, // AI_FLAG_TRY_TO_FAINT
[2] = AI_CheckViability, // AI_FLAG_CHECK_VIABILITY
[3] = AI_SetupFirstTurn, // AI_FLAG_SETUP_FIRST_TURN
[3] = AI_ForceSetupFirstTurn, // AI_FLAG_FORCE_SETUP_FIRST_TURN
[4] = AI_Risky, // AI_FLAG_RISKY
[5] = AI_PreferStrongestMove, // AI_FLAG_PREFER_STRONGEST_MOVE
[6] = AI_PreferBatonPass, // AI_FLAG_PREFER_BATON_PASS
@@ -196,14 +196,12 @@ void BattleAI_SetupFlags(void)
else
AI_THINKING_STRUCT->aiFlags[B_POSITION_PLAYER_LEFT] = 0; // player has no AI
#if DEBUG_OVERWORLD_MENU == TRUE
if (gIsDebugBattle)
if (DEBUG_OVERWORLD_MENU && gIsDebugBattle)
{
AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_LEFT] = gDebugAIFlags;
AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_RIGHT] = gDebugAIFlags;
return;
}
#endif
if (IsWildMonSmart() && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_TRAINER)))
{
@@ -811,7 +809,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
s8 atkPriority = GetMovePriority(battlerAtk, move);
u32 moveEffect = gMovesInfo[move].effect;
s32 moveType;
u32 moveTarget = AI_GetBattlerMoveTargetType(battlerAtk, move);
u32 moveTarget = GetBattlerMoveTargetType(battlerAtk, move);
struct AiLogicData *aiData = AI_DATA;
u32 effectiveness = aiData->effectiveness[battlerAtk][battlerDef][AI_THINKING_STRUCT->movesetIndex];
bool32 isDoubleBattle = IsValidDoubleBattle(battlerAtk);
@@ -854,6 +852,8 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_COUNTER:
case EFFECT_MIRROR_COAT:
case EFFECT_METAL_BURST:
case EFFECT_FINAL_GAMBIT:
case EFFECT_GUARDIAN_OF_ALOLA:
break;
default:
RETURN_SCORE_MINUS(10);
@@ -886,7 +886,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
break;
case ABILITY_WONDER_GUARD:
if (effectiveness < AI_EFFECTIVENESS_x2)
return 0;
RETURN_SCORE_MINUS(20);
break;
case ABILITY_JUSTIFIED:
if (moveType == TYPE_DARK && !IS_MOVE_STATUS(move))
@@ -1495,7 +1495,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
break;
case EFFECT_OHKO:
if (B_SHEER_COLD_IMMUNITY >= GEN_7 && move == MOVE_SHEER_COLD && IS_BATTLER_OF_TYPE(battlerDef, TYPE_ICE))
return 0;
RETURN_SCORE_MINUS(20);
if (!ShouldTryOHKO(battlerAtk, battlerDef, aiData->abilities[battlerAtk], aiData->abilities[battlerDef], move))
ADJUST_SCORE(-10);
else if (GetActiveGimmick(battlerDef) == GIMMICK_DYNAMAX)
@@ -1528,6 +1528,16 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
else if (HasSubstituteIgnoringMove(battlerDef))
ADJUST_SCORE(-8);
break;
case EFFECT_SHED_TAIL:
if (CountUsablePartyMons(battlerAtk) == 0)
ADJUST_SCORE(-10);
if (gBattleMons[battlerAtk].status2 & STATUS2_SUBSTITUTE || aiData->abilities[battlerDef] == ABILITY_INFILTRATOR)
ADJUST_SCORE(-8);
else if (aiData->hpPercents[battlerAtk] <= 50)
ADJUST_SCORE(-10);
else if (HasSubstituteIgnoringMove(battlerDef))
ADJUST_SCORE(-8);
break;
case EFFECT_LEECH_SEED:
if (gStatuses3[battlerDef] & STATUS3_LEECHSEED
|| IS_BATTLER_OF_TYPE(battlerDef, TYPE_GRASS)
@@ -1815,6 +1825,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
break;
case EFFECT_TRICK:
case EFFECT_KNOCK_OFF:
case EFFECT_CORROSIVE_GAS:
if (aiData->abilities[battlerDef] == ABILITY_STICKY_HOLD)
ADJUST_SCORE(-10);
break;
@@ -2163,6 +2174,17 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
else if (IsAbilityOfRating(aiData->abilities[battlerAtk], 5))
ADJUST_SCORE(-4);
break;
case EFFECT_DOODLE: // Same as Role Play, but also check if the partner's ability should be replaced
if (aiData->abilities[battlerAtk] == aiData->abilities[battlerDef]
|| aiData->abilities[BATTLE_PARTNER(battlerAtk)] == aiData->abilities[battlerDef]
|| aiData->abilities[battlerDef] == ABILITY_NONE
|| gAbilitiesInfo[aiData->abilities[battlerAtk]].cantBeSuppressed
|| gAbilitiesInfo[aiData->abilities[BATTLE_PARTNER(battlerAtk)]].cantBeSuppressed
|| gAbilitiesInfo[aiData->abilities[battlerDef]].cantBeCopied)
ADJUST_SCORE(-10);
else if (IsAbilityOfRating(aiData->abilities[battlerAtk], 5) || IsAbilityOfRating(aiData->abilities[BATTLE_PARTNER(battlerAtk)], 5))
ADJUST_SCORE(-4);
break;
case EFFECT_WISH:
if (gWishFutureKnock.wishCounter[battlerAtk] != 0)
ADJUST_SCORE(-10);
@@ -2483,7 +2505,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (IS_TARGETING_PARTNER(battlerAtk, battlerDef))
{
if (gStatuses3[battlerDef] & STATUS3_HEAL_BLOCK)
return 0;
return 0; // cannot even select
if (AtMaxHp(battlerDef))
ADJUST_SCORE(-10);
else if (gBattleMons[battlerDef].hp > gBattleMons[battlerDef].maxHP / 2)
@@ -2544,7 +2566,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
}
else
{
if (AI_GetBattlerMoveTargetType(battlerDef, instructedMove) & (MOVE_TARGET_SELECTED
if (GetBattlerMoveTargetType(battlerDef, instructedMove) & (MOVE_TARGET_SELECTED
| MOVE_TARGET_DEPENDS
| MOVE_TARGET_RANDOM
| MOVE_TARGET_BOTH
@@ -2742,7 +2764,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
// move data
u32 moveType = gMovesInfo[move].type;
u32 effect = gMovesInfo[move].effect;
u32 moveTarget = AI_GetBattlerMoveTargetType(battlerAtk, move);
u32 moveTarget = GetBattlerMoveTargetType(battlerAtk, move);
// ally data
u32 battlerAtkPartner = BATTLE_PARTNER(battlerAtk);
struct AiLogicData *aiData = AI_DATA;
@@ -2875,6 +2897,15 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
{
switch (atkPartnerAbility)
{
case ABILITY_ANGER_POINT:
if (gMovesInfo[move].alwaysCriticalHit == TRUE
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK)
&& AI_IsFaster(battlerAtk, battlerAtkPartner, move)
&& !CanIndexMoveFaintTarget(battlerAtk, battlerAtkPartner, AI_THINKING_STRUCT->movesetIndex, 1))
{
RETURN_SCORE_PLUS(GOOD_EFFECT);
}
break;
case ABILITY_VOLT_ABSORB:
if (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_HP_AWARE))
{
@@ -3015,15 +3046,26 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_SKILL_SWAP:
if (aiData->abilities[battlerAtk] != aiData->abilities[BATTLE_PARTNER(battlerAtk)] && !attackerHasBadAbility)
{
// Partner abilities
if (aiData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_TRUANT)
{
RETURN_SCORE_PLUS(10);
ADJUST_SCORE(10);
}
else if (aiData->abilities[battlerAtk] == ABILITY_COMPOUND_EYES
&& HasMoveWithLowAccuracy(battlerAtkPartner, FOE(battlerAtkPartner), 90, TRUE, atkPartnerAbility, aiData->abilities[FOE(battlerAtkPartner)], atkPartnerHoldEffect, aiData->holdEffects[FOE(battlerAtkPartner)]))
else if (aiData->abilities[BATTLE_PARTNER(battlerAtk)] == ABILITY_INTIMIDATE)
{
RETURN_SCORE_PLUS(3);
ADJUST_SCORE(DECENT_EFFECT);
}
// Active mon abilities
if (aiData->abilities[battlerAtk] == ABILITY_COMPOUND_EYES
&& HasMoveWithLowAccuracy(battlerAtkPartner, FOE(battlerAtkPartner), 90, TRUE, atkPartnerAbility, aiData->abilities[FOE(battlerAtkPartner)], atkPartnerHoldEffect, aiData->holdEffects[FOE(battlerAtkPartner)]))
{
ADJUST_SCORE(GOOD_EFFECT);
}
else if (aiData->abilities[battlerAtk] == ABILITY_CONTRARY && HasMoveThatLowersOwnStats(battlerAtkPartner))
{
ADJUST_SCORE(GOOD_EFFECT);
}
return score;
}
break;
case EFFECT_ROLE_PLAY:
@@ -3064,7 +3106,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (instructedMove != MOVE_NONE
&& !IS_MOVE_STATUS(instructedMove)
&& (AI_GetBattlerMoveTargetType(battlerAtkPartner, instructedMove) & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY))) // Use instruct on multi-target moves
&& (GetBattlerMoveTargetType(battlerAtkPartner, instructedMove) & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY))) // Use instruct on multi-target moves
{
RETURN_SCORE_PLUS(WEAK_EFFECT);
}
@@ -3476,6 +3518,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
break;
case EFFECT_MULTI_HIT:
case EFFECT_TRIPLE_KICK:
case EFFECT_POPULATION_BOMB:
if (AI_MoveMakesContact(aiData->abilities[battlerAtk], aiData->holdEffects[battlerAtk], move)
&& aiData->abilities[battlerAtk] != ABILITY_MAGIC_GUARD
&& aiData->holdEffects[battlerDef] == HOLD_EFFECT_ROCKY_HELMET)
@@ -3579,28 +3622,8 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
IncreaseParalyzeScore(battlerAtk, battlerDef, move, &score);
break;
case EFFECT_SUBSTITUTE:
if (HasAnyKnownMove(battlerDef) && GetBestDmgFromBattler(battlerDef, battlerAtk) < gBattleMons[battlerAtk].maxHP / 4)
ADJUST_SCORE(GOOD_EFFECT);
if (gStatuses3[battlerDef] & STATUS3_PERISH_SONG)
ADJUST_SCORE(GOOD_EFFECT);
if (gBattleMons[battlerDef].status1 & STATUS1_SLEEP)
ADJUST_SCORE(GOOD_EFFECT);
else if (gBattleMons[battlerDef].status1 & (STATUS1_BURN | STATUS1_PSN_ANY | STATUS1_FROSTBITE))
ADJUST_SCORE(DECENT_EFFECT);
// TODO:
// if (IsPredictedToSwitch(battlerDef, battlerAtk)
// ADJUST_SCORE(DECENT_EFFECT);
if (HasMoveEffect(battlerDef, EFFECT_SLEEP)
|| HasMoveEffect(battlerDef, EFFECT_TOXIC)
|| HasMoveEffect(battlerDef, EFFECT_POISON)
|| HasMoveEffect(battlerDef, EFFECT_PARALYZE)
|| HasMoveEffect(battlerDef, EFFECT_WILL_O_WISP)
|| HasMoveEffect(battlerDef, EFFECT_CONFUSE)
|| HasMoveEffect(battlerDef, EFFECT_LEECH_SEED))
ADJUST_SCORE(GOOD_EFFECT);
if (!gBattleMons[battlerDef].status2 & (STATUS2_WRAPPED | STATUS2_ESCAPE_PREVENTION && aiData->hpPercents[battlerAtk] > 70))
ADJUST_SCORE(WEAK_EFFECT);
break;
case EFFECT_SHED_TAIL:
IncreaseSubstituteMoveScore(battlerAtk, battlerDef, move, &score);
case EFFECT_MIMIC:
if (AI_IsFaster(battlerAtk, battlerDef, move))
{
@@ -3728,24 +3751,24 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
break;
case MOVE_WIDE_GUARD:
if (predictedMove != MOVE_NONE && AI_GetBattlerMoveTargetType(battlerDef, predictedMove) & (MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_BOTH))
if (predictedMove != MOVE_NONE && GetBattlerMoveTargetType(battlerDef, predictedMove) & (MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_BOTH))
{
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
}
else if (isDoubleBattle && AI_GetBattlerMoveTargetType(BATTLE_PARTNER(battlerAtk), aiData->partnerMove) & MOVE_TARGET_FOES_AND_ALLY)
else if (isDoubleBattle && GetBattlerMoveTargetType(BATTLE_PARTNER(battlerAtk), aiData->partnerMove) & MOVE_TARGET_FOES_AND_ALLY)
{
if (aiData->abilities[battlerAtk] != ABILITY_TELEPATHY)
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
}
break;
case MOVE_CRAFTY_SHIELD:
if (predictedMove != MOVE_NONE && IS_MOVE_STATUS(predictedMove) && !(AI_GetBattlerMoveTargetType(battlerDef, predictedMove) & MOVE_TARGET_USER))
if (predictedMove != MOVE_NONE && IS_MOVE_STATUS(predictedMove) && !(GetBattlerMoveTargetType(battlerDef, predictedMove) & MOVE_TARGET_USER))
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
break;
case MOVE_MAT_BLOCK:
if (gDisableStructs[battlerAtk].isFirstTurn && predictedMove != MOVE_NONE
&& !IS_MOVE_STATUS(predictedMove) && !(AI_GetBattlerMoveTargetType(battlerDef, predictedMove) & MOVE_TARGET_USER))
&& !IS_MOVE_STATUS(predictedMove) && !(GetBattlerMoveTargetType(battlerDef, predictedMove) & MOVE_TARGET_USER))
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
break;
case MOVE_KINGS_SHIELD:
@@ -4064,7 +4087,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(DECENT_EFFECT); // Force 'em out next turn
break;
default:
if (gMovesInfo[gCurrentMove].effect != EFFECT_BESTOW && aiData->items[battlerAtk] == ITEM_NONE)
if (gMovesInfo[move].effect != EFFECT_BESTOW && aiData->items[battlerAtk] == ITEM_NONE && aiData->items[battlerDef] != ITEM_NONE)
{
switch (aiData->holdEffects[battlerDef])
{
@@ -4096,11 +4119,27 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
}
}
break;
case EFFECT_CORROSIVE_GAS:
if (CanKnockOffItem(battlerDef, aiData->items[battlerDef]))
{
switch (aiData->holdEffects[battlerDef])
{
case HOLD_EFFECT_IRON_BALL:
if (HasMoveEffect(battlerDef, EFFECT_FLING))
ADJUST_SCORE(DECENT_EFFECT);
break;
case HOLD_EFFECT_LAGGING_TAIL:
case HOLD_EFFECT_STICKY_BARB:
break;
default:
ADJUST_SCORE(DECENT_EFFECT);
break;
}
}
break;
case EFFECT_ROLE_PLAY:
if (!gAbilitiesInfo[aiData->abilities[battlerAtk]].cantBeSuppressed
&& !gAbilitiesInfo[aiData->abilities[battlerDef]].cantBeCopied
&& !IsAbilityOfRating(aiData->abilities[battlerAtk], 5)
&& IsAbilityOfRating(aiData->abilities[battlerDef], 5))
case EFFECT_DOODLE:
if (IsAbilityOfRating(aiData->abilities[battlerDef], 5))
ADJUST_SCORE(DECENT_EFFECT);
break;
case EFFECT_INGRAIN:
@@ -4109,7 +4148,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(GOOD_EFFECT);
break;
case EFFECT_MAGIC_COAT:
if (IS_MOVE_STATUS(predictedMove) && AI_GetBattlerMoveTargetType(battlerDef, predictedMove) & (MOVE_TARGET_SELECTED | MOVE_TARGET_OPPONENTS_FIELD | MOVE_TARGET_BOTH))
if (IS_MOVE_STATUS(predictedMove) && GetBattlerMoveTargetType(battlerDef, predictedMove) & (MOVE_TARGET_SELECTED | MOVE_TARGET_OPPONENTS_FIELD | MOVE_TARGET_BOTH))
ADJUST_SCORE(GOOD_EFFECT);
break;
case EFFECT_RECYCLE:
@@ -4788,7 +4827,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
}
// Effects that are encouraged on the first turn of battle
static s32 AI_SetupFirstTurn(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
static s32 AI_ForceSetupFirstTurn(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
{
u8 i;
if (IS_TARGETING_PARTNER(battlerAtk, battlerDef)

View File

@@ -71,11 +71,11 @@ void GetAIPartyIndexes(u32 battler, s32 *firstId, s32 *lastId)
static bool32 HasBadOdds(u32 battler, bool32 emitResult)
{
//Variable initialization
u8 opposingPosition, atkType1, atkType2, defType1, defType2, effectiveness;
u8 opposingPosition, atkType1, atkType2, defType1, defType2, effectiveness;
s32 i, damageDealt = 0, maxDamageDealt = 0, damageTaken = 0, maxDamageTaken = 0;
u32 aiMove, playerMove, aiBestMove = MOVE_NONE, aiAbility = AI_DATA->abilities[battler], opposingBattler, weather = AI_GetWeather(AI_DATA);
bool32 getsOneShot = FALSE, hasStatusMove = FALSE, hasSuperEffectiveMove = FALSE;
u16 typeEffectiveness = UQ_4_12(1.0), aiMoveEffect; //baseline typing damage
u16 typeEffectiveness = UQ_4_12(1.0), aiMoveEffect; //baseline typing damage
// Only use this if AI_FLAG_SMART_SWITCHING is set for the trainer
if (!(AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SMART_SWITCHING))
@@ -85,14 +85,14 @@ static bool32 HasBadOdds(u32 battler, bool32 emitResult)
if (IsDoubleBattle())
return FALSE;
opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(battler));
opposingPosition = BATTLE_OPPOSITE(GetBattlerPosition(battler));
opposingBattler = GetBattlerAtPosition(opposingPosition);
// Gets types of player (opposingBattler) and computer (battler)
atkType1 = gBattleMons[opposingBattler].types[0];
atkType2 = gBattleMons[opposingBattler].types[1];
defType1 = gBattleMons[battler].types[0];
defType2 = gBattleMons[battler].types[1];
atkType1 = gBattleMons[opposingBattler].types[0];
atkType2 = gBattleMons[opposingBattler].types[1];
defType1 = gBattleMons[battler].types[0];
defType2 = gBattleMons[battler].types[1];
// Check AI moves for damage dealt
for (i = 0; i < MAX_MON_MOVES; i++)
@@ -180,7 +180,7 @@ static bool32 HasBadOdds(u32 battler, bool32 emitResult)
&& gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 4)))
{
// 50% chance to stay in regardless
if (!RandomPercentage(RNG_AI_HASBADODDS, 50))
if (!RandomPercentage(RNG_AI_SWITCH_HASBADODDS, 50))
return FALSE;
// Switch mon out
@@ -191,29 +191,29 @@ static bool32 HasBadOdds(u32 battler, bool32 emitResult)
}
// General bad type matchups have more wiggle room
if (typeEffectiveness >= UQ_4_12(2.0)) // If the player has at least a 2x type advantage
{
if (!hasSuperEffectiveMove // If the AI doesn't have a super effective move
&& (gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 2 // And the current mon has at least 1/2 their HP, or 1/4 HP and Regenerator
if (typeEffectiveness >= UQ_4_12(2.0)) // If the player has at least a 2x type advantage
{
if (!hasSuperEffectiveMove // If the AI doesn't have a super effective move
&& (gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 2 // And the current mon has at least 1/2 their HP, or 1/4 HP and Regenerator
|| (aiAbility == ABILITY_REGENERATOR
&& gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 4)))
{
{
// Then check if they have an important status move, which is worth using even in a bad matchup
if (hasStatusMove)
return FALSE;
// 50% chance to stay in regardless
if (!RandomPercentage(RNG_AI_HASBADODDS, 50))
if (!RandomPercentage(RNG_AI_SWITCH_HASBADODDS, 50))
return FALSE;
// Switch mon out
gBattleStruct->AI_monToSwitchIntoId[battler] = PARTY_SIZE;
if (emitResult)
gBattleStruct->AI_monToSwitchIntoId[battler] = PARTY_SIZE;
if (emitResult)
BtlController_EmitTwoReturnValues(battler, 1, B_ACTION_SWITCH, 0);
return TRUE;
}
}
return FALSE;
return TRUE;
}
}
return FALSE;
}
static bool32 ShouldSwitchIfAllBadMoves(u32 battler, bool32 emitResult)
@@ -284,7 +284,7 @@ static bool32 ShouldSwitchIfWonderGuard(u32 battler, bool32 emitResult)
move = GetMonData(&party[i], MON_DATA_MOVE1 + j);
if (move != MOVE_NONE)
{
if (AI_GetTypeEffectiveness(move, battler, opposingBattler) >= UQ_4_12(2.0) && Random() % 3 < 2)
if (AI_GetTypeEffectiveness(move, battler, opposingBattler) >= UQ_4_12(2.0) && (RandomPercentage(RNG_AI_SWITCH_WONDER_GUARD, 66) || ((AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SMART_SWITCHING))))
{
// We found a mon.
gBattleStruct->AI_monToSwitchIntoId[battler] = i;
@@ -1609,8 +1609,8 @@ static u32 GetSwitchinHitsToKO(s32 damageTaken, u32 battler)
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;
u32 opposingAbility = gBattleMons[opposingBattler].ability, ability = AI_DATA->switchinCandidate.battleMon.ability;
bool32 usedSingleUseHealingItem = FALSE, opponentCanBreakMold = IsMoldBreakerTypeAbility(opposingBattler, opposingAbility);
s32 currentHP = startingHP;
// No damage being dealt
@@ -1631,6 +1631,10 @@ static u32 GetSwitchinHitsToKO(s32 damageTaken, u32 battler)
// Take attack damage for the turn
currentHP = currentHP - damageTaken;
// One shot prevention effects
if (damageTaken >= maxHP && currentHP == maxHP && (heldItemEffect == HOLD_EFFECT_FOCUS_SASH || (!opponentCanBreakMold && B_STURDY >= GEN_5 && ability == ABILITY_STURDY)))
currentHP = 1;
// If mon is still alive, apply weather impact first, as it might KO the mon before it can heal with its item (order is weather -> item -> status)
if (currentHP != 0)
currentHP = currentHP - weatherImpact;
@@ -1693,6 +1697,10 @@ static u32 GetSwitchinHitsToKO(s32 damageTaken, u32 battler)
hitsToKO++;
}
// Disguise will always add an extra hit to KO
if (opponentCanBreakMold && AI_DATA->switchinCandidate.battleMon.species == SPECIES_MIMIKYU_DISGUISED)
hitsToKO++;
// If mon had a hypothetical status from TSpikes, clear it
if (AI_DATA->switchinCandidate.hypotheticalStatus == TRUE)
{
@@ -2002,7 +2010,7 @@ static u32 GetBestMonIntegrated(struct Pokemon *party, int firstId, int lastId,
}
// If ace mon is the last available Pokemon and U-Turn/Volt Switch was used - switch to the mon.
if (aceMonId != PARTY_SIZE
&& (gMovesInfo[gLastUsedMove].effect == EFFECT_HIT_ESCAPE || gMovesInfo[gLastUsedMove].effect == EFFECT_PARTING_SHOT || gMovesInfo[gLastUsedMove].effect == EFFECT_BATON_PASS || gMovesInfo[gLastUsedMove].effect == EFFECT_CHILLY_RECEPTION))
&& (gMovesInfo[gLastUsedMove].effect == EFFECT_HIT_ESCAPE || gMovesInfo[gLastUsedMove].effect == EFFECT_PARTING_SHOT || gMovesInfo[gLastUsedMove].effect == EFFECT_BATON_PASS || gMovesInfo[gLastUsedMove].effect == EFFECT_CHILLY_RECEPTION || gMovesInfo[gLastUsedMove].effect == EFFECT_SHED_TAIL))
return aceMonId;
return PARTY_SIZE;

View File

@@ -518,12 +518,16 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
{
struct SimulatedDamage simDamage;
s32 moveType;
u32 moveEffect = gMovesInfo[move].effect;
uq4_12_t effectivenessMultiplier;
bool32 isDamageMoveUnusable = FALSE;
bool32 toggledGimmick = FALSE;
struct AiLogicData *aiData = AI_DATA;
gBattleStruct->aiCalcInProgress = TRUE;
if (moveEffect == EFFECT_NATURE_POWER)
move = GetNaturePowerMove(battlerAtk);
// 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))
@@ -536,12 +540,25 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
SetActiveGimmick(battlerAtk, gBattleStruct->gimmick.usableGimmick[battlerAtk]);
}
if (gMovesInfo[move].effect == EFFECT_PHOTON_GEYSER)
gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) != gMovesInfo[gCurrentMove].category);
else if (gMovesInfo[move].effect == EFFECT_SHELL_SIDE_ARM)
gBattleStruct->swapDamageCategory = (gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] != gMovesInfo[gCurrentMove].category);
else if (gMovesInfo[move].effect == EFFECT_NATURE_POWER)
move = GetNaturePowerMove(battlerAtk);
moveEffect = gMovesInfo[move].effect;
switch (moveEffect)
{
case EFFECT_PHOTON_GEYSER:
gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(battlerAtk) == DAMAGE_CATEGORY_PHYSICAL);
break;
case EFFECT_SHELL_SIDE_ARM:
if (gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] == DAMAGE_CATEGORY_PHYSICAL)
gBattleStruct->swapDamageCategory = TRUE;
break;
case EFFECT_TERA_BLAST:
if (GetActiveGimmick(battlerAtk) == GIMMICK_TERA)
gBattleStruct->swapDamageCategory = GetCategoryBasedOnStats(battlerAtk) == DAMAGE_CATEGORY_PHYSICAL;
break;
case EFFECT_TERA_STARSTORM:
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_TERA && gBattleMons[gBattlerAttacker].species == SPECIES_TERAPAGOS_STELLAR)
gBattleStruct->swapDamageCategory = GetCategoryBasedOnStats(battlerAtk) == DAMAGE_CATEGORY_PHYSICAL;
break;
}
gBattleStruct->dynamicMoveType = 0;
@@ -558,7 +575,7 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
ProteanTryChangeType(battlerAtk, aiData->abilities[battlerAtk], move, moveType);
// Certain moves like Rollout calculate damage based on values which change during the move execution, but before calling dmg calc.
switch (gMovesInfo[move].effect)
switch (moveEffect)
{
case EFFECT_ROLLOUT:
n = gDisableStructs[battlerAtk].rolloutTimer - 1;
@@ -605,7 +622,7 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
else
{
s32 nonCritDmg = 0;
if (gMovesInfo[move].effect == EFFECT_TRIPLE_KICK)
if (moveEffect == EFFECT_TRIPLE_KICK)
{
for (gMultiHitCounter = gMovesInfo[move].strikeCount; gMultiHitCounter > 0; gMultiHitCounter--) // The global is used to simulate actual damage done
{
@@ -629,7 +646,7 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
if (GetActiveGimmick(battlerAtk) != GIMMICK_Z_MOVE)
{
// Handle dynamic move damage
switch (gMovesInfo[move].effect)
switch (moveEffect)
{
case EFFECT_LEVEL_DAMAGE:
simDamage.expected = simDamage.minimum = gBattleMons[battlerAtk].level * (aiData->abilities[battlerAtk] == ABILITY_PARENTAL_BOND ? 2 : 1);
@@ -845,7 +862,7 @@ static bool32 AI_IsMoveEffectInPlus(u32 battlerAtk, u32 battlerDef, u32 move, s3
case MOVE_EFFECT_SP_DEF_MINUS_2:
case MOVE_EFFECT_ACC_MINUS_2:
case MOVE_EFFECT_EVS_MINUS_2:
if (ShouldLowerStat(battlerDef, abilityDef, STAT_ATK + (gMovesInfo[move].additionalEffects[i].moveEffect - MOVE_EFFECT_ATK_MINUS_1)) && noOfHitsToKo != 1)
if (ShouldLowerStat(battlerDef, abilityDef, STAT_ATK + (gMovesInfo[move].additionalEffects[i].moveEffect - MOVE_EFFECT_ATK_MINUS_2)) && noOfHitsToKo != 1)
return TRUE;
break;
}
@@ -1096,6 +1113,26 @@ s32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler, u32 moveConsidered)
return AI_IS_SLOWER;
}
static bool32 CanEndureHit(u32 battler, u32 battlerTarget, u32 move)
{
if (!BATTLER_MAX_HP(battlerTarget) || gMovesInfo[move].effect == EFFECT_MULTI_HIT)
return FALSE;
if (gMovesInfo[move].strikeCount > 1 && !(gMovesInfo[move].effect == EFFECT_DRAGON_DARTS && IsValidDoubleBattle(battlerTarget)))
return FALSE;
if (AI_DATA->holdEffects[battlerTarget] == HOLD_EFFECT_FOCUS_SASH)
return TRUE;
if (!DoesBattlerIgnoreAbilityChecks(AI_DATA->abilities[battler], move))
{
if (B_STURDY >= GEN_5 && AI_DATA->abilities[battlerTarget] == ABILITY_STURDY)
return TRUE;
if (gBattleMons[battlerTarget].species == SPECIES_MIMIKYU_DISGUISED)
return TRUE;
}
return FALSE;
}
// Check if target has means to faint ai mon.
bool32 CanTargetFaintAi(u32 battlerDef, u32 battlerAtk)
{
@@ -1106,7 +1143,8 @@ bool32 CanTargetFaintAi(u32 battlerDef, u32 battlerAtk)
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (moves[i] != MOVE_NONE && moves[i] != MOVE_UNAVAILABLE && !(unusable & (1u << i))
&& AI_DATA->simulatedDmg[battlerDef][battlerAtk][i].expected >= gBattleMons[battlerAtk].hp)
&& AI_DATA->simulatedDmg[battlerDef][battlerAtk][i].expected >= gBattleMons[battlerAtk].hp
&& !CanEndureHit(battlerDef, battlerAtk, moves[i]))
{
return TRUE;
}
@@ -1193,7 +1231,13 @@ bool32 CanAIFaintTarget(u32 battlerAtk, u32 battlerDef, u32 numHits)
dmg *= numHits;
if (gBattleMons[battlerDef].hp <= dmg)
return TRUE;
{
if (numHits > 1)
return TRUE;
if (!CanEndureHit(battlerAtk, battlerDef, moves[i]))
return TRUE;
}
}
}
@@ -1388,14 +1432,6 @@ u32 AI_GetWeather(struct AiLogicData *aiData)
return gBattleWeather;
}
u32 AI_GetBattlerMoveTargetType(u32 battlerId, u32 move)
{
if (gMovesInfo[move].effect == EFFECT_EXPANDING_FORCE && AI_IsTerrainAffected(battlerId, STATUS_FIELD_PSYCHIC_TERRAIN))
return MOVE_TARGET_BOTH;
else
return gMovesInfo[move].target;
}
bool32 IsAromaVeilProtectedMove(u32 move)
{
switch (move)
@@ -1913,13 +1949,14 @@ bool32 ShouldLowerEvasion(u32 battlerAtk, u32 battlerDef, u32 defAbility)
bool32 CanIndexMoveFaintTarget(u32 battlerAtk, u32 battlerDef, u32 index, u32 numHits)
{
s32 dmg;
u16 *moves = gBattleMons[battlerAtk].moves;
if (numHits)
dmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][index].expected * numHits;
else
dmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][index].minimum;
if (gBattleMons[battlerDef].hp <= dmg)
if (gBattleMons[battlerDef].hp <= dmg && !CanEndureHit(battlerAtk, battlerDef, moves[index]))
return TRUE;
return FALSE;
}
@@ -2080,6 +2117,26 @@ bool32 HasAnyKnownMove(u32 battlerId)
return FALSE;
}
bool32 HasMoveThatLowersOwnStats(u32 battlerId)
{
s32 i, j;
u32 aiMove;
u16 *moves = GetMovesArray(battlerId);
for (i = 0; i < MAX_MON_MOVES; i++)
{
aiMove = moves[i];
if (aiMove != MOVE_NONE && aiMove != MOVE_UNAVAILABLE)
{
for (j = 0; j < gMovesInfo[aiMove].numAdditionalEffects; j++)
{
if (IsSelfStatLoweringEffect(gMovesInfo[aiMove].additionalEffects[j].moveEffect) && gMovesInfo[aiMove].additionalEffects[j].self)
return TRUE;
}
}
}
return FALSE;
}
bool32 HasMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef, u32 accCheck, bool32 ignoreStatus, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect)
{
s32 i;
@@ -2096,7 +2153,7 @@ bool32 HasMoveWithLowAccuracy(u32 battlerAtk, u32 battlerDef, u32 accCheck, bool
if (ignoreStatus && IS_MOVE_STATUS(moves[i]))
continue;
else if ((!IS_MOVE_STATUS(moves[i]) && gMovesInfo[moves[i]].accuracy == 0)
|| AI_GetBattlerMoveTargetType(battlerAtk, moves[i]) & (MOVE_TARGET_USER | MOVE_TARGET_OPPONENTS_FIELD))
|| GetBattlerMoveTargetType(battlerAtk, moves[i]) & (MOVE_TARGET_USER | MOVE_TARGET_OPPONENTS_FIELD))
continue;
if (AI_DATA->moveAccuracy[battlerAtk][battlerDef][i] <= accCheck)
@@ -2196,7 +2253,7 @@ bool32 IsAttackBoostMoveEffect(u32 effect)
switch (effect)
{
case EFFECT_ATTACK_UP:
case EFFECT_ATTACK_UP_2:
case EFFECT_ATTACK_UP_2:
case EFFECT_ATTACK_ACCURACY_UP:
case EFFECT_ATTACK_SPATK_UP:
case EFFECT_DRAGON_DANCE:
@@ -2216,34 +2273,34 @@ bool32 IsStatRaisingEffect(u32 effect)
switch (effect)
{
case EFFECT_ATTACK_UP:
case EFFECT_ATTACK_UP_2:
case EFFECT_DEFENSE_UP:
case EFFECT_DEFENSE_UP_2:
case EFFECT_ATTACK_UP_2:
case EFFECT_DEFENSE_UP:
case EFFECT_DEFENSE_UP_2:
case EFFECT_DEFENSE_UP_3:
case EFFECT_SPEED_UP:
case EFFECT_SPEED_UP_2:
case EFFECT_SPECIAL_ATTACK_UP:
case EFFECT_SPECIAL_ATTACK_UP_2:
case EFFECT_SPEED_UP:
case EFFECT_SPEED_UP_2:
case EFFECT_SPECIAL_ATTACK_UP:
case EFFECT_SPECIAL_ATTACK_UP_2:
case EFFECT_SPECIAL_ATTACK_UP_3:
case EFFECT_SPECIAL_DEFENSE_UP:
case EFFECT_SPECIAL_DEFENSE_UP_2:
case EFFECT_SPECIAL_DEFENSE_UP:
case EFFECT_SPECIAL_DEFENSE_UP_2:
case EFFECT_ACCURACY_UP:
case EFFECT_ACCURACY_UP_2:
case EFFECT_EVASION_UP:
case EFFECT_EVASION_UP_2:
case EFFECT_MINIMIZE:
case EFFECT_DEFENSE_CURL:
case EFFECT_CALM_MIND:
case EFFECT_CALM_MIND:
case EFFECT_COSMIC_POWER:
case EFFECT_DRAGON_DANCE:
case EFFECT_ACUPRESSURE:
case EFFECT_SHELL_SMASH:
case EFFECT_SHIFT_GEAR:
case EFFECT_ATTACK_ACCURACY_UP:
case EFFECT_ATTACK_SPATK_UP:
case EFFECT_GROWTH:
case EFFECT_COIL:
case EFFECT_QUIVER_DANCE:
case EFFECT_DRAGON_DANCE:
case EFFECT_ACUPRESSURE:
case EFFECT_SHELL_SMASH:
case EFFECT_SHIFT_GEAR:
case EFFECT_ATTACK_ACCURACY_UP:
case EFFECT_ATTACK_SPATK_UP:
case EFFECT_GROWTH:
case EFFECT_COIL:
case EFFECT_QUIVER_DANCE:
case EFFECT_BULK_UP:
case EFFECT_GEOMANCY:
case EFFECT_STOCKPILE:
@@ -2285,6 +2342,34 @@ bool32 IsStatLoweringEffect(u32 effect)
}
}
bool32 IsSelfStatLoweringEffect(u32 effect)
{
// Self stat lowering moves like Overheart, Superpower etc.
switch (effect)
{
case MOVE_EFFECT_ATK_MINUS_1:
case MOVE_EFFECT_DEF_MINUS_1:
case MOVE_EFFECT_SPD_MINUS_1:
case MOVE_EFFECT_SP_ATK_MINUS_1:
case MOVE_EFFECT_SP_DEF_MINUS_1:
case MOVE_EFFECT_EVS_MINUS_1:
case MOVE_EFFECT_ACC_MINUS_1:
case MOVE_EFFECT_ATK_MINUS_2:
case MOVE_EFFECT_DEF_MINUS_2:
case MOVE_EFFECT_SPD_MINUS_2:
case MOVE_EFFECT_SP_ATK_MINUS_2:
case MOVE_EFFECT_SP_DEF_MINUS_2:
case MOVE_EFFECT_EVS_MINUS_2:
case MOVE_EFFECT_ACC_MINUS_2:
case MOVE_EFFECT_V_CREATE:
case MOVE_EFFECT_ATK_DEF_DOWN:
case MOVE_EFFECT_DEF_SPDEF_DOWN:
return TRUE;
default:
return FALSE;
}
}
bool32 HasDamagingMove(u32 battlerId)
{
u32 i;
@@ -3735,11 +3820,14 @@ void IncreaseBurnScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)
if (AI_CanBurn(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove))
{
ADJUST_SCORE_PTR(WEAK_EFFECT); // burning is good
if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL))
if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL)
|| (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_OMNISCIENT) // Not Omniscient but expects physical attacker
&& gSpeciesInfo[gBattleMons[battlerDef].species].baseAttack >= gSpeciesInfo[gBattleMons[battlerDef].species].baseSpAttack + 10))
{
if (CanTargetFaintAi(battlerDef, battlerAtk))
ADJUST_SCORE_PTR(DECENT_EFFECT); // burning the target to stay alive is cool
if (gMovesInfo[GetBestDmgMoveFromBattler(battlerDef, battlerAtk)].category == DAMAGE_CATEGORY_PHYSICAL)
ADJUST_SCORE_PTR(DECENT_EFFECT);
else
ADJUST_SCORE_PTR(WEAK_EFFECT);
}
if (HasMoveEffectANDArg(battlerAtk, EFFECT_DOUBLE_POWER_ON_ARG_STATUS, STATUS1_BURN)
@@ -3816,11 +3904,14 @@ void IncreaseFrostbiteScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score
if (AI_CanGiveFrostbite(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove))
{
ADJUST_SCORE_PTR(WEAK_EFFECT); // frostbite is good
if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL))
if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL)
|| (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_OMNISCIENT) // Not Omniscient but expects special attacker
&& gSpeciesInfo[gBattleMons[battlerDef].species].baseSpAttack >= gSpeciesInfo[gBattleMons[battlerDef].species].baseAttack + 10))
{
if (CanTargetFaintAi(battlerDef, battlerAtk))
ADJUST_SCORE_PTR(DECENT_EFFECT); // frostbiting the target to stay alive is cool
if (gMovesInfo[GetBestDmgMoveFromBattler(battlerDef, battlerAtk)].category == DAMAGE_CATEGORY_SPECIAL)
ADJUST_SCORE_PTR(DECENT_EFFECT);
else
ADJUST_SCORE_PTR(WEAK_EFFECT);
}
if (HasMoveEffectANDArg(battlerAtk, EFFECT_DOUBLE_POWER_ON_ARG_STATUS, STATUS1_FROSTBITE)
@@ -3987,3 +4078,42 @@ bool32 AI_ShouldSpicyExtract(u32 battlerAtk, u32 battlerAtkPartner, u32 move, st
&& AI_IsFaster(battlerAtk, battlerAtkPartner, TRUE)
&& HasMoveWithCategory(battlerAtkPartner, DAMAGE_CATEGORY_PHYSICAL));
}
void IncreaseSubstituteMoveScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)
{
if (gMovesInfo[move].effect == EFFECT_SUBSTITUTE) // Substitute specific
{
if (HasAnyKnownMove(battlerDef) && GetBestDmgFromBattler(battlerDef, battlerAtk) < gBattleMons[battlerAtk].maxHP / 4)
ADJUST_SCORE_PTR(GOOD_EFFECT);
}
else if (gMovesInfo[move].effect == EFFECT_SHED_TAIL) // Shed Tail specific
{
if ((ShouldPivot(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_THINKING_STRUCT->movesetIndex))
&& (HasAnyKnownMove(battlerDef) && (GetBestDmgFromBattler(battlerDef, battlerAtk) < gBattleMons[battlerAtk].maxHP / 2)))
ADJUST_SCORE_PTR(BEST_EFFECT);
}
if (gStatuses3[battlerDef] & STATUS3_PERISH_SONG)
ADJUST_SCORE_PTR(GOOD_EFFECT);
if (gBattleMons[battlerDef].status1 & STATUS1_SLEEP)
ADJUST_SCORE_PTR(GOOD_EFFECT);
else if (gBattleMons[battlerDef].status1 & (STATUS1_BURN | STATUS1_PSN_ANY | STATUS1_FROSTBITE))
ADJUST_SCORE_PTR(DECENT_EFFECT);
// TODO:
// if (IsPredictedToSwitch(battlerDef, battlerAtk)
// ADJUST_SCORE_PTR(DECENT_EFFECT);
if (HasMoveEffect(battlerDef, EFFECT_SLEEP)
|| HasMoveEffect(battlerDef, EFFECT_TOXIC)
|| HasMoveEffect(battlerDef, EFFECT_POISON)
|| HasMoveEffect(battlerDef, EFFECT_PARALYZE)
|| HasMoveEffect(battlerDef, EFFECT_WILL_O_WISP)
|| HasMoveEffect(battlerDef, EFFECT_CONFUSE)
|| HasMoveEffect(battlerDef, EFFECT_LEECH_SEED))
ADJUST_SCORE_PTR(GOOD_EFFECT);
if (AI_DATA->hpPercents[battlerAtk] > 70)
ADJUST_SCORE_PTR(WEAK_EFFECT);
}

View File

@@ -10,8 +10,6 @@
#include "util.h"
#include "constants/battle_anim.h"
#define IS_DOUBLE_BATTLE() (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
static u8 GetBattlerSpriteFinal_Y(u8 battlerId, u16 species, bool8 a3);
static void PlayerThrowBall_AnimTranslateLinear_WithFollowup(struct Sprite *sprite);
static void AnimFastTranslateLinearWaitEnd(struct Sprite *sprite);
@@ -738,11 +736,6 @@ bool8 IsBattlerSpritePresent(u8 battlerId)
return TRUE;
}
bool8 IsDoubleBattle(void)
{
return IS_DOUBLE_BATTLE();
}
#define BG_ANIM_PAL_1 8
#define BG_ANIM_PAL_2 9

View File

@@ -28,7 +28,7 @@
#include "bg.h"
#include "string_util.h"
#include "pokemon_icon.h"
#include "level_caps.h"
#include "caps.h"
#include "m4a.h"
#include "mail.h"
#include "event_data.h"
@@ -1144,10 +1144,10 @@ static void Cmd_attackcanceler(void)
}
if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_OFF
&& GetBattlerAbility(gBattlerAttacker) == ABILITY_PARENTAL_BOND
&& IsMoveAffectedByParentalBond(gCurrentMove, gBattlerAttacker)
&& !(gAbsentBattlerFlags & (1u << gBattlerTarget))
&& GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE)
&& GetBattlerAbility(gBattlerAttacker) == ABILITY_PARENTAL_BOND
&& IsMoveAffectedByParentalBond(gCurrentMove, gBattlerAttacker)
&& !(gAbsentBattlerFlags & (1u << gBattlerTarget))
&& GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE)
{
gSpecialStatuses[gBattlerAttacker].parentalBondState = PARENTAL_BOND_1ST_HIT;
gMultiHitCounter = 2;
@@ -1188,16 +1188,40 @@ static void Cmd_attackcanceler(void)
if (!(gHitMarker & HITMARKER_OBEYS) && !(gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS))
{
switch (IsMonDisobedient())
switch (gBattleStruct->obedienceResult)
{
case 0:
case OBEYS:
break;
case 2:
case DISOBEYS_LOAFS:
// Randomly select, then print a disobedient string
// B_MSG_LOAFING, B_MSG_WONT_OBEY, B_MSG_TURNED_AWAY, or B_MSG_PRETEND_NOT_NOTICE
gBattleCommunication[MULTISTRING_CHOOSER] = MOD(Random(), NUM_LOAF_STRINGS);
gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround;
gMoveResultFlags |= MOVE_RESULT_MISSED;
return;
case DISOBEYS_HITS_SELF:
gBattlerTarget = gBattlerAttacker;
gBattleMoveDamage = CalculateMoveDamage(MOVE_NONE, gBattlerAttacker, gBattlerAttacker, TYPE_MYSTERY, 40, FALSE, FALSE, TRUE);
gBattlescriptCurrInstr = BattleScript_IgnoresAndHitsItself;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
gHitMarker |= HITMARKER_OBEYS;
return;
default:
case DISOBEYS_FALL_ASLEEP:
gBattlescriptCurrInstr = BattleScript_IgnoresAndFallsAsleep;
gMoveResultFlags |= MOVE_RESULT_MISSED;
return;
case DISOBEYS_WHILE_ASLEEP:
gBattlescriptCurrInstr = BattleScript_IgnoresWhileAsleep;
gMoveResultFlags |= MOVE_RESULT_MISSED;
return;
case DISOBEYS_RANDOM_MOVE:
gCalledMove = gBattleMons[gBattlerAttacker].moves[gCurrMovePos];
SetAtkCancellerForCalledMove();
gBattlescriptCurrInstr = BattleScript_IgnoresAndUsesRandomMove;
gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE);
gHitMarker |= HITMARKER_DISOBEDIENT_MOVE;
gHitMarker |= HITMARKER_OBEYS;
return;
}
}
@@ -1281,6 +1305,7 @@ static void Cmd_attackcanceler(void)
if ((gProtectStructs[gBattlerByTurnOrder[i]].stealMove) && gMovesInfo[gCurrentMove].snatchAffected)
{
gProtectStructs[gBattlerByTurnOrder[i]].stealMove = FALSE;
gBattleStruct->snatchedMoveIsUsed = TRUE;
gBattleScripting.battler = gBattlerByTurnOrder[i];
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_SnatchedMove;
@@ -1569,9 +1594,8 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u
break;
}
if (gProtectStructs[battlerAtk].usedMicleBerry)
if (gBattleStruct->usedMicleBerry & 1u << battlerAtk)
{
gProtectStructs[battlerAtk].usedMicleBerry = FALSE;
if (atkAbility == ABILITY_RIPEN)
calc = (calc * 140) / 100; // ripen gives 40% acc boost
else
@@ -1623,6 +1647,13 @@ static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u
gBattlescriptCurrInstr = failInstr;
else if (!JumpIfMoveAffectedByProtect(gCurrentMove))
gBattlescriptCurrInstr = nextInstr;
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_DYNAMAX)
{
if (gProtectStructs[gBattlerTarget].maxGuarded)
gBattlescriptCurrInstr = nextInstr;
else
AbilityBattleEffects(ABILITYEFFECT_ABSORBING, gBattlerTarget, 0, 0, gCurrentMove);
}
}
else if (gSpecialStatuses[gBattlerAttacker].parentalBondState == PARENTAL_BOND_2ND_HIT
|| (gSpecialStatuses[gBattlerAttacker].multiHitOn
@@ -4505,7 +4536,8 @@ static void MoveValuesCleanUp(void)
gIsCriticalHit = FALSE;
gBattleScripting.moveEffect = 0;
gBattleCommunication[MISS_TYPE] = 0;
gHitMarker &= ~HITMARKER_DESTINYBOND;
if (!gMultiHitCounter)
gHitMarker &= ~HITMARKER_DESTINYBOND;
gHitMarker &= ~HITMARKER_SYNCHRONISE_EFFECT;
}
@@ -5267,7 +5299,9 @@ static void Cmd_moveend(void)
case MOVEEND_PROTECT_LIKE_EFFECT:
if (gProtectStructs[gBattlerAttacker].touchedProtectLike)
{
if (gProtectStructs[gBattlerTarget].spikyShielded && GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD)
if (gProtectStructs[gBattlerTarget].spikyShielded
&& gMovesInfo[gCurrentMove].effect != EFFECT_COUNTER
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD)
{
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 8;
@@ -5842,17 +5876,18 @@ static void Cmd_moveend(void)
&& gMultiHitCounter
&& !(gMovesInfo[gCurrentMove].effect == EFFECT_PRESENT && gBattleStruct->presentBasePower == 0)) // Silly edge case
{
gBattleScripting.multihitString[4]++;
if (--gMultiHitCounter == 0)
{
if (gMovesInfo[gCurrentMove].argument == MOVE_EFFECT_SCALE_SHOT && !NoAliveMonsForEitherParty())
{
BattleScriptPush(gBattlescriptCurrInstr + 1);
gBattlescriptCurrInstr = BattleScript_DefDownSpeedUp;
}
gMultiHitCounter--;
if (!IsBattlerAlive(gBattlerTarget) && gMovesInfo[gCurrentMove].effect != EFFECT_DRAGON_DARTS)
gMultiHitCounter = 0;
gBattleScripting.multihitString[4]++;
if (gMultiHitCounter == 0)
{
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_MultiHitPrintStrings;
if (gMovesInfo[gCurrentMove].argument == MOVE_EFFECT_SCALE_SHOT && !NoAliveMonsForEitherParty())
gBattlescriptCurrInstr = BattleScript_ScaleShot;
else
gBattlescriptCurrInstr = BattleScript_MultiHitPrintStrings;
effect = TRUE;
}
else
@@ -5864,9 +5899,9 @@ static void Cmd_moveend(void)
gBattlerTarget = BATTLE_PARTNER(gBattlerTarget); // Target the partner in doubles for second hit.
if (gBattleMons[gBattlerAttacker].hp
&& gBattleMons[gBattlerTarget].hp
&& (gChosenMove == MOVE_SLEEP_TALK || !(gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP))
&& !(gBattleMons[gBattlerAttacker].status1 & STATUS1_FREEZE))
&& gBattleMons[gBattlerTarget].hp
&& (gChosenMove == MOVE_SLEEP_TALK || (gChosenMove == MOVE_SNORE) || !(gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP))
&& !(gBattleMons[gBattlerAttacker].status1 & STATUS1_FREEZE))
{
if (gSpecialStatuses[gBattlerAttacker].parentalBondState)
gSpecialStatuses[gBattlerAttacker].parentalBondState--;
@@ -6103,22 +6138,23 @@ static void Cmd_moveend(void)
gBattleScripting.moveendState++;
break;
case MOVEEND_DANCER: // Special case because it's so annoying
if (gMovesInfo[gCurrentMove].danceMove)
if (gMovesInfo[gCurrentMove].danceMove && !gBattleStruct->snatchedMoveIsUsed)
{
u32 battler, nextDancer = 0;
bool32 turnOnHitmarker = FALSE;
bool32 hasDancerTriggered = FALSE;
for (battler = 0; battler < gBattlersCount; battler++)
{
if (gSpecialStatuses[battler].dancerUsedMove)
{
// in case a battler fails to act on a Dancer-called move
turnOnHitmarker = TRUE;
hasDancerTriggered = TRUE;
break;
}
}
if (!(gMoveResultFlags & (MOVE_RESULT_FAILED | MOVE_RESULT_DOESNT_AFFECT_FOE)
|| (gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE && !hasDancerTriggered)
|| (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove && gBattleStruct->bouncedMoveIsUsed)))
{ // Dance move succeeds
// Set target for other Dancer mons; set bit so that mon cannot activate Dancer off of its own move
@@ -6132,8 +6168,6 @@ static void Cmd_moveend(void)
{
if (GetBattlerAbility(battler) == ABILITY_DANCER && !gSpecialStatuses[battler].dancerUsedMove)
{
if (turnOnHitmarker)
gHitMarker |= HITMARKER_ATTACKSTRING_PRINTED;
if (!nextDancer || (gBattleMons[battler].speed < gBattleMons[nextDancer & 0x3].speed))
nextDancer = battler | 0x4;
}
@@ -6233,11 +6267,10 @@ static void Cmd_moveend(void)
DebugPrintfLevel(MGBA_LOG_WARN, "savedTargetCount is greater than 0! More calls to SaveBattlerTarget than RestoreBattlerTarget!");
// #endif
}
gBattleStruct->targetsDone[gBattlerAttacker] = 0;
gProtectStructs[gBattlerAttacker].targetAffected = FALSE;
gProtectStructs[gBattlerAttacker].shellTrap = FALSE;
gBattleStruct->ateBoost[gBattlerAttacker] = 0;
gBattleStruct->ateBoost[gBattlerAttacker] = FALSE;
gStatuses3[gBattlerAttacker] &= ~STATUS3_ME_FIRST;
gSpecialStatuses[gBattlerAttacker].gemBoost = FALSE;
gSpecialStatuses[gBattlerAttacker].damagedMons = 0;
@@ -6249,13 +6282,19 @@ static void Cmd_moveend(void)
gBattleStruct->swapDamageCategory = FALSE;
gBattleStruct->categoryOverride = FALSE;
gBattleStruct->bouncedMoveIsUsed = FALSE;
gBattleStruct->snatchedMoveIsUsed = FALSE;
gBattleStruct->enduredDamage = 0;
gBattleStruct->additionalEffectsCounter = 0;
gBattleStruct->poisonPuppeteerConfusion = FALSE;
gBattleStruct->fickleBeamBoosted = FALSE;
gBattleStruct->distortedTypeMatchups = 0;
gBattleStruct->usedMicleBerry &= ~(1u << gBattlerAttacker);
if (gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
gBattleStruct->pledgeMove = FALSE;
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE)
SetActiveGimmick(gBattlerAttacker, GIMMICK_NONE);
gBattleStruct->distortedTypeMatchups = 0;
if (B_CHARGE <= GEN_8 || moveType == TYPE_ELECTRIC)
gStatuses3[gBattlerAttacker] &= ~(STATUS3_CHARGED_UP);
memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts));
gBattleScripting.moveendState++;
break;
@@ -7014,7 +7053,7 @@ static void UpdateSentMonFlags(u32 battler)
static bool32 DoSwitchInEffectsForBattler(u32 battler)
{
u32 i;
u32 i = 0;
// Neutralizing Gas announces itself before hazards
if (gBattleMons[battler].ability == ABILITY_NEUTRALIZING_GAS && gSpecialStatuses[battler].announceNeutralizingGas == 0)
{
@@ -7082,9 +7121,11 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler)
}
else if (IsBattlerAffectedByHazards(battler, TRUE))
{
i = GetBattlerAbility(battler);
if (!(gBattleMons[battler].status1 & STATUS1_ANY)
&& !IS_BATTLER_OF_TYPE(battler, TYPE_STEEL)
&& GetBattlerAbility(battler) != ABILITY_IMMUNITY
&& i != ABILITY_IMMUNITY
&& i != ABILITY_PURIFYING_SALT
&& !IsAbilityOnSide(battler, ABILITY_PASTEL_VEIL)
&& !(gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD)
&& !(gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN))
@@ -7253,9 +7294,9 @@ static void Cmd_switchineffects(void)
static void Cmd_trainerslidein(void)
{
CMD_ARGS(u8 battler);
CMD_ARGS(u8 position);
u32 battler = GetBattlerAtPosition(cmd->battler);
u32 battler = GetBattlerForBattleScript(cmd->position);
BtlController_EmitTrainerSlide(battler, BUFFER_A);
MarkBattlerForControllerExec(battler);
@@ -8462,6 +8503,10 @@ static void RemoveAllTerrains(void)
BattleScriptPushCursor(); \
gBattlescriptCurrInstr = battlescript; \
} \
else \
{ \
gBattlerAttacker = saveBattler; \
} \
return TRUE; \
} \
}
@@ -8476,9 +8521,9 @@ static bool32 TryDefogClear(u32 battlerAtk, bool32 clear)
struct SideTimer *sideTimer = &gSideTimers[i];
u32 *sideStatuses = &gSideStatuses[i];
gBattlerAttacker = i; // For correct battle string. Ally's / Foe's
if (GetBattlerSide(battlerAtk) != i)
{
gBattlerAttacker = i; // For correct battle string. Ally's / Foe's
DEFOG_CLEAR(SIDE_STATUS_REFLECT, reflectTimer, BattleScript_SideStatusWoreOffReturn, MOVE_REFLECT);
DEFOG_CLEAR(SIDE_STATUS_LIGHTSCREEN, lightscreenTimer, BattleScript_SideStatusWoreOffReturn, MOVE_LIGHT_SCREEN);
DEFOG_CLEAR(SIDE_STATUS_MIST, mistTimer, BattleScript_SideStatusWoreOffReturn, MOVE_MIST);
@@ -8487,6 +8532,7 @@ static bool32 TryDefogClear(u32 battlerAtk, bool32 clear)
}
if (B_DEFOG_EFFECT_CLEARING >= GEN_6)
{
gBattlerAttacker = i; // For correct battle string. Ally's / Foe's
DEFOG_CLEAR(SIDE_STATUS_SPIKES, spikesAmount, BattleScript_SpikesDefog, 0);
DEFOG_CLEAR(SIDE_STATUS_STEALTH_ROCK, stealthRockAmount, BattleScript_StealthRockDefog, 0);
DEFOG_CLEAR(SIDE_STATUS_TOXIC_SPIKES, toxicSpikesAmount, BattleScript_ToxicSpikesDefog, 0);
@@ -8747,29 +8793,40 @@ static bool32 ChangeOrderTargetAfterAttacker(void)
{
u32 i;
u8 data[MAX_BATTLERS_COUNT];
u8 actionsData[MAX_BATTLERS_COUNT];
if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget)
|| GetBattlerTurnOrderNum(gBattlerAttacker) + 1 == GetBattlerTurnOrderNum(gBattlerTarget))
if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget))
return FALSE;
if (GetBattlerTurnOrderNum(gBattlerAttacker) + 1 == GetBattlerTurnOrderNum(gBattlerTarget))
return B_AFTER_YOU_TURN_ORDER >= GEN_8;
for (i = 0; i < gBattlersCount; i++)
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
{
data[i] = gBattlerByTurnOrder[i];
actionsData[i] = gActionsByTurnOrder[i];
}
if (GetBattlerTurnOrderNum(gBattlerAttacker) == 0 && GetBattlerTurnOrderNum(gBattlerTarget) == 2)
{
gBattlerByTurnOrder[1] = gBattlerTarget;
gActionsByTurnOrder[1] = actionsData[2];
gBattlerByTurnOrder[2] = data[1];
gBattlerByTurnOrder[3] = data[3];
gActionsByTurnOrder[2] = actionsData[1];
}
else if (GetBattlerTurnOrderNum(gBattlerAttacker) == 0 && GetBattlerTurnOrderNum(gBattlerTarget) == 3)
{
gBattlerByTurnOrder[1] = gBattlerTarget;
gActionsByTurnOrder[1] = actionsData[3];
gBattlerByTurnOrder[2] = data[1];
gActionsByTurnOrder[2] = actionsData[1];
gBattlerByTurnOrder[3] = data[2];
gActionsByTurnOrder[3] = actionsData[2];
}
else // Attacker == 1, Target == 3
{
gBattlerByTurnOrder[2] = gBattlerTarget;
gActionsByTurnOrder[2] = actionsData[3];
gBattlerByTurnOrder[3] = data[2];
gActionsByTurnOrder[3] = actionsData[2];
}
return TRUE;
}
@@ -9612,10 +9669,7 @@ static void Cmd_various(void)
case VARIOUS_HANDLE_FORM_CHANGE:
{
VARIOUS_ARGS(u8 case_);
if (GetBattlerSide(battler) == B_SIDE_OPPONENT)
mon = &gEnemyParty[gBattlerPartyIndexes[battler]];
else
mon = &gPlayerParty[gBattlerPartyIndexes[battler]];
mon = GetPartyBattlerData(battler);
// Change species.
if (cmd->case_ == 0)
@@ -9703,23 +9757,23 @@ static void Cmd_various(void)
else
{
gSpecialStatuses[gBattlerTarget].instructedChosenTarget = *(gBattleStruct->moveTarget + gBattlerTarget) | 0x4;
gBattlerAttacker = gBattlerTarget;
gCalledMove = move;
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (gBattleMons[gBattlerAttacker].moves[i] == gCalledMove)
if (gBattleMons[gBattlerTarget].moves[i] == gCalledMove)
{
gCurrMovePos = i;
i = 4;
break;
}
}
if (i != 4 || gBattleMons[gBattlerAttacker].pp[gCurrMovePos] == 0)
if (i != 4 || gBattleMons[gBattlerTarget].pp[gCurrMovePos] == 0)
gBattlescriptCurrInstr = cmd->failInstr;
else
{
gBattlerTarget = gBattleStruct->lastMoveTarget[gBattlerAttacker];
gEffectBattler = gBattleStruct->lastMoveTarget[gBattlerTarget];
gHitMarker &= ~HITMARKER_ATTACKSTRING_PRINTED;
gBattleStruct->atkCancellerTracker = 0;
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, battler, gBattlerPartyIndexes[battler]);
gBattlescriptCurrInstr = cmd->nextInstr;
}
@@ -9855,7 +9909,6 @@ static void Cmd_various(void)
{
// Save sprite IDs, because trainer slide in will overwrite gBattlerSpriteIds variable.
gBattleScripting.savedDmg = (gBattlerSpriteIds[battler] & 0xFF) | (gBattlerSpriteIds[BATTLE_PARTNER(battler)] << 8);
HideBattlerShadowSprite(battler);
}
else if (cmd->case_ == 1)
{
@@ -10454,20 +10507,6 @@ static void Cmd_various(void)
}
return;
}
case VARIOUS_TRY_TAR_SHOT:
{
VARIOUS_ARGS(const u8 *failInstr);
if (gDisableStructs[battler].tarShot)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else
{
gDisableStructs[battler].tarShot = TRUE;
gBattlescriptCurrInstr = cmd->nextInstr;
}
return;
}
case VARIOUS_CAN_TAR_SHOT_WORK:
{
VARIOUS_ARGS(const u8 *failInstr);
@@ -10615,123 +10654,6 @@ static void Cmd_various(void)
gBattlescriptCurrInstr = cmd->nextInstr;
return;
}
case VARIOUS_TEATIME_TARGETS:
{
VARIOUS_ARGS(const u8 *jumpInstr);
u32 count = 0;
for (i = 0; i < gBattlersCount; i++)
{
if (IsTeatimeAffected(i))
count++;
}
if (count == 0)
gBattlescriptCurrInstr = cmd->jumpInstr; // Teatime fails
else
gBattlescriptCurrInstr = cmd->nextInstr;
return;
}
case VARIOUS_TEATIME_INVUL:
{
VARIOUS_ARGS(const u8 *jumpInstr);
if (ItemId_GetPocket(gBattleMons[battler].item) == POCKET_BERRIES && !(gStatuses3[gBattlerTarget] & (STATUS3_SEMI_INVULNERABLE)))
gBattlescriptCurrInstr = cmd->nextInstr;
else
gBattlescriptCurrInstr = cmd->jumpInstr;
return;
}
case VARIOUS_TRY_WIND_RIDER_POWER:
{
VARIOUS_ARGS(const u8 *failInstr);
u16 ability = GetBattlerAbility(battler);
if (GetBattlerSide(battler) == GetBattlerSide(gBattlerAttacker)
&& (ability == ABILITY_WIND_RIDER || ability == ABILITY_WIND_POWER))
{
gLastUsedAbility = ability;
RecordAbilityBattle(battler, gLastUsedAbility);
gBattlerAbility = gBattleScripting.battler = battler;
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
{
gBattlescriptCurrInstr = cmd->failInstr;
}
return;
}
case VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES:
{
VARIOUS_ARGS();
gBattlescriptCurrInstr = cmd->nextInstr;
AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, battler, 0, 0, 0);
return;
}
case VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES:
{
VARIOUS_ARGS();
gBattlescriptCurrInstr = cmd->nextInstr;
AbilityBattleEffects(ABILITYEFFECT_ON_TERRAIN, battler, 0, 0, 0);
return;
}
case VARIOUS_STORE_HEALING_WISH:
{
VARIOUS_ARGS();
if (gCurrentMove == MOVE_LUNAR_DANCE)
gBattleStruct->storedLunarDance |= 1u << battler;
else
gBattleStruct->storedHealingWish |= 1u << battler;
break;
}
case VARIOUS_HIT_SWITCH_TARGET_FAILED:
{
VARIOUS_ARGS();
gBattleStruct->hitSwitchTargetFailed = TRUE;
gBattlescriptCurrInstr = cmd->nextInstr;
return;
}
case VARIOUS_TRY_REVIVAL_BLESSING:
{
VARIOUS_ARGS(const u8 *failInstr);
u32 side = GetBattlerSide(gBattlerAttacker);
u8 index = GetFirstFaintedPartyIndex(gBattlerAttacker);
// Move fails if there are no battlers to revive.
if (index == PARTY_SIZE)
{
gBattlescriptCurrInstr = cmd->failInstr;
return;
}
// Battler selected! Revive and go to next instruction.
if (gSelectedMonPartyId != PARTY_SIZE)
{
struct Pokemon *party = GetSideParty(side);
u16 hp = GetMonData(&party[gSelectedMonPartyId], MON_DATA_MAX_HP) / 2;
BtlController_EmitSetMonData(gBattlerAttacker, BUFFER_A, REQUEST_HP_BATTLE, 1u << gSelectedMonPartyId, sizeof(hp), &hp);
MarkBattlerForControllerExec(gBattlerAttacker);
PREPARE_SPECIES_BUFFER(gBattleTextBuff1, GetMonData(&party[gSelectedMonPartyId], MON_DATA_SPECIES));
// If an on-field battler is revived, it needs to be sent out again.
if (IsDoubleBattle() &&
gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerAttacker)] == gSelectedMonPartyId)
{
gBattleScripting.battler = BATTLE_PARTNER(gBattlerAttacker);
gBattleCommunication[MULTIUSE_STATE] = TRUE;
}
gSelectedMonPartyId = PARTY_SIZE;
gBattlescriptCurrInstr = cmd->nextInstr;
return;
}
// Open party menu, wait to go to next instruction.
else
{
BtlController_EmitChoosePokemon(gBattlerAttacker, BUFFER_A, PARTY_ACTION_CHOOSE_FAINTED_MON, PARTY_SIZE, ABILITY_NONE, gBattleStruct->battlerPartyOrders[gBattlerAttacker]);
MarkBattlerForControllerExec(gBattlerAttacker);
}
return;
}
case VARIOUS_GET_BATTLERS_FOR_RECALL:
{
battler = 1;
@@ -11227,7 +11149,7 @@ static void Cmd_jumpifnotfirstturn(void)
const u8 *jumpInstr = cmd->jumpInstr;
if (gDisableStructs[gBattlerAttacker].isFirstTurn)
if (gDisableStructs[gBattlerAttacker].isFirstTurn && !(gSpecialStatuses[gBattlerAttacker].instructedChosenTarget))
gBattlescriptCurrInstr = cmd->nextInstr;
else
gBattlescriptCurrInstr = jumpInstr;
@@ -11346,7 +11268,7 @@ static void Cmd_stockpiletohpheal(void)
const u8 *failInstr = cmd->failInstr;
if (gDisableStructs[gBattlerAttacker].stockpileCounter == 0)
if (gDisableStructs[gBattlerAttacker].stockpileCounter == 0 && !gBattleStruct->snatchedMoveIsUsed)
{
gBattlescriptCurrInstr = failInstr;
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_SWALLOW_FAILED;
@@ -11362,14 +11284,22 @@ static void Cmd_stockpiletohpheal(void)
}
else
{
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / (1 << (3 - gDisableStructs[gBattlerAttacker].stockpileCounter));
if (gDisableStructs[gBattlerAttacker].stockpileCounter > 0)
{
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / (1 << (3 - gDisableStructs[gBattlerAttacker].stockpileCounter));
gBattleScripting.animTurn = gDisableStructs[gBattlerAttacker].stockpileCounter;
gBattleStruct->moveEffect2 = MOVE_EFFECT_STOCKPILE_WORE_OFF;
}
else // Snatched move
{
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 4;
gBattleScripting.animTurn = 1;
}
if (gBattleMoveDamage == 0)
gBattleMoveDamage = 1;
gBattleMoveDamage *= -1;
gBattleScripting.animTurn = gDisableStructs[gBattlerAttacker].stockpileCounter;
gBattleStruct->moveEffect2 = MOVE_EFFECT_STOCKPILE_WORE_OFF;
gBattlescriptCurrInstr = cmd->nextInstr;
gBattlerTarget = gBattlerAttacker;
}
@@ -13831,7 +13761,10 @@ static void Cmd_setcharge(void)
u8 battler = GetBattlerForBattleScript(cmd->battler);
gStatuses3[battler] |= STATUS3_CHARGED_UP;
gDisableStructs[battler].chargeTimer = 2;
if (B_CHARGE < GEN_9)
gDisableStructs[battler].chargeTimer = 2;
else
gDisableStructs[battler].chargeTimer = 0;
gBattlescriptCurrInstr++;
gBattlescriptCurrInstr = cmd->nextInstr;
}
@@ -15463,7 +15396,7 @@ static void Cmd_trainerslideout(void)
{
CMD_ARGS(u8 position);
u32 battler = GetBattlerAtPosition(cmd->position);
u32 battler = GetBattlerForBattleScript(cmd->position);
BtlController_EmitTrainerSlideBack(battler, BUFFER_A);
MarkBattlerForControllerExec(battler);
@@ -15936,7 +15869,6 @@ static void TryUpdateRoundTurnOrder(void)
for (i = 0; roundUsers[i] != 0xFF && i < 3; i++)
{
gBattlerByTurnOrder[currRounder] = roundUsers[i];
gActionsByTurnOrder[currRounder] = gActionsByTurnOrder[roundUsers[i]];
gProtectStructs[roundUsers[i]].quash = TRUE; // Make it so their turn order can't be changed again
currRounder++;
}
@@ -15945,7 +15877,6 @@ static void TryUpdateRoundTurnOrder(void)
for (i = 0; nonRoundUsers[i] != 0xFF && i < 3; i++)
{
gBattlerByTurnOrder[currRounder] = nonRoundUsers[i];
gActionsByTurnOrder[currRounder] = gActionsByTurnOrder[nonRoundUsers[i]];
currRounder++;
}
}
@@ -15999,7 +15930,7 @@ void ApplyExperienceMultipliers(s32 *expAmount, u8 expGetterMonId, u8 faintedBat
*expAmount = (*expAmount * 150) / 100;
if (B_UNEVOLVED_EXP_MULTIPLIER >= GEN_6 && IsMonPastEvolutionLevel(&gPlayerParty[expGetterMonId]))
*expAmount = (*expAmount * 4915) / 4096;
if (B_AFFECTION_MECHANICS == TRUE && GetBattlerAffectionHearts(expGetterMonId) >= AFFECTION_FOUR_HEARTS)
if (B_AFFECTION_MECHANICS == TRUE && GetMonAffectionHearts(&gPlayerParty[expGetterMonId]) >= AFFECTION_FOUR_HEARTS)
*expAmount = (*expAmount * 4915) / 4096;
if (CheckBagHasItem(ITEM_EXP_CHARM, 1)) //is also for other exp boosting Powers if/when implemented
*expAmount = (*expAmount * 150) / 100;
@@ -16476,7 +16407,7 @@ void BS_SetPledge(void)
u32 i = 0;
u32 k = 0;
if (gBattleStruct->pledgeMove)
if (gBattleStruct->pledgeMove && !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE))
{
PrepareStringBattle(STRINGID_USEDMOVE, gBattlerAttacker);
gHitMarker |= HITMARKER_ATTACKSTRING_PRINTED;
@@ -16505,6 +16436,8 @@ void BS_SetPledge(void)
else if ((gChosenActionByBattler[partner] == B_ACTION_USE_MOVE)
&& IsDoubleBattle()
&& IsBattlerAlive(partner)
&& GetBattlerTurnOrderNum(gBattlerAttacker) < GetBattlerTurnOrderNum(partner)
&& !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
&& gCurrentMove != partnerMove
&& gMovesInfo[partnerMove].effect == EFFECT_PLEDGE)
{
@@ -16543,8 +16476,8 @@ void BS_SetPledge(void)
}
else
{
gBattleStruct->pledgeMove = FALSE;
gBattlescriptCurrInstr = cmd->jumpInstr;
}
}
@@ -16914,7 +16847,7 @@ void BS_TryActivateGulpMissile(void)
void BS_TryQuash(void)
{
NATIVE_ARGS(const u8 *failInstr);
u32 i;
u32 i, j;
// It's true if foe is faster, has a bigger priority, or switches
if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget))
@@ -16925,20 +16858,18 @@ void BS_TryQuash(void)
// If the above condition is not true, it means we are faster than the foe, so we can set the quash bit
gProtectStructs[gBattlerTarget].quash = TRUE;
for (i = 0; i < gBattlersCount; i++)
// this implementation assumes turn order is correct when using Quash
i = GetBattlerTurnOrderNum(gBattlerTarget);
for (j = i + 1; j < gBattlersCount; j++)
{
gBattlerByTurnOrder[i] = i;
}
for (i = 0; i < gBattlersCount - 1; i++)
{
s32 j;
for (j = i + 1; j < gBattlersCount; j++)
{
if (!gProtectStructs[i].quash
&& !gProtectStructs[j].quash
&& GetWhichBattlerFaster(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], FALSE) == -1)
SwapTurnOrder(i, j);
}
// Gen 7- config makes target go last so that the order of quash targets is kept for the correct turn order
// Gen 8+ config alters Turn Order of the target according to speed, dynamic speed should handle the rest
if (B_QUASH_TURN_ORDER < GEN_8 || GetWhichBattlerFaster(gBattlerByTurnOrder[i], gBattlerByTurnOrder[j], FALSE) == -1)
SwapTurnOrder(i, j);
else
break;
i++;
}
gBattlescriptCurrInstr = cmd->nextInstr;
}
@@ -17017,3 +16948,160 @@ void BS_FickleBeamDamageCalculation(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
void BS_TryTarShot(void)
{
NATIVE_ARGS(const u8 *failInstr);
if (gDisableStructs[gBattlerTarget].tarShot || GetActiveGimmick(gBattlerTarget) == GIMMICK_TERA)
{
gBattlescriptCurrInstr = cmd->failInstr;
}
else
{
gDisableStructs[gBattlerTarget].tarShot = TRUE;
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
void BS_JumpIfBlockedBySoundproof(void)
{
NATIVE_ARGS(u8 battler, const u8 *jumpInstr);
u32 battler = GetBattlerForBattleScript(cmd->battler);
if (gMovesInfo[gCurrentMove].soundMove && GetBattlerAbility(battler) == ABILITY_SOUNDPROOF)
{
gLastUsedAbility = ABILITY_SOUNDPROOF;
gBattlescriptCurrInstr = cmd->jumpInstr;
RecordAbilityBattle(battler, gLastUsedAbility);
gBattlerAbility = battler;
}
else
{
gBattlescriptCurrInstr = cmd->nextInstr;
}
}
void BS_TeatimeInvul(void)
{
NATIVE_ARGS(u8 battler, const u8 *jumpInstr);
u32 battler = GetBattlerForBattleScript(cmd->battler);
if (ItemId_GetPocket(gBattleMons[battler].item) == POCKET_BERRIES && !(gStatuses3[gBattlerTarget] & (STATUS3_SEMI_INVULNERABLE)))
gBattlescriptCurrInstr = cmd->nextInstr;
else
gBattlescriptCurrInstr = cmd->jumpInstr;
}
void BS_TeatimeTargets(void)
{
NATIVE_ARGS(const u8 *failInstr);
u32 count = 0, i;
for (i = 0; i < gBattlersCount; i++)
{
if (IsTeatimeAffected(i))
count++;
}
if (count == 0)
gBattlescriptCurrInstr = cmd->failInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_TryWindRiderPower(void)
{
NATIVE_ARGS(u8 battler, const u8 *failInstr);
u32 battler = GetBattlerForBattleScript(cmd->battler);
u16 ability = GetBattlerAbility(battler);
if (GetBattlerSide(battler) == GetBattlerSide(gBattlerAttacker)
&& (ability == ABILITY_WIND_RIDER || ability == ABILITY_WIND_POWER))
{
gLastUsedAbility = ability;
RecordAbilityBattle(battler, gLastUsedAbility);
gBattlerAbility = gBattleScripting.battler = battler;
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
{
gBattlescriptCurrInstr = cmd->failInstr;
}
}
void BS_ActivateWeatherChangeAbilities(void)
{
NATIVE_ARGS(u8 battler);
u32 battler = GetBattlerForBattleScript(cmd->battler);
gBattlescriptCurrInstr = cmd->nextInstr;
AbilityBattleEffects(ABILITYEFFECT_ON_WEATHER, battler, 0, 0, 0);
}
void BS_ActivateTerrainChangeAbilities(void)
{
NATIVE_ARGS(u8 battler);
u32 battler = GetBattlerForBattleScript(cmd->battler);
gBattlescriptCurrInstr = cmd->nextInstr;
AbilityBattleEffects(ABILITYEFFECT_ON_TERRAIN, battler, 0, 0, 0);
}
void BS_StoreHealingWish(void)
{
NATIVE_ARGS(u8 battler);
u32 battler = GetBattlerForBattleScript(cmd->battler);
if (gCurrentMove == MOVE_LUNAR_DANCE)
gBattleStruct->storedLunarDance |= 1u << battler;
else
gBattleStruct->storedHealingWish |= 1u << battler;
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_HitSwitchTargetFailed(void)
{
NATIVE_ARGS();
gBattleStruct->hitSwitchTargetFailed = TRUE;
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_TryRevivalBlessing(void)
{
NATIVE_ARGS(const u8 *failInstr);
u32 side = GetBattlerSide(gBattlerAttacker);
u8 index = GetFirstFaintedPartyIndex(gBattlerAttacker);
// Move fails if there are no battlers to revive.
if (index == PARTY_SIZE)
{
gBattlescriptCurrInstr = cmd->failInstr;
return;
}
// Battler selected! Revive and go to next instruction.
if (gSelectedMonPartyId != PARTY_SIZE)
{
struct Pokemon *party = GetSideParty(side);
u16 hp = GetMonData(&party[gSelectedMonPartyId], MON_DATA_MAX_HP) / 2;
BtlController_EmitSetMonData(gBattlerAttacker, BUFFER_A, REQUEST_HP_BATTLE, 1u << gSelectedMonPartyId, sizeof(hp), &hp);
MarkBattlerForControllerExec(gBattlerAttacker);
PREPARE_SPECIES_BUFFER(gBattleTextBuff1, GetMonData(&party[gSelectedMonPartyId], MON_DATA_SPECIES));
// If an on-field battler is revived, it needs to be sent out again.
if (IsDoubleBattle() &&
gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerAttacker)] == gSelectedMonPartyId)
{
gBattleScripting.battler = BATTLE_PARTNER(gBattlerAttacker);
gBattleCommunication[MULTIUSE_STATE] = TRUE;
}
gSelectedMonPartyId = PARTY_SIZE;
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
{
// Open party menu, wait to go to next instruction.
BtlController_EmitChoosePokemon(gBattlerAttacker, BUFFER_A, PARTY_ACTION_CHOOSE_FAINTED_MON, PARTY_SIZE, ABILITY_NONE, gBattleStruct->battlerPartyOrders[gBattlerAttacker]);
MarkBattlerForControllerExec(gBattlerAttacker);
}
}

View File

@@ -122,6 +122,8 @@ void HandleAction_UseMove(void)
gBattleScripting.savedMoveEffect = 0;
gCurrMovePos = gChosenMovePos = *(gBattleStruct->chosenMovePositions + gBattlerAttacker);
gBattleStruct->obedienceResult = GetAttackerObedienceForAction();
// choose move
if (gProtectStructs[gBattlerAttacker].noValidMoves)
{
@@ -325,7 +327,7 @@ void HandleAction_UseMove(void)
else
{
gBattlerTarget = *(gBattleStruct->moveTarget + gBattlerAttacker);
if (!IsBattlerAlive(gBattlerTarget))
if (!IsBattlerAlive(gBattlerTarget) && moveTarget != MOVE_TARGET_OPPONENTS_FIELD)
{
if (GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gBattlerTarget))
{
@@ -1212,15 +1214,15 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler)
{
gBattleScripting.battler = battler;
gCurrentMove = move;
gSelectionBattleScripts[battler] = BattleScript_SelectingDisabledMove;
limitations++;
gSelectionBattleScripts[battler] = BattleScript_SelectingDisabledMove;
limitations++;
}
if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && move == gLastMoves[battler] && move != MOVE_STRUGGLE && (gBattleMons[battler].status2 & STATUS2_TORMENT))
{
CancelMultiTurnMoves(battler);
gSelectionBattleScripts[battler] = BattleScript_SelectingTormentedMove;
limitations++;
gSelectionBattleScripts[battler] = BattleScript_SelectingTormentedMove;
limitations++;
}
if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[battler].tauntTimer != 0 && IS_MOVE_STATUS(move))
@@ -1229,58 +1231,58 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler)
gCurrentMove = MOVE_MAX_GUARD;
else
gCurrentMove = move;
gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveTaunt;
limitations++;
gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveTaunt;
limitations++;
}
if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && gDisableStructs[battler].throatChopTimer != 0 && gMovesInfo[move].soundMove)
{
gCurrentMove = move;
gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveThroatChop;
limitations++;
gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveThroatChop;
limitations++;
}
if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && GetImprisonedMovesCount(battler, move))
{
gCurrentMove = move;
gSelectionBattleScripts[battler] = BattleScript_SelectingImprisonedMove;
limitations++;
gSelectionBattleScripts[battler] = BattleScript_SelectingImprisonedMove;
limitations++;
}
if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && IsGravityPreventingMove(move))
{
gCurrentMove = move;
gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveGravity;
limitations++;
gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveGravity;
limitations++;
}
if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && IsHealBlockPreventingMove(battler, move))
{
gCurrentMove = move;
gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveHealBlock;
limitations++;
gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveHealBlock;
limitations++;
}
if (DYNAMAX_BYPASS_CHECK && GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE && IsBelchPreventingMove(battler, move))
{
gCurrentMove = move;
gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedBelch;
limitations++;
gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedBelch;
limitations++;
}
if (DYNAMAX_BYPASS_CHECK && gMovesInfo[move].effect == EFFECT_STUFF_CHEEKS && ItemId_GetPocket(gBattleMons[battler].item) != POCKET_BERRIES)
{
gCurrentMove = move;
gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedStuffCheeks;
limitations++;
gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedStuffCheeks;
limitations++;
}
if (gMovesInfo[move].cantUseTwice && move == gLastResultingMoves[battler])
{
gCurrentMove = move;
PREPARE_MOVE_BUFFER(gBattleTextBuff1, gCurrentMove);
gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedCurrentMove;
limitations++;
gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedCurrentMove;
limitations++;
}
gPotentialItemEffectBattler = battler;
@@ -1288,8 +1290,8 @@ u32 TrySetCantSelectMoveBattleScript(u32 battler)
{
gCurrentMove = *choicedMove;
gLastUsedItem = gBattleMons[battler].item;
gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveChoiceItem;
limitations++;
gSelectionBattleScripts[battler] = BattleScript_SelectingNotAllowedMoveChoiceItem;
limitations++;
}
else if (holdEffect == HOLD_EFFECT_ASSAULT_VEST && IS_MOVE_STATUS(move) && gMovesInfo[move].effect != EFFECT_ME_FIRST)
{
@@ -2127,8 +2129,8 @@ enum
ENDTURN_HEALBLOCK,
ENDTURN_EMBARGO,
ENDTURN_LOCK_ON,
ENDTURN_CHARGE,
ENDTURN_LASER_FOCUS,
ENDTURN_CHARGE,
ENDTURN_TAUNT,
ENDTURN_YAWN,
ENDTURN_ITEMS2,
@@ -2145,6 +2147,7 @@ enum
ENDTURN_DYNAMAX,
ENDTURN_GMAX_MOVE_RESIDUAL_DAMAGE,
ENDTURN_SEA_OF_FIRE_DAMAGE,
ENDTURN_ITEMS3,
ENDTURN_BATTLER_COUNT
};
@@ -2277,6 +2280,15 @@ u8 DoBattlerEndTurnEffects(void)
effect++;
gBattleStruct->turnEffectsTracker++;
break;
case ENDTURN_ITEMS3: // berry effects
if (gItemsInfo[gBattleMons[battler].item].pocket == POCKET_BERRIES
|| GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_RESTORE_HP) // Edge case for Berry Juice
{
if (ItemBattleEffects(ITEMEFFECT_NORMAL, battler, FALSE))
effect++;
}
gBattleStruct->turnEffectsTracker++;
break;
case ENDTURN_ORBS:
if (IsBattlerAlive(battler) && ItemBattleEffects(ITEMEFFECT_ORBS, battler, FALSE))
effect++;
@@ -2591,7 +2603,7 @@ u8 DoBattlerEndTurnEffects(void)
gBattleStruct->turnEffectsTracker++;
break;
case ENDTURN_CHARGE: // charge
if (gDisableStructs[battler].chargeTimer && --gDisableStructs[battler].chargeTimer == 0)
if (gDisableStructs[battler].chargeTimer > 0 && --gDisableStructs[battler].chargeTimer == 0)
gStatuses3[battler] &= ~STATUS3_CHARGED_UP;
gBattleStruct->turnEffectsTracker++;
break;
@@ -2708,9 +2720,11 @@ u8 DoBattlerEndTurnEffects(void)
case ENDTURN_ELECTRIFY:
gStatuses4[battler] &= ~STATUS4_ELECTRIFIED;
gBattleStruct->turnEffectsTracker++;
break;
case ENDTURN_POWDER:
gBattleMons[battler].status2 &= ~STATUS2_POWDER;
gBattleStruct->turnEffectsTracker++;
break;
case ENDTURN_THROAT_CHOP:
if (gDisableStructs[battler].throatChopTimer && --gDisableStructs[battler].throatChopTimer == 0)
{
@@ -2778,7 +2792,7 @@ u8 DoBattlerEndTurnEffects(void)
&& --gBattleStruct->dynamax.dynamaxTurns[battler] == 0)
{
gBattleScripting.battler = battler;
UndoDynamax(battler);
UndoDynamax(battler);
BattleScriptExecute(BattleScript_DynamaxEnds);
effect++;
}
@@ -3041,7 +3055,7 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType)
case CANCELLER_FLAGS: // flags clear
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_DESTINY_BOND;
gStatuses3[gBattlerAttacker] &= ~STATUS3_GRUDGE;
gStatuses4[gBattlerAttacker] &= ~ STATUS4_GLAIVE_RUSH;
gStatuses4[gBattlerAttacker] &= ~STATUS4_GLAIVE_RUSH;
gBattleStruct->atkCancellerTracker++;
break;
case CANCELLER_SKY_DROP:
@@ -3104,7 +3118,7 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType)
if (!RandomPercentage(RNG_FROZEN, 20))
{
gBattlescriptCurrInstr = BattleScript_MoveUsedIsFrozen;
gHitMarker |= HITMARKER_NO_ATTACKSTRING;
gHitMarker |= (HITMARKER_NO_ATTACKSTRING | HITMARKER_UNABLE_TO_USE_MOVE);
}
else // unfreeze
{
@@ -3366,11 +3380,21 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType)
case CANCELLER_POWDER_STATUS:
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_POWDER)
{
if (moveType == TYPE_FIRE)
u32 partnerMove = gBattleMons[BATTLE_PARTNER(gBattlerAttacker)].moves[gBattleStruct->chosenMovePositions[BATTLE_PARTNER(gBattlerAttacker)]];
if ((moveType == TYPE_FIRE && !gBattleStruct->pledgeMove)
|| (gCurrentMove == MOVE_FIRE_PLEDGE && partnerMove == MOVE_GRASS_PLEDGE)
|| (gCurrentMove == MOVE_GRASS_PLEDGE && partnerMove == MOVE_FIRE_PLEDGE && gBattleStruct->pledgeMove))
{
gProtectStructs[gBattlerAttacker].powderSelfDmg = TRUE;
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 4;
gBattlescriptCurrInstr = BattleScript_MoveUsedPowder;
if (GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD
&& (B_POWDER_RAIN < GEN_7 || !IsBattlerWeatherAffected(gBattlerAttacker, B_WEATHER_RAIN_PRIMAL)))
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 4;
if (GetActiveGimmick(gBattlerAttacker) != GIMMICK_Z_MOVE
|| gBattleStruct->obedienceResult != OBEYS
|| HasTrainerUsedGimmick(gBattlerAttacker, GIMMICK_Z_MOVE))
gBattlescriptCurrInstr = BattleScript_MoveUsedPowder;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
effect = 1;
}
}
@@ -3388,7 +3412,8 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType)
gBattleStruct->atkCancellerTracker++;
break;
case CANCELLER_Z_MOVES:
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE)
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE
&& gBattleStruct->obedienceResult == OBEYS)
{
// For Z-Mirror Move, so it doesn't play the animation twice.
bool32 alreadyUsed = HasTrainerUsedGimmick(gBattlerAttacker, GIMMICK_Z_MOVE);
@@ -3398,7 +3423,15 @@ u8 AtkCanceller_UnableToUseMove(u32 moveType)
SetGimmickAsActivated(gBattlerAttacker, GIMMICK_Z_MOVE);
gBattleScripting.battler = gBattlerAttacker;
if (gMovesInfo[gCurrentMove].category == DAMAGE_CATEGORY_STATUS)
if (gProtectStructs[gBattlerAttacker].powderSelfDmg)
{
if (!alreadyUsed)
{
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_ZMoveActivatePowder;
}
}
else if (gMovesInfo[gCurrentMove].category == DAMAGE_CATEGORY_STATUS)
{
if (!alreadyUsed)
{
@@ -4157,13 +4190,10 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (gSpecialStatuses[battler].switchInAbilityDone)
break;
if (gBattleResources->flags->flags[battler] & RESOURCE_FLAG_TRACED)
break;
side = (BATTLE_OPPOSITE(GetBattlerPosition(battler))) & BIT_SIDE;
target1 = GetBattlerAtPosition(side);
target2 = GetBattlerAtPosition(side + BIT_FLANK);
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
if (IsDoubleBattle())
{
if (!gAbilitiesInfo[gBattleMons[target1].ability].cantBeTraced && gBattleMons[target1].hp != 0
@@ -4182,11 +4212,10 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (effect != 0)
{
BattleScriptPushCursorAndCallback(BattleScript_TraceActivatesEnd3);
gBattleResources->flags->flags[battler] &= ~RESOURCE_FLAG_TRACED;
BattleScriptPushCursorAndCallback(BattleScript_TraceActivates);
gBattleStruct->tracedAbility[battler] = gLastUsedAbility = gBattleMons[chosenTarget].ability;
RecordAbilityBattle(chosenTarget, gLastUsedAbility); // Record the opposing battler has this ability
battler = gBattlerAbility = gBattleScripting.battler = battler;
gBattlerAbility = battler;
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, chosenTarget, gBattlerPartyIndexes[chosenTarget])
PREPARE_ABILITY_BUFFER(gBattleTextBuff2, gLastUsedAbility)
@@ -5003,7 +5032,6 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
break;
case ABILITY_GOOD_AS_GOLD:
if (IS_MOVE_STATUS(gCurrentMove)
&& !(moveTarget & MOVE_TARGET_USER)
&& !(moveTarget & MOVE_TARGET_OPPONENTS_FIELD)
&& !(moveTarget & MOVE_TARGET_ALL_BATTLERS))
effect = 3;
@@ -5119,6 +5147,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
if (caseID == ABILITYEFFECT_WOULD_ABSORB)
{
gBattleStruct->pledgeMove = FALSE;
if (effect && gLastUsedAbility != 0xFFFF)
RecordAbilityBattle(battler, gLastUsedAbility);
@@ -5126,6 +5155,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
else if (effect == 1) // Drain Hp ability.
{
gBattleStruct->pledgeMove = FALSE;
if (BATTLER_MAX_HP(battler) || (B_HEAL_BLOCKING >= GEN_5 && gStatuses3[battler] & STATUS3_HEAL_BLOCK))
{
if ((gProtectStructs[gBattlerAttacker].notFirstStrike))
@@ -5148,6 +5178,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
else if (effect == 2) // Boost Stat ability;
{
gBattleStruct->pledgeMove = FALSE;
if (!CompareStat(battler, statId, MAX_STAT_STAGE, CMP_LESS_THAN))
{
if ((gProtectStructs[gBattlerAttacker].notFirstStrike))
@@ -5169,6 +5200,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
else if (effect == 3)
{
gBattleStruct->pledgeMove = FALSE;
if (!(gBattleResources->flags->flags[battler] & RESOURCE_FLAG_FLASH_FIRE))
{
gBattleCommunication[MULTISTRING_CHOOSER] = B_MSG_FLASH_FIRE_BOOST;
@@ -5739,7 +5771,6 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
&& HadMoreThanHalfHpNowDoesnt(gBattlerTarget)
&& !(TestIfSheerForceAffected(gBattlerAttacker, gCurrentMove)))
{
gBattlerAttacker = gBattlerTarget;
BattleScriptPushCursor();
gBattlescriptCurrInstr = BattleScript_AngerShellActivates;
effect++;
@@ -5860,7 +5891,6 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (IsBattlerAlive(battler)
&& (gMovesInfo[gCurrentMove].danceMove)
&& !gSpecialStatuses[battler].dancerUsedMove
&& (gHitMarker & HITMARKER_ATTACKSTRING_PRINTED)
&& gBattlerAttacker != battler)
{
// Set bit and save Dancer mon's original target
@@ -6174,14 +6204,14 @@ bool32 TryPrimalReversion(u32 battler)
{
if (gBattlerAttacker == battler)
{
BattleScriptExecute(BattleScript_PrimalReversion);
BattleScriptPushCursorAndCallback(BattleScript_PrimalReversion);
}
else
{
// edge case for scenarios like a switch-in after activated eject button
gBattleScripting.savedBattler = gBattlerAttacker;
gBattlerAttacker = battler;
BattleScriptExecute(BattleScript_PrimalReversionRestoreAttacker);
BattleScriptPushCursorAndCallback(BattleScript_PrimalReversionRestoreAttacker);
}
return TRUE;
}
@@ -6611,8 +6641,7 @@ static u8 TrySetMicleBerry(u32 battler, u32 itemId, bool32 end2)
{
if (HasEnoughHpToEatBerry(battler, 4, itemId))
{
gProtectStructs[battler].usedMicleBerry = TRUE; // battler's next attack has increased accuracy
gBattleStruct->usedMicleBerry |= 1u << battler;
if (end2)
{
BattleScriptExecute(BattleScript_MicleBerryActivateEnd2);
@@ -8120,11 +8149,13 @@ u32 GetMoveTarget(u16 move, u8 setTarget)
case MOVE_TARGET_DEPENDS:
case MOVE_TARGET_BOTH:
case MOVE_TARGET_FOES_AND_ALLY:
case MOVE_TARGET_OPPONENTS_FIELD:
targetBattler = GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerSide(gBattlerAttacker)));
if (!IsBattlerAlive(targetBattler))
targetBattler ^= BIT_FLANK;
break;
case MOVE_TARGET_OPPONENTS_FIELD:
targetBattler = GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerSide(gBattlerAttacker)));
break;
case MOVE_TARGET_RANDOM:
side = BATTLE_OPPOSITE(GetBattlerSide(gBattlerAttacker));
if (IsAffectedByFollowMe(gBattlerAttacker, side, move))
@@ -8152,12 +8183,7 @@ u32 GetMoveTarget(u16 move, u8 setTarget)
return targetBattler;
}
static bool32 IsBattlerModernFatefulEncounter(u32 battler)
{
return TRUE;
}
u8 IsMonDisobedient(void)
u8 GetAttackerObedienceForAction()
{
s32 rnd;
s32 calc;
@@ -8165,34 +8191,31 @@ u8 IsMonDisobedient(void)
u8 levelReferenced;
if (gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_RECORDED_LINK))
return 0;
return OBEYS;
if (BattlerHasAi(gBattlerAttacker))
return 0;
return OBEYS;
if ((gBattleTypeFlags & (BATTLE_TYPE_POKEDUDE)))
return 0;
return OBEYS;
if (IsBattlerModernFatefulEncounter(gBattlerAttacker)) // only false if illegal Mew or Deoxys
{
if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && GetBattlerPosition(gBattlerAttacker) == B_POSITION_PLAYER_RIGHT)
return 0;
if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER)
return 0;
if (gBattleTypeFlags & BATTLE_TYPE_RECORDED)
return 0;
if (B_OBEDIENCE_MECHANICS < GEN_8 && !IsOtherTrainer(gBattleMons[gBattlerAttacker].otId, gBattleMons[gBattlerAttacker].otName))
return 0;
if (FlagGet(FLAG_BADGE08_GET)) // Rain Badge, ignore obedience altogether
return 0;
if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && GetBattlerPosition(gBattlerAttacker) == B_POSITION_PLAYER_RIGHT)
return OBEYS;
if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER)
return OBEYS;
if (gBattleTypeFlags & BATTLE_TYPE_RECORDED)
return OBEYS;
if (B_OBEDIENCE_MECHANICS < GEN_8 && !IsOtherTrainer(gBattleMons[gBattlerAttacker].otId, gBattleMons[gBattlerAttacker].otName))
return OBEYS;
if (FlagGet(FLAG_BADGE08_GET)) // Rain Badge, ignore obedience altogether
return OBEYS;
obedienceLevel = 10;
obedienceLevel = 10;
if (FlagGet(FLAG_BADGE02_GET))
obedienceLevel = 30;
if (FlagGet(FLAG_BADGE04_GET))
obedienceLevel = 50;
if (FlagGet(FLAG_BADGE06_GET))
obedienceLevel = 70;
}
if (FlagGet(FLAG_BADGE02_GET))
obedienceLevel = 30;
if (FlagGet(FLAG_BADGE04_GET))
obedienceLevel = 50;
if (FlagGet(FLAG_BADGE06_GET))
obedienceLevel = 70;
if (B_OBEDIENCE_MECHANICS >= GEN_8
&& !IsOtherTrainer(gBattleMons[gBattlerAttacker].otId, gBattleMons[gBattlerAttacker].otName))
@@ -8201,86 +8224,58 @@ u8 IsMonDisobedient(void)
levelReferenced = gBattleMons[gBattlerAttacker].level;
if (levelReferenced <= obedienceLevel)
return 0;
rnd = (Random() & 255);
calc = (levelReferenced + obedienceLevel) * rnd >> 8;
return OBEYS;
rnd = Random();
calc = (levelReferenced + obedienceLevel) * (rnd & 255) >> 8;
if (calc < obedienceLevel)
return 0;
return OBEYS;
// Clear the Z-Move flags if the battler is disobedient as to not waste the Z-Move
if (GetActiveGimmick(gBattlerAttacker) == GIMMICK_Z_MOVE)
{
gBattleStruct->gimmick.activated[gBattlerAttacker][GIMMICK_Z_MOVE] = FALSE;
gBattleStruct->gimmick.activeGimmick[GetBattlerSide(gBattlerAttacker)][gBattlerPartyIndexes[gBattlerAttacker]] = GIMMICK_NONE;
}
// is not obedient
if (gCurrentMove == MOVE_RAGE)
gBattleMons[gBattlerAttacker].status2 &= ~STATUS2_RAGE;
if (gBattleMons[gBattlerAttacker].status1 & STATUS1_SLEEP && (gMovesInfo[gCurrentMove].effect == EFFECT_SNORE || gMovesInfo[gCurrentMove].effect == EFFECT_SLEEP_TALK))
{
gBattlescriptCurrInstr = BattleScript_IgnoresWhileAsleep;
return 1;
}
return DISOBEYS_WHILE_ASLEEP;
rnd = (Random() & 255);
calc = (levelReferenced + obedienceLevel) * rnd >> 8;
calc = (levelReferenced + obedienceLevel) * ((rnd >> 8) & 255) >> 8;
if (calc < obedienceLevel)
{
calc = CheckMoveLimitations(gBattlerAttacker, 1u << gCurrMovePos, MOVE_LIMITATIONS_ALL);
if (calc == ALL_MOVES_MASK) // all moves cannot be used
{
// Randomly select, then print a disobedient string
// B_MSG_LOAFING, B_MSG_WONT_OBEY, B_MSG_TURNED_AWAY, or B_MSG_PRETEND_NOT_NOTICE
gBattleCommunication[MULTISTRING_CHOOSER] = MOD(Random(), NUM_LOAF_STRINGS);
gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround;
return 1;
}
return DISOBEYS_LOAFS;
else // use a random move
{
do
{
gCurrMovePos = gChosenMovePos = MOD(Random(), MAX_MON_MOVES);
} while ((1u << gCurrMovePos) & calc);
gCalledMove = gBattleMons[gBattlerAttacker].moves[gCurrMovePos];
SetAtkCancellerForCalledMove();
gBattlescriptCurrInstr = BattleScript_IgnoresAndUsesRandomMove;
gBattlerTarget = GetMoveTarget(gCalledMove, NO_TARGET_OVERRIDE);
gHitMarker |= HITMARKER_DISOBEDIENT_MOVE;
return 2;
}
while ((1u << gCurrMovePos) & calc);
return DISOBEYS_RANDOM_MOVE;
}
else
{
obedienceLevel = levelReferenced - obedienceLevel;
calc = (Random() & 255);
calc = ((rnd >> 16) & 255);
if (calc < obedienceLevel && CanBeSlept(gBattlerAttacker, GetBattlerAbility(gBattlerAttacker)))
{
// try putting asleep
int i;
for (i = 0; i < gBattlersCount; i++)
{
if (gBattleMons[i].status2 & STATUS2_UPROAR)
break;
}
if (i == gBattlersCount)
{
gBattlescriptCurrInstr = BattleScript_IgnoresAndFallsAsleep;
return 1;
}
return DISOBEYS_FALL_ASLEEP;
}
calc -= obedienceLevel;
if (calc < obedienceLevel)
{
gBattleMoveDamage = CalculateMoveDamage(MOVE_NONE, gBattlerAttacker, gBattlerAttacker, TYPE_MYSTERY, 40, FALSE, FALSE, TRUE);
gBattlerTarget = gBattlerAttacker;
gBattlescriptCurrInstr = BattleScript_IgnoresAndHitsItself;
gHitMarker |= HITMARKER_UNABLE_TO_USE_MOVE;
return 2;
}
return DISOBEYS_HITS_SELF;
else
{
// Randomly select, then print a disobedient string
// B_MSG_LOAFING, B_MSG_WONT_OBEY, B_MSG_TURNED_AWAY, or B_MSG_PRETEND_NOT_NOTICE
gBattleCommunication[MULTISTRING_CHOOSER] = MOD(Random(), NUM_LOAF_STRINGS);
gBattlescriptCurrInstr = BattleScript_MoveUsedLoafingAround;
return 1;
}
return DISOBEYS_LOAFS;
}
}
@@ -8373,13 +8368,9 @@ bool32 IsBattlerProtected(u32 battlerAtk, u32 battlerDef, u32 move)
if (gProtectStructs[battlerDef].maxGuarded && IsMoveBlockedByMaxGuard(move))
return TRUE;
// Protective Pads doesn't stop Unseen Fist from bypassing Protect effects, so IsMoveMakingContact() isn't used here.
// This means extra logic is needed to handle Shell Side Arm.
if (GetBattlerAbility(gBattlerAttacker) == ABILITY_UNSEEN_FIST
&& (gMovesInfo[move].makesContact
|| (gMovesInfo[move].effect == EFFECT_SHELL_SIDE_ARM
&& gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] == DAMAGE_CATEGORY_PHYSICAL))
&& !gProtectStructs[battlerDef].maxGuarded) // Max Guard cannot be bypassed by Unseen Fist
if (!gProtectStructs[battlerDef].maxGuarded // Max Guard cannot be bypassed by Unseen Fist
&& IsMoveMakingContact(move, gBattlerAttacker)
&& GetBattlerAbility(gBattlerAttacker) == ABILITY_UNSEEN_FIST)
return FALSE;
else if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_CRAFTY_SHIELD
&& IS_MOVE_STATUS(move))
@@ -8520,7 +8511,7 @@ u32 CountBattlerStatIncreases(u32 battler, bool32 countEvasionAcc)
u32 GetMoveTargetCount(u32 move, u32 battlerAtk, u32 battlerDef)
{
switch (GetBattlerMoveTargetType(gBattlerAttacker, move))
switch (GetBattlerMoveTargetType(battlerAtk, move))
{
case MOVE_TARGET_BOTH:
return !(gAbsentBattlerFlags & (1u << battlerDef))
@@ -8666,6 +8657,9 @@ static inline u32 CalcMoveBasePower(u32 move, u32 battlerAtk, u32 battlerDef, u3
if (GetActiveGimmick(battlerAtk) == GIMMICK_Z_MOVE)
return GetZMovePower(gBattleStruct->zmove.baseMoves[battlerAtk]);
if (GetActiveGimmick(battlerAtk) == GIMMICK_DYNAMAX)
return GetMaxMovePower(move);
switch (gMovesInfo[move].effect)
{
case EFFECT_PLEDGE:
@@ -9265,6 +9259,10 @@ static inline u32 CalcMoveBasePowerAfterModifiers(u32 move, u32 battlerAtk, u32
if (gMovesInfo[move].punchingMove)
modifier = uq4_12_multiply(modifier, UQ_4_12(1.1));
break;
case HOLD_EFFECT_OGERPON_MASK:
if (GET_BASE_SPECIES_ID(gBattleMons[battlerAtk].species) == SPECIES_OGERPON)
modifier = uq4_12_multiply(modifier, UQ_4_12(1.2));
break;
}
// Terastallization boosts weak, non-priority, non-multi hit moves after modifiers to 60 BP.
@@ -11267,14 +11265,11 @@ bool32 IsBattlerWeatherAffected(u32 battler, u32 weatherFlags)
// Possible return values are defined in battle.h following MOVE_TARGET_SELECTED
u32 GetBattlerMoveTargetType(u32 battler, u32 move)
{
if (move == MOVE_CURSE
&& !IS_BATTLER_OF_TYPE(battler, TYPE_GHOST))
if (move == MOVE_CURSE && !IS_BATTLER_OF_TYPE(battler, TYPE_GHOST))
return MOVE_TARGET_USER;
else if (gMovesInfo[move].effect == EFFECT_EXPANDING_FORCE
&& IsBattlerTerrainAffected(battler, STATUS_FIELD_PSYCHIC_TERRAIN))
if (gMovesInfo[move].effect == EFFECT_EXPANDING_FORCE && IsBattlerTerrainAffected(battler, STATUS_FIELD_PSYCHIC_TERRAIN))
return MOVE_TARGET_BOTH;
else if (gMovesInfo[move].effect == EFFECT_TERA_STARSTORM
&& gBattleMons[battler].species == SPECIES_TERAPAGOS_STELLAR)
if (gMovesInfo[move].effect == EFFECT_TERA_STARSTORM && gBattleMons[battler].species == SPECIES_TERAPAGOS_STELLAR)
return MOVE_TARGET_BOTH;
return gMovesInfo[move].target;
@@ -11617,23 +11612,28 @@ bool32 CanTargetPartner(u32 battlerAtk, u32 battlerDef)
&& battlerDef != BATTLE_PARTNER(battlerAtk));
}
static inline bool32 DoesCurrentTargetHaveAbilityImmunity(void)
static inline bool32 DoesBattlerHaveAbilityImmunity(u32 battlerDef)
{
return (AbilityBattleEffects(ABILITYEFFECT_WOULD_BLOCK, gBattlerTarget, 0, 0, 0)
|| AbilityBattleEffects(ABILITYEFFECT_WOULD_ABSORB, gBattlerTarget, 0, 0, 0));
return (AbilityBattleEffects(ABILITYEFFECT_WOULD_BLOCK, battlerDef, 0, 0, 0)
|| AbilityBattleEffects(ABILITYEFFECT_WOULD_ABSORB, battlerDef, 0, 0, 0));
}
bool32 TargetFullyImmuneToCurrMove(u32 BattlerAtk, u32 battlerDef)
bool32 TargetFullyImmuneToCurrMove(u32 battlerAtk, u32 battlerDef)
{
return ((CalcTypeEffectivenessMultiplier(gCurrentMove, GetMoveType(gCurrentMove), BattlerAtk, battlerDef, GetBattlerAbility(battlerDef), FALSE) == UQ_4_12(0.0))
|| IsBattlerProtected(BattlerAtk, battlerDef, gCurrentMove)
return ((CalcTypeEffectivenessMultiplier(gCurrentMove, GetMoveType(gCurrentMove), battlerAtk, battlerDef, GetBattlerAbility(battlerDef), FALSE) == UQ_4_12(0.0))
|| IsBattlerProtected(battlerAtk, battlerDef, gCurrentMove)
|| IsSemiInvulnerable(battlerDef, gCurrentMove)
|| DoesCurrentTargetHaveAbilityImmunity());
|| DoesBattlerHaveAbilityImmunity(battlerDef));
}
u32 GetMoveType(u32 move)
{
if (gMain.inBattle && gBattleStruct->dynamicMoveType)
return gBattleStruct->dynamicMoveType & DYNAMIC_TYPE_MASK;
else if (B_UPDATED_MOVE_TYPES < GEN_5
&& (move == MOVE_BEAT_UP
|| move == MOVE_FUTURE_SIGHT
|| move == MOVE_DOOM_DESIRE))
return TYPE_MYSTERY;
return gMovesInfo[move].type;
}

View File

@@ -1,7 +1,7 @@
#include "global.h"
#include "battle.h"
#include "event_data.h"
#include "level_caps.h"
#include "caps.h"
#include "pokemon.h"
@@ -81,3 +81,39 @@ u32 GetSoftLevelCapExpValue(u32 level, u32 expValue)
return expValue;
}
}
u32 GetCurrentEVCap(void)
{
static const u16 sEvCapFlagMap[][2] = {
// Define EV caps for each milestone
{FLAG_BADGE01_GET, 30},
{FLAG_BADGE02_GET, 90},
{FLAG_BADGE03_GET, 150},
{FLAG_BADGE04_GET, 210},
{FLAG_BADGE05_GET, 270},
{FLAG_BADGE06_GET, 330},
{FLAG_BADGE07_GET, 390},
{FLAG_BADGE08_GET, 450},
{FLAG_SYS_GAME_CLEAR, MAX_TOTAL_EVS},
};
if (B_EV_CAP_TYPE == EV_CAP_FLAG_LIST)
{
for (u32 evCap = 0; evCap < ARRAY_COUNT(sEvCapFlagMap); evCap++)
{
if (!FlagGet(sEvCapFlagMap[evCap][0]))
return sEvCapFlagMap[evCap][1];
}
}
else if (B_EV_CAP_TYPE == EV_CAP_VARIABLE)
{
return VarGet(B_EV_CAP_VARIABLE);
}
else if (B_EV_CAP_TYPE == EV_CAP_NO_GAIN)
{
return 0;
}
return MAX_TOTAL_EVS;
}

View File

@@ -110,11 +110,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_COMPOUND_EYES] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Compound Eyes"),
#else
.name = _("CompoundEyes"),
#endif
.description = COMPOUND_STRING("Raises accuracy."),
.aiRating = 7,
},
@@ -244,11 +240,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_LIGHTNING_ROD] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Lightning Rod"),
#else
.name = _("LightningRod"),
#endif
.description = COMPOUND_STRING("Draws electrical moves."),
.aiRating = 7,
.breakable = TRUE,
@@ -1324,11 +1316,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_STANCE_CHANGE] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Stance Change"),
#else
.name = _("StanceChange"),
#endif
.description = COMPOUND_STRING("Transforms as it battles."),
.aiRating = 10,
.cantBeCopied = TRUE,
@@ -1347,11 +1335,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_MEGA_LAUNCHER] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Mega Launcher"),
#else
.name = _("MegaLauncher"),
#endif
.description = COMPOUND_STRING("Boosts pulse moves."),
.aiRating = 7,
},
@@ -1401,11 +1385,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_PARENTAL_BOND] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Parental Bond"),
#else
.name = _("ParentalBond"),
#endif
.description = COMPOUND_STRING("Moves hit twice."),
.aiRating = 10,
},
@@ -1433,22 +1413,14 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_PRIMORDIAL_SEA] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Primordial Sea"),
#else
.name = _("PrimrdialSea"),
#endif
.description = COMPOUND_STRING("Summons heavy rain."),
.aiRating = 10,
},
[ABILITY_DESOLATE_LAND] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Desolate Land"),
#else
.name = _("DesolateLand"),
#endif
.description = COMPOUND_STRING("Summons intense sunlight."),
.aiRating = 10,
},
@@ -1476,22 +1448,14 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_EMERGENCY_EXIT] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Emergency Exit"),
#else
.name = _("EmergncyExit"),
#endif
.description = COMPOUND_STRING("Flees at half HP."),
.aiRating = 3,
},
[ABILITY_WATER_COMPACTION] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Water Compaction"),
#else
.name = _("WtrCmpaction"),
#endif
.description = COMPOUND_STRING("Water boosts Defense."),
.aiRating = 4,
},
@@ -1625,11 +1589,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_POWER_CONSTRUCT] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Power Construct"),
#else
.name = _("PwrConstruct"),
#endif
.description = COMPOUND_STRING("Cells aid it when weakened."),
.aiRating = 10,
.cantBeCopied = TRUE,
@@ -1660,11 +1620,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_QUEENLY_MAJESTY] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Queenly Majesty"),
#else
.name = _("QueenlyMjsty"),
#endif
.description = COMPOUND_STRING("Protects from priority."),
.aiRating = 6,
.breakable = TRUE,
@@ -1716,11 +1672,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_TANGLING_HAIR] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Tangling Hair"),
#else
.name = _("TanglingHair"),
#endif
.description = COMPOUND_STRING("Lowers Speed on contact."),
.aiRating = 5,
},
@@ -1736,11 +1688,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_POWER_OF_ALCHEMY] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Power Of Alchemy"),
#else
.name = _("PwrOfAlchemy"),
#endif
.description = COMPOUND_STRING("Copies ally's ability."),
.aiRating = 0,
.cantBeCopied = TRUE,
@@ -1768,22 +1716,14 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_ELECTRIC_SURGE] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Electric Surge"),
#else
.name = _("ElectrcSurge"),
#endif
.description = COMPOUND_STRING("Field becomes Electric."),
.aiRating = 8,
},
[ABILITY_PSYCHIC_SURGE] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Psychic Surge"),
#else
.name = _("PsychicSurge"),
#endif
.description = COMPOUND_STRING("Field becomes weird."),
.aiRating = 8,
},
@@ -1804,22 +1744,14 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_FULL_METAL_BODY] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Full Metal Body"),
#else
.name = _("FullMetalBdy"),
#endif
.description = COMPOUND_STRING("Prevents stat reduction."),
.aiRating = 4,
},
[ABILITY_SHADOW_SHIELD] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Shadow Shield"),
#else
.name = _("ShadowShield"),
#endif
.description = COMPOUND_STRING("Halves damage at full HP."),
.aiRating = 8,
},
@@ -1840,22 +1772,14 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_INTREPID_SWORD] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Intrepid Sword"),
#else
.name = _("IntrepidSwrd"),
#endif
.description = COMPOUND_STRING("Ups Attack on entry."),
.aiRating = 3,
},
[ABILITY_DAUNTLESS_SHIELD] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Dauntless Shield"),
#else
.name = _("DauntlssShld"),
#endif
.description = COMPOUND_STRING("Ups Defense on entry."),
.aiRating = 3,
},
@@ -1882,11 +1806,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_PROPELLER_TAIL] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Propeller Tail"),
#else
.name = _("PropellrTail"),
#endif
.description = COMPOUND_STRING("Ignores foe's redirection."),
.aiRating = 2,
},
@@ -1984,22 +1904,14 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_SCREEN_CLEANER] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Screen Cleaner"),
#else
.name = _("ScreenCleanr"),
#endif
.description = COMPOUND_STRING("Removes walls of light."),
.aiRating = 3,
},
[ABILITY_STEELY_SPIRIT] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Steely Spirit"),
#else
.name = _("SteelySpirit"),
#endif
.description = COMPOUND_STRING("Boosts ally's Steel moves."),
.aiRating = 2,
},
@@ -2013,33 +1925,21 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_WANDERING_SPIRIT] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Wandering Spirit"),
#else
.name = _("WandrngSprit"),
#endif
.description = COMPOUND_STRING("Trade abilities on contact."),
.aiRating = 2,
},
[ABILITY_GORILLA_TACTICS] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Gorilla Tactics"),
#else
.name = _("GorillaTacti"),
#endif
.description = COMPOUND_STRING("Ups Attack and locks move."),
.aiRating = 4,
},
[ABILITY_NEUTRALIZING_GAS] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Neutralizing Gas"),
#else
.name = _("NeutrlzngGas"),
#endif
.description = COMPOUND_STRING("All Abilities are nullified."),
.aiRating = 5,
.cantBeCopied = TRUE,
@@ -2058,11 +1958,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_HUNGER_SWITCH] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Hunger Switch"),
#else
.name = _("HungerSwitch"),
#endif
.description = COMPOUND_STRING("Changes form each turn."),
.aiRating = 2,
.cantBeCopied = TRUE,
@@ -2087,11 +1983,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_CURIOUS_MEDICINE] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Curious Medicine"),
#else
.name = _("CuriusMedicn"),
#endif
.description = COMPOUND_STRING("Remove ally's stat changes."),
.aiRating = 3,
},
@@ -2112,11 +2004,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_CHILLING_NEIGH] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Chilling Neigh"),
#else
.name = _("ChillngNeigh"),
#endif
.description = COMPOUND_STRING("KOs boost Attack stat."),
.aiRating = 7,
},
@@ -2154,11 +2042,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_LINGERING_AROMA] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Lingering Aroma"),
#else
.name = _("LngerngAroma"),
#endif
.description = COMPOUND_STRING("Spreads with contact."),
.aiRating = 5,
},
@@ -2172,11 +2056,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_THERMAL_EXCHANGE] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Thermal Exchange"),
#else
.name = _("ThrmlExchnge"),
#endif
.description = COMPOUND_STRING("Fire hits up Attack."),
.aiRating = 4,
.breakable = TRUE,
@@ -2191,11 +2071,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_PURIFYING_SALT] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Purifying Salt"),
#else
.name = _("PurfyingSalt"),
#endif
.description = COMPOUND_STRING("Protected by pure salts."),
.aiRating = 6,
.breakable = TRUE,
@@ -2203,11 +2079,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_WELL_BAKED_BODY] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Well-Baked Body"),
#else
.name = _("WellBakedBdy"),
#endif
.description = COMPOUND_STRING("Strengthened by Fire."),
.aiRating = 5,
.breakable = TRUE,
@@ -2231,11 +2103,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_ROCKY_PAYLOAD] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Rocky Payload"),
#else
.name = _("RockyPayload"),
#endif
.description = COMPOUND_STRING("Powers up Rock moves."),
.aiRating = 6,
},
@@ -2273,22 +2141,14 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_ELECTROMORPHOSIS] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Electromorphosis"),
#else
.name = _("Elecmrphosis"),
#endif
.description = COMPOUND_STRING("Gets Charged when hit."),
.aiRating = 5,
},
[ABILITY_PROTOSYNTHESIS] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Protosynthesis"),
#else
.name = _("Protosnthsis"),
#endif
.description = COMPOUND_STRING("Sun boosts best stat."),
.aiRating = 7,
.cantBeCopied = TRUE,
@@ -2318,11 +2178,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_VESSEL_OF_RUIN] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Vessel of Ruin"),
#else
.name = _("VesselOfRuin"),
#endif
.description = COMPOUND_STRING("Lowers foes' sp. damage."),
.aiRating = 5,
.breakable = TRUE,
@@ -2330,11 +2186,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_SWORD_OF_RUIN] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Sword of Ruin"),
#else
.name = _("SwordOfRuin"),
#endif
.description = COMPOUND_STRING("Lowers foes' Defense."),
.aiRating = 5,
.breakable = TRUE,
@@ -2342,11 +2194,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_TABLETS_OF_RUIN] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Tablets of Ruin"),
#else
.name = _("TabltsOfRuin"),
#endif
.description = COMPOUND_STRING("Lowers foes' damage."),
.aiRating = 5,
.breakable = TRUE,
@@ -2354,11 +2202,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_BEADS_OF_RUIN] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Beads of Ruin"),
#else
.name = _("BeadsOfRuin"),
#endif
.description = COMPOUND_STRING("Lowers foes' Sp. Defense."),
.aiRating = 5,
.breakable = TRUE,
@@ -2366,22 +2210,14 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_ORICHALCUM_PULSE] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Orichalcum Pulse"),
#else
.name = _("OrchlcumPlse"),
#endif
.description = COMPOUND_STRING("Summons sunlight in battle."),
.aiRating = 8,
},
[ABILITY_HADRON_ENGINE] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Hadron Engine"),
#else
.name = _("HadronEngine"),
#endif
.description = COMPOUND_STRING("Field becomes Electric."),
.aiRating = 8,
},
@@ -2409,11 +2245,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_SUPREME_OVERLORD] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Supreme Overlord"),
#else
.name = _("SuprmeOvrlrd"),
#endif
.description = COMPOUND_STRING("Inherits fallen's strength."),
.aiRating = 6,
},
@@ -2450,11 +2282,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_MYCELIUM_MIGHT] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Mycelium Might"),
#else
.name = _("MceliumMight"),
#endif
.description = COMPOUND_STRING("Status moves never fail."),
.aiRating = 2,
},
@@ -2476,11 +2304,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_EMBODY_ASPECT_TEAL_MASK] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Embody Aspect"),
#else
.name = _("EmbodyAspect"),
#endif
.description = COMPOUND_STRING("Raises Speed."),
.aiRating = 6,
.cantBeCopied = TRUE,
@@ -2491,11 +2315,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_EMBODY_ASPECT_HEARTHFLAME_MASK] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Embody Aspect"),
#else
.name = _("EmbodyAspect"),
#endif
.description = COMPOUND_STRING("Raises Attack."),
.aiRating = 6,
.cantBeCopied = TRUE,
@@ -2506,11 +2326,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_EMBODY_ASPECT_WELLSPRING_MASK] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Embody Aspect"),
#else
.name = _("EmbodyAspect"),
#endif
.description = COMPOUND_STRING("Raises Sp. Def."),
.aiRating = 6,
.cantBeCopied = TRUE,
@@ -2521,11 +2337,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_EMBODY_ASPECT_CORNERSTONE_MASK] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Embody Aspect"),
#else
.name = _("EmbodyAspect"),
#endif
.description = COMPOUND_STRING("Raises Defense."),
.aiRating = 6,
.cantBeCopied = TRUE,
@@ -2543,11 +2355,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_SUPERSWEET_SYRUP] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Supersweet Syrup"),
#else
.name = _("SuprswtSyrup"),
#endif
.description = COMPOUND_STRING("Lowers the foe's Speed."),
.aiRating = 5,
},
@@ -2578,11 +2386,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_TERAFORM_ZERO] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Teraform Zero"),
#else
.name = _("TeraformZero"),
#endif
.description = COMPOUND_STRING("Removes weather and terrain."),
.aiRating = 10,
.cantBeCopied = TRUE,
@@ -2592,11 +2396,7 @@ const struct Ability gAbilitiesInfo[ABILITIES_COUNT] =
[ABILITY_POISON_PUPPETEER] =
{
#if B_EXPANDED_ABILITY_NAMES == TRUE
.name = _("Poison Puppeteer"),
#else
.name = _("PoisnPuppter"),
#endif
.description = COMPOUND_STRING("Confuses poisoned foes."),
.aiRating = 8,
.cantBeCopied = TRUE,

File diff suppressed because it is too large Load Diff

View File

@@ -40,12 +40,6 @@ const uq4_12_t gTypeEffectivenessTable[NUMBER_OF_MON_TYPES][NUMBER_OF_MON_TYPES]
#undef ______
#undef X
#if B_EXPANDED_TYPE_NAMES == TRUE
#define HANDLE_EXPANDED_TYPE_NAME(_name, ...) _(DEFAULT(_name, __VA_ARGS__))
#else
#define HANDLE_EXPANDED_TYPE_NAME(_name, ...) _(_name)
#endif
// .generic is large enough that the text for TYPE_ELECTRIC will exceed TEXT_BUFF_ARRAY_COUNT.
// In this array there's commented-out data such as references to type-resist berries that would otherwise would go unused.
// However, we figured this information would be useful for users that want to add their own types as a reminder of
@@ -60,6 +54,8 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
.teraTypeRGBValue = RGB_WHITE,
.damageCategory = DAMAGE_CATEGORY_PHYSICAL,
.paletteTMHM = gItemIconPalette_NormalTMHM,
.useSecondTypeIconPalette = FALSE,
.isSpecialCaseType = TRUE,
},
[TYPE_NORMAL] =
{
@@ -71,6 +67,8 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
.teraTypeRGBValue = RGB_WHITE, // custom
.damageCategory = DAMAGE_CATEGORY_PHYSICAL,
.paletteTMHM = gItemIconPalette_NormalTMHM,
.useSecondTypeIconPalette = FALSE,
.isSpecialCaseType = FALSE,
//.enhanceItem = ITEM_SILK_SCARF,
//.berry = ITEM_CHILAN_BERRY,
//.gem = ITEM_NORMAL_GEM,
@@ -80,7 +78,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
},
[TYPE_FIGHTING] =
{
.name = HANDLE_EXPANDED_TYPE_NAME("Fight", "Fighting"),
.name = _("Fighting"),
.generic = _("a FIGHTING move"),
.palette = 13,
.zMove = MOVE_ALL_OUT_PUMMELING,
@@ -88,6 +86,8 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
.teraTypeRGBValue = RGB(26, 8, 14),
.damageCategory = DAMAGE_CATEGORY_PHYSICAL,
.paletteTMHM = gItemIconPalette_FightingTMHM,
.useSecondTypeIconPalette = FALSE,
.isSpecialCaseType = FALSE,
//.enhanceItem = ITEM_BLACK_BELT,
//.berry = ITEM_CHOPLE_BERRY,
//.gem = ITEM_FIGHTING_GEM,
@@ -107,6 +107,8 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
.teraTypeRGBValue = RGB(31, 26, 7),
.damageCategory = DAMAGE_CATEGORY_PHYSICAL,
.paletteTMHM = gItemIconPalette_FlyingTMHM,
.useSecondTypeIconPalette = FALSE,
.isSpecialCaseType = FALSE,
//.enhanceItem = ITEM_SHARP_BEAK,
//.berry = ITEM_COBA_BERRY,
//.gem = ITEM_FLYING_GEM,
@@ -126,6 +128,8 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
.teraTypeRGBValue = RGB(26, 10, 25), // custom
.damageCategory = DAMAGE_CATEGORY_PHYSICAL,
.paletteTMHM = gItemIconPalette_PoisonTMHM,
.useSecondTypeIconPalette = FALSE,
.isSpecialCaseType = FALSE,
//.enhanceItem = ITEM_POISON_BARB,
//.berry = ITEM_KEBIA_BERRY,
//.gem = ITEM_POISON_GEM,
@@ -145,6 +149,8 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
.teraTypeRGBValue = RGB(25, 23, 18),
.damageCategory = DAMAGE_CATEGORY_PHYSICAL,
.paletteTMHM = gItemIconPalette_GroundTMHM,
.useSecondTypeIconPalette = FALSE,
.isSpecialCaseType = FALSE,
//.enhanceItem = ITEM_SOFT_SAND,
//.berry = ITEM_SHUCA_BERRY,
//.gem = ITEM_GROUND_GEM,
@@ -164,6 +170,8 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
.teraTypeRGBValue = RGB(18, 16, 8), // custom
.damageCategory = DAMAGE_CATEGORY_PHYSICAL,
.paletteTMHM = gItemIconPalette_RockTMHM,
.useSecondTypeIconPalette = FALSE,
.isSpecialCaseType = FALSE,
//.enhanceItem = ITEM_HARD_STONE,
//.berry = ITEM_CHARTI_BERRY,
//.gem = ITEM_ROCK_GEM,
@@ -183,6 +191,8 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
.teraTypeRGBValue = RGB(18, 24, 6),
.damageCategory = DAMAGE_CATEGORY_PHYSICAL,
.paletteTMHM = gItemIconPalette_BugTMHM,
.useSecondTypeIconPalette = FALSE,
.isSpecialCaseType = FALSE,
//.enhanceItem = ITEM_SILVER_POWDER,
//.berry = ITEM_TANGA_BERRY,
//.gem = ITEM_BUG_GEM,
@@ -202,6 +212,8 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
.teraTypeRGBValue = RGB(12, 10, 16),
.damageCategory = DAMAGE_CATEGORY_PHYSICAL,
.paletteTMHM = gItemIconPalette_GhostTMHM,
.useSecondTypeIconPalette = FALSE,
.isSpecialCaseType = FALSE,
//.enhanceItem = ITEM_SPELL_TAG,
//.berry = ITEM_KASIB_BERRY,
//.gem = ITEM_GHOST_GEM,
@@ -221,6 +233,8 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
.teraTypeRGBValue = RGB(19, 19, 20),
.damageCategory = DAMAGE_CATEGORY_PHYSICAL,
.paletteTMHM = gItemIconPalette_SteelTMHM,
.useSecondTypeIconPalette = FALSE,
.isSpecialCaseType = FALSE,
//.enhanceItem = ITEM_METAL_COAT,
//.berry = ITEM_BABIRI_BERRY,
//.gem = ITEM_STEEL_GEM,
@@ -237,6 +251,8 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
.palette = 15,
.teraTypeRGBValue = RGB_WHITE,
.damageCategory = DAMAGE_CATEGORY_SPECIAL,
.useSecondTypeIconPalette = FALSE,
.isSpecialCaseType = TRUE,
},
[TYPE_FIRE] =
{
@@ -248,6 +264,8 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
.teraTypeRGBValue = RGB(31, 20, 11),
.damageCategory = DAMAGE_CATEGORY_SPECIAL,
.paletteTMHM = gItemIconPalette_FireTMHM,
.useSecondTypeIconPalette = TRUE,
.isSpecialCaseType = FALSE,
//.enhanceItem = ITEM_CHARCOAL,
//.berry = ITEM_OCCA_BERRY,
//.gem = ITEM_FIRE_GEM,
@@ -267,6 +285,8 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
.teraTypeRGBValue = RGB(10, 18, 27),
.damageCategory = DAMAGE_CATEGORY_SPECIAL,
.paletteTMHM = gItemIconPalette_WaterTMHM,
.useSecondTypeIconPalette = TRUE,
.isSpecialCaseType = FALSE,
//.enhanceItem = ITEM_MYSTIC_WATER,
//.berry = ITEM_PASSHO_BERRY,
//.gem = ITEM_WATER_GEM,
@@ -286,6 +306,8 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
.teraTypeRGBValue = RGB(12, 24, 11),
.damageCategory = DAMAGE_CATEGORY_SPECIAL,
.paletteTMHM = gItemIconPalette_GrassTMHM,
.useSecondTypeIconPalette = TRUE,
.isSpecialCaseType = FALSE,
//.enhanceItem = ITEM_MIRACLE_SEED,
//.berry = ITEM_RINDO_BERRY,
//.gem = ITEM_GRASS_GEM,
@@ -297,7 +319,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
},
[TYPE_ELECTRIC] =
{
.name = HANDLE_EXPANDED_TYPE_NAME("Electr", "Electric"),
.name = _("Electric"),
.generic = _("an ELECTRIC move"),
.palette = 13,
.zMove = MOVE_GIGAVOLT_HAVOC,
@@ -305,6 +327,8 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
.teraTypeRGBValue = RGB(30, 26, 7),
.damageCategory = DAMAGE_CATEGORY_SPECIAL,
.paletteTMHM = gItemIconPalette_ElectricTMHM,
.useSecondTypeIconPalette = TRUE,
.isSpecialCaseType = FALSE,
//.enhanceItem = ITEM_MAGNET,
//.berry = ITEM_WACAN_BERRY,
//.gem = ITEM_ELECTRIC_GEM,
@@ -316,7 +340,7 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
},
[TYPE_PSYCHIC] =
{
.name = HANDLE_EXPANDED_TYPE_NAME("Psychc", "Psychic"),
.name = _("Psychic"),
.generic = _("a PSYCHIC move"),
.palette = 14,
.zMove = MOVE_SHATTERED_PSYCHE,
@@ -324,6 +348,8 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
.teraTypeRGBValue = RGB(31, 14, 15),
.damageCategory = DAMAGE_CATEGORY_SPECIAL,
.paletteTMHM = gItemIconPalette_PsychicTMHM,
.useSecondTypeIconPalette = TRUE,
.isSpecialCaseType = FALSE,
//.enhanceItem = ITEM_TWISTED_SPOON,
//.berry = ITEM_PAYAPA_BERRY,
//.gem = ITEM_PSYCHIC_GEM,
@@ -343,6 +369,8 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
.teraTypeRGBValue = RGB(14, 26, 25),
.damageCategory = DAMAGE_CATEGORY_SPECIAL,
.paletteTMHM = gItemIconPalette_IceTMHM,
.useSecondTypeIconPalette = TRUE,
.isSpecialCaseType = FALSE,
//.enhanceItem = ITEM_NEVER_MELT_ICE,
//.berry = ITEM_YACHE_BERRY,
//.gem = ITEM_ICE_GEM,
@@ -362,6 +390,8 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
.teraTypeRGBValue = RGB(10, 18, 27),
.damageCategory = DAMAGE_CATEGORY_SPECIAL,
.paletteTMHM = gItemIconPalette_DragonTMHM,
.useSecondTypeIconPalette = TRUE,
.isSpecialCaseType = FALSE,
//.enhanceItem = ITEM_DRAGON_FANG,
//.berry = ITEM_HABAN_BERRY,
//.gem = ITEM_DRAGON_GEM,
@@ -381,6 +411,8 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
.teraTypeRGBValue = RGB(6, 5, 8),
.damageCategory = DAMAGE_CATEGORY_SPECIAL,
.paletteTMHM = gItemIconPalette_DarkTMHM,
.useSecondTypeIconPalette = TRUE,
.isSpecialCaseType = FALSE,
//.enhanceItem = ITEM_BLACK_GLASSES,
//.berry = ITEM_COLBUR_BERRY,
//.gem = ITEM_DARK_GEM,
@@ -400,6 +432,8 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
.teraTypeRGBValue = RGB(31, 15, 21),
.damageCategory = DAMAGE_CATEGORY_SPECIAL,
.paletteTMHM = gItemIconPalette_FairyTMHM,
.useSecondTypeIconPalette = TRUE,
.isSpecialCaseType = FALSE,
//.enhanceItem = ITEM_FAIRY_FEATHER,
//.berry = ITEM_ROSELI_BERRY,
//.gem = ITEM_FAIRY_GEM,
@@ -411,13 +445,15 @@ const struct TypeInfo gTypesInfo[NUMBER_OF_MON_TYPES] =
},
[TYPE_STELLAR] =
{
.name = HANDLE_EXPANDED_TYPE_NAME("Stellr", "Stellar"),
.name = _("Stellar"),
.generic = _("a STELLAR move"),
.palette = 15,
.zMove = MOVE_BREAKNECK_BLITZ,
.maxMove = MOVE_MAX_STRIKE,
.teraTypeRGBValue = RGB(10, 18, 27),
.paletteTMHM = gItemIconPalette_NormalTMHM, // failsafe
.useSecondTypeIconPalette = FALSE,
.isSpecialCaseType = TRUE,
// .teraShard = ITEM_STELLAR_TERA_SHARD,
},
};

View File

@@ -10,7 +10,7 @@
#include "event_data.h"
#include "random.h"
#include "constants/moves.h"
#include "level_caps.h"
#include "caps.h"
#include "menu.h"
#include "script.h"
#include "strings.h"

View File

@@ -24,7 +24,7 @@
#include "item.h"
#include "item_menu.h"
#include "item_use.h"
#include "level_caps.h"
#include "caps.h"
#include "link.h"
#include "link_rfu.h"
#include "load_save.h"

View File

@@ -21,7 +21,7 @@
#include "battle_message.h"
#include "battle_util.h"
#include "link.h"
#include "level_caps.h"
#include "caps.h"
#include "pokemon_animation.h"
#include "m4a.h"
#include "pokedex.h"