Dynamic move targeting

Properly implement Expanding Force by replacing all  instances that read move target from gBattleMoves with a function.
This commit is contained in:
BuffelSaft 2021-11-23 16:56:11 +13:00
parent 6b8f86c276
commit fa0b5188bb
11 changed files with 87 additions and 75 deletions

View File

@ -163,7 +163,7 @@ bool32 IsBattlerWeatherAffected(u8 battlerId, u32 weatherFlags);
void TryToApplyMimicry(u8 battlerId, bool8 various);
void TryToRevertMimicry(void);
void RestoreBattlerOriginalTypes(u8 battlerId);
u32 GetMoveTargetType(u8 battlerId, u16 move);
// Ability checks
bool32 IsRolePlayBannedAbilityAtk(u16 ability);
bool32 IsRolePlayBannedAbility(u16 ability);

View File

@ -521,7 +521,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
u8 atkPriority = GetMovePriority(battlerAtk, move);
u16 moveEffect = gBattleMoves[move].effect;
s32 moveType;
u16 moveTarget = gBattleMoves[move].target;
u16 moveTarget = GetMoveTargetType(battlerAtk, move);
u16 accuracy = AI_GetMoveAccuracy(battlerAtk, battlerDef, AI_DATA->atkAbility, AI_DATA->defAbility, AI_DATA->atkHoldEffect, AI_DATA->defHoldEffect, move);
u8 effectiveness = AI_GetMoveEffectiveness(move, battlerAtk, battlerDef);
bool32 isDoubleBattle = IsValidDoubleBattle(battlerAtk);
@ -537,7 +537,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
GET_MOVE_TYPE(move, moveType);
// check non-user target
if (!(gBattleMoves[move].target & MOVE_TARGET_USER))
if (moveTarget & MOVE_TARGET_USER)
{
// handle negative checks on non-user target
// check powder moves
@ -2336,7 +2336,7 @@ static s16 AI_CheckBadMove(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
}
else
{
if (gBattleMoves[instructedMove].target & (MOVE_TARGET_SELECTED
if (GetMoveTargetType(battlerDef, instructedMove) & (MOVE_TARGET_SELECTED
| MOVE_TARGET_DEPENDS
| MOVE_TARGET_RANDOM
| MOVE_TARGET_BOTH
@ -2533,7 +2533,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
// move data
u8 moveType = gBattleMoves[move].type;
u16 effect = gBattleMoves[move].effect;
u16 target = gBattleMoves[move].target;
u16 moveTarget = GetMoveTargetType(battlerAtk, move);
// ally data
u8 battlerAtkPartner = AI_DATA->battlerAtkPartner;
u16 atkPartnerAbility = AI_DATA->atkPartnerAbility;
@ -2572,7 +2572,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
if (IsAttackBoostMoveEffect(effect))
score -= 3;
// encourage moves hitting multiple opponents
if (!IS_MOVE_STATUS(move) && (gBattleMoves[move].target & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY)))
if (!IS_MOVE_STATUS(move) && (moveTarget & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY)))
score += 3;
}
}
@ -2640,7 +2640,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
if (GetMoveDamageResult(move) == MOVE_POWER_OTHER)
{
// partner ability checks
if (!partnerProtecting && gBattleMoves[move].target != MOVE_TARGET_BOTH && !DoesBattlerIgnoreAbilityChecks(AI_DATA->atkAbility, move))
if (!partnerProtecting && moveTarget != MOVE_TARGET_BOTH && !DoesBattlerIgnoreAbilityChecks(AI_DATA->atkAbility, move))
{
switch (atkPartnerAbility)
{
@ -2840,7 +2840,7 @@ static s16 AI_DoubleBattle(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
if (instructedMove != MOVE_NONE
&& !IS_MOVE_STATUS(instructedMove)
&& gBattleMoves[instructedMove].target & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY)) //Use instruct on multi-target moves
&& (GetMoveTargetType(battlerAtkPartner, instructedMove) & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY))) // Use instruct on multi-target moves
{
RETURN_SCORE_PLUS(1);
}
@ -3631,24 +3631,24 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
break;
case MOVE_WIDE_GUARD:
if (predictedMove != MOVE_NONE && gBattleMoves[predictedMove].target & (MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_BOTH))
if (predictedMove != MOVE_NONE && GetMoveTargetType(battlerDef, predictedMove) & (MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_BOTH))
{
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
}
else if (isDoubleBattle && gBattleMoves[AI_DATA->partnerMove].target & MOVE_TARGET_FOES_AND_ALLY)
else if (isDoubleBattle && GetMoveTargetType(AI_DATA->battlerAtkPartner, AI_DATA->partnerMove) & MOVE_TARGET_FOES_AND_ALLY)
{
if (AI_DATA->atkAbility != ABILITY_TELEPATHY)
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
}
break;
case MOVE_CRAFTY_SHIELD:
if (predictedMove != MOVE_NONE && IS_MOVE_STATUS(predictedMove) && !(gBattleMoves[predictedMove].target & MOVE_TARGET_USER))
if (predictedMove != MOVE_NONE && IS_MOVE_STATUS(predictedMove) && !(GetMoveTargetType(battlerDef, predictedMove) & MOVE_TARGET_USER))
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
break;
case MOVE_MAT_BLOCK:
if (gDisableStructs[battlerAtk].isFirstTurn && predictedMove != MOVE_NONE
&& !IS_MOVE_STATUS(predictedMove) && !(gBattleMoves[predictedMove].target & MOVE_TARGET_USER))
&& !IS_MOVE_STATUS(predictedMove) && !(GetMoveTargetType(battlerDef, predictedMove) & MOVE_TARGET_USER))
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
break;
case MOVE_KINGS_SHIELD:
@ -4093,7 +4093,7 @@ static s16 AI_CheckViability(u8 battlerAtk, u8 battlerDef, u16 move, s16 score)
score += 10;
break;
case EFFECT_MAGIC_COAT:
if (IS_MOVE_STATUS(predictedMove) && gBattleMoves[predictedMove].target & (MOVE_TARGET_SELECTED | MOVE_TARGET_OPPONENTS_FIELD | MOVE_TARGET_BOTH))
if (IS_MOVE_STATUS(predictedMove) && GetMoveTargetType(battlerDef, predictedMove) & (MOVE_TARGET_SELECTED | MOVE_TARGET_OPPONENTS_FIELD | MOVE_TARGET_BOTH))
score += 3;
break;
case EFFECT_RECYCLE:

View File

@ -1919,9 +1919,8 @@ bool32 HasMoveWithLowAccuracy(u8 battlerAtk, u8 battlerDef, u8 accCheck, bool32
if (ignoreStatus && IS_MOVE_STATUS(moves[i]))
continue;
else if ((!IS_MOVE_STATUS(moves[i]) && gBattleMoves[moves[i]].accuracy == 0)
|| gBattleMoves[moves[i]].target & (MOVE_TARGET_USER | MOVE_TARGET_OPPONENTS_FIELD))
|| GetMoveTargetType(battlerAtk, moves[i]) & (MOVE_TARGET_USER | MOVE_TARGET_OPPONENTS_FIELD))
continue;
if (AI_GetMoveAccuracy(battlerAtk, battlerDef, atkAbility, defAbility, atkHoldEffect, defHoldEffect, moves[i]) <= accCheck)
return TRUE;
}

View File

@ -2195,7 +2195,7 @@ void DoMoveAnim(u16 move)
gBattleAnimAttacker = gBattlerAttacker;
gBattleAnimTarget = gBattlerTarget;
// Make sure the anim target of moves hitting everyone is at the opposite side.
if (gBattleMoves[move].target & MOVE_TARGET_FOES_AND_ALLY && IsDoubleBattle())
if (GetMoveTargetType(gBattlerAttacker, move) & MOVE_TARGET_FOES_AND_ALLY && IsDoubleBattle())
{
while (GET_BATTLER_SIDE(gBattleAnimAttacker) == GET_BATTLER_SIDE(gBattleAnimTarget))
{

View File

@ -1586,9 +1586,9 @@ static void OpponentHandleChooseMove(void)
BtlController_EmitTwoReturnValues(1, 15, gBattlerTarget);
break;
default:
if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER))
if (GetMoveTargetType(gActiveBattler, moveInfo->moves[chosenMoveId]) & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER))
gBattlerTarget = gActiveBattler;
if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & MOVE_TARGET_BOTH)
if (GetMoveTargetType(gActiveBattler, moveInfo->moves[chosenMoveId]) & MOVE_TARGET_BOTH)
{
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_LEFT);
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])
@ -1611,7 +1611,7 @@ static void OpponentHandleChooseMove(void)
move = moveInfo->moves[chosenMoveId];
} while (move == MOVE_NONE);
if (gBattleMoves[move].target & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER))
if (GetMoveTargetType(gActiveBattler, move) & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER))
BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (gActiveBattler << 8));
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
BtlController_EmitTwoReturnValues(1, 10, (chosenMoveId) | (GetBattlerAtPosition(Random() & 2) << 8));

View File

@ -360,6 +360,7 @@ static void HandleInputChooseTarget(void)
s32 i;
static const u8 identities[MAX_BATTLERS_COUNT] = {B_POSITION_PLAYER_LEFT, B_POSITION_PLAYER_RIGHT, B_POSITION_OPPONENT_RIGHT, B_POSITION_OPPONENT_LEFT};
u16 move = GetMonData(&gPlayerParty[gBattlerPartyIndexes[gActiveBattler]], MON_DATA_MOVE1 + gMoveSelectionCursor[gActiveBattler]);
u16 moveTarget = GetMoveTargetType(gActiveBattler, move);
DoBounceEffect(gMultiUsePlayerCursor, BOUNCE_HEALTHBOX, 15, 1);
for (i = 0; i < gBattlersCount; i++)
@ -400,7 +401,7 @@ static void HandleInputChooseTarget(void)
PlaySE(SE_SELECT);
gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = SpriteCb_HideAsMoveTarget;
if (gBattleMoves[move].target == (MOVE_TARGET_USER | MOVE_TARGET_ALLY))
if (moveTarget == (MOVE_TARGET_USER | MOVE_TARGET_ALLY))
{
gMultiUsePlayerCursor ^= BIT_FLANK;
}
@ -429,7 +430,7 @@ static void HandleInputChooseTarget(void)
case B_POSITION_PLAYER_RIGHT:
if (gActiveBattler != gMultiUsePlayerCursor)
i++;
else if (gBattleMoves[move].target & MOVE_TARGET_USER_OR_SELECTED)
else if (moveTarget & MOVE_TARGET_USER_OR_SELECTED)
i++;
break;
case B_POSITION_OPPONENT_LEFT:
@ -449,7 +450,7 @@ static void HandleInputChooseTarget(void)
PlaySE(SE_SELECT);
gSprites[gBattlerSpriteIds[gMultiUsePlayerCursor]].callback = SpriteCb_HideAsMoveTarget;
if (gBattleMoves[move].target == (MOVE_TARGET_USER | MOVE_TARGET_ALLY))
if (moveTarget == (MOVE_TARGET_USER | MOVE_TARGET_ALLY))
{
gMultiUsePlayerCursor ^= BIT_FLANK;
}
@ -478,7 +479,7 @@ static void HandleInputChooseTarget(void)
case B_POSITION_PLAYER_RIGHT:
if (gActiveBattler != gMultiUsePlayerCursor)
i++;
else if (gBattleMoves[move].target & MOVE_TARGET_USER_OR_SELECTED)
else if (moveTarget & MOVE_TARGET_USER_OR_SELECTED)
i++;
break;
case B_POSITION_OPPONENT_LEFT:
@ -609,16 +610,9 @@ static void HandleInputChooseMove(void)
else
moveTarget = MOVE_TARGET_SELECTED;
}
else if (moveInfo->moves[gMoveSelectionCursor[gActiveBattler]] == MOVE_EXPANDING_FORCE)
{
if (IsBattlerTerrainAffected(gActiveBattler, STATUS_FIELD_PSYCHIC_TERRAIN))
moveTarget = MOVE_TARGET_BOTH;
else
moveTarget = MOVE_TARGET_SELECTED;
}
else
{
moveTarget = gBattleMoves[moveInfo->moves[gMoveSelectionCursor[gActiveBattler]]].target;
moveTarget = GetMoveTargetType(gActiveBattler, moveInfo->moves[gMoveSelectionCursor[gActiveBattler]]);
}
if (moveTarget & MOVE_TARGET_USER)

View File

@ -1526,9 +1526,9 @@ static void PlayerPartnerHandleChooseMove(void)
BattleAI_SetupAIData(0xF);
chosenMoveId = BattleAI_ChooseMoveOrAction();
if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & (MOVE_TARGET_USER | MOVE_TARGET_USER_OR_SELECTED))
if (GetMoveTargetType(gActiveBattler, moveInfo->moves[chosenMoveId]) & (MOVE_TARGET_USER_OR_SELECTED | MOVE_TARGET_USER))
gBattlerTarget = gActiveBattler;
if (gBattleMoves[moveInfo->moves[chosenMoveId]].target & MOVE_TARGET_BOTH)
if (GetMoveTargetType(gActiveBattler, moveInfo->moves[chosenMoveId]) & MOVE_TARGET_BOTH)
{
gBattlerTarget = GetBattlerAtPosition(B_POSITION_OPPONENT_LEFT);
if (gAbsentBattlerFlags & gBitTable[gBattlerTarget])

View File

@ -38,7 +38,7 @@ extern const struct CompressedSpriteSheet gSpriteSheet_EnemyShadow;
extern const struct SpriteTemplate gSpriteTemplate_EnemyShadow;
// this file's functions
static u8 GetBattlePalaceMoveGroup(u16 move);
static u8 GetBattlePalaceMoveGroup(u8 battlerId, u16 move);
static u16 GetBattlePalaceTarget(void);
static void SpriteCB_TrainerSlideVertical(struct Sprite *sprite);
static bool8 ShouldAnimBeDoneRegardlessOfSubstitute(u8 animId);
@ -155,7 +155,7 @@ u16 ChooseMoveAndTargetInBattlePalace(void)
{
if (moveInfo->moves[i] == MOVE_NONE)
break;
if (selectedGroup == GetBattlePalaceMoveGroup(moveInfo->moves[i]) && moveInfo->currentPp[i] != 0)
if (selectedGroup == GetBattlePalaceMoveGroup(gActiveBattler, moveInfo->moves[i]) && moveInfo->currentPp[i] != 0)
selectedMoves |= gBitTable[i];
}
@ -181,11 +181,11 @@ u16 ChooseMoveAndTargetInBattlePalace(void)
{
// validMoveFlags is used here as a bitfield for which moves can be used for each move group type
// first 4 bits are for attack (1 for each move), then 4 bits for defense, and 4 for support
if (GetBattlePalaceMoveGroup(moveInfo->moves[i]) == PALACE_MOVE_GROUP_ATTACK && !(gBitTable[i] & unusableMovesBits))
if (GetBattlePalaceMoveGroup(gActiveBattler, moveInfo->moves[i]) == PALACE_MOVE_GROUP_ATTACK && !(gBitTable[i] & unusableMovesBits))
validMoveFlags += (1 << 0);
if (GetBattlePalaceMoveGroup(moveInfo->moves[i]) == PALACE_MOVE_GROUP_DEFENSE && !(gBitTable[i] & unusableMovesBits))
if (GetBattlePalaceMoveGroup(gActiveBattler, moveInfo->moves[i]) == PALACE_MOVE_GROUP_DEFENSE && !(gBitTable[i] & unusableMovesBits))
validMoveFlags += (1 << 4);
if (GetBattlePalaceMoveGroup(moveInfo->moves[i]) == PALACE_MOVE_GROUP_SUPPORT && !(gBitTable[i] & unusableMovesBits))
if (GetBattlePalaceMoveGroup(gActiveBattler, moveInfo->moves[i]) == PALACE_MOVE_GROUP_SUPPORT && !(gBitTable[i] & unusableMovesBits))
validMoveFlags += (1 << 8);
}
@ -222,7 +222,7 @@ u16 ChooseMoveAndTargetInBattlePalace(void)
do
{
i = Random() % MAX_MON_MOVES;
if (!(gBitTable[i] & unusableMovesBits) && validMoveGroup == GetBattlePalaceMoveGroup(moveInfo->moves[i]))
if (!(gBitTable[i] & unusableMovesBits) && validMoveGroup == GetBattlePalaceMoveGroup(gActiveBattler, moveInfo->moves[i]))
chosenMoveId = i;
} while (chosenMoveId == -1);
}
@ -251,7 +251,7 @@ u16 ChooseMoveAndTargetInBattlePalace(void)
}
else
{
moveTarget = gBattleMoves[moveInfo->moves[chosenMoveId]].target;
moveTarget = GetMoveTargetType(gActiveBattler, moveInfo->moves[chosenMoveId]);
}
if (moveTarget & MOVE_TARGET_USER)
@ -273,9 +273,9 @@ u16 ChooseMoveAndTargetInBattlePalace(void)
#undef numValidMoveGroups
#undef validMoveGroup
static u8 GetBattlePalaceMoveGroup(u16 move)
static u8 GetBattlePalaceMoveGroup(u8 battlerId, u16 move)
{
switch (gBattleMoves[move].target)
switch (GetMoveTargetType(battlerId, move))
{
case MOVE_TARGET_SELECTED:
case MOVE_TARGET_USER_OR_SELECTED:

View File

@ -1313,12 +1313,12 @@ static const u8 sBattlePalaceNatureToFlavorTextId[NUM_NATURES] =
[NATURE_QUIRKY] = B_MSG_EAGER_FOR_MORE,
};
static bool32 NoTargetPresent(u32 move)
static bool32 NoTargetPresent(u8 battlerId, u32 move)
{
if (!IsBattlerAlive(gBattlerTarget))
gBattlerTarget = GetMoveTarget(move, 0);
switch (gBattleMoves[move].target)
switch (GetMoveTargetType(battlerId, move))
{
case MOVE_TARGET_SELECTED:
case MOVE_TARGET_DEPENDS:
@ -1459,7 +1459,7 @@ static void Cmd_attackcanceler(void)
}
gHitMarker |= HITMARKER_OBEYS;
if (NoTargetPresent(gCurrentMove) && (!IsTwoTurnsMove(gCurrentMove) || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)))
if (NoTargetPresent(gBattlerAttacker, gCurrentMove) && (!IsTwoTurnsMove(gCurrentMove) || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)))
{
gBattlescriptCurrInstr = BattleScript_ButItFailedAtkStringPpReduce;
if (!IsTwoTurnsMove(gCurrentMove) || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS))
@ -1741,6 +1741,7 @@ u32 GetTotalAccuracy(u32 battlerAtk, u32 battlerDef, u32 move)
static void Cmd_accuracycheck(void)
{
u16 type, move = T2_READ_16(gBattlescriptCurrInstr + 5);
u16 moveTarget = GetMoveTargetType(gBattlerAttacker, move);
if (move == ACC_CURR_MOVE)
move = gCurrentMove;
@ -1770,7 +1771,7 @@ static void Cmd_accuracycheck(void)
gBattleStruct->blunderPolicy = TRUE; // Only activates from missing through acc/evasion checks
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE &&
(gBattleMoves[move].target == MOVE_TARGET_BOTH || gBattleMoves[move].target == MOVE_TARGET_FOES_AND_ALLY))
(moveTarget == MOVE_TARGET_BOTH || moveTarget == MOVE_TARGET_FOES_AND_ALLY))
gBattleCommunication[MISS_TYPE] = B_MSG_AVOIDED_ATK;
else
gBattleCommunication[MISS_TYPE] = B_MSG_MISSED;
@ -1804,7 +1805,7 @@ static void Cmd_ppreduce(void)
if (!gSpecialStatuses[gBattlerAttacker].ppNotAffectedByPressure)
{
switch (gBattleMoves[gCurrentMove].target)
switch (GetMoveTargetType(gBattlerAttacker, gCurrentMove))
{
case MOVE_TARGET_FOES_AND_ALLY:
for (i = 0; i < gBattlersCount; i++)
@ -2103,6 +2104,8 @@ static void Cmd_multihitresultmessage(void)
static void Cmd_attackanimation(void)
{
u16 moveTarget = GetMoveTargetType(gBattlerAttacker, gCurrentMove);
if (gBattleControllerExecFlags)
return;
@ -2119,9 +2122,9 @@ static void Cmd_attackanimation(void)
}
else
{
if ((gBattleMoves[gCurrentMove].target & MOVE_TARGET_BOTH
|| gBattleMoves[gCurrentMove].target & MOVE_TARGET_FOES_AND_ALLY
|| gBattleMoves[gCurrentMove].target & MOVE_TARGET_DEPENDS)
if ((moveTarget & MOVE_TARGET_BOTH
|| moveTarget & MOVE_TARGET_FOES_AND_ALLY
|| moveTarget & MOVE_TARGET_DEPENDS)
&& gBattleScripting.animTargetsHit)
{
gBattlescriptCurrInstr++;
@ -5237,6 +5240,8 @@ static void Cmd_moveend(void)
gBattleScripting.moveendState++;
break;
case MOVEEND_NEXT_TARGET: // For moves hitting two opposing Pokemon.
{
u16 moveTarget = GetMoveTargetType(gBattlerAttacker, gCurrentMove);
// Set a flag if move hits either target (for throat spray that can't check damage)
if (!(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
&& !(gMoveResultFlags & MOVE_RESULT_NO_EFFECT))
@ -5245,14 +5250,13 @@ static void Cmd_moveend(void)
if (!(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
&& gBattleTypeFlags & BATTLE_TYPE_DOUBLE
&& !gProtectStructs[gBattlerAttacker].chargingTurn
&& (gBattleMoves[gCurrentMove].target == MOVE_TARGET_BOTH
|| gBattleMoves[gCurrentMove].target == MOVE_TARGET_FOES_AND_ALLY
|| (gBattleMoves[gCurrentMove].effect == EFFECT_EXPANDING_FORCE && IsBattlerTerrainAffected(gBattlerAttacker, STATUS_FIELD_PSYCHIC_TERRAIN)))
&& (moveTarget == MOVE_TARGET_BOTH
|| moveTarget == MOVE_TARGET_FOES_AND_ALLY)
&& !(gHitMarker & HITMARKER_NO_ATTACKSTRING))
{
u8 battlerId;
if (gBattleMoves[gCurrentMove].target == MOVE_TARGET_FOES_AND_ALLY)
if (moveTarget == MOVE_TARGET_FOES_AND_ALLY)
{
gHitMarker |= HITMARKER_NO_PPDEDUCT;
for (battlerId = gBattlerTarget + 1; battlerId < gBattlersCount; battlerId++)
@ -5288,6 +5292,7 @@ static void Cmd_moveend(void)
RecordLastUsedMoveBy(gBattlerAttacker, gCurrentMove);
gBattleScripting.moveendState++;
break;
}
case MOVEEND_EJECT_BUTTON:
if (gCurrentMove != MOVE_DRAGON_TAIL
&& gCurrentMove != MOVE_CIRCLE_THROW
@ -9506,7 +9511,7 @@ static void Cmd_jumpifnexttargetvalid(void)
for (gBattlerTarget++; gBattlerTarget < gBattlersCount; gBattlerTarget++)
{
if (gBattlerTarget == gBattlerAttacker && !(gBattleMoves[gCurrentMove].target & MOVE_TARGET_USER))
if (gBattlerTarget == gBattlerAttacker && !(GetMoveTargetType(gBattlerAttacker, gCurrentMove) & MOVE_TARGET_USER))
continue;
if (IsBattlerAlive(gBattlerTarget))
break;
@ -12000,7 +12005,7 @@ static void Cmd_selectfirstvalidtarget(void)
{
for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++)
{
if (gBattlerTarget == gBattlerAttacker && !(gBattleMoves[gCurrentMove].target & MOVE_TARGET_USER))
if (gBattlerTarget == gBattlerAttacker && !(GetMoveTargetType(gBattlerAttacker, gCurrentMove) & MOVE_TARGET_USER))
continue;
if (IsBattlerAlive(gBattlerTarget))
break;

View File

@ -237,6 +237,7 @@ bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move)
void HandleAction_UseMove(void)
{
u32 i, side, moveType, var = 4;
u16 moveTarget = GetMoveTargetType(gBattlerAttacker, gCurrentMove);
gBattlerAttacker = gBattlerByTurnOrder[gCurrentTurnActionNumber];
if (gBattleStruct->field_91 & gBitTable[gBattlerAttacker] || !IsBattlerAlive(gBattlerAttacker))
@ -309,14 +310,14 @@ void HandleAction_UseMove(void)
// choose target
side = GetBattlerSide(gBattlerAttacker) ^ BIT_SIDE;
if (IsAffectedByFollowMe(gBattlerAttacker, side, gCurrentMove)
&& gBattleMoves[gCurrentMove].target == MOVE_TARGET_SELECTED
&& moveTarget == MOVE_TARGET_SELECTED
&& GetBattlerSide(gBattlerAttacker) != GetBattlerSide(gSideTimers[side].followmeTarget))
{
gBattlerTarget = gSideTimers[side].followmeTarget;
}
else if ((gBattleTypeFlags & BATTLE_TYPE_DOUBLE)
&& gSideTimers[side].followmeTimer == 0
&& (gBattleMoves[gCurrentMove].power != 0 || gBattleMoves[gCurrentMove].target != MOVE_TARGET_USER)
&& (gBattleMoves[gCurrentMove].power != 0 || moveTarget != MOVE_TARGET_USER)
&& ((GetBattlerAbility(*(gBattleStruct->moveTarget + gBattlerAttacker)) != ABILITY_LIGHTNING_ROD && moveType == TYPE_ELECTRIC)
|| (GetBattlerAbility(*(gBattleStruct->moveTarget + gBattlerAttacker)) != ABILITY_STORM_DRAIN && moveType == TYPE_WATER)))
{
@ -337,7 +338,7 @@ void HandleAction_UseMove(void)
}
if (var == 4)
{
if (gBattleMoves[gChosenMove].target & MOVE_TARGET_RANDOM)
if (moveTarget & MOVE_TARGET_RANDOM)
{
if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER)
{
@ -354,7 +355,7 @@ void HandleAction_UseMove(void)
gBattlerTarget = GetBattlerAtPosition(B_POSITION_PLAYER_RIGHT);
}
}
else if (gBattleMoves[gChosenMove].target & MOVE_TARGET_FOES_AND_ALLY)
else if (moveTarget & MOVE_TARGET_FOES_AND_ALLY)
{
for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++)
{
@ -395,7 +396,7 @@ void HandleAction_UseMove(void)
}
}
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
&& gBattleMoves[gChosenMove].target & MOVE_TARGET_RANDOM)
&& moveTarget & MOVE_TARGET_RANDOM)
{
if (GetBattlerSide(gBattlerAttacker) == B_SIDE_PLAYER)
{
@ -418,7 +419,7 @@ void HandleAction_UseMove(void)
gBattlerTarget = GetBattlerAtPosition(GetBattlerPosition(gBattlerTarget) ^ BIT_FLANK);
}
}
else if (gBattleMoves[gChosenMove].target == MOVE_TARGET_ALLY)
else if (moveTarget == MOVE_TARGET_ALLY)
{
if (IsBattlerAlive(BATTLE_PARTNER(gBattlerAttacker)))
gBattlerTarget = BATTLE_PARTNER(gBattlerAttacker);
@ -426,7 +427,7 @@ void HandleAction_UseMove(void)
gBattlerTarget = gBattlerAttacker;
}
else if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
&& gBattleMoves[gChosenMove].target == MOVE_TARGET_FOES_AND_ALLY)
&& moveTarget == MOVE_TARGET_FOES_AND_ALLY)
{
for (gBattlerTarget = 0; gBattlerTarget < gBattlersCount; gBattlerTarget++)
{
@ -4668,7 +4669,10 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
}
break;
case ABILITYEFFECT_MOVES_BLOCK: // 2
if ((gLastUsedAbility == ABILITY_SOUNDPROOF && gBattleMoves[move].flags & FLAG_SOUND && !(gBattleMoves[move].target & MOVE_TARGET_USER))
{
u16 moveTarget = GetMoveTargetType(battler, move);
if ((gLastUsedAbility == ABILITY_SOUNDPROOF && gBattleMoves[move].flags & FLAG_SOUND && !(moveTarget & MOVE_TARGET_USER))
|| (gLastUsedAbility == ABILITY_BULLETPROOF && gBattleMoves[move].flags & FLAG_BALLISTIC))
{
if (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)
@ -4691,14 +4695,14 @@ u8 AbilityBattleEffects(u8 caseID, u8 battler, u16 ability, u8 special, u16 move
else if (BlocksPrankster(move, gBattlerAttacker, gBattlerTarget, TRUE)
&& !(IS_MOVE_STATUS(move) && GetBattlerAbility(gBattlerTarget) == ABILITY_MAGIC_BOUNCE))
{
if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE) || !(gBattleMoves[move].target & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY)))
if (!(gBattleTypeFlags & BATTLE_TYPE_DOUBLE) || !(moveTarget & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY)))
CancelMultiTurnMoves(gBattlerAttacker); // Don't cancel moves that can hit two targets bc one target might not be protected
gBattleScripting.battler = gBattlerAbility = gBattlerTarget;
gBattlescriptCurrInstr = BattleScript_DarkTypePreventsPrankster;
effect = 1;
}
break;
}
case ABILITYEFFECT_ABSORBING: // 3
if (move != MOVE_NONE)
{
@ -7257,15 +7261,12 @@ u32 GetMoveTarget(u16 move, u8 setTarget)
if (setTarget)
moveTarget = setTarget - 1;
else
moveTarget = gBattleMoves[move].target;
moveTarget = GetMoveTargetType(gBattlerAttacker, move);
// Special cases
if (move == MOVE_CURSE && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST))
moveTarget = MOVE_TARGET_USER;
if (gBattleMoves[move].effect == EFFECT_EXPANDING_FORCE && IsBattlerTerrainAffected(gBattlerAttacker, STATUS_FIELD_PSYCHIC_TERRAIN))
moveTarget = MOVE_TARGET_BOTH;
switch (moveTarget)
{
case MOVE_TARGET_SELECTED:
@ -7528,7 +7529,7 @@ bool32 IsBattlerProtected(u8 battlerId, u16 move)
else if (gProtectStructs[battlerId].protected)
return TRUE;
else if (gSideStatuses[GetBattlerSide(battlerId)] & SIDE_STATUS_WIDE_GUARD
&& gBattleMoves[move].target & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY))
&& GetMoveTargetType(gBattlerAttacker, move) & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY))
return TRUE;
else if (gProtectStructs[battlerId].banefulBunkered)
return TRUE;
@ -7654,7 +7655,7 @@ u32 CountBattlerStatIncreases(u8 battlerId, bool32 countEvasionAcc)
u32 GetMoveTargetCount(u16 move, u8 battlerAtk, u8 battlerDef)
{
switch (gBattleMoves[move].target)
switch (GetMoveTargetType(gBattlerAttacker, move))
{
case MOVE_TARGET_BOTH:
return IsBattlerAlive(battlerDef)
@ -9818,7 +9819,7 @@ bool32 BlocksPrankster(u16 move, u8 battlerPrankster, u8 battlerDef, bool32 chec
return FALSE;
if (GetBattlerSide(battlerPrankster) == GetBattlerSide(battlerDef))
return FALSE;
if (checkTarget && (gBattleMoves[move].target & (MOVE_TARGET_OPPONENTS_FIELD | MOVE_TARGET_DEPENDS)))
if (checkTarget && (GetMoveTargetType(battlerPrankster, move) & (MOVE_TARGET_OPPONENTS_FIELD | MOVE_TARGET_DEPENDS)))
return FALSE;
if (!IS_BATTLER_OF_TYPE(battlerDef, TYPE_DARK))
return FALSE;
@ -9850,3 +9851,16 @@ bool32 IsBattlerWeatherAffected(u8 battlerId, u32 weatherFlags)
}
return FALSE;
}
// Gets move target before redirection effects etc. are applied
// Possible return values are defined in battle.h following MOVE_TARGET_SELECTED
u32 GetMoveTargetType(u8 battlerId, u16 move)
{
u32 target;
if (gBattleMoves[move].effect == EFFECT_EXPANDING_FORCE
&& IsBattlerTerrainAffected(gActiveBattler, STATUS_FIELD_PSYCHIC_TERRAIN))
return MOVE_TARGET_BOTH;
else
return gBattleMoves[move].target;
}

View File

@ -5388,7 +5388,7 @@ static void SetBattleTargetSpritePosition(void)
static void SetMoveTargetPosition(u16 move)
{
switch (gBattleMoves[move].target)
switch (GetMoveTargetType(gBattlerAttacker, move))
{
case MOVE_TARGET_USER_OR_SELECTED:
case MOVE_TARGET_USER: