25-03-25 master to upcoming merge

This commit is contained in:
AlexOn1ine 2025-03-25 21:39:58 +01:00
commit ae640f0714
15 changed files with 249 additions and 59 deletions

View File

@ -16762,6 +16762,7 @@ gBattleAnimMove_JetPunch::
createvisualtask AnimTask_BlendBattleAnimPal, 10, F_PAL_TARGET, 2, 0, 9, RGB_BLUE
delay 8
createvisualtask AnimTask_ExtremeSpeedMonReappear, 2
setarg 0x7, 0x1000
createsprite gSmallBubblePairSpriteTemplate, ANIM_TARGET, 2, 0x14, 0xffec, 0x14, ANIM_TARGET
createsprite gSmallBubblePairSpriteTemplate, ANIM_TARGET, 2, 0xa, 0xa, 0x14, ANIM_TARGET
createsprite gFistFootSpriteTemplate, ANIM_TARGET, 3, 0, 0, 8, 1, 0

View File

@ -810,7 +810,8 @@ struct BattleStruct
u8 monCausingSleepClause[NUM_BATTLE_SIDES]; // Stores which pokemon on a given side is causing Sleep Clause to be active as the mon's index in the party
u8 additionalEffectsCounter:4; // A counter for the additionalEffects applied by the current move in Cmd_setadditionaleffects
u8 redCardActivates:1;
u8 padding2:2; // padding in the middle so pursuit fields are together
u8 cheekPouchActivated:1;
u8 padding2:1; // padding in the middle so pursuit fields are together
u8 pursuitStoredSwitch; // Stored id for the Pursuit target's switch
s32 battlerExpReward;
u16 prevTurnSpecies[MAX_BATTLERS_COUNT]; // Stores species the AI has in play at start of turn
@ -832,6 +833,9 @@ struct BattleStruct
u8 trainerSlideSpriteIds[MAX_BATTLERS_COUNT];
u8 embodyAspectBoost[NUM_BATTLE_SIDES];
u16 savedMove; // backup current move for mid-turn switching, e.g. Red Card
u16 opponentMonCanTera:6;
u16 opponentMonCanDynamax:6;
u16 padding:4;
};
struct AiBattleData

View File

@ -218,9 +218,11 @@
// Ingame partner flag
#define B_SHOW_PARTNER_TARGET FALSE // Shows the battler partner will target.
// Move description menu
#define B_SHOW_MOVE_DESCRIPTION TRUE // Shows move information in battler
// Weather settings
// Search for 'rain', 'sunny day', and 'hail' for move-specific or species-specific weather interactions.
#define B_ICE_WEATHER_BOTH 0
#define B_ICE_WEATHER_HAIL 1
#define B_ICE_WEATHER_SNOW 2

View File

@ -66,12 +66,12 @@
void EndDexNavSearch(u8 taskId);
void Task_OpenDexNavFromStartMenu(u8 taskId);
bool8 TryStartDexNavSearch(void);
void TryIncrementSpeciesSearchLevel(u16 dexNum);
void TryIncrementSpeciesSearchLevel(void);
void ResetDexNavSearch(void);
bool8 TryFindHiddenPokemon(void);
u32 CalculateDexNavShinyRolls(void);
void IncrementDexNavChain(void);
extern bool8 gDexNavBattle;
extern u16 gDexNavSpecies;
#endif // GUARD_DEXNAV_H

View File

@ -86,6 +86,7 @@ static void MoveSelectionDisplayPpNumber(u32 battler);
static void MoveSelectionDisplayPpString(u32 battler);
static void MoveSelectionDisplayMoveType(u32 battler);
static void MoveSelectionDisplayMoveNames(u32 battler);
static void TryMoveSelectionDisplayMoveDescription(u32 battler);
static void MoveSelectionDisplayMoveDescription(u32 battler);
static void SwitchIn_HandleSoundAndEnd(u32 battler);
static void WaitForMonSelection(u32 battler);
@ -786,8 +787,7 @@ void HandleInputChooseMove(u32 battler)
MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0);
MoveSelectionDisplayPpNumber(battler);
MoveSelectionDisplayMoveType(battler);
if (gBattleStruct->descriptionSubmenu)
MoveSelectionDisplayMoveDescription(battler);
TryMoveSelectionDisplayMoveDescription(battler);
TryChangeZTrigger(battler, gMoveSelectionCursor[battler]);
}
}
@ -802,8 +802,7 @@ void HandleInputChooseMove(u32 battler)
MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0);
MoveSelectionDisplayPpNumber(battler);
MoveSelectionDisplayMoveType(battler);
if (gBattleStruct->descriptionSubmenu)
MoveSelectionDisplayMoveDescription(battler);
TryMoveSelectionDisplayMoveDescription(battler);
TryChangeZTrigger(battler, gMoveSelectionCursor[battler]);
}
}
@ -817,8 +816,7 @@ void HandleInputChooseMove(u32 battler)
MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0);
MoveSelectionDisplayPpNumber(battler);
MoveSelectionDisplayMoveType(battler);
if (gBattleStruct->descriptionSubmenu)
MoveSelectionDisplayMoveDescription(battler);
TryMoveSelectionDisplayMoveDescription(battler);
TryChangeZTrigger(battler, gMoveSelectionCursor[battler]);
}
}
@ -833,8 +831,7 @@ void HandleInputChooseMove(u32 battler)
MoveSelectionCreateCursorAt(gMoveSelectionCursor[battler], 0);
MoveSelectionDisplayPpNumber(battler);
MoveSelectionDisplayMoveType(battler);
if (gBattleStruct->descriptionSubmenu)
MoveSelectionDisplayMoveDescription(battler);
TryMoveSelectionDisplayMoveDescription(battler);
TryChangeZTrigger(battler, gMoveSelectionCursor[battler]);
}
}
@ -876,7 +873,7 @@ void HandleInputChooseMove(u32 battler)
else if (JOY_NEW(B_MOVE_DESCRIPTION_BUTTON))
{
gBattleStruct->descriptionSubmenu = TRUE;
MoveSelectionDisplayMoveDescription(battler);
TryMoveSelectionDisplayMoveDescription(battler);
}
else if (JOY_NEW(START_BUTTON))
{
@ -1745,6 +1742,15 @@ static void MoveSelectionDisplayMoveType(u32 battler)
BattlePutTextOnWindow(gDisplayedStringBattle, B_WIN_MOVE_TYPE);
}
static void TryMoveSelectionDisplayMoveDescription(u32 battler)
{
if (!B_SHOW_MOVE_DESCRIPTION)
return;
if (gBattleStruct->descriptionSubmenu)
MoveSelectionDisplayMoveDescription(battler);
}
static void MoveSelectionDisplayMoveDescription(u32 battler)
{
struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct*)(&gBattleResources->bufferA[battler][4]);

View File

@ -218,7 +218,7 @@ enum
LIST_AI_CHECK_BAD_MOVE,
LIST_AI_TRY_TO_FAINT,
LIST_AI_CHECK_VIABILITY,
LIST_AI_SETUP_FIRST_TURN,
LIST_AI_FORCE_SETUP_FIRST_TURN,
LIST_AI_RISKY,
LIST_AI_TRY_TO_2HKO,
LIST_AI_PREFER_BATON_PASS,
@ -227,13 +227,20 @@ enum
LIST_AI_POWERFUL_STATUS,
LIST_AI_NEGATE_UNAWARE,
LIST_AI_WILL_SUICIDE,
LIST_AI_HELP_PARTNER,
LIST_AI_PREFER_STATUS_MOVES,
LIST_AI_STALL,
LIST_AI_SMART_SWITCHING,
LIST_AI_ACE_POKEMON,
LIST_AI_OMNISCIENT,
LIST_AI_SMART_MON_CHOICES,
LIST_AI_CONSERVATIVE,
LIST_AI_SEQUENCE_SWITCHING,
LIST_AI_DOUBLE_ACE_POKEMON,
LIST_AI_WEIGH_ABILITY_PREDICTION,
LIST_AI_PREFER_HIGHEST_DAMAGE_MOVE,
LIST_AI_PREDICT_SWITCH,
LIST_AI_PREDICT_INCOMING_MON,
LIST_AI_DYNAMIC_FUNC,
LIST_AI_ROAMING,
LIST_AI_SAFARI,
LIST_AI_FIRST_BATTLE,
@ -383,7 +390,7 @@ static const u8 sText_Swamp[] = _("Swamp");
static const u8 sText_CheckBadMove[] = _("Check Bad Move");
static const u8 sText_TryToFaint[] = _("Try to Faint");
static const u8 sText_CheckViability[] = _("Check Viability");
static const u8 sText_SetUpFirstTurn[] = _("Setup First Turn");
static const u8 sText_ForceSetupFirstTurn[] = _("Force Setup First Turn");
static const u8 sText_Risky[] = _("Risky");
static const u8 sText_TryTo2HKO[] = _("Try to 2HKO");
static const u8 sText_PreferBatonPass[] = _("Prefer Baton Pass");
@ -392,13 +399,20 @@ static const u8 sText_HpAware[] = _("HP Aware");
static const u8 sText_PowerfulStatus[] = _("Powerful Status");
static const u8 sText_NegateUnaware[] = _("Negate Unaware");
static const u8 sText_WillSuicide[] = _("Will Suicide");
static const u8 sText_HelpPartner[] = _("Help Partner");
static const u8 sText_PreferStatusMoves[] = _("Prefer Status Moves");
static const u8 sText_Stall[] = _("Stall");
static const u8 sText_SmartSwitching[] = _("Smart Switching");
static const u8 sText_AcePokemon[] = _("Ace Pokemon");
static const u8 sText_AcePokemon[] = _("Ace Pokémon");
static const u8 sText_Omniscient[] = _("Omniscient");
static const u8 sText_SmartMonChoices[] = _("Smart Mon Choices");
static const u8 sText_Conservative[] = _("Conservative");
static const u8 sText_SequenceSwitching[] = _("Sequence Switching");
static const u8 sText_DoubleAcePokemon[] = _("Double Ace Pokémon");
static const u8 sText_WeighAbilityPrediction[] = _("Weigh Ability Prediction");
static const u8 sText_PreferHighestDamageMove[] = _("Prefer Highest Damage Move");
static const u8 sText_PredictSwitch[] = _("Predict Switch");
static const u8 sText_PredictIncomingMon[] = _("Predict Incoming Mon");
static const u8 sText_DynamicFunc[] = _("Dynamic Func");
static const u8 sText_Roaming[] = _("Roaming");
static const u8 sText_Safari[] = _("Safari");
static const u8 sText_FirstBattle[] = _("First Battle");
@ -479,7 +493,7 @@ static const struct BitfieldInfo sAIBitfield[] =
{/*Check Bad Move*/ 1, 0},
{/*Try to Faint*/ 1, 1},
{/*Check Viability*/ 1, 2},
{/*Setup First Turn*/ 1, 3},
{/*Force Setup First Turn*/ 1, 3},
{/*Risky*/ 1, 4},
{/*Prefer Strongest Move*/ 1, 5},
{/*Prefer Baton Pass*/ 1, 6},
@ -488,16 +502,20 @@ static const struct BitfieldInfo sAIBitfield[] =
{/*Powerful Status*/ 1, 9},
{/*Negate Unaware*/ 1, 10},
{/*Will Suicide*/ 1, 11},
{/*Help Partner*/ 1, 12},
{/*Prefer Status Moves*/ 1, 13},
{/*Stall*/ 1, 14},
{/*Smart Switching*/ 1, 15},
{/*Ace Pokemon*/ 1, 16},
{/*Omniscient*/ 1, 17},
{/*Smart Mon Choices*/ 1, 18},
{/*Ace Pokemon*/ 1, 16},
{/*Omniscient*/ 1, 17},
{/*Smart Mon Choices*/ 1, 18},
{/*Prefer Status Moves*/ 1, 12},
{/*Stall*/ 1, 13},
{/*Smart Switching*/ 1, 14},
{/*Ace Pokemon*/ 1, 15},
{/*Omniscient*/ 1, 16},
{/*Smart Mon Choices*/ 1, 17},
{/*Conservative*/ 1, 18},
{/*Sequence Switching*/ 1, 19},
{/*Double Ace Pokemon*/ 1, 20},
{/*Weigh Ability Prediction*/ 1, 21},
{/*Prefer Highest Damage Move*/ 1, 22},
{/*Predict Switch*/ 1, 23},
{/*Predict Incoming Mon*/ 1, 24},
{/*Dynamic Func*/ 1, 28},
{/*Roaming*/ 1, 29},
{/*Safari*/ 1, 30},
{/*First Battle*/ 1, 31},
@ -626,7 +644,7 @@ static const struct ListMenuItem sAIListItems[] =
{sText_CheckBadMove, LIST_AI_CHECK_BAD_MOVE},
{sText_TryToFaint, LIST_AI_TRY_TO_FAINT},
{sText_CheckViability, LIST_AI_CHECK_VIABILITY},
{sText_SetUpFirstTurn, LIST_AI_SETUP_FIRST_TURN},
{sText_ForceSetupFirstTurn, LIST_AI_FORCE_SETUP_FIRST_TURN},
{sText_Risky, LIST_AI_RISKY},
{sText_TryTo2HKO, LIST_AI_TRY_TO_2HKO},
{sText_PreferBatonPass, LIST_AI_PREFER_BATON_PASS},
@ -635,13 +653,20 @@ static const struct ListMenuItem sAIListItems[] =
{sText_PowerfulStatus, LIST_AI_POWERFUL_STATUS},
{sText_NegateUnaware, LIST_AI_NEGATE_UNAWARE},
{sText_WillSuicide, LIST_AI_WILL_SUICIDE},
{sText_HelpPartner, LIST_AI_HELP_PARTNER},
{sText_PreferStatusMoves, LIST_AI_PREFER_STATUS_MOVES},
{sText_Stall, LIST_AI_STALL},
{sText_SmartSwitching, LIST_AI_SMART_SWITCHING},
{sText_AcePokemon, LIST_AI_ACE_POKEMON},
{sText_Omniscient, LIST_AI_OMNISCIENT},
{sText_SmartMonChoices, LIST_AI_SMART_MON_CHOICES},
{sText_Conservative, LIST_AI_CONSERVATIVE},
{sText_SequenceSwitching, LIST_AI_SEQUENCE_SWITCHING},
{sText_DoubleAcePokemon, LIST_AI_DOUBLE_ACE_POKEMON},
{sText_WeighAbilityPrediction, LIST_AI_WEIGH_ABILITY_PREDICTION},
{sText_PreferHighestDamageMove, LIST_AI_PREFER_HIGHEST_DAMAGE_MOVE},
{sText_PredictSwitch, LIST_AI_PREDICT_SWITCH},
{sText_PredictIncomingMon, LIST_AI_PREDICT_INCOMING_MON},
{sText_DynamicFunc, LIST_AI_DYNAMIC_FUNC},
{sText_Roaming, LIST_AI_ROAMING},
{sText_Safari, LIST_AI_SAFARI},
{sText_FirstBattle, LIST_AI_FIRST_BATTLE},
@ -2580,8 +2605,8 @@ static const u8 *const sHoldEffectNames[] =
[HOLD_EFFECT_COVERT_CLOAK] = sText_HoldEffectCovertCloak,
[HOLD_EFFECT_LOADED_DICE] = sText_HoldEffectLoadedDice,
[HOLD_EFFECT_BOOSTER_ENERGY] = sText_HoldEffectBoosterEnergy,
[HOLD_EFFECT_BERSERK_GENE] = sText_HoldEffectBerserkGene,
[HOLD_EFFECT_OGERPON_MASK] = sText_HoldEffectOgerponMask,
[HOLD_EFFECT_BERSERK_GENE] = sText_HoldEffectBerserkGene,
};
static const u8 *GetHoldEffectName(u16 holdEffect)
{

View File

@ -79,13 +79,9 @@ bool32 ShouldTrainerBattlerUseGimmick(u32 battler, enum Gimmick gimmick)
// Check the trainer party data to see if a gimmick is intended.
else
{
bool32 isSecondTrainer = (GetBattlerPosition(battler) == B_POSITION_OPPONENT_RIGHT) && (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS) && !BATTLE_TWO_VS_ONE_OPPONENT;
u16 trainerId = isSecondTrainer ? TRAINER_BATTLE_PARAM.opponentB : TRAINER_BATTLE_PARAM.opponentA;
const struct TrainerMon *mon = &GetTrainerPartyFromId(trainerId)[isSecondTrainer ? gBattlerPartyIndexes[battler] - MULTI_PARTY_SIZE : gBattlerPartyIndexes[battler]];
if (gimmick == GIMMICK_TERA && mon->teraType != TYPE_NONE)
if (gimmick == GIMMICK_TERA && gBattleStruct->opponentMonCanTera & 1 << gBattlerPartyIndexes[battler])
return TRUE;
if (gimmick == GIMMICK_DYNAMAX && mon->shouldUseDynamax)
if (gimmick == GIMMICK_DYNAMAX && gBattleStruct->opponentMonCanDynamax & 1 << gBattlerPartyIndexes[battler])
return TRUE;
}

View File

@ -3042,6 +3042,9 @@ static void DestroyLastUsedBallGfx(struct Sprite *sprite)
void TryToAddMoveInfoWindow(void)
{
if (!B_SHOW_MOVE_DESCRIPTION)
return;
LoadSpritePalette(&sSpritePalette_AbilityPopUp);
if (GetSpriteTileStartByTag(MOVE_INFO_WINDOW_TAG) == 0xFFFF)
LoadSpriteSheet(&sSpriteSheet_MoveInfoWindow);
@ -3104,7 +3107,7 @@ static void SpriteCB_LastUsedBall(struct Sprite *sprite)
}
static void SpriteCB_MoveInfoWin(struct Sprite *sprite)
{
{
if (sprite->sHide)
{
if (sprite->x != LAST_BALL_WIN_X_0)

View File

@ -1969,6 +1969,8 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer
if (partyData[monIndex].dynamaxLevel > 0)
{
u32 data = partyData[monIndex].dynamaxLevel;
if (partyData[monIndex].shouldUseDynamax)
gBattleStruct->opponentMonCanDynamax |= 1 << i;
SetMonData(&party[i], MON_DATA_DYNAMAX_LEVEL, &data);
}
if (partyData[monIndex].gigantamaxFactor)
@ -1978,6 +1980,7 @@ u8 CreateNPCTrainerPartyFromTrainer(struct Pokemon *party, const struct Trainer
}
if (partyData[monIndex].teraType > 0)
{
gBattleStruct->opponentMonCanTera |= 1 << i;
u32 data = partyData[monIndex].teraType;
SetMonData(&party[i], MON_DATA_TERA_TYPE, &data);
}
@ -5631,12 +5634,15 @@ static void FreeResetData_ReturnToOvOrDoEvolutions(void)
{
gIsFishingEncounter = FALSE;
gIsSurfingEncounter = FALSE;
if (gDexNavBattle && (gBattleOutcome == B_OUTCOME_WON || gBattleOutcome == B_OUTCOME_CAUGHT))
if (gDexNavSpecies && (gBattleOutcome == B_OUTCOME_WON || gBattleOutcome == B_OUTCOME_CAUGHT))
{
IncrementDexNavChain();
TryIncrementSpeciesSearchLevel();
}
else
gSaveBlock3Ptr->dexNavChain = 0;
gDexNavBattle = FALSE;
gDexNavSpecies = SPECIES_NONE;
ResetSpriteData();
if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK
| BATTLE_TYPE_RECORDED_LINK

View File

@ -344,6 +344,7 @@ static u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent);
static void TryUpdateEvolutionTracker(u32 evolutionMethod, u32 upAmount, u16 usedMove);
static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u8 *failInstr, u16 move);
static void ResetValuesForCalledMove(void);
static void TryRestoreDamageAfterCheeckPouch(u32 battler);
static void Cmd_attackcanceler(void);
static void Cmd_accuracycheck(void);
@ -2650,6 +2651,7 @@ static void Cmd_datahpupdate(void)
}
}
TryRestoreDamageAfterCheeckPouch(battler);
gBattlescriptCurrInstr = cmd->nextInstr;
}
@ -6153,6 +6155,20 @@ static u32 GetNextTarget(u32 moveTarget, bool32 excludeCurrent)
return battler;
}
static inline bool32 IsProtectivePadsProtected(u32 battler, u32 move)
{
if (!IsMoveMakingContact(move, battler))
return FALSE;
if (GetBattlerHoldEffect(battler, TRUE) == HOLD_EFFECT_PROTECTIVE_PADS)
{
RecordItemEffectBattle(battler, HOLD_EFFECT_PROTECTIVE_PADS);
return TRUE;
}
return FALSE;
}
static void Cmd_moveend(void)
{
CMD_ARGS(u8 endMode, u8 endState);
@ -6190,6 +6206,7 @@ static void Cmd_moveend(void)
{
if (gProtectStructs[gBattlerTarget].spikyShielded
&& moveEffect != EFFECT_COUNTER
&& !IsProtectivePadsProtected(gBattlerAttacker, gCurrentMove)
&& GetBattlerAbility(gBattlerAttacker) != ABILITY_MAGIC_GUARD)
{
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
@ -6201,7 +6218,8 @@ static void Cmd_moveend(void)
gBattlescriptCurrInstr = BattleScript_SpikyShieldEffect;
effect = 1;
}
else if (gProtectStructs[gBattlerTarget].kingsShielded)
else if (gProtectStructs[gBattlerTarget].kingsShielded
&& !IsProtectivePadsProtected(gBattlerAttacker, gCurrentMove))
{
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
i = gBattlerAttacker;
@ -6215,7 +6233,8 @@ static void Cmd_moveend(void)
gBattlescriptCurrInstr = BattleScript_KingsShieldEffect;
effect = 1;
}
else if (gProtectStructs[gBattlerTarget].banefulBunkered)
else if (gProtectStructs[gBattlerTarget].banefulBunkered
&& !IsProtectivePadsProtected(gBattlerAttacker, gCurrentMove))
{
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
gBattleScripting.moveEffect = MOVE_EFFECT_POISON | MOVE_EFFECT_AFFECTS_USER;
@ -6225,7 +6244,9 @@ static void Cmd_moveend(void)
effect = 1;
}
else if (gProtectStructs[gBattlerTarget].obstructed
&& moveEffect != EFFECT_SUCKER_PUNCH && moveEffect != EFFECT_UPPER_HAND)
&& moveEffect != EFFECT_SUCKER_PUNCH
&& moveEffect != EFFECT_UPPER_HAND
&& !IsProtectivePadsProtected(gBattlerAttacker, gCurrentMove))
{
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
i = gBattlerAttacker;
@ -6236,7 +6257,8 @@ static void Cmd_moveend(void)
gBattlescriptCurrInstr = BattleScript_KingsShieldEffect;
effect = 1;
}
else if (gProtectStructs[gBattlerTarget].silkTrapped)
else if (gProtectStructs[gBattlerTarget].silkTrapped
&& !IsProtectivePadsProtected(gBattlerAttacker, gCurrentMove))
{
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
i = gBattlerAttacker;
@ -6247,7 +6269,8 @@ static void Cmd_moveend(void)
gBattlescriptCurrInstr = BattleScript_KingsShieldEffect;
effect = 1;
}
else if (gProtectStructs[gBattlerTarget].burningBulwarked)
else if (gProtectStructs[gBattlerTarget].burningBulwarked
&& !IsProtectivePadsProtected(gBattlerAttacker, gCurrentMove))
{
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
gBattleScripting.moveEffect = MOVE_EFFECT_BURN | MOVE_EFFECT_AFFECTS_USER;
@ -6463,9 +6486,10 @@ static void Cmd_moveend(void)
{
u16 *choicedMoveAtk = &gBattleStruct->choicedMove[gBattlerAttacker];
if (gHitMarker & HITMARKER_OBEYS
&& (HOLD_EFFECT_CHOICE(holdEffectAtk) || GetBattlerAbility(gBattlerAttacker) == ABILITY_GORILLA_TACTICS)
&& !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
&& gChosenMove != MOVE_STRUGGLE
&& (*choicedMoveAtk == MOVE_NONE || *choicedMoveAtk == MOVE_UNAVAILABLE))
&& (*choicedMoveAtk == MOVE_NONE || *choicedMoveAtk == MOVE_UNAVAILABLE)
&& (HOLD_EFFECT_CHOICE(holdEffectAtk) || GetBattlerAbility(gBattlerAttacker) == ABILITY_GORILLA_TACTICS))
{
if ((moveEffect == EFFECT_BATON_PASS || moveEffect == EFFECT_HEALING_WISH)
&& !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_FAILED))
@ -8918,6 +8942,8 @@ static bool32 TryCheekPouch(u32 battler, u32 itemId)
&& gBattleStruct->ateBerry[GetBattlerSide(battler)] & (1u << gBattlerPartyIndexes[battler])
&& !IsBattlerAtMaxHp(battler))
{
gBattleStruct->cheekPouchActivated = TRUE;
gBattleScripting.savedDmg = gBattleStruct->moveDamage[battler];
gBattleStruct->moveDamage[battler] = GetNonDynamaxMaxHP(battler) / 3;
if (gBattleStruct->moveDamage[battler] == 0)
gBattleStruct->moveDamage[battler] = 1;
@ -8930,6 +8956,16 @@ static bool32 TryCheekPouch(u32 battler, u32 itemId)
return FALSE;
}
// When Cheek Pouch activates mid-battle it overwrites the current damage, so restore it
static void TryRestoreDamageAfterCheeckPouch(u32 battler)
{
if (gBattleStruct->cheekPouchActivated)
{
gBattleStruct->moveDamage[battler] = gBattleScripting.savedDmg;
gBattleStruct->cheekPouchActivated = FALSE;
}
}
// Used by Bestow and Symbiosis to take an item from one battler and give to another.
static void BestowItem(u32 battlerAtk, u32 battlerDef)
{
@ -10602,7 +10638,7 @@ static void Cmd_various(void)
gBattlescriptCurrInstr = cmd->failInstr;
else if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget))
gBattlescriptCurrInstr = cmd->failInstr;
else if (IsBattleMoveStatus(gBattleMons[gBattlerTarget].moves[gBattleStruct->chosenMovePositions[gBattlerTarget]]))
else if (IsBattleMoveStatus(gBattleMons[gBattlerTarget].moves[gBattleStruct->chosenMovePositions[gBattlerTarget]]) && !gProtectStructs[gBattlerTarget].noValidMoves)
gBattlescriptCurrInstr = cmd->failInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;

View File

@ -134,7 +134,7 @@ struct DexNavGUI
EWRAM_DATA static struct DexNavSearch *sDexNavSearchDataPtr = NULL;
EWRAM_DATA static struct DexNavGUI *sDexNavUiDataPtr = NULL;
EWRAM_DATA static u8 *sBg1TilemapBuffer = NULL;
EWRAM_DATA bool8 gDexNavBattle = FALSE;
EWRAM_DATA u16 gDexNavSpecies = SPECIES_NONE;
//// Function Declarations
//GUI
@ -807,11 +807,11 @@ static void LoadSearchIconData(void)
LoadCompressedSpriteSheetUsingHeap(&sHiddenMonIconSpriteSheet);
}
static u8 GetSearchLevel(u16 dexNum)
static u8 GetSearchLevel(u16 species)
{
u8 searchLevel;
#if USE_DEXNAV_SEARCH_LEVELS == TRUE
searchLevel = gSaveBlock3Ptr->dexNavSearchLevels[dexNum];
searchLevel = gSaveBlock3Ptr->dexNavSearchLevels[species];
#else
searchLevel = 0;
#endif
@ -829,7 +829,7 @@ static void Task_SetUpDexNavSearch(u8 taskId)
struct Task *task = &gTasks[taskId];
u16 species = sDexNavSearchDataPtr->species;
u8 searchLevel = GetSearchLevel(SpeciesToNationalPokedexNum(species));
u8 searchLevel = GetSearchLevel(species);
// init sprites
sDexNavSearchDataPtr->iconSpriteId = MAX_SPRITES;
@ -1110,7 +1110,7 @@ static void Task_DexNavSearch(u8 taskId)
if (sDexNavSearchDataPtr->proximity < 1)
{
gDexNavBattle = TRUE;
gDexNavSpecies = sDexNavSearchDataPtr->species;
CreateDexNavWildMon(sDexNavSearchDataPtr->species, sDexNavSearchDataPtr->potential, sDexNavSearchDataPtr->monLevel,
sDexNavSearchDataPtr->abilityNum, sDexNavSearchDataPtr->heldItem, sDexNavSearchDataPtr->moves);
@ -2143,7 +2143,7 @@ static void PrintCurrentSpeciesInfo(void)
}
else
{
ConvertIntToDecimalStringN(gStringVar4, GetSearchLevel(dexNum), 0, 4);
ConvertIntToDecimalStringN(gStringVar4, GetSearchLevel(species), 0, 4);
AddTextPrinterParameterized3(WINDOW_INFO, 0, 0, SEARCH_LEVEL_Y, sFontColor_Black, 0, gStringVar4);
}
@ -2666,11 +2666,11 @@ u32 CalculateDexNavShinyRolls(void)
return chainBonus + rndBonus;
}
void TryIncrementSpeciesSearchLevel(u16 dexNum)
void TryIncrementSpeciesSearchLevel()
{
#if USE_DEXNAV_SEARCH_LEVELS == TRUE
if (gMapHeader.regionMapSectionId != MAPSEC_BATTLE_FRONTIER && gSaveBlock3Ptr->dexNavSearchLevels[dexNum] < 255)
gSaveBlock3Ptr->dexNavSearchLevels[dexNum]++;
if (gMapHeader.regionMapSectionId != MAPSEC_BATTLE_FRONTIER && gSaveBlock3Ptr->dexNavSearchLevels[gDexNavSpecies] < 255)
gSaveBlock3Ptr->dexNavSearchLevels[gDexNavSpecies]++;
#endif
}

View File

@ -1163,7 +1163,7 @@ void CreateBoxMon(struct BoxPokemon *boxMon, u16 species, u8 level, u8 fixedIV,
totalRerolls += 1;
if (I_FISHING_CHAIN && gIsFishingEncounter)
totalRerolls += CalculateChainFishingShinyRolls();
if (gDexNavBattle)
if (gDexNavSpecies)
totalRerolls += CalculateDexNavShinyRolls();
while (GET_SHINY_VALUE(value, personality) >= SHINY_ODDS && totalRerolls > 0)

View File

@ -0,0 +1,21 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Cheek Pouch activation doesn't mutate damage when restoring HP mid battle")
{
s16 damage;
s16 healing;
GIVEN {
PLAYER(SPECIES_GREEDENT) { Ability(ABILITY_CHEEK_POUCH); Item(ITEM_CHOPLE_BERRY); HP(100); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_KARATE_CHOP); }
ABILITY_POPUP(player, ABILITY_CHEEK_POUCH);
HP_BAR(player, captureDamage: &healing);
HP_BAR(player, captureDamage: &damage);
} THEN {
EXPECT_LT(healing, 0);
EXPECT_GT(damage, 0);
}
}

View File

@ -73,3 +73,40 @@ SINGLE_BATTLE_TEST("Protective Pads protects from Rocly Helmet Damage")
}
}
}
SINGLE_BATTLE_TEST("Protective Pads protects from Protect's secondary effects")
{
u32 move;
PARAMETRIZE { move = MOVE_SPIKY_SHIELD; }
PARAMETRIZE { move = MOVE_BANEFUL_BUNKER; }
PARAMETRIZE { move = MOVE_BURNING_BULWARK; }
PARAMETRIZE { move = MOVE_KINGS_SHIELD; }
PARAMETRIZE { move = MOVE_SILK_TRAP; }
PARAMETRIZE { move = MOVE_OBSTRUCT; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_PROTECTIVE_PADS); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, move); MOVE(player, MOVE_TACKLE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, move, opponent);
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player);
if (move == MOVE_SPIKY_SHIELD) {
HP_BAR(player);
} else if (move == MOVE_BANEFUL_BUNKER) {
STATUS_ICON(player, STATUS1_BURN);
} else if (move == MOVE_BURNING_BULWARK) {
STATUS_ICON(player, STATUS1_POISON);
} else if (move == MOVE_KINGS_SHIELD) {
MESSAGE("Wobbuffet's Attack fell!");
} else if (move == MOVE_SILK_TRAP) {
MESSAGE("Wobbuffet's Speed fell!");
} else if (move == MOVE_OBSTRUCT) {
MESSAGE("Wobbuffet's Defense harshly fell!");
}
}
}
}

View File

@ -0,0 +1,53 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Sucker Punch hits targets that are about to attack")
{
GIVEN {
ASSUME(GetMoveCategory(MOVE_TACKLE) != DAMAGE_CATEGORY_STATUS);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SUCKER_PUNCH); MOVE(opponent, MOVE_TACKLE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUCKER_PUNCH, player);
HP_BAR(opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
HP_BAR(player);
}
}
SINGLE_BATTLE_TEST("Sucker Punch doesn't hit targets using status moves")
{
GIVEN {
ASSUME(GetMoveCategory(MOVE_GROWL) == DAMAGE_CATEGORY_STATUS);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SUCKER_PUNCH); MOVE(opponent, MOVE_GROWL); }
} SCENE {
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUCKER_PUNCH, player);
HP_BAR(opponent);
}
ANIMATION(ANIM_TYPE_MOVE, MOVE_GROWL, opponent);
}
}
SINGLE_BATTLE_TEST("Sucker Punch doesn't hit targets that has already moved")
{
GIVEN {
ASSUME(GetMovePriority(MOVE_QUICK_ATTACK) == GetMovePriority(MOVE_SUCKER_PUNCH));
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_QUICK_ATTACK); MOVE(player, MOVE_SUCKER_PUNCH); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, opponent);
HP_BAR(player);
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUCKER_PUNCH, player);
HP_BAR(opponent);
}
}
}