Merge branch 'upcoming' into berryexpansion

This commit is contained in:
Bassoonian 2023-12-27 14:00:12 +01:00 committed by GitHub
commit a4b819ac41
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 367 additions and 87 deletions

View file

@ -8853,6 +8853,16 @@ BattleScript_ZeroToHeroActivates::
waitmessage B_WAIT_TIME_LONG
end3
BattleScript_HospitalityActivates::
pause B_WAIT_TIME_SHORT
call BattleScript_AbilityPopUp
printstring STRINGID_HOSPITALITYRESTORATION
waitmessage B_WAIT_TIME_LONG
orword gHitMarker, HITMARKER_IGNORE_SUBSTITUTE
healthbarupdate BS_TARGET
datahpupdate BS_TARGET
end3
BattleScript_AttackWeakenedByStrongWinds::
pause B_WAIT_TIME_SHORT
printstring STRINGID_ATTACKWEAKENEDBSTRONGWINDS
@ -9249,6 +9259,7 @@ BattleScript_BattlerAbilityStatRaiseOnSwitchIn::
waitanimation
printstring STRINGID_BATTLERABILITYRAISEDSTAT
waitmessage B_WAIT_TIME_LONG
copybyte gBattlerAttacker, sSAVED_BATTLER
end3
BattleScript_ScriptingAbilityStatRaise::

View file

@ -336,7 +336,7 @@ struct AI_ThinkingStruct
u16 moveConsidered;
s32 score[MAX_MON_MOVES];
u32 funcResult;
u32 aiFlags;
u32 aiFlags[MAX_BATTLERS_COUNT];
u8 aiAction;
u8 aiLogicId;
struct AI_SavedBattleMon saved[MAX_BATTLERS_COUNT];

View file

@ -461,6 +461,7 @@ extern const u8 BattleScript_CudChewActivates[];
extern const u8 BattleScript_SupremeOverlordActivates[];
extern const u8 BattleScript_CostarActivates[];
extern const u8 BattleScript_ZeroToHeroActivates[];
extern const u8 BattleScript_HospitalityActivates[];
extern const u8 BattleScript_ToxicDebrisActivates[];
extern const u8 BattleScript_EarthEaterActivates[];
extern const u8 BattleScript_MimicryActivates_End3[];

View file

@ -696,8 +696,9 @@
#define STRINGID_SWAMPENVELOPEDSIDE 694
#define STRINGID_THESWAMPDISAPPEARED 695
#define STRINGID_PKMNTELLCHILLINGRECEPTIONJOKE 696
#define STRINGID_HOSPITALITYRESTORATION 697
#define BATTLESTRINGS_COUNT 697
#define BATTLESTRINGS_COUNT 698
// This is the string id that gBattleStringsTable starts with.
// String ids before this (e.g. STRINGID_INTROMSG) are not in the table,

View file

@ -22,6 +22,7 @@
#include "constants/hold_effects.h"
#include "constants/moves.h"
#include "constants/items.h"
#include "constants/trainers.h"
#define AI_ACTION_DONE (1 << 0)
#define AI_ACTION_FLEE (1 << 1)
@ -139,36 +140,76 @@ static u32 GetWildAiFlags(void)
return flags;
}
static u32 GetAiFlags(u16 trainerId)
{
u32 flags = 0;
if (!(gBattleTypeFlags & BATTLE_TYPE_HAS_AI) && !IsWildMonSmart())
return 0;
if (trainerId == 0xFFFF)
{
flags = GetWildAiFlags();
}
else
{
if (gBattleTypeFlags & BATTLE_TYPE_RECORDED)
flags = GetAiScriptsInRecordedBattle();
else if (gBattleTypeFlags & BATTLE_TYPE_SAFARI)
flags = AI_FLAG_SAFARI;
else if (gBattleTypeFlags & BATTLE_TYPE_ROAMER)
flags = AI_FLAG_ROAMING;
else if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE)
flags = AI_FLAG_FIRST_BATTLE;
else if (gBattleTypeFlags & BATTLE_TYPE_FACTORY)
flags = GetAiScriptsInBattleFactory();
else if (gBattleTypeFlags & (BATTLE_TYPE_FRONTIER | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_TRAINER_HILL | BATTLE_TYPE_SECRET_BASE))
flags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT;
else
flags = gTrainers[trainerId].aiFlags;
}
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
flags |= AI_FLAG_DOUBLE_BATTLE;
return flags;
}
void BattleAI_SetupFlags(void)
{
AI_THINKING_STRUCT->aiFlags[B_POSITION_PLAYER_LEFT] = 0; // player has no AI
#if DEBUG_OVERWORLD_MENU == TRUE
if (gIsDebugBattle)
AI_THINKING_STRUCT->aiFlags = gDebugAIFlags;
else
{
AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_LEFT] = gDebugAIFlags;
AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_RIGHT] = gDebugAIFlags;
return;
}
#endif
if (gBattleTypeFlags & BATTLE_TYPE_RECORDED)
AI_THINKING_STRUCT->aiFlags = GetAiScriptsInRecordedBattle();
else if (gBattleTypeFlags & BATTLE_TYPE_SAFARI)
AI_THINKING_STRUCT->aiFlags = AI_FLAG_SAFARI;
else if (gBattleTypeFlags & BATTLE_TYPE_ROAMER)
AI_THINKING_STRUCT->aiFlags = AI_FLAG_ROAMING;
else if (gBattleTypeFlags & BATTLE_TYPE_FIRST_BATTLE)
AI_THINKING_STRUCT->aiFlags = AI_FLAG_FIRST_BATTLE;
else if (gBattleTypeFlags & BATTLE_TYPE_FACTORY)
AI_THINKING_STRUCT->aiFlags = GetAiScriptsInBattleFactory();
else if (gBattleTypeFlags & (BATTLE_TYPE_FRONTIER | BATTLE_TYPE_EREADER_TRAINER | BATTLE_TYPE_TRAINER_HILL | BATTLE_TYPE_SECRET_BASE))
AI_THINKING_STRUCT->aiFlags = AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT;
else if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS)
AI_THINKING_STRUCT->aiFlags = gTrainers[gTrainerBattleOpponent_A].aiFlags | gTrainers[gTrainerBattleOpponent_B].aiFlags;
if (IsWildMonSmart() && !(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_TRAINER)))
{
// smart wild AI
AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_LEFT] = GetAiFlags(0xFFFF);
AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_RIGHT] = GetAiFlags(0xFFFF);
}
else
AI_THINKING_STRUCT->aiFlags = gTrainers[gTrainerBattleOpponent_A].aiFlags;
// check smart wild AI
if (!(gBattleTypeFlags & (BATTLE_TYPE_LINK | BATTLE_TYPE_TRAINER)) && IsWildMonSmart())
AI_THINKING_STRUCT->aiFlags |= GetWildAiFlags();
if (gBattleTypeFlags & (BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS) || gTrainers[gTrainerBattleOpponent_A].doubleBattle)
AI_THINKING_STRUCT->aiFlags |= AI_FLAG_DOUBLE_BATTLE; // Act smart in doubles and don't attack your partner.
{
AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_LEFT] = GetAiFlags(gTrainerBattleOpponent_A);
if (gTrainerBattleOpponent_B != 0)
AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_RIGHT] = GetAiFlags(gTrainerBattleOpponent_B);
else
AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_RIGHT] = AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_LEFT];
}
if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER)
{
AI_THINKING_STRUCT->aiFlags[B_POSITION_PLAYER_RIGHT] = GetAiFlags(gPartnerTrainerId - TRAINER_PARTNER(PARTNER_NONE));
}
else
{
AI_THINKING_STRUCT->aiFlags[B_POSITION_PLAYER_RIGHT] = 0; // player
}
}
// sBattler_AI set in ComputeBattleAiScores
@ -176,11 +217,12 @@ void BattleAI_SetupAIData(u8 defaultScoreMoves, u32 battler)
{
s32 i;
u8 moveLimitations;
u32 flags[MAX_BATTLERS_COUNT];
// Clear AI data but preserve the flags.
u32 flags = AI_THINKING_STRUCT->aiFlags;
memcpy(&flags[0], &AI_THINKING_STRUCT->aiFlags[0], sizeof(u32) * MAX_BATTLERS_COUNT);
memset(AI_THINKING_STRUCT, 0, sizeof(struct AI_ThinkingStruct));
AI_THINKING_STRUCT->aiFlags = flags;
memcpy(&AI_THINKING_STRUCT->aiFlags[0], &flags[0], sizeof(u32) * MAX_BATTLERS_COUNT);
// Conditional score reset, unlike Ruby.
for (i = 0; i < MAX_MON_MOVES; i++)
@ -251,7 +293,7 @@ static void CopyBattlerDataToAIParty(u32 bPosition, u32 side)
void Ai_InitPartyStruct(void)
{
u32 i;
bool32 isOmniscient = (AI_THINKING_STRUCT->aiFlags & AI_FLAG_OMNISCIENT);
bool32 isOmniscient = (AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_LEFT] & AI_FLAG_OMNISCIENT) || (AI_THINKING_STRUCT->aiFlags[B_POSITION_OPPONENT_RIGHT] & AI_FLAG_OMNISCIENT);
struct Pokemon *mon;
AI_PARTY->count[B_SIDE_PLAYER] = gPlayerPartyCount;
@ -433,13 +475,13 @@ static bool32 AI_ShouldSwitchIfBadMoves(u32 battler, bool32 doubleBattle)
if (CountUsablePartyMons(battler) > 0
&& !IsBattlerTrapped(battler, TRUE)
&& !(gBattleTypeFlags & (BATTLE_TYPE_ARENA | BATTLE_TYPE_PALACE))
&& AI_THINKING_STRUCT->aiFlags & (AI_FLAG_CHECK_VIABILITY | AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_PREFER_BATON_PASS))
&& AI_THINKING_STRUCT->aiFlags[battler] & (AI_FLAG_CHECK_VIABILITY | AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_PREFER_BATON_PASS))
{
// Consider switching if all moves are worthless to use.
if (GetTotalBaseStat(gBattleMons[battler].species) >= 310 // Mon is not weak.
&& gBattleMons[battler].hp >= gBattleMons[battler].maxHP / 2) // Mon has more than 50% of its HP
{
s32 cap = AI_THINKING_STRUCT->aiFlags & (AI_FLAG_CHECK_VIABILITY) ? 95 : 93;
s32 cap = AI_THINKING_STRUCT->aiFlags[battler] & (AI_FLAG_CHECK_VIABILITY) ? 95 : 93;
if (doubleBattle)
{
for (i = 0; i < MAX_BATTLERS_COUNT; i++)
@ -492,7 +534,7 @@ static u32 ChooseMoveOrAction_Singles(u32 battlerAi)
u8 consideredMoveArray[MAX_MON_MOVES];
u32 numOfBestMoves;
s32 i;
u32 flags = AI_THINKING_STRUCT->aiFlags;
u32 flags = AI_THINKING_STRUCT->aiFlags[battlerAi];
AI_DATA->partnerMove = 0; // no ally
while (flags != 0)
@ -577,7 +619,7 @@ static u32 ChooseMoveOrAction_Doubles(u32 battlerAi)
AI_DATA->partnerMove = GetAllyChosenMove(battlerAi);
AI_THINKING_STRUCT->aiLogicId = 0;
AI_THINKING_STRUCT->movesetIndex = 0;
flags = AI_THINKING_STRUCT->aiFlags;
flags = AI_THINKING_STRUCT->aiFlags[sBattler_AI];
while (flags != 0)
{
@ -1017,7 +1059,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(-10);
break;
case EFFECT_EXPLOSION:
if (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_WILL_SUICIDE))
if (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_WILL_SUICIDE))
ADJUST_SCORE(-2);
if (effectiveness == AI_EFFECTIVENESS_x0)
@ -1996,7 +2038,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
}
}
/*if (AI_THINKING_STRUCT->aiFlags == AI_SCRIPT_CHECK_BAD_MOVE //Only basic AI
/*if (AI_THINKING_STRUCT->aiFlags[battlerAtk] == AI_SCRIPT_CHECK_BAD_MOVE //Only basic AI
&& IS_DOUBLE_BATTLE) //Make the regular AI know how to use Protect minimally in Doubles
{
u8 shouldProtect = ShouldProtect(battlerAtk, battlerDef, move);
@ -2803,7 +2845,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
switch (atkPartnerAbility)
{
case ABILITY_VOLT_ABSORB:
if (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_HP_AWARE))
if (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_HP_AWARE))
{
RETURN_SCORE_MINUS(10);
}
@ -2824,7 +2866,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
break;
case ABILITY_WATER_ABSORB:
case ABILITY_DRY_SKIN:
if (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_HP_AWARE))
if (!(AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_HP_AWARE))
{
RETURN_SCORE_MINUS(10);
}
@ -3233,7 +3275,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
}
// check status move preference
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_PREFER_STATUS_MOVES && IS_MOVE_STATUS(move) && effectiveness != AI_EFFECTIVENESS_x0)
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_PREFER_STATUS_MOVES && IS_MOVE_STATUS(move) && effectiveness != AI_EFFECTIVENESS_x0)
ADJUST_SCORE(1);
// check thawing moves
@ -3241,7 +3283,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
ADJUST_SCORE(10);
// check burn / frostbite
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_SWITCHING && AI_DATA->abilities[battlerAtk] == ABILITY_NATURAL_CURE)
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_SMART_SWITCHING && AI_DATA->abilities[battlerAtk] == ABILITY_NATURAL_CURE)
{
if ((gBattleMons[battlerAtk].status1 & STATUS1_BURN && HasOnlyMovesWithCategory(battlerAtk, BATTLE_CATEGORY_PHYSICAL, TRUE))
|| (gBattleMons[battlerAtk].status1 & STATUS1_FROSTBITE && HasOnlyMovesWithCategory(battlerAtk, BATTLE_CATEGORY_SPECIAL, TRUE)))
@ -3263,7 +3305,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
ADJUST_SCORE(2);
case EFFECT_EXPLOSION:
case EFFECT_MEMENTO:
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_WILL_SUICIDE && gBattleMons[battlerDef].statStages[STAT_EVASION] < 7)
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_WILL_SUICIDE && gBattleMons[battlerDef].statStages[STAT_EVASION] < 7)
{
if (aiData->hpPercents[battlerAtk] < 50 && AI_RandLessThan(128))
ADJUST_SCORE(1);
@ -3569,7 +3611,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
ADJUST_SCORE(5);
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_LIGHT_CLAY)
ADJUST_SCORE(2);
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_SCREENER)
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_SCREENER)
ADJUST_SCORE(2);
}
break;
@ -3611,7 +3653,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
ADJUST_SCORE(5);
break;
case EFFECT_MIST:
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_SCREENER)
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_SCREENER)
ADJUST_SCORE(2);
break;
case EFFECT_FOCUS_ENERGY:
@ -4070,7 +4112,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
break;
case STAT_DEF:
case STAT_SPDEF:
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_STALL)
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_STALL)
ADJUST_SCORE(1);
break;
}
@ -4868,7 +4910,7 @@ static s32 AI_SetupFirstTurn(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
|| gBattleResults.battleTurnCounter != 0)
return score;
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_SWITCHING
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_SMART_SWITCHING
&& AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER
&& CanTargetFaintAi(battlerDef, battlerAtk)
&& GetMovePriority(battlerAtk, move) == 0)

View file

@ -38,10 +38,10 @@ static void InitializeSwitchinCandidate(struct Pokemon *mon)
static bool32 IsAceMon(u32 battler, u32 monPartyId)
{
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON
&& !(gBattleStruct->forcedSwitch & gBitTable[battler])
&& monPartyId == CalculateEnemyPartyCount()-1)
return TRUE;
if (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_ACE_POKEMON
&& !(gBattleStruct->forcedSwitch & gBitTable[battler])
&& monPartyId == CalculateEnemyPartyCount()-1)
return TRUE;
return FALSE;
}
@ -77,7 +77,7 @@ static bool32 HasBadOdds(u32 battler, bool32 emitResult)
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 & AI_FLAG_SMART_SWITCHING))
if (!(AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SMART_SWITCHING))
return FALSE;
// Won't bother configuring this for double battles
@ -433,7 +433,7 @@ static bool32 ShouldSwitchIfGameStatePrompt(u32 battler, bool32 emitResult)
&& monAbility != ABILITY_SOUNDPROOF)
switchMon = TRUE;
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_SWITCHING)
if (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SMART_SWITCHING)
{
//Yawn
if (gStatuses3[battler] & STATUS3_YAWN
@ -851,7 +851,7 @@ static bool32 CanMonSurviveHazardSwitchin(u32 battler)
static bool32 ShouldSwitchIfEncored(u32 battler, bool32 emitResult)
{
// Only use this if AI_FLAG_SMART_SWITCHING is set for the trainer
if (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_SWITCHING))
if (!(AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SMART_SWITCHING))
return FALSE;
// If not Encored or if no good switchin, don't switch
@ -877,7 +877,7 @@ static bool32 AreAttackingStatsLowered(u32 battler, bool32 emitResult)
s8 spAttackingStage = gBattleMons[battler].statStages[STAT_SPATK];
// Only use this if AI_FLAG_SMART_SWITCHING is set for the trainer
if (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_SWITCHING))
if (!(AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SMART_SWITCHING))
return FALSE;
// Physical attacker
@ -1015,7 +1015,7 @@ bool32 ShouldSwitch(u32 battler, bool32 emitResult)
return TRUE;
//These Functions can prompt switch to generic pary members
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_SWITCHING) && (CanMonSurviveHazardSwitchin(battler) == FALSE))
if ((AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SMART_SWITCHING) && (CanMonSurviveHazardSwitchin(battler) == FALSE))
return FALSE;
if (ShouldSwitchIfAllBadMoves(battler, emitResult))
return TRUE;
@ -1939,7 +1939,7 @@ u8 GetMostSuitableMonToSwitchInto(u32 battler, bool32 switchAfterMonKOd)
// Split ideal mon decision between after previous mon KO'd (prioritize offensive options) and after switching active mon out (prioritize defensive options), and expand the scope of both.
// Only use better mon selection if AI_FLAG_SMART_MON_CHOICES is set for the trainer.
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_SMART_MON_CHOICES)
if (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_SMART_MON_CHOICES)
{
bestMonId = GetBestMonIntegrated(party, firstId, lastId, battler, opposingBattler, battlerIn1, battlerIn2, switchAfterMonKOd);
return bestMonId;

View file

@ -404,7 +404,7 @@ bool32 BattlerHasAi(u32 battlerId)
bool32 IsAiBattlerAware(u32 battlerId)
{
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_OMNISCIENT)
if (AI_THINKING_STRUCT->aiFlags[battlerId] & AI_FLAG_OMNISCIENT)
return TRUE;
return BattlerHasAi(battlerId);
@ -1290,7 +1290,7 @@ u32 AI_DecideHoldEffectForTurn(u32 battlerId)
else
holdEffect = GetBattlerHoldEffect(battlerId, FALSE);
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_NEGATE_UNAWARE)
if (AI_THINKING_STRUCT->aiFlags[battlerId] & AI_FLAG_NEGATE_UNAWARE)
return holdEffect;
if (gStatuses3[battlerId] & STATUS3_EMBARGO)
@ -1341,7 +1341,7 @@ bool32 AI_IsBattlerGrounded(u32 battlerId)
bool32 DoesBattlerIgnoreAbilityChecks(u32 atkAbility, u32 move)
{
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_NEGATE_UNAWARE)
if (AI_THINKING_STRUCT->aiFlags[sBattler_AI] & AI_FLAG_NEGATE_UNAWARE)
return FALSE; // AI handicap flag: doesn't understand ability suppression concept
if (IsMoldBreakerTypeAbility(atkAbility) || gBattleMoves[move].ignoresTargetAbility)
@ -1352,7 +1352,7 @@ bool32 DoesBattlerIgnoreAbilityChecks(u32 atkAbility, u32 move)
static inline bool32 AI_WeatherHasEffect(struct AiLogicData *aiData)
{
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_NEGATE_UNAWARE)
if (AI_THINKING_STRUCT->aiFlags[sBattler_AI] & AI_FLAG_NEGATE_UNAWARE)
return TRUE; // AI doesn't understand weather supression (handicap)
return aiData->weatherHasEffect; // weather damping abilities are announced
@ -1437,7 +1437,7 @@ bool32 IsHazardMoveEffect(u32 moveEffect)
bool32 IsMoveRedirectionPrevented(u32 move, u32 atkAbility)
{
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_NEGATE_UNAWARE)
if (AI_THINKING_STRUCT->aiFlags[sBattler_AI] & AI_FLAG_NEGATE_UNAWARE)
return FALSE;
if (move == MOVE_SKY_DROP
@ -1766,7 +1766,9 @@ u32 CountNegativeStatStages(u32 battlerId)
bool32 ShouldLowerAttack(u32 battlerAtk, u32 battlerDef, u32 defAbility)
{
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
&& (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT)
&& CanAIFaintTarget(battlerAtk, battlerDef, 0))
return FALSE; // Don't bother lowering stats if can kill enemy.
if (gBattleMons[battlerDef].statStages[STAT_ATK] > 4
@ -1783,7 +1785,9 @@ bool32 ShouldLowerAttack(u32 battlerAtk, u32 battlerDef, u32 defAbility)
bool32 ShouldLowerDefense(u32 battlerAtk, u32 battlerDef, u32 defAbility)
{
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
&& (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT)
&& CanAIFaintTarget(battlerAtk, battlerDef, 0))
return FALSE; // Don't bother lowering stats if can kill enemy.
if (gBattleMons[battlerDef].statStages[STAT_DEF] > 4
@ -1800,7 +1804,9 @@ bool32 ShouldLowerDefense(u32 battlerAtk, u32 battlerDef, u32 defAbility)
bool32 ShouldLowerSpeed(u32 battlerAtk, u32 battlerDef, u32 defAbility)
{
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
&& (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT)
&& CanAIFaintTarget(battlerAtk, battlerDef, 0))
return FALSE; // Don't bother lowering stats if can kill enemy.
if (!AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
@ -1815,7 +1821,9 @@ bool32 ShouldLowerSpeed(u32 battlerAtk, u32 battlerDef, u32 defAbility)
bool32 ShouldLowerSpAtk(u32 battlerAtk, u32 battlerDef, u32 defAbility)
{
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
&& (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT)
&& CanAIFaintTarget(battlerAtk, battlerDef, 0))
return FALSE; // Don't bother lowering stats if can kill enemy.
if (gBattleMons[battlerDef].statStages[STAT_SPATK] > 4
@ -1831,7 +1839,9 @@ bool32 ShouldLowerSpAtk(u32 battlerAtk, u32 battlerDef, u32 defAbility)
bool32 ShouldLowerSpDef(u32 battlerAtk, u32 battlerDef, u32 defAbility)
{
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
&& (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT)
&& CanAIFaintTarget(battlerAtk, battlerDef, 0))
return FALSE; // Don't bother lowering stats if can kill enemy.
if (gBattleMons[battlerDef].statStages[STAT_SPDEF] > 4
@ -1847,7 +1857,9 @@ bool32 ShouldLowerSpDef(u32 battlerAtk, u32 battlerDef, u32 defAbility)
bool32 ShouldLowerAccuracy(u32 battlerAtk, u32 battlerDef, u32 defAbility)
{
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
&& (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT)
&& CanAIFaintTarget(battlerAtk, battlerDef, 0))
return FALSE; // Don't bother lowering stats if can kill enemy.
if (defAbility != ABILITY_CONTRARY
@ -1864,7 +1876,9 @@ bool32 ShouldLowerAccuracy(u32 battlerAtk, u32 battlerDef, u32 defAbility)
bool32 ShouldLowerEvasion(u32 battlerAtk, u32 battlerDef, u32 defAbility)
{
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered) && (AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
if (AI_STRIKES_FIRST(battlerAtk, battlerDef, AI_THINKING_STRUCT->moveConsidered)
&& (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT)
&& CanAIFaintTarget(battlerAtk, battlerDef, 0))
return FALSE; // Don't bother lowering stats if can kill enemy.
if (gBattleMons[battlerDef].statStages[STAT_EVASION] > DEFAULT_STAT_STAGE
@ -2969,7 +2983,7 @@ bool32 ShouldTrap(u32 battlerAtk, u32 battlerDef, u32 move)
if (BattlerWillFaintFromSecondaryDamage(battlerDef, AI_DATA->abilities[battlerDef]))
return TRUE; // battler is taking secondary damage with low HP
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_STALL)
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_STALL)
{
if (!CanTargetFaintAi(battlerDef, battlerAtk))
return TRUE; // attacker goes first and opponent can't kill us
@ -3553,7 +3567,7 @@ void IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, u32 statId, s32 *score)
if (AI_DATA->hpPercents[battlerAtk] < 80 && AI_RandLessThan(128))
return;
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
if ((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
return; // Damaging moves would get a score boost from AI_TryToFaint or PreferStrongestMove so we don't consider them here
switch (statId)
@ -3627,8 +3641,8 @@ void IncreaseStatUpScore(u32 battlerAtk, u32 battlerDef, u32 statId, s32 *score)
void IncreasePoisonScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)
{
if (((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|| AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_PSN || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS)
if (((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|| AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_PSN || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS)
return;
if (AI_CanPoison(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove) && AI_DATA->hpPercents[battlerDef] > 20)
@ -3636,7 +3650,7 @@ void IncreasePoisonScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)
if (!HasDamagingMove(battlerDef))
ADJUST_SCORE_PTR(2);
if (AI_THINKING_STRUCT->aiFlags & AI_FLAG_STALL && HasMoveEffect(battlerAtk, EFFECT_PROTECT))
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_STALL && HasMoveEffect(battlerAtk, EFFECT_PROTECT))
ADJUST_SCORE_PTR(1); // stall tactic
if (HasMoveEffect(battlerAtk, EFFECT_VENOSHOCK)
@ -3651,8 +3665,8 @@ void IncreasePoisonScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)
void IncreaseBurnScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)
{
if (((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|| AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_BRN || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS)
if (((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|| AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_BRN || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS)
return;
if (AI_CanBurn(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove))
@ -3671,8 +3685,8 @@ void IncreaseBurnScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)
void IncreaseParalyzeScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)
{
if (((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|| AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_PAR || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS)
if (((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|| AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_PAR || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS)
return;
if (AI_CanParalyze(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove))
@ -3693,8 +3707,8 @@ void IncreaseParalyzeScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)
void IncreaseSleepScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)
{
if (((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|| AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_SLP || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS)
if (((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|| AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_SLP || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS)
return;
if (AI_CanPutToSleep(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], move, AI_DATA->partnerMove))
@ -3712,8 +3726,8 @@ void IncreaseSleepScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)
void IncreaseConfusionScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)
{
if (((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|| AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_CONFUSION || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS)
if (((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
|| AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_CONFUSION || AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_CURE_STATUS)
return;
if (AI_CanConfuse(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove)
@ -3731,7 +3745,7 @@ void IncreaseConfusionScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score
void IncreaseFrostbiteScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)
{
if ((AI_THINKING_STRUCT->aiFlags & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
if ((AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_TRY_TO_FAINT) && CanAIFaintTarget(battlerAtk, battlerDef, 0))
return;
if (AI_CanGiveFrostbite(battlerAtk, battlerDef, AI_DATA->abilities[battlerDef], BATTLE_PARTNER(battlerAtk), move, AI_DATA->partnerMove))

View file

@ -679,7 +679,7 @@ static void OpponentHandleChoosePokemon(u32 battler)
if (IsValidForBattle(&gEnemyParty[chosenMonId])
&& chosenMonId != gBattlerPartyIndexes[battler1]
&& chosenMonId != gBattlerPartyIndexes[battler2]
&& (!(AI_THINKING_STRUCT->aiFlags & AI_FLAG_ACE_POKEMON)
&& (!(AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_ACE_POKEMON)
|| chosenMonId != CalculateEnemyPartyCount() - 1
|| CountAIAliveNonEggMonsExcept(PARTY_SIZE) == pokemonInBattle))
{

View file

@ -1901,8 +1901,8 @@ static void SetUpModifyArrows(struct BattleDebugMenu *data)
data->modifyArrows.typeOfVal = VAL_BITFIELD_32;
goto CASE_ITEM_STATUS;
case LIST_ITEM_AI:
data->modifyArrows.modifiedValPtr = &gBattleResources->ai->aiFlags;
data->modifyArrows.currValue = GetBitfieldValue(gBattleResources->ai->aiFlags, data->bitfield[data->currentSecondaryListItemId].currBit, data->bitfield[data->currentSecondaryListItemId].bitsCount);
data->modifyArrows.modifiedValPtr = &gBattleResources->ai->aiFlags[data->battlerId];
data->modifyArrows.currValue = GetBitfieldValue(gBattleResources->ai->aiFlags[data->battlerId], data->bitfield[data->currentSecondaryListItemId].currBit, data->bitfield[data->currentSecondaryListItemId].bitsCount);
data->modifyArrows.typeOfVal = VAL_BITFIELD_32;
goto CASE_ITEM_STATUS;
CASE_ITEM_STATUS:

View file

@ -833,9 +833,11 @@ static const u8 sText_HurtByTheSeaOfFire[] = _("{B_ATK_TEAM1} {B_ATK_NAME_WITH_P
static const u8 sText_TheSeaOfFireDisappeared[] = _("The sea of fire around {B_ATK_TEAM2}\nteam disappeared!");
static const u8 sText_SwampEnvelopedSide[] = _("A swamp enveloped\n{B_DEF_TEAM2} team!");
static const u8 sText_TheSwampDisappeared[] = _("The swamp around {B_ATK_TEAM2}\nteam disappeared!");
static const u8 sText_HospitalityRestoration[] = _("The {B_ATK_PARTNER_NAME} drank down all\nthe matcha that Sinistcha made!");
const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
{
[STRINGID_HOSPITALITYRESTORATION - BATTLESTRINGS_TABLE_START] = sText_HospitalityRestoration,
[STRINGID_THESWAMPDISAPPEARED - BATTLESTRINGS_TABLE_START] = sText_TheSwampDisappeared,
[STRINGID_SWAMPENVELOPEDSIDE - BATTLESTRINGS_TABLE_START] = sText_SwampEnvelopedSide,
[STRINGID_THESEAOFFIREDISAPPEARED - BATTLESTRINGS_TABLE_START] = sText_TheSeaOfFireDisappeared,

View file

@ -4314,6 +4314,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
u32 moveType, move;
u32 side;
u32 i, j;
u32 partner;
struct Pokemon *mon;
if (gBattleTypeFlags & BATTLE_TYPE_SAFARI)
@ -4799,6 +4800,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (!gSpecialStatuses[battler].switchInAbilityDone && CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)
&& !(gBattleStruct->intrepidSwordBoost[GetBattlerSide(battler)] & gBitTable[gBattlerPartyIndexes[battler]]))
{
gBattleScripting.savedBattler = gBattlerAttacker;
gBattlerAttacker = battler;
if (B_INTREPID_SWORD == GEN_9)
gBattleStruct->intrepidSwordBoost[GetBattlerSide(battler)] |= gBitTable[gBattlerPartyIndexes[battler]];
@ -4812,6 +4814,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
if (!gSpecialStatuses[battler].switchInAbilityDone && CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN)
&& !(gBattleStruct->dauntlessShieldBoost[GetBattlerSide(battler)] & gBitTable[gBattlerPartyIndexes[battler]]))
{
gBattleScripting.savedBattler = gBattlerAttacker;
gBattlerAttacker = battler;
if (B_DAUNTLESS_SHIELD == GEN_9)
gBattleStruct->dauntlessShieldBoost[GetBattlerSide(battler)] |= gBitTable[gBattlerPartyIndexes[battler]];
@ -4922,6 +4925,46 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
effect++;
}
break;
case ABILITY_HOSPITALITY:
partner = BATTLE_PARTNER(battler);
if (!gSpecialStatuses[battler].switchInAbilityDone && IsDoubleBattle() && gBattleMons[partner].hp < gBattleMons[partner].maxHP)
{
gBattlerTarget = partner;
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
gBattleMoveDamage = (GetNonDynamaxMaxHP(partner) / 4) * -1;
BattleScriptPushCursorAndCallback(BattleScript_HospitalityActivates);
effect++;
}
break;
case ABILITY_EMBODY_ASPECT_TEAL:
case ABILITY_EMBODY_ASPECT_HEARTHFLAME:
case ABILITY_EMBODY_ASPECT_WELLSPRING:
case ABILITY_EMBODY_ASPECT_CORNERSTONE:
if (!gSpecialStatuses[battler].switchInAbilityDone)
{
u32 stat = STAT_SPATK;
if (gLastUsedAbility == ABILITY_EMBODY_ASPECT_TEAL)
stat = STAT_SPATK;
else if (gLastUsedAbility == ABILITY_EMBODY_ASPECT_HEARTHFLAME)
stat = STAT_ATK;
else if (gLastUsedAbility == ABILITY_EMBODY_ASPECT_WELLSPRING)
stat = STAT_SPDEF;
else if (gLastUsedAbility == ABILITY_EMBODY_ASPECT_CORNERSTONE)
stat = STAT_DEF;
if (CompareStat(battler, stat, MAX_STAT_STAGE, CMP_EQUAL))
break;
gBattleScripting.savedBattler = gBattlerAttacker;
gBattlerAttacker = battler;
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
SET_STATCHANGER(stat, 1, FALSE);
BattleScriptPushCursorAndCallback(BattleScript_BattlerAbilityStatRaiseOnSwitchIn);
effect++;
}
break;
}
break;
case ABILITYEFFECT_ENDTURN: // 1

View file

@ -87,7 +87,7 @@ void RecordedBattle_Init(u8 mode)
for (j = 0; j < BATTLER_RECORD_SIZE; j++)
sBattleRecords[i][j] = 0xFF;
sBattleFlags = gBattleTypeFlags;
sAI_Scripts = gBattleResources->ai->aiFlags;
sAI_Scripts = gBattleResources->ai->aiFlags[B_POSITION_OPPONENT_LEFT];
}
}
}

View file

@ -3,7 +3,6 @@
ASSUMPTIONS
{
ASSUME(P_GEN_8_POKEMON == TRUE);
ASSUME(B_PROTEAN_LIBERO == GEN_9);
}
@ -45,3 +44,23 @@ SINGLE_BATTLE_TEST("Dauntless Shield raises Attack by one stage only once per ba
EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
}
}
SINGLE_BATTLE_TEST("Dauntless Shield activates when it's no longer effected by Neutralizing Gas")
{
GIVEN {
PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ZAMAZENTA) { Ability(ABILITY_DAUNTLESS_SHIELD); }
} WHEN {
TURN { SWITCH(player, 1); }
} SCENE {
ABILITY_POPUP(player, ABILITY_NEUTRALIZING_GAS);
MESSAGE("Neutralizing Gas filled the area!");
MESSAGE("Weezing, that's enough! Come back!");
MESSAGE("The effects of Neutralizing Gas wore off!");
ABILITY_POPUP(opponent, ABILITY_DAUNTLESS_SHIELD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Zamazenta's Dauntless Shield raised its Defense!");
}
}

View file

@ -0,0 +1,59 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Embodoy Aspect raises a stat depending on the users form by one stage")
{
u16 species, ability;
PARAMETRIZE { species = SPECIES_OGERPON_TEAL_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_TEAL; }
PARAMETRIZE { species = SPECIES_OGERPON_HEARTHFLAME_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_HEARTHFLAME; }
PARAMETRIZE { species = SPECIES_OGERPON_WELLSPRING_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_WELLSPRING; }
PARAMETRIZE { species = SPECIES_OGERPON_CORNERSTONE_MASK_TERA; ability = ABILITY_EMBODY_ASPECT_CORNERSTONE; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species) { Ability(ability); }
} WHEN {
TURN { }
} SCENE {
ABILITY_POPUP(opponent, ability);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
if (ability == ABILITY_EMBODY_ASPECT_TEAL)
MESSAGE("Foe Ogerpon's Embody Aspect raised its Sp. Atk!");
else if (ability == ABILITY_EMBODY_ASPECT_HEARTHFLAME)
MESSAGE("Foe Ogerpon's Embody Aspect raised its Attack!");
else if (ability == ABILITY_EMBODY_ASPECT_WELLSPRING)
MESSAGE("Foe Ogerpon's Embody Aspect raised its Sp. Def!");
else if (ability == ABILITY_EMBODY_ASPECT_CORNERSTONE)
MESSAGE("Foe Ogerpon's Embody Aspect raised its Defense!");
} THEN {
if (ability == ABILITY_EMBODY_ASPECT_TEAL)
EXPECT_EQ(opponent->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
else if (ability == ABILITY_EMBODY_ASPECT_HEARTHFLAME)
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
else if (ability == ABILITY_EMBODY_ASPECT_WELLSPRING)
EXPECT_EQ(opponent->statStages[STAT_SPDEF], DEFAULT_STAT_STAGE + 1);
else if (ability == ABILITY_EMBODY_ASPECT_CORNERSTONE)
EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1);
}
}
SINGLE_BATTLE_TEST("Embodoy Aspect activates when it's no longer effected by Neutralizing Gas")
{
GIVEN {
PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_OGERPON_TEAL_MASK_TERA) { Ability(ABILITY_EMBODY_ASPECT_TEAL); }
} WHEN {
TURN { SWITCH(player, 1); }
} SCENE {
ABILITY_POPUP(player, ABILITY_NEUTRALIZING_GAS);
MESSAGE("Neutralizing Gas filled the area!");
MESSAGE("Weezing, that's enough! Come back!");
MESSAGE("The effects of Neutralizing Gas wore off!");
ABILITY_POPUP(opponent, ABILITY_EMBODY_ASPECT_TEAL);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Ogerpon's Embody Aspect raised its Sp. Atk!");
}
}

View file

@ -0,0 +1,70 @@
#include "global.h"
#include "test/battle.h"
DOUBLE_BATTLE_TEST("Hospitality user restores 25% of ally's health")
{
s16 health;
PARAMETRIZE { health = 75; }
PARAMETRIZE { health = 100; }
GIVEN {
PLAYER(SPECIES_POLTCHAGEIST) { Ability(ABILITY_HOSPITALITY); }
PLAYER(SPECIES_WOBBUFFET) { HP(health); MaxHP(100); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { }
} SCENE {
if (health == 75) {
ABILITY_POPUP(playerLeft, ABILITY_HOSPITALITY);
MESSAGE("The Wobbuffet drank down all the matcha that Sinistcha made!");
HP_BAR(playerRight, damage: -25);
} else {
NONE_OF {
ABILITY_POPUP(playerLeft, ABILITY_HOSPITALITY);
MESSAGE("The Wobbuffet drank down all the matcha that Sinistcha made!");
HP_BAR(playerRight, damage: -25);
}
}
}
}
DOUBLE_BATTLE_TEST("Hospitality user restores 25% of ally's health on switch-in")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET)
PLAYER(SPECIES_WOBBUFFET) { HP(75); MaxHP(100); }
PLAYER(SPECIES_POLTCHAGEIST) { Ability(ABILITY_HOSPITALITY); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { SWITCH(playerLeft, 2); }
} SCENE {
MESSAGE("Wobbuffet, that's enough! Come back!");
MESSAGE("Go! Ptchageist!");
ABILITY_POPUP(playerLeft, ABILITY_HOSPITALITY);
MESSAGE("The Wobbuffet drank down all the matcha that Sinistcha made!");
HP_BAR(playerRight, damage: -25);
}
}
DOUBLE_BATTLE_TEST("Hospitality ignores Substitute")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_POLTCHAGEIST) { Ability(ABILITY_HOSPITALITY); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerRight, MOVE_SUBSTITUTE); }
TURN { SWITCH(playerLeft, 2); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, playerRight);
MESSAGE("Wobbuffet, that's enough! Come back!");
MESSAGE("Go! Ptchageist!");
ABILITY_POPUP(playerLeft, ABILITY_HOSPITALITY);
MESSAGE("The Wobbuffet drank down all the matcha that Sinistcha made!");
}
}

View file

@ -3,7 +3,6 @@
ASSUMPTIONS
{
ASSUME(P_GEN_8_POKEMON == TRUE);
ASSUME(B_INTREPID_SWORD == GEN_9);
}
@ -45,3 +44,23 @@ SINGLE_BATTLE_TEST("Intrepid Sword raises Attack by one stage only once per batt
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
}
}
SINGLE_BATTLE_TEST("Intrepid Sword activates when it's no longer effected by Neutralizing Gas")
{
GIVEN {
PLAYER(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); }
} WHEN {
TURN { SWITCH(player, 1); }
} SCENE {
ABILITY_POPUP(player, ABILITY_NEUTRALIZING_GAS);
MESSAGE("Neutralizing Gas filled the area!");
MESSAGE("Weezing, that's enough! Come back!");
MESSAGE("The effects of Neutralizing Gas wore off!");
ABILITY_POPUP(opponent, ABILITY_INTREPID_SWORD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Zacian's Intrepid Sword raised its Attack!");
}
}

View file

@ -98,7 +98,6 @@ SINGLE_BATTLE_TEST("U-turn switches the user out if Wimp Out fails to activate")
SINGLE_BATTLE_TEST("U-turn switches the user out after Ice Face activates")
{
GIVEN {
ASSUME(P_GEN_8_POKEMON == TRUE);
PLAYER(SPECIES_BEEDRILL);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_EISCUE) { Ability(ABILITY_ICE_FACE); }