Final 1.10 master upcoming merge (#6350)
@ -1034,6 +1034,7 @@ BattleScript_EffectCoaching::
|
||||
setallytonexttarget EffectCoaching_CheckAllyStats
|
||||
goto BattleScript_ButItFailed
|
||||
EffectCoaching_CheckAllyStats:
|
||||
accuracycheck BattleScript_ButItFailed, NO_ACC_CALC_CHECK_LOCK_ON
|
||||
jumpifstat BS_TARGET, CMP_NOT_EQUAL, STAT_ATK, MAX_STAT_STAGE, BattleScript_CoachingWorks
|
||||
jumpifstat BS_TARGET, CMP_NOT_EQUAL, STAT_DEF, MAX_STAT_STAGE, BattleScript_CoachingWorks
|
||||
goto BattleScript_ButItFailed @ ally at max atk, def
|
||||
@ -3776,6 +3777,7 @@ BattleScript_TwoTurnMovesSecondTurn::
|
||||
|
||||
BattleScript_TwoTurnMovesSecondTurnRet:
|
||||
setbyte sB_ANIM_TURN, 1
|
||||
setbyte sB_ANIM_TARGETS_HIT, 0
|
||||
clearstatusfromeffect BS_ATTACKER, MOVE_EFFECT_CHARGING
|
||||
clearsemiinvulnerablebit @ only for moves with EFFECT_SEMI_INVULNERABLE/EFFECT_SKY_DROP
|
||||
return
|
||||
|
||||
@ -54,6 +54,7 @@ Pull Requests that fall into this category are not in scope by default and shoul
|
||||
2. **Fangame Features**: Adds a popular feature from other fangames
|
||||
3. **Popular Non-SS Features**: Exceptions can be made for uniquely popular or requested features (Drowsy, PLA Legend Plate, etc.)
|
||||
4. **External Program**: External programs like poryscript, porymoves, etc.
|
||||
5. **Intergenerational Feature Compatibility**: Addresses limitations and issues resulting from including all generational behaviours in a GBA native title, and extrapolation of features no longer supported by GameFreak
|
||||
|
||||
## Workflow for Proposed Feature Scope Discussion
|
||||
For the contributor:
|
||||
|
||||
|
Before Width: | Height: | Size: 211 B After Width: | Height: | Size: 178 B |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 568 B |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 560 B |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 567 B |
|
Before Width: | Height: | Size: 268 B After Width: | Height: | Size: 184 B |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 555 B |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 570 B |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 563 B |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 565 B |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 568 B |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 566 B |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 557 B |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 551 B |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 558 B |
|
Before Width: | Height: | Size: 365 B After Width: | Height: | Size: 218 B |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 559 B |
|
Before Width: | Height: | Size: 209 B After Width: | Height: | Size: 177 B |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 566 B |
|
Before Width: | Height: | Size: 5.0 KiB After Width: | Height: | Size: 563 B |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 563 B |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 555 B |
|
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 582 B |
|
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 560 B |
@ -829,7 +829,10 @@ struct BattleStruct
|
||||
u8 calculatedSpreadMoveAccuracy:1;
|
||||
u8 printedStrongWindsWeakenedAttack:1;
|
||||
u8 numSpreadTargets:2;
|
||||
u8 padding3:2;
|
||||
u8 bypassMoldBreakerChecks:1; // for ABILITYEFFECT_IMMUNITY
|
||||
u8 padding3:1;
|
||||
u8 usedEjectItem;
|
||||
u8 usedMicleBerry;
|
||||
struct MessageStatus slideMessageStatus;
|
||||
u8 trainerSlideSpriteIds[MAX_BATTLERS_COUNT];
|
||||
u8 embodyAspectBoost[NUM_BATTLE_SIDES];
|
||||
|
||||
@ -218,6 +218,7 @@ bool32 AI_ShouldSetUpHazards(u32 battlerAtk, u32 battlerDef, struct AiLogicData
|
||||
void IncreaseTidyUpScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score);
|
||||
bool32 AI_ShouldSpicyExtract(u32 battlerAtk, u32 battlerAtkPartner, u32 move, struct AiLogicData *aiData);
|
||||
u32 IncreaseSubstituteMoveScore(u32 battlerAtk, u32 battlerDef, u32 move);
|
||||
bool32 IsBattlerItemEnabled(u32 battler);
|
||||
bool32 IsBattlerPredictedToSwitch(u32 battler);
|
||||
bool32 HasLowAccuracyMove(u32 battlerAtk, u32 battlerDef);
|
||||
|
||||
|
||||
@ -558,7 +558,7 @@ enum MoveEffects
|
||||
|
||||
#define MOVE_TARGET_SELECTED 0
|
||||
#define MOVE_TARGET_DEPENDS (1 << 0)
|
||||
#define MOVE_TARGET_USER_OR_SELECTED (1 << 1)
|
||||
#define MOVE_TARGET_OPPONENT (1 << 1)
|
||||
#define MOVE_TARGET_RANDOM (1 << 2)
|
||||
#define MOVE_TARGET_BOTH (1 << 3)
|
||||
#define MOVE_TARGET_USER (1 << 4)
|
||||
|
||||
@ -270,10 +270,10 @@
|
||||
#define OBJ_EVENT_GFX_VAR_E (OBJ_EVENT_GFX_VARS + 0xE)
|
||||
#define OBJ_EVENT_GFX_VAR_F (OBJ_EVENT_GFX_VARS + 0xF)
|
||||
|
||||
#define OBJ_EVENT_MON (1u << 15)
|
||||
#define OBJ_EVENT_MON_SHINY (1u << 14)
|
||||
#define OBJ_EVENT_MON_FEMALE (1u << 13)
|
||||
#define OBJ_EVENT_MON_SPECIES_MASK (~(7u << 13))
|
||||
#define OBJ_EVENT_MON (1u << 14)
|
||||
#define OBJ_EVENT_MON_SHINY (1u << 13)
|
||||
#define OBJ_EVENT_MON_FEMALE (1u << 12)
|
||||
#define OBJ_EVENT_MON_SPECIES_MASK (~(7u << 12))
|
||||
|
||||
// Used to call a specific species' follower graphics. Useful for static encounters.
|
||||
#define OBJ_EVENT_GFX_SPECIES(name) (SPECIES_##name + OBJ_EVENT_MON)
|
||||
|
||||
@ -2349,9 +2349,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
}
|
||||
break;
|
||||
case EFFECT_NATURAL_GIFT:
|
||||
if (aiData->abilities[battlerAtk] == ABILITY_KLUTZ
|
||||
|| gFieldStatuses & STATUS_FIELD_MAGIC_ROOM
|
||||
|| GetPocketByItemId(gBattleMons[battlerAtk].item) != POCKET_BERRIES)
|
||||
if (!IsBattlerItemEnabled(battlerAtk) || GetPocketByItemId(gBattleMons[battlerAtk].item) != POCKET_BERRIES)
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_GRASSY_TERRAIN:
|
||||
@ -2458,8 +2456,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
}
|
||||
break;
|
||||
case EFFECT_EMBARGO:
|
||||
if (aiData->abilities[battlerDef] == ABILITY_KLUTZ
|
||||
|| gFieldStatuses & STATUS_FIELD_MAGIC_ROOM
|
||||
if (!IsBattlerItemEnabled(battlerAtk)
|
||||
|| gDisableStructs[battlerDef].embargoTimer != 0
|
||||
|| PartnerMoveIsSameAsAttacker(BATTLE_PARTNER(battlerAtk), battlerDef, move, aiData->partnerMove))
|
||||
ADJUST_SCORE(-10);
|
||||
@ -2706,7 +2703,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
} // move effect checks
|
||||
|
||||
// Choice items
|
||||
if (HOLD_EFFECT_CHOICE(aiData->holdEffects[battlerAtk]) && gBattleMons[battlerAtk].ability != ABILITY_KLUTZ)
|
||||
if (HOLD_EFFECT_CHOICE(aiData->holdEffects[battlerAtk]) && IsBattlerItemEnabled(battlerAtk))
|
||||
{
|
||||
// Don't use user-target moves ie. Swords Dance, with exceptions
|
||||
if ((moveTarget & MOVE_TARGET_USER)
|
||||
@ -3369,6 +3366,12 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_BIG_ROOT && effectiveness >= UQ_4_12(1.0))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
break;
|
||||
case EFFECT_DREAM_EATER:
|
||||
case EFFECT_STRENGTH_SAP:
|
||||
case EFFECT_AQUA_RING:
|
||||
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_BIG_ROOT)
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
break;
|
||||
case EFFECT_EXPLOSION:
|
||||
case EFFECT_MEMENTO:
|
||||
if (AI_THINKING_STRUCT->aiFlags[battlerAtk] & AI_FLAG_WILL_SUICIDE && gBattleMons[battlerDef].statStages[STAT_EVASION] < 7)
|
||||
@ -3590,8 +3593,6 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
case EFFECT_MOONLIGHT:
|
||||
if (ShouldRecover(battlerAtk, battlerDef, move, 50))
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
if (aiData->holdEffects[battlerAtk] == HOLD_EFFECT_BIG_ROOT)
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
break;
|
||||
case EFFECT_TOXIC:
|
||||
case EFFECT_POISON:
|
||||
@ -3667,7 +3668,8 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
|| aiData->abilities[battlerDef] == ABILITY_MAGIC_GUARD)
|
||||
break;
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
if (!HasDamagingMove(battlerDef) || IsBattlerTrapped(battlerDef, FALSE))
|
||||
if (!HasDamagingMove(battlerDef) || IsBattlerTrapped(battlerDef, FALSE)
|
||||
|| aiData->holdEffects[battlerAtk] == HOLD_EFFECT_BIG_ROOT)
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
break;
|
||||
case EFFECT_DO_NOTHING:
|
||||
|
||||
@ -966,7 +966,7 @@ static bool32 ShouldSwitchIfBadChoiceLock(u32 battler)
|
||||
{
|
||||
u32 holdEffect = GetBattlerHoldEffect(battler, FALSE);
|
||||
|
||||
if (HOLD_EFFECT_CHOICE(holdEffect) && gBattleMons[battler].ability != ABILITY_KLUTZ)
|
||||
if (HOLD_EFFECT_CHOICE(holdEffect) && IsBattlerItemEnabled(battler))
|
||||
{
|
||||
if (GetMoveCategory(AI_DATA->lastUsedMove[battler]) == DAMAGE_CATEGORY_STATUS && RandomPercentage(RNG_AI_SWITCH_CHOICE_LOCKED, GetSwitchChance(SHOULD_SWITCH_CHOICE_LOCKED)))
|
||||
return SetSwitchinAndSwitch(battler, PARTY_SIZE);
|
||||
@ -1379,10 +1379,10 @@ bool32 IsMonGrounded(u16 heldItemEffect, u32 ability, u8 type1, u8 type2)
|
||||
{
|
||||
// List that makes mon not grounded
|
||||
if (type1 == TYPE_FLYING || type2 == TYPE_FLYING || ability == ABILITY_LEVITATE
|
||||
|| (heldItemEffect == HOLD_EFFECT_AIR_BALLOON && ability != ABILITY_KLUTZ))
|
||||
|| (heldItemEffect == HOLD_EFFECT_AIR_BALLOON && !(ability == ABILITY_KLUTZ || (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM))))
|
||||
{
|
||||
// List that overrides being off the ground
|
||||
if ((heldItemEffect == HOLD_EFFECT_IRON_BALL && ability != ABILITY_KLUTZ) || (gFieldStatuses & STATUS_FIELD_GRAVITY) || (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM))
|
||||
if ((heldItemEffect == HOLD_EFFECT_IRON_BALL && !(ability == ABILITY_KLUTZ || (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM))) || (gFieldStatuses & STATUS_FIELD_GRAVITY) || (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM))
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
|
||||
@ -423,15 +423,6 @@ static inline s32 DmgRoll(s32 dmg)
|
||||
bool32 IsDamageMoveUnusable(u32 battlerAtk, u32 battlerDef, u32 move, u32 moveType)
|
||||
{
|
||||
struct AiLogicData *aiData = AI_DATA;
|
||||
u32 battlerDefAbility;
|
||||
|
||||
if (DoesBattlerIgnoreAbilityChecks(battlerAtk, aiData->abilities[battlerAtk], move))
|
||||
battlerDefAbility = ABILITY_NONE;
|
||||
else
|
||||
battlerDefAbility = aiData->abilities[battlerDef];
|
||||
|
||||
if (battlerDef == BATTLE_PARTNER(battlerAtk))
|
||||
battlerDefAbility = aiData->abilities[battlerDef];
|
||||
|
||||
if (gBattleStruct->battlerState[battlerDef].commandingDondozo)
|
||||
return TRUE;
|
||||
@ -470,7 +461,7 @@ bool32 IsDamageMoveUnusable(u32 battlerAtk, u32 battlerDef, u32 move, u32 moveTy
|
||||
return TRUE;
|
||||
break;
|
||||
case EFFECT_POLTERGEIST:
|
||||
if (AI_DATA->items[battlerDef] == ITEM_NONE || gFieldStatuses & STATUS_FIELD_MAGIC_ROOM || battlerDefAbility == ABILITY_KLUTZ)
|
||||
if (AI_DATA->items[battlerDef] == ITEM_NONE || !IsBattlerItemEnabled(battlerDef))
|
||||
return TRUE;
|
||||
break;
|
||||
case EFFECT_FIRST_TURN_ONLY:
|
||||
@ -4296,3 +4287,16 @@ bool32 HasLowAccuracyMove(u32 battlerAtk, u32 battlerDef)
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 IsBattlerItemEnabled(u32 battler)
|
||||
{
|
||||
if (AI_THINKING_STRUCT->aiFlags[battler] & AI_FLAG_NEGATE_UNAWARE)
|
||||
return TRUE;
|
||||
if (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM)
|
||||
return FALSE;
|
||||
if (gStatuses3[battler] & STATUS3_EMBARGO)
|
||||
return FALSE;
|
||||
if (gBattleMons[battler].ability == ABILITY_KLUTZ && !(gStatuses3[battler] & STATUS3_GASTRO_ACID))
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -560,7 +560,7 @@ static void OpponentHandleChooseMove(u32 battler)
|
||||
default:
|
||||
{
|
||||
u16 chosenMove = moveInfo->moves[chosenMoveId];
|
||||
if (GetBattlerMoveTargetType(battler, chosenMove) & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER))
|
||||
if (GetBattlerMoveTargetType(battler, chosenMove) & MOVE_TARGET_USER)
|
||||
gBattlerTarget = battler;
|
||||
if (GetBattlerMoveTargetType(battler, chosenMove) & MOVE_TARGET_BOTH)
|
||||
{
|
||||
@ -595,7 +595,7 @@ static void OpponentHandleChooseMove(u32 battler)
|
||||
move = moveInfo->moves[chosenMoveId];
|
||||
} while (move == MOVE_NONE);
|
||||
|
||||
if (GetBattlerMoveTargetType(battler, move) & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER))
|
||||
if (GetBattlerMoveTargetType(battler, move) & MOVE_TARGET_USER)
|
||||
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, (chosenMoveId) | (battler << 8));
|
||||
else if (IsDoubleBattle())
|
||||
{
|
||||
|
||||
@ -495,8 +495,6 @@ void HandleInputChooseTarget(u32 battler)
|
||||
case B_POSITION_PLAYER_RIGHT:
|
||||
if (battler != gMultiUsePlayerCursor)
|
||||
i++;
|
||||
else if (moveTarget & MOVE_TARGET_USER_OR_SELECTED)
|
||||
i++;
|
||||
break;
|
||||
case B_POSITION_OPPONENT_LEFT:
|
||||
case B_POSITION_OPPONENT_RIGHT:
|
||||
@ -505,7 +503,8 @@ void HandleInputChooseTarget(u32 battler)
|
||||
}
|
||||
|
||||
if (gAbsentBattlerFlags & (1u << gMultiUsePlayerCursor)
|
||||
|| !CanTargetBattler(battler, gMultiUsePlayerCursor, move))
|
||||
|| !CanTargetBattler(battler, gMultiUsePlayerCursor, move)
|
||||
|| (moveTarget & MOVE_TARGET_OPPONENT && GetBattlerSide(gMultiUsePlayerCursor) == B_SIDE_PLAYER))
|
||||
i = 0;
|
||||
} while (i == 0);
|
||||
}
|
||||
@ -545,8 +544,6 @@ void HandleInputChooseTarget(u32 battler)
|
||||
case B_POSITION_PLAYER_RIGHT:
|
||||
if (battler != gMultiUsePlayerCursor)
|
||||
i++;
|
||||
else if (moveTarget & MOVE_TARGET_USER_OR_SELECTED)
|
||||
i++;
|
||||
break;
|
||||
case B_POSITION_OPPONENT_LEFT:
|
||||
case B_POSITION_OPPONENT_RIGHT:
|
||||
@ -555,7 +552,8 @@ void HandleInputChooseTarget(u32 battler)
|
||||
}
|
||||
|
||||
if (gAbsentBattlerFlags & (1u << gMultiUsePlayerCursor)
|
||||
|| !CanTargetBattler(battler, gMultiUsePlayerCursor, move))
|
||||
|| !CanTargetBattler(battler, gMultiUsePlayerCursor, move)
|
||||
|| (moveTarget & MOVE_TARGET_OPPONENT && GetBattlerSide(gMultiUsePlayerCursor) == B_SIDE_PLAYER))
|
||||
i = 0;
|
||||
} while (i == 0);
|
||||
}
|
||||
@ -690,12 +688,7 @@ void HandleInputChooseMove(u32 battler)
|
||||
else
|
||||
gMultiUsePlayerCursor = GetOpposingSideBattler(battler);
|
||||
|
||||
if (!gBattleResources->bufferA[battler][1]) // not a double battle
|
||||
{
|
||||
if (moveTarget & MOVE_TARGET_USER_OR_SELECTED && !gBattleResources->bufferA[battler][2])
|
||||
canSelectTarget = 1;
|
||||
}
|
||||
else // double battle
|
||||
if (gBattleResources->bufferA[battler][1]) // a double battle
|
||||
{
|
||||
if (!(moveTarget & (MOVE_TARGET_RANDOM | MOVE_TARGET_BOTH | MOVE_TARGET_DEPENDS | MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_OPPONENTS_FIELD | MOVE_TARGET_USER | MOVE_TARGET_ALLY)))
|
||||
canSelectTarget = 1; // either selected or user
|
||||
@ -706,7 +699,7 @@ void HandleInputChooseMove(u32 battler)
|
||||
{
|
||||
canSelectTarget = 0;
|
||||
}
|
||||
else if (!(moveTarget & (MOVE_TARGET_USER | MOVE_TARGET_USER_OR_SELECTED)) && CountAliveMonsInBattle(BATTLE_ALIVE_EXCEPT_BATTLER, battler) <= 1)
|
||||
else if (!(moveTarget & MOVE_TARGET_USER) && CountAliveMonsInBattle(BATTLE_ALIVE_EXCEPT_BATTLER, battler) <= 1)
|
||||
{
|
||||
gMultiUsePlayerCursor = GetDefaultMoveTarget(battler);
|
||||
canSelectTarget = 0;
|
||||
@ -749,7 +742,7 @@ void HandleInputChooseMove(u32 battler)
|
||||
case 1:
|
||||
gBattlerControllerFuncs[battler] = HandleInputChooseTarget;
|
||||
|
||||
if (moveTarget & (MOVE_TARGET_USER | MOVE_TARGET_USER_OR_SELECTED))
|
||||
if (moveTarget & MOVE_TARGET_USER)
|
||||
gMultiUsePlayerCursor = battler;
|
||||
else if (gAbsentBattlerFlags & (1u << GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT)))
|
||||
gMultiUsePlayerCursor = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
|
||||
|
||||
@ -353,7 +353,7 @@ static void PlayerPartnerHandleChooseMove(u32 battler)
|
||||
gBattlerTarget = gBattleStruct->aiChosenTarget[battler];
|
||||
u32 moveTarget = GetBattlerMoveTargetType(battler, moveInfo->moves[chosenMoveId]);
|
||||
|
||||
if (moveTarget & (MOVE_TARGET_USER | MOVE_TARGET_USER_OR_SELECTED))
|
||||
if (moveTarget & MOVE_TARGET_USER)
|
||||
gBattlerTarget = battler;
|
||||
else if (moveTarget & MOVE_TARGET_BOTH)
|
||||
{
|
||||
|
||||
@ -326,7 +326,7 @@ static u8 GetBattlePalaceMoveGroup(u8 battler, u16 move)
|
||||
switch (GetBattlerMoveTargetType(battler, move))
|
||||
{
|
||||
case MOVE_TARGET_SELECTED:
|
||||
case MOVE_TARGET_USER_OR_SELECTED:
|
||||
case MOVE_TARGET_OPPONENT:
|
||||
case MOVE_TARGET_RANDOM:
|
||||
case MOVE_TARGET_BOTH:
|
||||
case MOVE_TARGET_FOES_AND_ALLY:
|
||||
|
||||
@ -371,10 +371,10 @@ void UpdateIndicatorLevelData(u32 healthboxId, u32 level)
|
||||
|
||||
static const s8 sIndicatorPositions[][2] =
|
||||
{
|
||||
[B_POSITION_PLAYER_LEFT] = {53, -9},
|
||||
[B_POSITION_OPPONENT_LEFT] = {44, -9},
|
||||
[B_POSITION_PLAYER_RIGHT] = {52, -9},
|
||||
[B_POSITION_OPPONENT_RIGHT] = {44, -9},
|
||||
[B_POSITION_PLAYER_LEFT] = {49, -9},
|
||||
[B_POSITION_OPPONENT_LEFT] = {40, -9},
|
||||
[B_POSITION_PLAYER_RIGHT] = {48, -9},
|
||||
[B_POSITION_OPPONENT_RIGHT] = {40, -9},
|
||||
};
|
||||
|
||||
void CreateIndicatorSprite(u32 battler)
|
||||
|
||||
@ -3205,6 +3205,9 @@ void SwitchInClearSetData(u32 battler)
|
||||
gCurrentMove = MOVE_NONE;
|
||||
gBattleStruct->arenaTurnCounter = 0xFF;
|
||||
|
||||
// Restore struct member so replacement does not miss timing
|
||||
gSpecialStatuses[battler].switchInAbilityDone = FALSE;
|
||||
|
||||
// Reset damage to prevent things like red card activating if the switched-in mon is holding it
|
||||
gSpecialStatuses[battler].physicalDmg = 0;
|
||||
gSpecialStatuses[battler].specialDmg = 0;
|
||||
|
||||
@ -1197,6 +1197,9 @@ static void Cmd_attackcanceler(void)
|
||||
|
||||
if (AbilityBattleEffects(ABILITYEFFECT_MOVES_BLOCK, gBattlerTarget, 0, 0, 0))
|
||||
return;
|
||||
if (gMovesInfo[gCurrentMove].effect == EFFECT_PARALYZE && AbilityBattleEffects(ABILITYEFFECT_ABSORBING, gBattlerTarget, 0, 0, gCurrentMove))
|
||||
return;
|
||||
|
||||
if (!gBattleMons[gBattlerAttacker].pp[gCurrMovePos] && gCurrentMove != MOVE_STRUGGLE
|
||||
&& !(gHitMarker & (HITMARKER_ALLOW_NO_PP | HITMARKER_NO_ATTACKSTRING | HITMARKER_NO_PPDEDUCT))
|
||||
&& !(gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS))
|
||||
@ -3884,7 +3887,7 @@ void SetMoveEffect(bool32 primary, bool32 certain)
|
||||
gProtectStructs[gBattlerTarget].banefulBunkered = FALSE;
|
||||
gProtectStructs[gBattlerTarget].obstructed = FALSE;
|
||||
gProtectStructs[gBattlerTarget].silkTrapped = FALSE;
|
||||
gProtectStructs[gBattlerAttacker].burningBulwarked = FALSE;
|
||||
gProtectStructs[gBattlerTarget].burningBulwarked = FALSE;
|
||||
BattleScriptPush(gBattlescriptCurrInstr + 1);
|
||||
if (gCurrentMove == MOVE_HYPERSPACE_FURY)
|
||||
gBattlescriptCurrInstr = BattleScript_HyperspaceFuryRemoveProtect;
|
||||
@ -6360,6 +6363,7 @@ static void Cmd_moveend(void)
|
||||
else if (moveRecoil > 0
|
||||
&& !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)
|
||||
&& IsBattlerAlive(gBattlerAttacker)
|
||||
&& IsBattlerTurnDamaged(gBattlerTarget)
|
||||
&& gBattleScripting.savedDmg != 0) // Some checks may be redundant alongside this one
|
||||
{
|
||||
gBattleStruct->moveDamage[gBattlerAttacker] = max(1, gBattleScripting.savedDmg * max(1, moveRecoil) / 100);
|
||||
@ -11605,7 +11609,8 @@ static void TryResetProtectUseCounter(u32 battler)
|
||||
u32 lastEffect = GetMoveEffect(lastMove);
|
||||
if (lastMove == MOVE_UNAVAILABLE
|
||||
|| (!gBattleMoveEffects[lastEffect].usesProtectCounter
|
||||
&& (B_ALLY_SWITCH_FAIL_CHANCE >= GEN_9 && lastEffect != EFFECT_ALLY_SWITCH)))
|
||||
&& ((B_ALLY_SWITCH_FAIL_CHANCE >= GEN_9 && lastEffect != EFFECT_ALLY_SWITCH)
|
||||
|| B_ALLY_SWITCH_FAIL_CHANCE < GEN_9)))
|
||||
gDisableStructs[battler].protectUses = 0;
|
||||
}
|
||||
|
||||
|
||||
@ -3887,6 +3887,21 @@ bool32 HasNoMonsToSwitch(u32 battler, u8 partyIdBattlerOn1, u8 partyIdBattlerOn2
|
||||
playerId = GetBattlerAtPosition(B_POSITION_OPPONENT_RIGHT);
|
||||
party = gEnemyParty;
|
||||
|
||||
// Edge case: If both opposing Pokemon were knocked out on the same turn,
|
||||
// make sure opponent only sents out the final Pokemon once.
|
||||
if (battler == playerId
|
||||
&& (gHitMarker & HITMARKER_FAINTED(flankId))
|
||||
&& (gHitMarker & HITMARKER_FAINTED(playerId)))
|
||||
{
|
||||
u8 count = 0;
|
||||
for (i = 0; i < PARTY_SIZE; i++)
|
||||
if (IsValidForBattle(&party[i]))
|
||||
count++;
|
||||
|
||||
if (count < 2)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (partyIdBattlerOn1 == PARTY_SIZE)
|
||||
partyIdBattlerOn1 = gBattlerPartyIndexes[flankId];
|
||||
if (partyIdBattlerOn2 == PARTY_SIZE)
|
||||
@ -6340,6 +6355,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
|
||||
}
|
||||
break;
|
||||
case ABILITYEFFECT_IMMUNITY:
|
||||
gBattleStruct->bypassMoldBreakerChecks = TRUE;
|
||||
for (battler = 0; battler < gBattlersCount; battler++)
|
||||
{
|
||||
switch (GetBattlerAbility(battler))
|
||||
@ -6431,6 +6447,7 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
|
||||
return effect;
|
||||
}
|
||||
}
|
||||
gBattleStruct->bypassMoldBreakerChecks = FALSE;
|
||||
break;
|
||||
case ABILITYEFFECT_SYNCHRONIZE:
|
||||
if (gLastUsedAbility == ABILITY_SYNCHRONIZE && (gHitMarker & HITMARKER_SYNCHRONISE_EFFECT))
|
||||
@ -6670,7 +6687,9 @@ u32 GetBattlerAbility(u32 battler)
|
||||
&& gBattleMons[battler].ability == ABILITY_COMATOSE)
|
||||
return ABILITY_NONE;
|
||||
|
||||
if (noAbilityShield && CanBreakThroughAbility(gBattlerAttacker, battler, gBattleMons[gBattlerAttacker].ability))
|
||||
if (!gBattleStruct->bypassMoldBreakerChecks
|
||||
&& noAbilityShield
|
||||
&& CanBreakThroughAbility(gBattlerAttacker, battler, gBattleMons[gBattlerAttacker].ability))
|
||||
return ABILITY_NONE;
|
||||
|
||||
return gBattleMons[battler].ability;
|
||||
@ -6684,7 +6703,9 @@ u32 GetBattlerAbility(u32 battler)
|
||||
&& noAbilityShield)
|
||||
return ABILITY_NONE;
|
||||
|
||||
if (noAbilityShield && CanBreakThroughAbility(gBattlerAttacker, battler, gBattleMons[gBattlerAttacker].ability))
|
||||
if (!gBattleStruct->bypassMoldBreakerChecks
|
||||
&& noAbilityShield
|
||||
&& CanBreakThroughAbility(gBattlerAttacker, battler, gBattleMons[gBattlerAttacker].ability))
|
||||
return ABILITY_NONE;
|
||||
|
||||
return gBattleMons[battler].ability;
|
||||
@ -8517,6 +8538,7 @@ u32 GetBattleMoveTarget(u16 move, u8 setTarget)
|
||||
switch (moveTarget)
|
||||
{
|
||||
case MOVE_TARGET_SELECTED:
|
||||
case MOVE_TARGET_OPPONENT:
|
||||
side = BATTLE_OPPOSITE(GetBattlerSide(gBattlerAttacker));
|
||||
if (IsAffectedByFollowMe(gBattlerAttacker, side, move))
|
||||
{
|
||||
@ -8576,7 +8598,6 @@ u32 GetBattleMoveTarget(u16 move, u8 setTarget)
|
||||
else
|
||||
targetBattler = GetOpposingSideBattler(gBattlerAttacker);
|
||||
break;
|
||||
case MOVE_TARGET_USER_OR_SELECTED:
|
||||
case MOVE_TARGET_USER:
|
||||
default:
|
||||
targetBattler = gBattlerAttacker;
|
||||
@ -8715,7 +8736,7 @@ u32 GetBattlerHoldEffectInternal(u32 battler, bool32 checkNegating, bool32 check
|
||||
return HOLD_EFFECT_NONE;
|
||||
if (gFieldStatuses & STATUS_FIELD_MAGIC_ROOM)
|
||||
return HOLD_EFFECT_NONE;
|
||||
if (checkAbility && GetBattlerAbility(battler) == ABILITY_KLUTZ)
|
||||
if (checkAbility && GetBattlerAbility(battler) == ABILITY_KLUTZ && !(gStatuses3[battler] & STATUS3_GASTRO_ACID))
|
||||
return HOLD_EFFECT_NONE;
|
||||
}
|
||||
|
||||
@ -8927,7 +8948,7 @@ u32 GetMoveTargetCount(struct DamageCalculationData *damageCalcData)
|
||||
case MOVE_TARGET_DEPENDS:
|
||||
case MOVE_TARGET_SELECTED:
|
||||
case MOVE_TARGET_RANDOM:
|
||||
case MOVE_TARGET_USER_OR_SELECTED:
|
||||
case MOVE_TARGET_OPPONENT:
|
||||
return IsBattlerAlive(battlerDef);
|
||||
case MOVE_TARGET_USER:
|
||||
return IsBattlerAlive(battlerAtk);
|
||||
@ -11533,8 +11554,12 @@ bool32 CanStealItem(u32 battlerStealing, u32 battlerItem, u16 item)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// It's supposed to pop before trying to steal but this also works
|
||||
if (ItemId_GetHoldEffect(item) == HOLD_EFFECT_AIR_BALLOON)
|
||||
return FALSE;
|
||||
|
||||
if (!CanBattlerGetOrLoseItem(battlerItem, item) // Battler with item cannot have it stolen
|
||||
||!CanBattlerGetOrLoseItem(battlerStealing, item)) // Stealer cannot take the item
|
||||
|| !CanBattlerGetOrLoseItem(battlerStealing, item)) // Stealer cannot take the item
|
||||
return FALSE;
|
||||
|
||||
return TRUE;
|
||||
|
||||
@ -5485,10 +5485,10 @@ static void SetMoveTargetPosition(u16 move)
|
||||
{
|
||||
switch (GetBattlerMoveTargetType(gBattlerAttacker, move))
|
||||
{
|
||||
case MOVE_TARGET_USER_OR_SELECTED:
|
||||
case MOVE_TARGET_USER:
|
||||
gBattlerTarget = B_POSITION_PLAYER_RIGHT;
|
||||
break;
|
||||
case MOVE_TARGET_OPPONENT:
|
||||
case MOVE_TARGET_SELECTED:
|
||||
case MOVE_TARGET_RANDOM:
|
||||
case MOVE_TARGET_BOTH:
|
||||
|
||||
@ -135,8 +135,8 @@ static const struct SpritePalette sSpritePalette_TeraIndicator = {sTeraIndicator
|
||||
|
||||
static const struct OamData sOamData_GimmickIndicator =
|
||||
{
|
||||
.shape = SPRITE_SHAPE(16x16),
|
||||
.size = SPRITE_SIZE(16x16),
|
||||
.shape = SPRITE_SHAPE(8x16),
|
||||
.size = SPRITE_SIZE(8x16),
|
||||
.priority = 1,
|
||||
};
|
||||
|
||||
|
||||
@ -9892,7 +9892,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
|
||||
.type = TYPE_NORMAL,
|
||||
.accuracy = 0,
|
||||
.pp = 20,
|
||||
.target = MOVE_TARGET_SELECTED,
|
||||
.target = MOVE_TARGET_OPPONENT,
|
||||
.priority = 0,
|
||||
.category = DAMAGE_CATEGORY_STATUS,
|
||||
.zMove = { .effect = Z_EFFECT_SPD_UP_2 },
|
||||
|
||||
@ -1186,6 +1186,15 @@ void ItemUseInBattle_PartyMenuChooseMove(u8 taskId)
|
||||
ItemUseInBattle_ShowPartyMenu(taskId);
|
||||
}
|
||||
|
||||
static bool32 SelectedMonHasStatus2(u16 itemId)
|
||||
{
|
||||
if (gPartyMenu.slotId == 0)
|
||||
return gBattleMons[0].status2 & GetItemStatus2Mask(itemId);
|
||||
else if (gBattleTypeFlags & (BATTLE_TYPE_DOUBLE | BATTLE_TYPE_MULTI) && gPartyMenu.slotId == 1)
|
||||
return gBattleMons[2].status2 & GetItemStatus2Mask(itemId);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Returns whether an item can be used in battle and sets the fail text.
|
||||
bool32 CannotUseItemsInBattle(u16 itemId, struct Pokemon *mon)
|
||||
{
|
||||
@ -1258,13 +1267,13 @@ bool32 CannotUseItemsInBattle(u16 itemId, struct Pokemon *mon)
|
||||
break;
|
||||
case EFFECT_ITEM_CURE_STATUS:
|
||||
if (!((GetMonData(mon, MON_DATA_STATUS) & GetItemStatus1Mask(itemId))
|
||||
|| (gPartyMenu.slotId == 0 && gBattleMons[gBattlerInMenuId].status2 & GetItemStatus2Mask(itemId))))
|
||||
|| SelectedMonHasStatus2(itemId)))
|
||||
cannotUse = TRUE;
|
||||
break;
|
||||
case EFFECT_ITEM_HEAL_AND_CURE_STATUS:
|
||||
if ((hp == 0 || hp == GetMonData(mon, MON_DATA_MAX_HP))
|
||||
&& !((GetMonData(mon, MON_DATA_STATUS) & GetItemStatus1Mask(itemId))
|
||||
|| (gPartyMenu.slotId == 0 && gBattleMons[gBattlerInMenuId].status2 & GetItemStatus2Mask(itemId))))
|
||||
|| SelectedMonHasStatus2(itemId)))
|
||||
cannotUse = TRUE;
|
||||
break;
|
||||
case EFFECT_ITEM_REVIVE:
|
||||
|
||||
@ -375,3 +375,18 @@ DOUBLE_BATTLE_TEST("Intimidate will correctly decrease the attack of the second
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Intimdate does not lose timing after mega evolution and switch out by a hit escape move")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_U_TURN].effect == EFFECT_HIT_ESCAPE);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_MANECTRIC) { Item(ITEM_MANECTITE); }
|
||||
OPPONENT(SPECIES_ARBOK) { Ability(ABILITY_INTIMIDATE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_U_TURN, gimmick: GIMMICK_MEGA); SEND_OUT(opponent, 1); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(opponent, ABILITY_INTIMIDATE);
|
||||
ABILITY_POPUP(opponent, ABILITY_INTIMIDATE);
|
||||
}
|
||||
}
|
||||
|
||||
14
test/battle/ability/motor_drive.c
Normal file
@ -0,0 +1,14 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Motor Drive absorbs status moves")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_EMOLGA) { Ability(ABILITY_MOTOR_DRIVE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_THUNDER_WAVE); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(opponent, ABILITY_MOTOR_DRIVE);
|
||||
}
|
||||
}
|
||||
@ -58,7 +58,6 @@ SINGLE_BATTLE_TEST("Own Tempo prevents confusion from moves by the user")
|
||||
|
||||
SINGLE_BATTLE_TEST("Mold Breaker ignores Own Tempo")
|
||||
{
|
||||
KNOWN_FAILING; // Ideally the func CanBeConfused should be split into AttackerCanBeConfused and TargetCanBeConfused or we do it in the same func but have a check for when battlerAtk == battlerDef
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_CONFUSE_RAY) == EFFECT_CONFUSE);
|
||||
PLAYER(SPECIES_PINSIR) { Ability(ABILITY_MOLD_BREAKER); }
|
||||
@ -66,16 +65,13 @@ SINGLE_BATTLE_TEST("Mold Breaker ignores Own Tempo")
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_CONFUSE_RAY); }
|
||||
} SCENE {
|
||||
NONE_OF {
|
||||
ABILITY_POPUP(opponent, ABILITY_OWN_TEMPO);
|
||||
MESSAGE("The opposing Slowpoke's Own Tempo prevents confusion!");
|
||||
}
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_CONFUSE_RAY, player);
|
||||
NOT MESSAGE("The opposing Slowpoke's Own Tempo prevents confusion!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Mold Breaker does not prevent Own Tempo from curing confusion right after")
|
||||
{
|
||||
KNOWN_FAILING;
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_CONFUSE_RAY) == EFFECT_CONFUSE);
|
||||
PLAYER(SPECIES_PINSIR) { Ability(ABILITY_MOLD_BREAKER); };
|
||||
|
||||
@ -33,7 +33,6 @@ DOUBLE_BATTLE_TEST("Pastel Veil prevents Poison Sting poison on partner")
|
||||
|
||||
SINGLE_BATTLE_TEST("Pastel Veil immediately cures Mold Breaker poison")
|
||||
{
|
||||
KNOWN_FAILING;
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_TOXIC) == EFFECT_TOXIC);
|
||||
PLAYER(SPECIES_PINSIR) { Ability(ABILITY_MOLD_BREAKER); }
|
||||
|
||||
@ -105,7 +105,6 @@ SINGLE_BATTLE_TEST("Air Balloon pops before it can be stolen with Magician")
|
||||
SINGLE_BATTLE_TEST("Air Balloon pops before it can be stolen with Thief or Covet")
|
||||
{
|
||||
u32 move;
|
||||
KNOWN_FAILING;
|
||||
PARAMETRIZE { move = MOVE_THIEF; }
|
||||
PARAMETRIZE { move = MOVE_COVET; }
|
||||
GIVEN {
|
||||
|
||||
@ -54,7 +54,7 @@ SINGLE_BATTLE_TEST("Jaboca Berry tirggers before Bug Bite can steal it")
|
||||
HP_BAR(opponent);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponent);
|
||||
HP_BAR(player);
|
||||
MESSAGE("Wyanut was hurt by the opposing Wobbuffet's Jaboca Berry!");
|
||||
NOT MESSAGE("Wynaut stole and ate the opposing its target's Jaboca Berry!");
|
||||
MESSAGE("Wynaut was hurt by the opposing Wobbuffet's Jaboca Berry!");
|
||||
NOT MESSAGE("Wynaut stole and ate the opposing Wobbuffet's Jaboca Berry!");
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,7 +58,6 @@ DOUBLE_BATTLE_TEST("Coaching bypasses Crafty Shield")
|
||||
|
||||
DOUBLE_BATTLE_TEST("Coaching fails if all allies are is semi-invulnerable")
|
||||
{
|
||||
KNOWN_FAILING; // Coaching succeeds
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_FLY) == EFFECT_SEMI_INVULNERABLE);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
|
||||
@ -43,7 +43,6 @@ SINGLE_BATTLE_TEST("Razor Wind needs a charging turn")
|
||||
|
||||
SINGLE_BATTLE_TEST("Razor Wind doesn't need to charge with Power Herb")
|
||||
{
|
||||
KNOWN_FAILING;
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_POWER_HERB); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
@ -63,7 +62,6 @@ SINGLE_BATTLE_TEST("Razor Wind doesn't need to charge with Power Herb")
|
||||
MESSAGE("Wobbuffet became fully charged due to its Power Herb!");
|
||||
if (B_UPDATED_MOVE_DATA < GEN_5)
|
||||
MESSAGE("Wobbuffet used Razor Wind!");
|
||||
// For some reason, this breaks with and only with Razor Wind...
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_RAZOR_WIND, player);
|
||||
HP_BAR(opponent);
|
||||
}
|
||||
|
||||
@ -83,3 +83,22 @@ SINGLE_BATTLE_TEST("Flare Blitz deals 33% of recoil damage to the user and can b
|
||||
EXPECT_MUL_EQ(directDamage, UQ_4_12(0.33), recoilDamage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Flare Blitz is absorbed by Flash Fire and no recoil damage is dealt")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_FLARE_BLITZ].recoil > 0);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_VULPIX) { Ability(ABILITY_FLASH_FIRE); };
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_TACKLE); MOVE(player, MOVE_FLARE_BLITZ); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
|
||||
HP_BAR(player);
|
||||
NONE_OF {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_FLARE_BLITZ, player);
|
||||
HP_BAR(opponent);
|
||||
HP_BAR(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2043,7 +2043,7 @@ s32 MoveGetTarget(s32 battlerId, u32 moveId, struct MoveContext *ctx, u32 source
|
||||
{
|
||||
target = BATTLE_OPPOSITE(battlerId);
|
||||
}
|
||||
else if (move->target == MOVE_TARGET_SELECTED)
|
||||
else if (move->target == MOVE_TARGET_SELECTED || move->target == MOVE_TARGET_OPPONENT)
|
||||
{
|
||||
// In AI Doubles not specified target allows any target for EXPECT_MOVE.
|
||||
if (GetBattleTest()->type != BATTLE_TEST_AI_DOUBLES)
|
||||
|
||||