updated up to Cmd_seteffectsecondary, Cmd_setgastroacid and Cmd_setstealthrock

This commit is contained in:
cawtds 2024-04-30 20:02:18 +02:00
parent 3ad08c53b0
commit a5f2b89e29
21 changed files with 3751 additions and 1576 deletions

View File

@ -92,7 +92,7 @@
.4byte \ptr
.endm
.macro seteffectwithchance
.macro setadditionaleffects
.byte 0x15
.endm
@ -1095,8 +1095,9 @@
.4byte \param0
.endm
.macro doubledamagedealtifdamaged
.macro setgastroacid failInstr:req
.byte 0xd6
.4byte \failInstr
.endm
.macro setyawn param0:req
@ -1123,9 +1124,9 @@
.4byte \param0
.endm
.macro trysetgrudge param0:req
.macro setstealthrock failInstr:req
.byte 0xdc
.4byte \param0
.4byte \failInstr
.endm
.macro weightdamagecalculation
@ -1307,13 +1308,13 @@
various \battler, VARIOUS_WAIT_FANFARE
.endm
@ pokeemerald
.macro handleformchange battler:req, case:req
various \battler, VARIOUS_HANDLE_FORM_CHANGE
.byte \case
.endm
@ pokeemerald
.macro playmoveanimation battler:req, move:req
various \battler, VARIOUS_PLAY_MOVE_ANIMATION
.2byte \move
@ -1334,10 +1335,26 @@
.byte \equal
.endm
.macro jumpifnoholdeffect battler:req, holdEffect:req, jumpInstr:req
jumpifholdeffect \battler, \holdEffect, \jumpInstr, FALSE
.macro savetarget
various BS_TARGET, VARIOUS_SAVE_TARGET
.endm
.macro restoretarget
various BS_TARGET, VARIOUS_RESTORE_TARGET
.endm
.macro spectralthiefprintstats
various BS_ATTACKER, VARIOUS_SPECTRAL_THIEF
.endm
.macro trytoclearprimalweather
various BS_ATTACKER, VARIOUS_TRY_TO_CLEAR_PRIMAL_WEATHER
.endm
.macro consumeberry battler:req, fromBattler:req
various \battler, VARIOUS_CONSUME_BERRY
.byte \fromBattler
.endm
@ helpful macros
.macro setstatchanger stat:req, stages:req, down:req
@ -1416,6 +1433,10 @@
jumpifword CMP_NO_COMMON_BITS, gBattleTypeFlags, \flags, \jumpptr
.endm
.macro jumpifnoholdeffect battler:req, holdEffect:req, jumpInstr:req
jumpifholdeffect \battler, \holdEffect, \jumpInstr, FALSE
.endm
@ callnative macros
.macro itemrestorehp
callnative BS_ItemRestoreHP
@ -1443,3 +1464,12 @@
printstring STRINGID_EMPTYSTRING3
waitmessage 1
.endm
.macro tryrevertweatherform
callnative BS_TryRevertWeatherForm
.endm
@ Used by effects that may proc Symbiosis but do not call removeitem.
.macro trysymbiosis
callnative BS_TrySymbiosis
.endm

View File

@ -406,6 +406,7 @@ B_DEF_PREFIX3 = FD 2D
B_TRAINER2_LOSE_TEXT = FD 2E
B_TRAINER2_WIN_TEXT = FD 2F
B_BUFF3 = FD 30
B_DEF_TEAM2 = FD 3B
@ indicates the end of a town/city name (before " TOWN" or " CITY")
NAME_END = FC 00

View File

@ -268,7 +268,7 @@ BattleScript_HitFromAtkAnimation::
waitmessage B_WAIT_TIME_LONG
resultmessage
waitmessage B_WAIT_TIME_LONG
seteffectwithchance
setadditionaleffects
tryfaintmon BS_TARGET
BattleScript_MoveEnd::
moveendall
@ -514,6 +514,7 @@ BattleScript_StatUpEnd::
BattleScript_StatUp::
playanimation BS_EFFECT_BATTLER, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1
BattleScript_StatUpMsg::
printfromtable gStatUpStringIds
waitmessage B_WAIT_TIME_LONG
return
@ -652,7 +653,7 @@ BattleScript_MultiHitPrintStrings::
printstring STRINGID_HITXTIMES
waitmessage B_WAIT_TIME_LONG
BattleScript_MultiHitEnd::
seteffectwithchance
setadditionaleffects
tryfaintmon BS_TARGET
moveendcase MOVEEND_SYNCHRONIZE_TARGET
moveendfrom MOVEEND_IMMUNITY_ABILITIES
@ -1431,7 +1432,7 @@ BattleScript_TripleKickPrintStrings::
printstring STRINGID_HITXTIMES
waitmessage B_WAIT_TIME_LONG
BattleScript_TripleKickEnd::
seteffectwithchance
setadditionaleffects
tryfaintmon BS_TARGET
moveendfrom MOVEEND_UPDATE_LAST_MOVES
end
@ -2412,7 +2413,7 @@ BattleScript_EffectRecycle::
goto BattleScript_MoveEnd
BattleScript_EffectRevenge::
doubledamagedealtifdamaged
setgastroacid BattleScript_CoreEnforcerRet
goto BattleScript_EffectHit
BattleScript_EffectBrickBreak::
@ -2444,7 +2445,7 @@ BattleScript_BrickBreakDoHit::
waitmessage B_WAIT_TIME_LONG
resultmessage
waitmessage B_WAIT_TIME_LONG
seteffectwithchance
setadditionaleffects
tryfaintmon BS_TARGET
goto BattleScript_MoveEnd
@ -2533,7 +2534,7 @@ BattleScript_EffectGrudge::
attackcanceler
attackstring
ppreduce
trysetgrudge BattleScript_ButItFailed
setstealthrock BattleScript_ButItFailed
attackanimation
waitanimation
printstring STRINGID_PKMNWANTSGRUDGE
@ -3591,6 +3592,25 @@ BattleScript_AtkDefDownAtkFail::
BattleScript_AtkDefDownDefFail::
return
BattleScript_DefSpDefDown::
setbyte sSTAT_ANIM_PLAYED, FALSE
playstatchangeanimation BS_ATTACKER, BIT_DEF | BIT_SPDEF, STAT_CHANGE_CANT_PREVENT | STAT_CHANGE_NEGATIVE | STAT_CHANGE_MULTIPLE_STATS
playstatchangeanimation BS_ATTACKER, BIT_DEF, STAT_CHANGE_CANT_PREVENT | STAT_CHANGE_NEGATIVE
setstatchanger STAT_DEF, 1, TRUE
statbuffchange MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN | STAT_CHANGE_ALLOW_PTR, BattleScript_DefSpDefDownTrySpDef
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_DECREASE, BattleScript_DefSpDefDownTrySpDef
printfromtable gStatDownStringIds
waitmessage B_WAIT_TIME_LONG
BattleScript_DefSpDefDownTrySpDef::
playstatchangeanimation BS_ATTACKER, BIT_SPDEF, STAT_CHANGE_CANT_PREVENT | STAT_CHANGE_NEGATIVE
setstatchanger STAT_SPDEF, 1, TRUE
statbuffchange MOVE_EFFECT_AFFECTS_USER | MOVE_EFFECT_CERTAIN | STAT_CHANGE_ALLOW_PTR, BattleScript_DefSpDefDownRet
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_DECREASE, BattleScript_DefSpDefDownRet
printfromtable gStatDownStringIds
waitmessage B_WAIT_TIME_LONG
BattleScript_DefSpDefDownRet::
return
BattleScript_KnockedOff::
playanimation BS_TARGET, B_ANIM_ITEM_KNOCKOFF
printstring STRINGID_PKMNKNOCKEDOFF
@ -4580,3 +4600,165 @@ BattleScript_TargetFormChangeWithStringNoPopup::
printstring STRINGID_PKMNTRANSFORMED
waitmessage B_WAIT_TIME_LONG
return
BattleScript_IceFaceNullsDamage::
call BattleScript_TargetFormChangeWithString
return
BattleScript_AffectionBasedEndurance::
@ playanimation BS_TARGET, B_ANIM_AFFECTION_HANGED_ON @ TODO: Animation
printstring STRINGID_TARGETTOUGHEDITOUT
waitmessage B_WAIT_TIME_LONG
return
BattleScript_MoveEffectClearSmog::
printstring STRINGID_RESETSTARGETSSTATLEVELS
waitmessage B_WAIT_TIME_LONG
return
BattleScript_MoveEffectFlameBurst::
tryfaintmon BS_TARGET
copybyte sBATTLER, sSAVED_BATTLER
printstring STRINGID_BURSTINGFLAMESHIT
waitmessage B_WAIT_TIME_LONG
savetarget
copybyte gBattlerTarget, sSAVED_BATTLER
healthbarupdate BS_TARGET
datahpupdate BS_TARGET
tryfaintmon BS_TARGET
restoretarget
goto BattleScript_MoveEnd
BattleScript_HyperspaceFuryRemoveProtect::
printstring STRINGID_BROKETHROUGHPROTECTION
waitmessage B_WAIT_TIME_LONG
return
BattleScript_MoveEffectFeint::
printstring STRINGID_FELLFORFEINT
waitmessage B_WAIT_TIME_LONG
return
BattleScript_SpectralThiefSteal::
printstring STRINGID_SPECTRALTHIEFSTEAL
waitmessage B_WAIT_TIME_LONG
setbyte sB_ANIM_ARG2, 0
playanimation BS_ATTACKER, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1
spectralthiefprintstats
return
BattleScript_VCreateStatLoss::
jumpifstat BS_ATTACKER, CMP_GREATER_THAN, STAT_DEF, MIN_STAT_STAGE, BattleScript_VCreateStatAnim
jumpifstat BS_ATTACKER, CMP_GREATER_THAN, STAT_SPDEF, MIN_STAT_STAGE, BattleScript_VCreateStatAnim
jumpifstat BS_ATTACKER, CMP_EQUAL, STAT_SPEED, MIN_STAT_STAGE, BattleScript_VCreateStatLossRet
BattleScript_VCreateStatAnim:
setbyte sSTAT_ANIM_PLAYED, FALSE
playstatchangeanimation BS_ATTACKER, BIT_DEF | BIT_SPDEF | BIT_SPEED, STAT_CHANGE_NEGATIVE | STAT_CHANGE_CANT_PREVENT
setstatchanger STAT_DEF, 1, TRUE
statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_NOT_PROTECT_AFFECTED | MOVE_EFFECT_CERTAIN, BattleScript_VCreateTrySpDef
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_DECREASE, BattleScript_VCreateTrySpDef
printfromtable gStatDownStringIds
waitmessage B_WAIT_TIME_LONG
BattleScript_VCreateTrySpDef:
setstatchanger STAT_SPDEF, 1, TRUE
statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_NOT_PROTECT_AFFECTED | MOVE_EFFECT_CERTAIN, BattleScript_VCreateTrySpeed
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_DECREASE, BattleScript_VCreateTrySpeed
printfromtable gStatDownStringIds
waitmessage B_WAIT_TIME_LONG
BattleScript_VCreateTrySpeed:
setstatchanger STAT_SPEED, 1, TRUE
statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_NOT_PROTECT_AFFECTED | MOVE_EFFECT_CERTAIN, BattleScript_VCreateStatLossRet
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_DECREASE, BattleScript_VCreateStatLossRet
printfromtable gStatDownStringIds
waitmessage B_WAIT_TIME_LONG
BattleScript_VCreateStatLossRet:
return
BattleScript_MoveEffectCoreEnforcer::
setgastroacid BattleScript_CoreEnforcerRet
printstring STRINGID_PKMNSABILITYSUPPRESSED
waitmessage B_WAIT_TIME_LONG
trytoclearprimalweather
tryrevertweatherform
flushtextbox
BattleScript_CoreEnforcerRet:
return
BattleScript_MoveEffectIncinerate::
printstring STRINGID_INCINERATEBURN
waitmessage B_WAIT_TIME_LONG
return
BattleScript_MoveEffectBugBite::
printstring STRINGID_BUGBITE
waitmessage B_WAIT_TIME_LONG
orword gHitMarker, HITMARKER_DISABLE_ANIMATION
setbyte sBERRY_OVERRIDE, 1 @ override the requirements for eating berries
savetarget
consumeberry BS_ATTACKER, FALSE
bicword gHitMarker, HITMARKER_DISABLE_ANIMATION
setbyte sBERRY_OVERRIDE, 0
trysymbiosis
restoretarget
return
BattleScript_BothCanNoLongerEscape::
printstring STRINGID_BOTHCANNOLONGERESCAPE
waitmessage B_WAIT_TIME_LONG
return
BattleScript_RemoveFireType::
printstring STRINGID_ATTACKERLOSTFIRETYPE
waitmessage B_WAIT_TIME_LONG
return
BattleScript_RemoveElectricType::
printstring STRINGID_ATTACKERLOSTELECTRICTYPE
waitmessage B_WAIT_TIME_LONG
return
BattleScript_RemoveGenericType::
printstring STRINGID_ATTACKERLOSTITSTYPE
waitmessage B_WAIT_TIME_LONG
return
BattleScript_StealthRockActivates::
setstealthrock BattleScript_MoveEnd
printfromtable gDmgHazardsStringIds
waitmessage B_WAIT_TIME_LONG
return
BattleScript_SpikesActivates::
trysetspikes BattleScript_MoveEnd
printfromtable gDmgHazardsStringIds
waitmessage B_WAIT_TIME_LONG
return
BattleScript_SyrupBombActivates::
printstring STRINGID_TARGETCOVEREDINSTICKYCANDYSYRUP
waitmessage B_WAIT_TIME_LONG
return
BattleScript_AromaVeilProtectsRet::
pause B_WAIT_TIME_SHORT
call BattleScript_AbilityPopUp
printstring STRINGID_AROMAVEILPROTECTED
waitmessage B_WAIT_TIME_LONG
return
BattleScript_EffectPsychicNoise::
printstring STRINGID_PKMNPREVENTEDFROMHEALING
waitmessage B_WAIT_TIME_LONG
return
BattleScript_SymbiosisActivates::
call BattleScript_AbilityPopUp
printstring STRINGID_SYMBIOSISITEMPASS
waitmessage B_WAIT_TIME_LONG
return
BattleScript_NoItemSteal::
pause B_WAIT_TIME_SHORT
printstring STRINGID_PKMNSXMADEYINEFFECTIVE
waitmessage B_WAIT_TIME_LONG
return

View File

@ -4,6 +4,7 @@
#include <limits.h>
#include "global.h"
#include "constants/battle.h"
#include "constants/form_change_types.h"
#include "constants/battle_script_commands.h"
#include "battle_util.h"
#include "battle_script_commands.h"
@ -352,6 +353,7 @@ struct SideTimer
/*0x0B*/ u8 fieldB;
// pokeemerald
u8 retaliateTimer;
u8 stealthRockAmount;
};
extern struct SideTimer gSideTimers[];
@ -485,6 +487,12 @@ struct Illusion
struct Pokemon *mon;
};
struct LostItem
{
u16 originalItem:15;
u16 stolen:1;
};
struct BattleStruct
{
u8 turnEffectsTracker;
@ -593,6 +601,17 @@ struct BattleStruct
u8 bonusCritStages[MAX_BATTLERS_COUNT]; // G-Max Chi Strike boosts crit stages of allies.
u8 enduredDamage;
u16 changedSpecies[NUM_BATTLE_SIDES][PARTY_SIZE]; // For forms when multiple mons can change into the same pokemon.
u8 trainerSlideFirstCriticalHitMsgState:2;
u8 trainerSlideFirstSuperEffectiveHitMsgState:2;
u8 additionalEffectsCounter:4; // A counter for the additionalEffects applied by the current move in Cmd_setadditionaleffects
u8 targetsDone[MAX_BATTLERS_COUNT]; // Each battler as a bit.
u16 moveEffect2; // For Knock Off
u8 stolenStats[NUM_BATTLE_STATS]; // hp byte is used for which stats to raise, other inform about by how many stages
u8 stickySyrupdBy[MAX_BATTLERS_COUNT];
u8 moneyMultiplierMove:1;
struct LostItem itemLost[PARTY_SIZE]; // Player's team that had items consumed or stolen (two bytes per party member)
u8 savedBattlerTarget;
u8 ateBerry[2]; // array id determined by side, each party pokemon as bit
}; // size == 0x200 bytes
extern struct BattleStruct *gBattleStruct;
@ -621,15 +640,29 @@ extern struct BattleStruct *gBattleStruct;
#define BATTLER_MAX_HP(battlerId)(gBattleMons[battlerId].hp == gBattleMons[battlerId].maxHP)
#define TARGET_TURN_DAMAGED ((gSpecialStatuses[gBattlerTarget].physicalDmg != 0 || gSpecialStatuses[gBattlerTarget].specialDmg != 0))
#define IS_BATTLER_OF_TYPE(battlerId, type)((gBattleMons[battlerId].type1 == type || gBattleMons[battlerId].type2 == type))
#define SET_BATTLER_TYPE(battlerId, type) \
{ \
gBattleMons[battlerId].type1 = type; \
gBattleMons[battlerId].type2 = type; \
#define IS_BATTLER_OF_TYPE(battlerId, type)((GetBattlerType(battlerId, 0) == type || GetBattlerType(battlerId, 1) == type || (GetBattlerType(battlerId, 2) != TYPE_MYSTERY && GetBattlerType(battlerId, 2) == type)))
#define SET_BATTLER_TYPE(battlerId, type) \
{ \
gBattleMons[battlerId].type1 = type; \
gBattleMons[battlerId].type2 = type; \
gBattleMons[battlerId].type3 = TYPE_MYSTERY; \
}
#define IS_BATTLER_PROTECTED(battlerId)(gProtectStructs[battlerId].protected \
|| gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_WIDE_GUARD \
|| gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_QUICK_GUARD \
|| gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_CRAFTY_SHIELD \
|| gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_MAT_BLOCK \
|| gProtectStructs[battlerId].spikyShielded \
|| gProtectStructs[battlerId].kingsShielded \
|| gProtectStructs[battlerId].banefulBunkered \
|| gProtectStructs[battlerId].burningBulwarked \
|| gProtectStructs[battlerId].obstructed \
|| gProtectStructs[battlerId].silkTrapped)
#define GET_STAT_BUFF_ID(n)((n & 0xF)) // first four bits 0x1, 0x2, 0x4, 0x8
#define GET_STAT_BUFF_VALUE2(n)((n & 0xF0))
#define GET_STAT_BUFF_VALUE_WITH_SIGN(n)((n & 0xF8))
#define GET_STAT_BUFF_VALUE(n)(((n >> 4) & 7)) // 0x10, 0x20, 0x40
#define STAT_BUFF_NEGATIVE 0x80 // 0x80, the sign bit

View File

@ -233,7 +233,6 @@ extern const u8 *const gRefereeStringsTable[];
extern const u8 *const gStatNamesTable2[];
extern const u16 gMissStringIds[];
extern const u16 gTrappingMoves[];
extern const u8 gText_Sleep[];
extern const u8 gText_Poison[];

View File

@ -30,6 +30,15 @@ u32 GetHighestStatId(u32 battlerId);
u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move, u32 atkAbility, u32 defAbility, u32 atkHoldEffect, u32 defHoldEffect);
bool32 DoesSubstituteBlockMove(u32 battlerAtk, u32 battlerDef, u32 move);
bool32 DoesDisguiseBlockMove(u32 battler, u32 move);
bool32 CanPoisonType(u8 battlerAttacker, u8 battlerTarget);
bool32 CanParalyzeType(u8 battlerAttacker, u8 battlerTarget);
bool32 NoAliveMonsForPlayer(void);
bool32 NoAliveMonsForEitherParty(void);
void StealTargetItem(u8 battlerStealer, u8 battlerItem);
u32 IsFlowerVeilProtected(u32 battler);
u32 IsLeafGuardProtected(u32 battler);
bool32 IsShieldsDownProtected(u32 battler);
u32 IsAbilityStatusProtected(u32 battler);
extern void (* const gBattleScriptingCommandsTable[])(void);
extern const struct StatFractions gAccuracyStageRatios[];

View File

@ -476,5 +476,28 @@ extern const u8 BattleScript_HangedOnMsg[];
extern const u8 BattleScript_PrintBerryReduceString[];
extern const u8 BattleScript_TargetFormChange[];
extern const u8 BattleScript_TargetFormChangeWithStringNoPopup[];
extern const u8 BattleScript_IceFaceNullsDamage[];
extern const u8 BattleScript_AffectionBasedEndurance[];
extern const u8 BattleScript_DefSpDefDown[];
extern const u8 BattleScript_MoveEffectClearSmog[];
extern const u8 BattleScript_MoveEffectFlameBurst[];
extern const u8 BattleScript_HyperspaceFuryRemoveProtect[];
extern const u8 BattleScript_MoveEffectFeint[];
extern const u8 BattleScript_SpectralThiefSteal[];
extern const u8 BattleScript_VCreateStatLoss[];
extern const u8 BattleScript_MoveEffectCoreEnforcer[];
extern const u8 BattleScript_MoveEffectIncinerate[];
extern const u8 BattleScript_MoveEffectBugBite[];
extern const u8 BattleScript_BothCanNoLongerEscape[];
extern const u8 BattleScript_RemoveFireType[];
extern const u8 BattleScript_RemoveElectricType[];
extern const u8 BattleScript_RemoveGenericType[];
extern const u8 BattleScript_StealthRockActivates[];
extern const u8 BattleScript_SpikesActivates[];
extern const u8 BattleScript_SyrupBombActivates[];
extern const u8 BattleScript_AromaVeilProtectsRet[];
extern const u8 BattleScript_EffectPsychicNoise[];
extern const u8 BattleScript_StatUpMsg[];
extern const u8 BattleScript_SymbiosisActivates[];
#endif // GUARD_BATTLE_SCRIPTS_H

View File

@ -39,12 +39,17 @@
#define ABILITY_ON_FIELD(abilityId)(AbilityBattleEffects(ABILITYEFFECT_CHECK_ON_FIELD, 0, abilityId, 0, 0))
#define ABILITY_ON_FIELD2(abilityId)(AbilityBattleEffects(ABILITYEFFECT_FIELD_SPORT, 0, abilityId, 0, 0))
// For the first argument of ItemBattleEffects, to deteremine which block of item effects to try
/// For the first argument of ItemBattleEffects, to deteremine which block of item effects to try
#define ITEMEFFECT_ON_SWITCH_IN 0
#define ITEMEFFECT_NORMAL 1
#define ITEMEFFECT_DUMMY 2 // Unused, empty
#define ITEMEFFECT_MOVE_END 3
#define ITEMEFFECT_KINGSROCK_SHELLBELL 4
#define ITEMEFFECT_KINGSROCK 4
#define ITEMEFFECT_TARGET 5
#define ITEMEFFECT_ORBS 6
#define ITEMEFFECT_LIFEORB_SHELLBELL 7
#define ITEMEFFECT_USE_LAST_ITEM 8 // move end effects for just the battler, not whole field
#define ITEMEFFECT_STATS_CHANGED 9 // For White Herb and Eject Pack
#define WEATHER_HAS_EFFECT ((!AbilityBattleEffects(ABILITYEFFECT_CHECK_ON_FIELD, 0, ABILITY_CLOUD_NINE, 0, 0) && !AbilityBattleEffects(ABILITYEFFECT_CHECK_ON_FIELD, 0, ABILITY_AIR_LOCK, 0, 0)))
#define WEATHER_HAS_EFFECT2 ((!AbilityBattleEffects(ABILITYEFFECT_FIELD_SPORT, 0, ABILITY_CLOUD_NINE, 0, 0) && !AbilityBattleEffects(ABILITYEFFECT_FIELD_SPORT, 0, ABILITY_AIR_LOCK, 0, 0)))
@ -103,7 +108,7 @@ void PressurePPLoseOnUsingImprison(u8 attacker);
void PressurePPLoseOnUsingPerishSong(u8 attacker);
void MarkBattlerForControllerExec(u8 battlerId);
void MarkBattlerReceivedLinkData(u8 battlerId);
void CancelMultiTurnMoves(u8 battler);
const u8* CancelMultiTurnMoves(u32 battler);
bool32 WasUnableToUseMove(u32 battler);
void PrepareStringBattle(u16 stringId, u8 battler);
void ResetSentPokesToOpponentValue(void);
@ -182,6 +187,22 @@ u32 GetBattlerHoldEffect(u32 battler, bool32 checkNegating);
u32 GetBattlerHoldEffectIgnoreAbility(u32 battler, bool32 checkNegating);
u32 GetBattlerHoldEffectInternal(u32 battler, bool32 checkNegating, bool32 checkAbility);
void RecalcBattlerStats(u32 battler, struct Pokemon *mon);
bool32 TestIfSheerForceAffected(u32 battler, u16 move);
bool32 CanSleep(u32 battler);
bool32 CanBePoisoned(u32 battlerAttacker, u32 battlerTarget);
bool32 CanBeBurned(u32 battler);
bool32 CanBeParalyzed(u32 battler);
bool32 CanBeFrozen(u32 battler);
bool32 CanGetFrostbite(u32 battler);
bool32 CanBeConfused(u32 battler);
bool32 CanStealItem(u32 battlerStealing, u32 battlerItem, u16 item);
void TrySaveExchangedItem(u32 battler, u16 stolenItem);
void RemoveBattlerType(u32 battler, u8 type);
bool32 IsBattlerMegaEvolved(u32 battler);
bool32 IsBattlerPrimalReverted(u32 battler);
bool32 IsBattlerUltraBursted(u32 battler);
u16 GetBattleFormChangeTargetSpecies(u32 battler, u16 method);
bool32 TryBattleFormChange(u32 battler, u16 method);
// battle_ai_util.h
bool32 IsHealingMove(u32 move);

View File

@ -66,6 +66,7 @@
#define BATTLE_TYPE_WILD_SCRIPTED (1 << 17) // Used in pokeemerald as BATTLE_TYPE_PALACE.
#define BATTLE_TYPE_LEGENDARY_FRLG (1 << 18) // Used in pokeemerald as BATTLE_TYPE_ARENA.
#define BATTLE_TYPE_TRAINER_TOWER (1 << 19) // Used in pokeemerald as BATTLE_TYPE_FACTORY.
#define BATTLE_TYPE_INGAME_PARTNER (1 << 20)
#define IS_BATTLE_TYPE_GHOST_WITHOUT_SCOPE(flags) ((flags) & BATTLE_TYPE_GHOST && !((flags) & BATTLE_TYPE_GHOST_UNVEILED))
#define IS_BATTLE_TYPE_GHOST_WITH_SCOPE(flags) ((flags) & BATTLE_TYPE_GHOST && (flags) & BATTLE_TYPE_GHOST_UNVEILED)

View File

@ -263,7 +263,9 @@
// Cmd_statbuffchange
#define STAT_CHANGE_ALLOW_PTR (1 << 0) // If set, allow use of jumpptr. Set in every use of statbuffchange
#define STAT_CHANGE_MIRROR_ARMOR (1 << 1) // Stat change redirection caused by Mirror Armor ability.
#define STAT_CHANGE_NOT_PROTECT_AFFECTED (1 << 5)
#define STAT_CHANGE_UPDATE_MOVE_EFFECT (1 << 6)
// stat change flags for Cmd_playstatchangeanimation
#define STAT_CHANGE_NEGATIVE (1 << 0)

View File

@ -411,8 +411,36 @@
#define STRINGID_ATTACKWEAKENEDBSTRONGWINDS 409
#define STRINGID_ENDUREDSTURDY 410
#define STRINGID_BERRYDMGREDUCES 411
#define STRINGID_TARGETTOUGHEDITOUT 412
#define STRINGID_TRAPPEDBYSWIRLINGMAGMA 413
#define STRINGID_INFESTATION 414
#define STRINGID_PKMNINSNAPTRAP 415
#define STRINGID_THUNDERCAGETRAPPED 416
#define STRINGID_RESETSTARGETSSTATLEVELS 417
#define STRINGID_EXTREMESUNLIGHTFADED 418
#define STRINGID_HEAVYRAINLIFTED 419
#define STRINGID_STRONGWINDSDISSIPATED 420
#define STRINGID_SYMBIOSISITEMPASS 421
#define STRINGID_BURSTINGFLAMESHIT 422
#define STRINGID_BROKETHROUGHPROTECTION 423
#define STRINGID_FELLFORFEINT 424
#define STRINGID_SPECTRALTHIEFSTEAL 425
#define STRINGID_PKMNSABILITYSUPPRESSED 426
#define STRINGID_INCINERATEBURN 427
#define STRINGID_BUGBITE 428
#define STRINGID_BOTHCANNOLONGERESCAPE 429
#define STRINGID_ATTACKERLOSTFIRETYPE 430
#define STRINGID_ATTACKERLOSTELECTRICTYPE 431
#define STRINGID_ATTACKERLOSTITSTYPE 432
#define STRINGID_STEALTHROCKDMG 433
#define STRINGID_SHARPSTEELDMG 434
#define STRINGID_POINTEDSTONESFLOAT 435
#define STRINGID_SHARPSTEELFLOATS 436
#define STRINGID_TARGETCOVEREDINSTICKYCANDYSYRUP 437
#define STRINGID_AROMAVEILPROTECTED 438
#define STRINGID_PKMNPREVENTEDFROMHEALING 439
#define BATTLESTRINGS_COUNT 412
#define BATTLESTRINGS_COUNT 440
// This is the string id that gBattleStringsTable starts with.
// String ids before this (e.g. STRINGID_INTROMSG) are not in the table,
@ -621,6 +649,25 @@
#define B_MSG_FROSTBITE_HEALED 0
#define B_MSG_FROSTBITE_HEALED_BY_MOVE 1
#define NUM_TRAPPING_MOVES 6
// gWrappedStringIds
#define B_MSG_WRAPPED_BIND 0
#define B_MSG_WRAPPED_WRAP 1
#define B_MSG_WRAPPED_FIRE_SPIN 2
#define B_MSG_WRAPPED_CLAMP 3
#define B_MSG_WRAPPED_WHIRLPOOL 4
#define B_MSG_WRAPPED_SAND_TOMB 5
#define B_MSG_WRAPPED_MAGMA_STORM 6
#define B_MSG_WRAPPED_INFESTATION 7
#define B_MSG_WRAPPED_SNAP_TRAP 8
#define B_MSG_WRAPPED_THUNDER_CAGE 9
#define NUM_TRAPPING_MOVES 10
// gDmgHazardsStringIds
#define B_MSG_PKMNHURTBYSPIKES 0
#define B_MSG_STEALTHROCKDMG 1
#define B_MSG_SHARPSTEELDMG 2
#define B_MSG_POINTEDSTONESFLOAT 3
#define B_MSG_SPIKESSCATTERED 4
#define B_MSG_SHARPSTEELFLOATS 5
#endif // GUARD_BATTLE_STRING_IDS_H

View File

@ -0,0 +1,123 @@
#ifndef GUARD_CONSTANTS_FORM_CHANGE_TYPES_H
#define GUARD_CONSTANTS_FORM_CHANGE_TYPES_H
// FORM_CHANGE_BATTLE_HP_PERCENT param2 arguments
#define HP_HIGHER_THAN 1
#define HP_LOWER_EQ_THAN 2
// FORM_CHANGE_MOVE param2 Arguments
#define WHEN_LEARNED 0
#define WHEN_FORGOTTEN 1
// FORM_CHANGE_ITEM_USE param2 Arguments
#define DAY 1
#define NIGHT 2
#define FUSION_TERMINATOR 0xFF
#define FORM_CHANGE_TERMINATOR 0
// Form change that activates when the specified item is given to or taken from the selected Pokémon.
// param1: item to hold.
// param2: ability to check for, optional.
#define FORM_CHANGE_ITEM_HOLD 1
// Form change that activates when the item is used on the selected Pokémon.
// param1: item to use
// param2: time of day to check, optional.
// - DAY if Form change that activates in the daytime.
// - NIGHT if Form change that activates at nighttime.
#define FORM_CHANGE_ITEM_USE 2
// TODO: Form change that activates when the Pokémon learns or forgets the move.
// param1: move to check for
// param2:
// - WHEN_LEARNED if Form change that activates when move is forgotten
// - WHEN_FORGOTTEN if Form change that activates when move is learned
#define FORM_CHANGE_MOVE 3
// Form change that activates when the Pokémon is withdrawn from the PC or Daycare.
// Daycare withdraw done, PC withdraw TODO.
// - No parameters.
#define FORM_CHANGE_WITHDRAW 4
// Form change that activates when the Pokémon faints, either in battle or in the overworld by poison.
// If species is not specified and it's on the player's side, it will try to use the value
// saved in gBattleStruct->changedSpecies from a previous form change.
// - No parameters.
#define FORM_CHANGE_FAINT 5
// Form change that activates when the Pokémon is sent out at the beginning of a battle
// param1: item to hold, optional
// param2: a move that will be replaced, optional
// param3: a new move to replace it with, optional
#define FORM_CHANGE_BEGIN_BATTLE 6
// Form change that activates at the end of a battle. If species is not specified and it's on the player's side, it will try to use the value saved in gBattleStruct->changedSpecies from a previous form change.
// param1: item to hold, optional
// param2: a move that will be replaced, optional
// param3: a new move to replace it with, optional
#define FORM_CHANGE_END_BATTLE 7
// Form change that activates at the end of a battle based on the terrain if it participated in the battle and hasn't fainted. Takes priority over FORM_CHANGE_END_BATTLE.
// param1: battle terrain to check.
#define FORM_CHANGE_END_BATTLE_TERRAIN 8
// Form change that activates when the Pokémon is switched out in battle.
// - No parameters.
#define FORM_CHANGE_BATTLE_SWITCH 9
// Form change that activates when the Pokémon's HP % passes a certain threshold.
// param1: Ability to check.
// param2: HP comparer
// - HP_HIGHER_THAN if the form triggers when the current HP is higher than the specified threshold.
// - HP_LOWER_EQ_THAN if the form triggers when the current HP is lower or equal than the specified threshold.
// param3: HP percentage threshold.
#define FORM_CHANGE_BATTLE_HP_PERCENT 10
// Form change that activates when the mon has the defined item.
// If it's on the player's side, it also requires ITEM_MEGA_RING in the user's bag and for the player to trigger it by pressing START before selecting a move.
// param1: item to hold.
#define FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM 11
// Form change that activates when the mon has the defined move.
// If it's on the player's side, it also requires ITEM_MEGA_RING in the user's bag and for the player to trigger it by pressing START before selecting a move.
// param1: move to have.
#define FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE 12
// Form change that activates automatically when entering battle with the specified item.
// If the item is a Red Orb, it uses the Omega Symbol for the animation and icon. Otherwise, it defaults to the Alpha symbol.
// The battle indicator icon is based on the species, with Primal Groudon's as Omega and otherwise being Alpha.
// param1: item to hold.
#define FORM_CHANGE_BATTLE_PRIMAL_REVERSION 13
// Form change that activates when a specific weather is set during battle.
// param1: weather to check
// param2: (optional) revert if specified ability is lost
#define FORM_CHANGE_BATTLE_WEATHER 14
// Form change that activates automatically when the turn ends.
// param1: ability to check.
#define FORM_CHANGE_BATTLE_TURN_END 15
// Form change that activates when the mon has the defined item.
// If it's on the player's side, it also requires for the player to trigger it by pressing START before selecting a move.
// param1: item to hold.
#define FORM_CHANGE_BATTLE_ULTRA_BURST 16
// Form change that activates when the mon Dynamaxes (TODO: with Gigantamax factor).
// - No parameters
#define FORM_CHANGE_BATTLE_GIGANTAMAX 17
// Form change that activates at a certain time of day in the overworld automatically.
// param1: time of day to check.
// - DAY if Form change that activates in the daytime.
// - NIGHT if Form change that activates at nighttime.
#define FORM_CHANGE_TIME_OF_DAY 18
// Form change that depends on a multichoice (e.g. Rotom Catalog).
// param1: multichoice list (starting at 0).
#define FORM_CHANGE_ITEM_USE_MULTICHOICE 19
// Form change that activates when inflicted with a specific status
// param1: status
#define FORM_CHANGE_STATUS 20
#endif // GUARD_CONSTANTS_FORM_CHANGE_TYPES_H

File diff suppressed because it is too large Load Diff

View File

@ -62,6 +62,7 @@ void ItemUseCB_PPUp(u8 taskId, TaskFunc func);
u16 ItemIdToBattleMoveId(u16 item);
bool8 IsMoveHm(u16 move);
bool8 MonKnowsMove(struct Pokemon *mon, u16 move);
bool8 BoxMonKnowsMove(struct BoxPokemon *boxMon, u16 move);
void ItemUseCB_TMHM(u8 taskId, TaskFunc func);
void ItemUseCB_RareCandy(u8 taskId, TaskFunc func);
void ItemUseCB_SacredAsh(u8 taskId, TaskFunc func);

View File

@ -791,6 +791,11 @@ struct MonSpritesGfxManager *CreateMonSpritesGfxManager(u8 battlePosition, u8 mo
void DestroyMonSpritesGfxManager(void);
u8 *MonSpritesGfxManager_GetSpritePtr(u8 bufferId);
u16 GetFormSpeciesId(u16 speciesId, u8 formId);
u8 GetFormIdFromFormSpeciesId(u16 formSpeciesId);
u16 GetFormChangeTargetSpecies(struct Pokemon *mon, u16 method, u32 arg);
u16 GetFormChangeTargetSpeciesBoxMon(struct BoxPokemon *boxMon, u16 method, u32 arg);
bool32 DoesSpeciesHaveFormChangeMethod(u16 species, u16 method);
void TryToSetBattleFormChangeMoves(struct Pokemon *mon, u16 method);
bool8 HealStatusConditions(struct Pokemon *mon, u32 healMask, u8 battleId);

View File

@ -541,6 +541,34 @@ static const u8 sText_GemActivates[] = _("{B_LAST_ITEM} strengthened\n{B_ATK_NAM
static const u8 sText_AttackWeakenedByStrongWinds[] = _("The mysterious strong winds\nweakened the attack!");
static const u8 sText_EnduredViaSturdy[] = _("{B_DEF_NAME_WITH_PREFIX} endured\nthe hit using {B_DEF_ABILITY}!");
static const u8 sText_BerryDmgReducing[] = _("{B_LAST_ITEM} weakened the damage\nto {B_DEF_NAME_WITH_PREFIX}!");
static const u8 sText_TargetToughedItOut[] = _("{B_DEF_NAME_WITH_PREFIX} toughed it out\nto show you its best side!");
static const u8 sText_TrappedBySwirlingMagma[] =_("{B_DEF_NAME_WITH_PREFIX} became\ntrapped by swirling magma!");
static const u8 sText_Infestation[] = _("{B_DEF_NAME_WITH_PREFIX} has been afflicted\nwith an infestation by {B_ATK_NAME_WITH_PREFIX}!");
static const u8 sText_PkmnInSnapTrap[] = _("{B_DEF_NAME_WITH_PREFIX} got trapped\nby a snap trap!");
static const u8 sText_AtkTrappedDef[] = _("{B_ATK_NAME_WITH_PREFIX} trapped\nthe {B_DEF_NAME_WITH_PREFIX}!");
static const u8 sText_ResetsTargetsStatLevels[] =_("{B_DEF_NAME_WITH_PREFIX}'s stat changes\nwere removed!");
static const u8 sText_StrongWindsDissipated[] = _("The mysterious strong winds\nhave dissipated!{PAUSE 64}");
static const u8 sText_ExtremeSunlightFaded[] = _("The extreme sunlight faded.{PAUSE 64}");
static const u8 sText_HeavyRainLifted[] = _("The heavy rain has lifted!{PAUSE 64}");
static const u8 sText_SymbiosisItemPass[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} passed its {B_LAST_ITEM}\nto {B_ATK_NAME_WITH_PREFIX} through {B_LAST_ABILITY}!");
static const u8 sText_BurstingFlames[] = _("The bursting flames\nhit {B_SCR_ACTIVE_NAME_WITH_PREFIX}!");
static const u8 sText_BrokeThroughProtection[] = _("It broke through\n{B_DEF_NAME_WITH_PREFIX}'s protection!");
static const u8 sText_FellForFeint[] = _("{B_DEF_NAME_WITH_PREFIX} fell for\nthe feint!");
static const u8 sText_SpectralThiefSteal[] = _("{B_ATK_NAME_WITH_PREFIX} stole the target's\nboosted stats!");
static const u8 sText_PkmnsAbilitySuppressed[] = _("{B_DEF_NAME_WITH_PREFIX}'s ability\nwas suppressed!");
static const u8 sText_IncinerateBurn[] = _("{B_EFF_NAME_WITH_PREFIX}'s {B_LAST_ITEM}\nwas burnt up!");
static const u8 sText_BugBite[] = _("{B_ATK_NAME_WITH_PREFIX} stole and ate\n{B_EFF_NAME_WITH_PREFIX}'s {B_LAST_ITEM}!");
static const u8 sText_BothCanNoLongerEscape[] = _("Neither Pokémon can run away!");
static const u8 sText_AttackerLostFireType[] = _("{B_ATK_NAME_WITH_PREFIX} burned itself out!");
static const u8 sText_AttackerLostElectricType[] = _("{B_ATK_NAME_WITH_PREFIX} used up all\nof its electricity!");
static const u8 sText_AttackerLostItsType[] = _("{B_ATK_NAME_WITH_PREFIX} lost\nits {B_BUFF1} type!");
static const u8 sText_StealthRockDmg[] = _("Pointed stones dug into\n{B_SCR_ACTIVE_NAME_WITH_PREFIX}!");
static const u8 sText_SharpSteelDmg[] = _("Sharp steel bit into {B_DEF_NAME_WITH_PREFIX}!");
static const u8 sText_PointedStonesFloat[] =_("Pointed stones float in the air\naround {B_DEF_TEAM2} team!");
static const u8 sText_SharpSteelFloats[] = _("Sharp-pointed steel floats\naround {B_DEF_TEAM2} team!");
static const u8 sText_TargetCoveredInStickyCandySyrup[] = _("{B_DEF_NAME_WITH_PREFIX} got covered\nin sticky syrup!");
static const u8 sText_AromaVeilProtected[] = _("{B_DEF_NAME_WITH_PREFIX} is protected\nby an aromatic veil!");
static const u8 sText_PkmnPreventedFromHealing[] = _("{B_DEF_NAME_WITH_PREFIX} was prevented\nfrom healing!");
const u16 gTrainerUsedItemStringIds[] =
@ -952,6 +980,34 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT - BATTLESTRINGS_TABLE_ST
[STRINGID_ATTACKWEAKENEDBSTRONGWINDS - BATTLESTRINGS_TABLE_START] = sText_AttackWeakenedByStrongWinds,
[STRINGID_ENDUREDSTURDY - BATTLESTRINGS_TABLE_START] = sText_EnduredViaSturdy,
[STRINGID_BERRYDMGREDUCES - BATTLESTRINGS_TABLE_START] = sText_BerryDmgReducing,
[STRINGID_TARGETTOUGHEDITOUT - BATTLESTRINGS_TABLE_START] = sText_TargetToughedItOut,
[STRINGID_TRAPPEDBYSWIRLINGMAGMA - BATTLESTRINGS_TABLE_START] = sText_TrappedBySwirlingMagma,
[STRINGID_INFESTATION - BATTLESTRINGS_TABLE_START] = sText_Infestation,
[STRINGID_PKMNINSNAPTRAP - BATTLESTRINGS_TABLE_START] = sText_PkmnInSnapTrap,
[STRINGID_THUNDERCAGETRAPPED - BATTLESTRINGS_TABLE_START] = sText_AtkTrappedDef,
[STRINGID_RESETSTARGETSSTATLEVELS - BATTLESTRINGS_TABLE_START] = sText_ResetsTargetsStatLevels,
[STRINGID_STRONGWINDSDISSIPATED - BATTLESTRINGS_TABLE_START] = sText_StrongWindsDissipated,
[STRINGID_EXTREMESUNLIGHTFADED - BATTLESTRINGS_TABLE_START] = sText_ExtremeSunlightFaded,
[STRINGID_HEAVYRAINLIFTED - BATTLESTRINGS_TABLE_START] = sText_HeavyRainLifted,
[STRINGID_SYMBIOSISITEMPASS - BATTLESTRINGS_TABLE_START] = sText_SymbiosisItemPass,
[STRINGID_BURSTINGFLAMESHIT - BATTLESTRINGS_TABLE_START] = sText_BurstingFlames,
[STRINGID_BROKETHROUGHPROTECTION - BATTLESTRINGS_TABLE_START] = sText_BrokeThroughProtection,
[STRINGID_FELLFORFEINT - BATTLESTRINGS_TABLE_START] = sText_FellForFeint,
[STRINGID_SPECTRALTHIEFSTEAL - BATTLESTRINGS_TABLE_START] = sText_SpectralThiefSteal,
[STRINGID_PKMNSABILITYSUPPRESSED - BATTLESTRINGS_TABLE_START] = sText_PkmnsAbilitySuppressed,
[STRINGID_INCINERATEBURN - BATTLESTRINGS_TABLE_START] = sText_IncinerateBurn,
[STRINGID_BUGBITE - BATTLESTRINGS_TABLE_START] = sText_BugBite,
[STRINGID_BOTHCANNOLONGERESCAPE - BATTLESTRINGS_TABLE_START] = sText_BothCanNoLongerEscape,
[STRINGID_ATTACKERLOSTFIRETYPE - BATTLESTRINGS_TABLE_START] = sText_AttackerLostFireType,
[STRINGID_ATTACKERLOSTELECTRICTYPE - BATTLESTRINGS_TABLE_START] = sText_AttackerLostElectricType,
[STRINGID_ATTACKERLOSTITSTYPE - BATTLESTRINGS_TABLE_START] = sText_AttackerLostItsType,
[STRINGID_STEALTHROCKDMG - BATTLESTRINGS_TABLE_START] = sText_StealthRockDmg,
[STRINGID_SHARPSTEELDMG - BATTLESTRINGS_TABLE_START] = sText_SharpSteelDmg,
[STRINGID_POINTEDSTONESFLOAT - BATTLESTRINGS_TABLE_START] = sText_PointedStonesFloat,
[STRINGID_SHARPSTEELFLOATS - BATTLESTRINGS_TABLE_START] = sText_SharpSteelFloats,
[STRINGID_TARGETCOVEREDINSTICKYCANDYSYRUP - BATTLESTRINGS_TABLE_START] = sText_TargetCoveredInStickyCandySyrup,
[STRINGID_AROMAVEILPROTECTED - BATTLESTRINGS_TABLE_START] = sText_AromaVeilProtected,
[STRINGID_PKMNPREVENTEDFROMHEALING - BATTLESTRINGS_TABLE_START] = sText_PkmnPreventedFromHealing,
};
const u16 gMagicCoatBounceStringIds[] =
@ -959,6 +1015,16 @@ const u16 gMagicCoatBounceStringIds[] =
STRINGID_PKMNMOVEBOUNCED, STRINGID_PKMNMOVEBOUNCEDABILITY
};
const u16 gDmgHazardsStringIds[] =
{
[B_MSG_PKMNHURTBYSPIKES] = STRINGID_PKMNHURTBYSPIKES,
[B_MSG_STEALTHROCKDMG] = STRINGID_STEALTHROCKDMG,
[B_MSG_SHARPSTEELDMG] = STRINGID_SHARPSTEELDMG,
[B_MSG_POINTEDSTONESFLOAT] = STRINGID_POINTEDSTONESFLOAT,
[B_MSG_SPIKESSCATTERED] = STRINGID_SPIKESSCATTERED,
[B_MSG_SHARPSTEELFLOATS] = STRINGID_SHARPSTEELFLOATS,
};
const u16 gMissStringIds[] =
{
[B_MSG_MISSED] = STRINGID_ATTACKMISSED,
@ -1106,15 +1172,19 @@ const u16 gFirstTurnOfTwoStringIds[] =
[B_MSG_TURN1_BOUNCE] = STRINGID_PKMNSPRANGUP
};
// Index copied from move's index in gTrappingMoves
const u16 gWrappedStringIds[] =
// Index copied from move's index in sTrappingMoves
const u16 gWrappedStringIds[NUM_TRAPPING_MOVES] =
{
STRINGID_PKMNSQUEEZEDBYBIND, // MOVE_BIND
STRINGID_PKMNWRAPPEDBY, // MOVE_WRAP
STRINGID_PKMNTRAPPEDINVORTEX, // MOVE_FIRE_SPIN
STRINGID_PKMNCLAMPED, // MOVE_CLAMP
STRINGID_PKMNTRAPPEDINVORTEX, // MOVE_WHIRLPOOL
STRINGID_PKMNTRAPPEDBYSANDTOMB // MOVE_SAND_TOMB
[B_MSG_WRAPPED_BIND] = STRINGID_PKMNSQUEEZEDBYBIND, // MOVE_BIND
[B_MSG_WRAPPED_WRAP] = STRINGID_PKMNWRAPPEDBY, // MOVE_WRAP
[B_MSG_WRAPPED_FIRE_SPIN] = STRINGID_PKMNTRAPPEDINVORTEX, // MOVE_FIRE_SPIN
[B_MSG_WRAPPED_CLAMP] = STRINGID_PKMNCLAMPED, // MOVE_CLAMP
[B_MSG_WRAPPED_WHIRLPOOL] = STRINGID_PKMNTRAPPEDINVORTEX, // MOVE_WHIRLPOOL
[B_MSG_WRAPPED_SAND_TOMB] = STRINGID_PKMNTRAPPEDBYSANDTOMB, // MOVE_SAND_TOMB
[B_MSG_WRAPPED_MAGMA_STORM] = STRINGID_TRAPPEDBYSWIRLINGMAGMA, // MOVE_MAGMA_STORM
[B_MSG_WRAPPED_INFESTATION] = STRINGID_INFESTATION, // MOVE_INFESTATION
[B_MSG_WRAPPED_SNAP_TRAP] = STRINGID_PKMNINSNAPTRAP, // MOVE_SNAP_TRAP
[B_MSG_WRAPPED_THUNDER_CAGE]= STRINGID_THUNDERCAGETRAPPED, // MOVE_THUNDER_CAGE
};
const u16 gMistUsedStringIds[] =
@ -1340,17 +1410,6 @@ const u16 gDoubleBattleRecallStrings[1 << (MAX_BATTLERS_COUNT / 2)] =
STRINGID_TRAINER1MON1AND2COMEBACK
};
const u16 gTrappingMoves[NUM_TRAPPING_MOVES + 1] =
{
MOVE_BIND,
MOVE_WRAP,
MOVE_FIRE_SPIN,
MOVE_CLAMP,
MOVE_WHIRLPOOL,
MOVE_SAND_TOMB,
0xFFFF // Never read
};
const u8 gText_PkmnIsEvolving[] = _("What?\n{STR_VAR_1} is evolving!");
const u8 gText_CongratsPkmnEvolved[] = _("Congratulations! Your {STR_VAR_1}\nevolved into {STR_VAR_2}!{WAIT_SE}\p");
const u8 gText_PkmnStoppedEvolving[] = _("Huh? {STR_VAR_1}\nstopped evolving!\p");

File diff suppressed because it is too large Load Diff

View File

@ -227,17 +227,87 @@ void MarkBattlerReceivedLinkData(u8 battlerId)
gBattleControllerExecFlags &= ~((1 << 28) << battlerId);
}
void CancelMultiTurnMoves(u8 battler)
const u8* CancelMultiTurnMoves(u32 battler)
{
gBattleMons[battler].status2 &= ~STATUS2_MULTIPLETURNS;
gBattleMons[battler].status2 &= ~STATUS2_LOCK_CONFUSE;
gBattleMons[battler].status2 &= ~STATUS2_UPROAR;
gBattleMons[battler].status2 &= ~STATUS2_BIDE;
const u8 *result = NULL;
gBattleMons[battler].status2 &= ~(STATUS2_MULTIPLETURNS);
gBattleMons[battler].status2 &= ~(STATUS2_LOCK_CONFUSE);
gBattleMons[battler].status2 &= ~(STATUS2_UPROAR);
gBattleMons[battler].status2 &= ~(STATUS2_BIDE);
gStatuses3[battler] &= ~STATUS3_SEMI_INVULNERABLE;
// Clear battler's semi-invulnerable bits if they are not held by Sky Drop.
if (!(gStatuses3[battler] & STATUS3_SKY_DROPPED))
gStatuses3[battler] &= ~(STATUS3_SEMI_INVULNERABLE);
// Check to see if this Pokemon was in the middle of using Sky Drop. If so, release the target.
if (gBattleStruct->skyDropTargets[battler] != 0xFF && !(gStatuses3[battler] & STATUS3_SKY_DROPPED))
{
// Get the target's battler id
u8 otherSkyDropper = gBattleStruct->skyDropTargets[battler];
// Clears sky_dropped and on_air statuses
gStatuses3[otherSkyDropper] &= ~(STATUS3_SKY_DROPPED | STATUS3_ON_AIR);
// Makes both attacker and target's sprites visible
gSprites[gBattlerSpriteIds[battler]].invisible = FALSE;
gSprites[gBattlerSpriteIds[otherSkyDropper]].invisible = FALSE;
// If target was sky dropped in the middle of Outrage/Thrash/Petal Dance,
// confuse them upon release and display "confused by fatigue" message & animation.
// Don't do this if this CancelMultiTurnMoves is caused by falling asleep via Yawn.
if (gBattleMons[otherSkyDropper].status2 & STATUS2_LOCK_CONFUSE && gBattleStruct->turnEffectsTracker != 24)
{
gBattleMons[otherSkyDropper].status2 &= ~(STATUS2_LOCK_CONFUSE);
// If the target can be confused, confuse them.
// Don't use CanBeConfused, can cause issues in edge cases.
if (!(GetBattlerAbility(otherSkyDropper) == ABILITY_OWN_TEMPO
|| gBattleMons[otherSkyDropper].status2 & STATUS2_CONFUSION
|| IsBattlerTerrainAffected(otherSkyDropper, STATUS_FIELD_MISTY_TERRAIN)))
{
// Set confused status
gBattleMons[otherSkyDropper].status2 |= STATUS2_CONFUSION_TURN(((Random()) % 4) + 2);
// If this CancelMultiTurnMoves is occuring due to attackcanceller
if (gBattlescriptCurrInstr[0] == 0x0)
{
gBattleStruct->skyDropTargets[battler] = 0xFE;
}
// If this CancelMultiTurnMoves is occuring due to VARIOUS_GRAVITY_ON_AIRBORNE_MONS
// Reapplying STATUS3_SKY_DROPPED allows for avoiding unecessary messages when Gravity is applied to the target.
else if (gBattlescriptCurrInstr[0] == 0x76 && gBattlescriptCurrInstr[2] == 76)
{
gBattleStruct->skyDropTargets[battler] = 0xFE;
gStatuses3[otherSkyDropper] |= STATUS3_SKY_DROPPED;
}
// If this CancelMultiTurnMoves is occuring due to cancelmultiturnmoves script
else if (gBattlescriptCurrInstr[0] == 0x76 && gBattlescriptCurrInstr[2] == 0)
{
gBattlerAttacker = otherSkyDropper;
result = BattleScript_ThrashConfuses;
}
// If this CancelMultiTurnMoves is occuring due to receiving Sleep/Freeze status
else if (gBattleScripting.moveEffect <= PRIMARY_STATUS_MOVE_EFFECT)
{
gBattlerAttacker = otherSkyDropper;
BattleScriptPush(gBattlescriptCurrInstr + 1);
result = BattleScript_ThrashConfuses;
}
}
}
// Clear skyDropTargets data, unless this CancelMultiTurnMoves is caused by Yawn, attackcanceler, or VARIOUS_GRAVITY_ON_AIRBORNE_MONS
if (!(gBattleMons[otherSkyDropper].status2 & STATUS2_LOCK_CONFUSE) && gBattleStruct->skyDropTargets[battler] < 4)
{
gBattleStruct->skyDropTargets[battler] = 0xFF;
gBattleStruct->skyDropTargets[otherSkyDropper] = 0xFF;
}
}
gDisableStructs[battler].rolloutTimer = 0;
gDisableStructs[battler].furyCutterCounter = 0;
return result;
}
bool32 WasUnableToUseMove(u32 battler)
@ -3255,7 +3325,7 @@ u8 ItemBattleEffects(u8 caseID, u8 battlerId, bool8 moveTurn)
}
}
break;
case ITEMEFFECT_KINGSROCK_SHELLBELL:
case ITEMEFFECT_KINGSROCK:
if (gBattleMoveDamage)
{
switch (atkHoldEffect)
@ -6213,6 +6283,366 @@ void RecalcBattlerStats(u32 battler, struct Pokemon *mon)
CopyMonAbilityAndTypesToBattleMon(battler, mon);
}
bool32 TestIfSheerForceAffected(u32 battler, u16 move)
{
return GetBattlerAbility(battler) == ABILITY_SHEER_FORCE && MoveIsAffectedBySheerForce(move);
}
bool32 CanSleep(u32 battler)
{
u16 ability = GetBattlerAbility(battler);
if (ability == ABILITY_INSOMNIA
|| ability == ABILITY_VITAL_SPIRIT
|| ability == ABILITY_COMATOSE
|| ability == ABILITY_PURIFYING_SALT
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD
|| gBattleMons[battler].status1 & STATUS1_ANY
|| IsAbilityOnSide(battler, ABILITY_SWEET_VEIL)
|| IsAbilityStatusProtected(battler)
|| IsBattlerTerrainAffected(battler, STATUS_FIELD_ELECTRIC_TERRAIN | STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
return TRUE;
}
bool32 CanBePoisoned(u32 battlerAttacker, u32 battlerTarget)
{
u16 ability = GetBattlerAbility(battlerTarget);
if (!(CanPoisonType(battlerAttacker, battlerTarget))
|| gSideStatuses[GetBattlerSide(battlerTarget)] & SIDE_STATUS_SAFEGUARD
|| gBattleMons[battlerTarget].status1 & STATUS1_ANY
|| ability == ABILITY_IMMUNITY
|| ability == ABILITY_COMATOSE
|| ability == ABILITY_PURIFYING_SALT
|| IsAbilityOnSide(battlerTarget, ABILITY_PASTEL_VEIL)
|| IsAbilityStatusProtected(battlerTarget)
|| IsBattlerTerrainAffected(battlerTarget, STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
return TRUE;
}
bool32 CanBeBurned(u32 battler)
{
u16 ability = GetBattlerAbility(battler);
if (IS_BATTLER_OF_TYPE(battler, TYPE_FIRE)
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD
|| gBattleMons[battler].status1 & STATUS1_ANY
|| ability == ABILITY_WATER_VEIL
|| ability == ABILITY_WATER_BUBBLE
|| ability == ABILITY_COMATOSE
|| ability == ABILITY_THERMAL_EXCHANGE
|| ability == ABILITY_PURIFYING_SALT
|| IsAbilityStatusProtected(battler)
|| IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
return TRUE;
}
bool32 CanBeParalyzed(u32 battler)
{
u16 ability = GetBattlerAbility(battler);
if ((B_PARALYZE_ELECTRIC >= GEN_6 && IS_BATTLER_OF_TYPE(battler, TYPE_ELECTRIC))
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD
|| ability == ABILITY_LIMBER
|| ability == ABILITY_COMATOSE
|| ability == ABILITY_PURIFYING_SALT
|| gBattleMons[battler].status1 & STATUS1_ANY
|| IsAbilityStatusProtected(battler)
|| IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
return TRUE;
}
bool32 CanBeFrozen(u32 battler)
{
u16 ability = GetBattlerAbility(battler);
if (IS_BATTLER_OF_TYPE(battler, TYPE_ICE)
|| IsBattlerWeatherAffected(battler, B_WEATHER_SUN)
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD
|| ability == ABILITY_MAGMA_ARMOR
|| ability == ABILITY_COMATOSE
|| ability == ABILITY_PURIFYING_SALT
|| gBattleMons[battler].status1 & STATUS1_ANY
|| IsAbilityStatusProtected(battler)
|| IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
return TRUE;
}
bool32 CanGetFrostbite(u32 battler)
{
u16 ability = GetBattlerAbility(battler);
if (IS_BATTLER_OF_TYPE(battler, TYPE_ICE)
|| gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_SAFEGUARD
|| ability == ABILITY_MAGMA_ARMOR
|| ability == ABILITY_COMATOSE
|| ability == ABILITY_PURIFYING_SALT
|| gBattleMons[battler].status1 & STATUS1_ANY
|| IsAbilityStatusProtected(battler)
|| IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
return TRUE;
}
bool32 CanBeConfused(u32 battler)
{
if (GetBattlerAbility(battler) == ABILITY_OWN_TEMPO
|| gBattleMons[battler].status2 & STATUS2_CONFUSION
|| IsBattlerTerrainAffected(battler, STATUS_FIELD_MISTY_TERRAIN))
return FALSE;
return TRUE;
}
bool32 CanStealItem(u32 battlerStealing, u32 battlerItem, u16 item)
{
u8 stealerSide = GetBattlerSide(battlerStealing);
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER_TOWER)
return FALSE;
// Check if the battler trying to steal should be able to
if (stealerSide == B_SIDE_OPPONENT
&& !(gBattleTypeFlags &
(BATTLE_TYPE_EREADER_TRAINER
| BATTLE_TYPE_TRAINER_TOWER
| BATTLE_TYPE_LINK
| (B_TRAINERS_KNOCK_OFF_ITEMS == TRUE ? BATTLE_TYPE_TRAINER : 0)
)))
{
return FALSE;
}
else if (!(gBattleTypeFlags &
(BATTLE_TYPE_EREADER_TRAINER
| BATTLE_TYPE_BATTLE_TOWER
| BATTLE_TYPE_LINK))
&& (gWishFutureKnock.knockedOffMons[stealerSide] & gBitTable[gBattlerPartyIndexes[battlerStealing]]))
{
return FALSE;
}
if (!CanBattlerGetOrLoseItem(battlerItem, item) // Battler with item cannot have it stolen
||!CanBattlerGetOrLoseItem(battlerStealing, item)) // Stealer cannot take the item
return FALSE;
return TRUE;
}
void TrySaveExchangedItem(u32 battler, u16 stolenItem)
{
// Because BtlController_EmitSetMonData does SetMonData, we need to save the stolen item only if it matches the battler's original
// So, if the player steals an item during battle and has it stolen from it, it will not end the battle with it (naturally)
if (B_TRAINERS_KNOCK_OFF_ITEMS == FALSE)
return;
// If regular trainer battle and mon's original item matches what is being stolen, save it to be restored at end of battle
if (gBattleTypeFlags & BATTLE_TYPE_TRAINER
&& !(gBattleTypeFlags & BATTLE_TYPE_BATTLE_TOWER)
&& GetBattlerSide(battler) == B_SIDE_PLAYER
&& stolenItem == gBattleStruct->itemLost[gBattlerPartyIndexes[battler]].originalItem)
gBattleStruct->itemLost[gBattlerPartyIndexes[battler]].stolen = TRUE;
}
void RemoveBattlerType(u32 battler, u8 type)
{
u32 i;
for (i = 0; i < 3; i++)
{
if (*(u8 *)(&gBattleMons[battler].type1 + i) == type)
*(u8 *)(&gBattleMons[battler].type1 + i) = TYPE_MYSTERY;
}
}
// Returns SPECIES_NONE if no form change is possible
u16 GetBattleFormChangeTargetSpecies(u32 battler, u16 method)
{
u32 i;
u16 targetSpecies = SPECIES_NONE;
u16 species = gBattleMons[battler].species;
const struct FormChange *formChanges = GetSpeciesFormChanges(species);
struct Pokemon *mon = &GetBattlerParty(battler)[gBattlerPartyIndexes[battler]];
u16 heldItem;
if (formChanges != NULL)
{
heldItem = gBattleMons[battler].item;
for (i = 0; formChanges[i].method != FORM_CHANGE_TERMINATOR; i++)
{
if (method == formChanges[i].method && species != formChanges[i].targetSpecies)
{
switch (method)
{
case FORM_CHANGE_BATTLE_MEGA_EVOLUTION_ITEM:
case FORM_CHANGE_BATTLE_PRIMAL_REVERSION:
case FORM_CHANGE_BATTLE_ULTRA_BURST:
if (heldItem == formChanges[i].param1)
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_BATTLE_MEGA_EVOLUTION_MOVE:
if (gBattleMons[battler].moves[0] == formChanges[i].param1
|| gBattleMons[battler].moves[1] == formChanges[i].param1
|| gBattleMons[battler].moves[2] == formChanges[i].param1
|| gBattleMons[battler].moves[3] == formChanges[i].param1)
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_BATTLE_SWITCH:
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_BATTLE_HP_PERCENT:
if (formChanges[i].param1 == GetBattlerAbility(battler))
{
// We multiply by 100 to make sure that integer division doesn't mess with the health check.
u32 hpCheck = gBattleMons[battler].hp * 100 * 100 / gBattleMons[battler].maxHP;
switch(formChanges[i].param2)
{
case HP_HIGHER_THAN:
if (hpCheck > formChanges[i].param3 * 100)
targetSpecies = formChanges[i].targetSpecies;
break;
case HP_LOWER_EQ_THAN:
if (hpCheck <= formChanges[i].param3 * 100)
targetSpecies = formChanges[i].targetSpecies;
break;
}
}
break;
case FORM_CHANGE_BATTLE_GIGANTAMAX:
if (GetMonData(mon, MON_DATA_GIGANTAMAX_FACTOR))
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_BATTLE_WEATHER:
// Check if there is a required ability and if the battler's ability does not match it
// or is suppressed. If so, revert to the no weather form.
if (formChanges[i].param2
&& GetBattlerAbility(battler) != formChanges[i].param2
&& formChanges[i].param1 == B_WEATHER_NONE)
{
targetSpecies = formChanges[i].targetSpecies;
}
// We need to revert the weather form if the field is under Air Lock, too.
else if (!WEATHER_HAS_EFFECT && formChanges[i].param1 == B_WEATHER_NONE)
{
targetSpecies = formChanges[i].targetSpecies;
}
// Otherwise, just check for a match between the weather and the form change table.
// Added a check for whether the weather is in effect to prevent end-of-turn soft locks with Cloud Nine / Air Lock
else if (((gBattleWeather & formChanges[i].param1) && WEATHER_HAS_EFFECT)
|| (gBattleWeather == B_WEATHER_NONE && formChanges[i].param1 == B_WEATHER_NONE))
{
targetSpecies = formChanges[i].targetSpecies;
}
break;
case FORM_CHANGE_BATTLE_TURN_END:
if (formChanges[i].param1 == GetBattlerAbility(battler))
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_STATUS:
if (gBattleMons[battler].status1 & formChanges[i].param1)
targetSpecies = formChanges[i].targetSpecies;
break;
}
}
}
}
return targetSpecies;
}
bool32 IsBattlerMegaEvolved(u32 battler)
{
// While Transform does copy stats and visuals, it shouldn't be counted as true Mega Evolution.
if (gBattleMons[battler].status2 & STATUS2_TRANSFORMED)
return FALSE;
return (gSpeciesInfo[gBattleMons[battler].species].isMegaEvolution);
}
bool32 IsBattlerPrimalReverted(u32 battler)
{
// While Transform does copy stats and visuals, it shouldn't be counted as true Primal Revesion.
if (gBattleMons[battler].status2 & STATUS2_TRANSFORMED)
return FALSE;
return (gSpeciesInfo[gBattleMons[battler].species].isPrimalReversion);
}
bool32 IsBattlerUltraBursted(u32 battler)
{
// While Transform does copy stats and visuals, it shouldn't be counted as true Ultra Burst.
if (gBattleMons[battler].status2 & STATUS2_TRANSFORMED)
return FALSE;
return (gSpeciesInfo[gBattleMons[battler].species].isUltraBurst);
}
bool32 CanBattlerFormChange(u32 battler, u16 method)
{
// Can't change form if transformed.
if (gBattleMons[battler].status2 & STATUS2_TRANSFORMED
&& B_TRANSFORM_FORM_CHANGES >= GEN_5)
return FALSE;
// Mega Evolved and Ultra Bursted Pokémon should always revert to normal upon fainting or ending the battle.
if ((IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler)) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE))
return TRUE;
else if (IsBattlerPrimalReverted(battler) && (method == FORM_CHANGE_END_BATTLE))
return TRUE;
// TODO: Dynamax
// Gigantamaxed Pokemon should revert upon fainting, switching, or ending the battle.
/* else if (IsGigantamaxed(battler) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_BATTLE_SWITCH || method == FORM_CHANGE_END_BATTLE))
return TRUE; */
return DoesSpeciesHaveFormChangeMethod(gBattleMons[battler].species, method);
}
bool32 TryBattleFormChange(u32 battler, u16 method)
{
u8 monId = gBattlerPartyIndexes[battler];
u8 side = GetBattlerSide(battler);
struct Pokemon *party = GetBattlerParty(battler);
u16 targetSpecies;
if (!CanBattlerFormChange(battler, method))
return FALSE;
targetSpecies = GetBattleFormChangeTargetSpecies(battler, method);
if (targetSpecies == SPECIES_NONE)
targetSpecies = GetFormChangeTargetSpecies(&party[monId], method, 0);
if (targetSpecies != SPECIES_NONE)
{
// Saves the original species on the first form change.
if (gBattleStruct->changedSpecies[side][monId] == SPECIES_NONE)
gBattleStruct->changedSpecies[side][monId] = gBattleMons[battler].species;
TryToSetBattleFormChangeMoves(&party[monId], method);
SetMonData(&party[monId], MON_DATA_SPECIES, &targetSpecies);
gBattleMons[battler].species = targetSpecies;
RecalcBattlerStats(battler, &party[monId]);
return TRUE;
}
else if (gBattleStruct->changedSpecies[side][monId] != SPECIES_NONE)
{
bool32 restoreSpecies = FALSE;
// Mega Evolved and Ultra Bursted Pokémon should always revert to normal upon fainting or ending the battle, so no need to add it to the form change tables.
if ((IsBattlerMegaEvolved(battler) || IsBattlerUltraBursted(battler)) && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_END_BATTLE))
restoreSpecies = TRUE;
// Unlike Megas, Primal Reversion isn't canceled on fainting.
else if (IsBattlerPrimalReverted(battler) && (method == FORM_CHANGE_END_BATTLE))
restoreSpecies = TRUE;
// Gigantamax Pokemon have their forms reverted after fainting, switching, or ending the battle.
else if (FALSE /* IsGigantamaxed(battler) */ && (method == FORM_CHANGE_FAINT || method == FORM_CHANGE_BATTLE_SWITCH || method == FORM_CHANGE_END_BATTLE))
restoreSpecies = TRUE;
if (restoreSpecies)
{
// Reverts the original species
TryToSetBattleFormChangeMoves(&party[monId], method);
SetMonData(&party[monId], MON_DATA_SPECIES, &gBattleStruct->changedSpecies[side][monId]);
RecalcBattlerStats(battler, &party[monId]);
return TRUE;
}
}
return FALSE;
}
// battle_ai_util.c

File diff suppressed because it is too large Load Diff

View File

@ -5128,7 +5128,7 @@ bool8 MonKnowsMove(struct Pokemon *mon, u16 move)
{
u8 i;
for (i = 0; i < MAX_MON_MOVES; ++i)
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (GetMonData(mon, MON_DATA_MOVE1 + i) == move)
return TRUE;
@ -5136,6 +5136,18 @@ bool8 MonKnowsMove(struct Pokemon *mon, u16 move)
return FALSE;
}
bool8 BoxMonKnowsMove(struct BoxPokemon *boxMon, u16 move)
{
u8 i;
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (GetBoxMonData(boxMon, MON_DATA_MOVE1 + i) == move)
return TRUE;
}
return FALSE;
}
static void DisplayLearnMoveMessage(const u8 *str)
{
StringExpandPlaceholders(gStringVar4, str);

View File

@ -30,6 +30,7 @@
#include "constants/items.h"
#include "constants/item_effects.h"
#include "constants/cries.h"
#include "constants/form_change_types.h"
#include "constants/pokemon.h"
#include "constants/abilities.h"
#include "constants/moves.h"
@ -1673,6 +1674,17 @@ void SetMonMoveSlot(struct Pokemon *mon, u16 move, u8 slot)
SetMonData(mon, MON_DATA_PP1 + slot, &gMovesInfo[move].pp);
}
static void SetMonMoveSlot_KeepPP(struct Pokemon *mon, u16 move, u8 slot)
{
u8 ppBonuses = GetMonData(mon, MON_DATA_PP_BONUSES, NULL);
u8 currPP = GetMonData(mon, MON_DATA_PP1 + slot, NULL);
u8 newPP = CalculatePPWithBonus(move, ppBonuses, slot);
u16 finalPP = min(currPP, newPP);
SetMonData(mon, MON_DATA_MOVE1 + slot, &move);
SetMonData(mon, MON_DATA_PP1 + slot, &finalPP);
}
void SetBattleMonMoveSlot(struct BattlePokemon *mon, u16 move, u8 slot)
{
mon->moves[slot] = move;
@ -6171,6 +6183,140 @@ u16 GetFormSpeciesId(u16 speciesId, u8 formId)
return speciesId;
}
u8 GetFormIdFromFormSpeciesId(u16 formSpeciesId)
{
u8 targetFormId = 0;
if (GetSpeciesFormTable(formSpeciesId) != NULL)
{
for (targetFormId = 0; GetSpeciesFormTable(formSpeciesId)[targetFormId] != FORM_SPECIES_END; targetFormId++)
{
if (formSpeciesId == GetSpeciesFormTable(formSpeciesId)[targetFormId])
break;
}
}
return targetFormId;
}
u16 GetFormChangeTargetSpecies(struct Pokemon *mon, u16 method, u32 arg)
{
return GetFormChangeTargetSpeciesBoxMon(&mon->box, method, arg);
}
// Returns SPECIES_NONE if no form change is possible
u16 GetFormChangeTargetSpeciesBoxMon(struct BoxPokemon *boxMon, u16 method, u32 arg)
{
u32 i;
u16 targetSpecies = SPECIES_NONE;
u16 species = GetBoxMonData(boxMon, MON_DATA_SPECIES, NULL);
const struct FormChange *formChanges = GetSpeciesFormChanges(species);
u16 heldItem;
u32 ability;
if (formChanges != NULL)
{
heldItem = GetBoxMonData(boxMon, MON_DATA_HELD_ITEM, NULL);
ability = GetAbilityBySpecies(species, GetBoxMonData(boxMon, MON_DATA_ABILITY_NUM, NULL));
for (i = 0; formChanges[i].method != FORM_CHANGE_TERMINATOR; i++)
{
if (method == formChanges[i].method && species != formChanges[i].targetSpecies)
{
switch (method)
{
case FORM_CHANGE_ITEM_HOLD:
if ((heldItem == formChanges[i].param1 || formChanges[i].param1 == ITEM_NONE)
&& (ability == formChanges[i].param2 || formChanges[i].param2 == ABILITY_NONE))
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_ITEM_USE:
if (arg == formChanges[i].param1)
{
// TODO: time
switch (formChanges[i].param2)
{
case DAY:
// if (GetTimeOfDay() != TIME_NIGHT)
// targetSpecies = formChanges[i].targetSpecies;
targetSpecies = SPECIES_NONE;
break;
case NIGHT:
// if (GetTimeOfDay() == TIME_NIGHT)
// targetSpecies = formChanges[i].targetSpecies;
targetSpecies = SPECIES_NONE;
break;
default:
targetSpecies = formChanges[i].targetSpecies;
break;
}
}
break;
case FORM_CHANGE_ITEM_USE_MULTICHOICE:
if (arg == formChanges[i].param1)
{
if (formChanges[i].param2 == gSpecialVar_Result)
targetSpecies = formChanges[i].targetSpecies;
}
break;
case FORM_CHANGE_MOVE:
if (BoxMonKnowsMove(boxMon, formChanges[i].param1) != formChanges[i].param2)
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_BEGIN_BATTLE:
case FORM_CHANGE_END_BATTLE:
if (heldItem == formChanges[i].param1 || formChanges[i].param1 == ITEM_NONE)
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_END_BATTLE_TERRAIN:
if (gBattleTerrain == formChanges[i].param1)
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_WITHDRAW:
case FORM_CHANGE_FAINT:
case FORM_CHANGE_STATUS:
targetSpecies = formChanges[i].targetSpecies;
break;
case FORM_CHANGE_TIME_OF_DAY:
// TODO: time
// switch (formChanges[i].param1)
// {
// case DAY:
// if (GetTimeOfDay() != TIME_NIGHT)
// targetSpecies = formChanges[i].targetSpecies;
// break;
// case NIGHT:
// if (GetTimeOfDay() == TIME_NIGHT)
// targetSpecies = formChanges[i].targetSpecies;
// break;
// }
// break;
targetSpecies = SPECIES_NONE;
break;
}
}
}
}
return targetSpecies;
}
bool32 DoesSpeciesHaveFormChangeMethod(u16 species, u16 method)
{
u32 i;
const struct FormChange *formChanges = GetSpeciesFormChanges(species);
if (formChanges != NULL)
{
for (i = 0; formChanges[i].method != FORM_CHANGE_TERMINATOR; i++)
{
if (method == formChanges[i].method && species != formChanges[i].targetSpecies)
return TRUE;
}
}
return FALSE;
}
u32 GetMonAffectionHearts(struct Pokemon *pokemon)
{
u32 friendship = GetMonData(pokemon, MON_DATA_FRIENDSHIP, NULL);
@ -6188,3 +6334,34 @@ u32 GetMonAffectionHearts(struct Pokemon *pokemon)
return AFFECTION_NO_HEARTS;
}
void TryToSetBattleFormChangeMoves(struct Pokemon *mon, u16 method)
{
int i, j;
u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
const struct FormChange *formChanges = GetSpeciesFormChanges(species);
if (formChanges == NULL
|| (method != FORM_CHANGE_BEGIN_BATTLE && method != FORM_CHANGE_END_BATTLE))
return;
for (i = 0; formChanges[i].method != FORM_CHANGE_TERMINATOR; i++)
{
if (formChanges[i].method == method
&& formChanges[i].param2
&& formChanges[i].param3
&& formChanges[i].targetSpecies != species)
{
u16 originalMove = formChanges[i].param2;
u16 newMove = formChanges[i].param3;
for (j = 0; j < MAX_MON_MOVES; j++)
{
u16 currMove = GetMonData(mon, MON_DATA_MOVE1 + j, NULL);
if (currMove == originalMove)
SetMonMoveSlot_KeepPP(mon, newMove, j);
}
break;
}
}
}