Adds smart target type (#8639)

This commit is contained in:
Alex 2025-12-30 19:16:03 +01:00 committed by GitHub
parent f10d722af7
commit 5774dde7ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 50 additions and 43 deletions

View File

@ -696,6 +696,7 @@ enum MoveTarget
{
TARGET_NONE,
TARGET_SELECTED,
TARGET_SMART, // Like target select but can also smartly redirect to partner. Works only with strikeCount > 1 moves
TARGET_DEPENDS,
TARGET_OPPONENT,
TARGET_RANDOM,

View File

@ -333,7 +333,6 @@ enum __attribute__((packed)) BattleMoveEffects
EFFECT_SPICY_EXTRACT,
EFFECT_TERA_BLAST,
EFFECT_TERA_STARSTORM,
EFFECT_DRAGON_DARTS,
EFFECT_SHELL_SIDE_ARM,
EFFECT_RAPID_SPIN,
EFFECT_SPECTRAL_THIEF,

View File

@ -246,7 +246,10 @@ static inline u32 GetMoveAccuracy(u32 moveId)
static inline u32 GetMoveTarget(u32 moveId)
{
return gMovesInfo[SanitizeMoveId(moveId)].target;
moveId = SanitizeMoveId(moveId);
u32 target = gMovesInfo[moveId].target;
assertf(target != TARGET_SMART || gMovesInfo[moveId].strikeCount > 1, "Smart target requires strikeCount > 1: %S", gMovesInfo[moveId].name);
return target;
}
static inline u32 GetMovePP(u32 moveId)

View File

@ -3556,7 +3556,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(DECENT_EFFECT);
}
if (GetMoveStrikeCount(move) > 1 && effect != EFFECT_DRAGON_DARTS)
if (GetMoveStrikeCount(move) > 1 && moveTarget != TARGET_SMART)
{
ADJUST_SCORE(DECENT_EFFECT);
}
@ -3591,7 +3591,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(DECENT_EFFECT);
}
if (GetMoveStrikeCount(move) > 1 && effect != EFFECT_DRAGON_DARTS)
if (GetMoveStrikeCount(move) > 1 && moveTarget != TARGET_SMART)
{
ADJUST_SCORE(WEAK_EFFECT);
}
@ -3649,7 +3649,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
ADJUST_SCORE(GOOD_EFFECT);
}
if (GetMoveStrikeCount(move) > 1 && effect != EFFECT_DRAGON_DARTS)
if (GetMoveStrikeCount(move) > 1 && moveTarget != TARGET_SMART)
{
ADJUST_SCORE(WEAK_EFFECT);
}

View File

@ -1478,10 +1478,9 @@ s32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler, u32 aiMoveConsidered, u32 pla
bool32 CanEndureHit(u32 battler, u32 battlerTarget, u32 move)
{
enum BattleMoveEffects effect = GetMoveEffect(move);
if (!AI_BattlerAtMaxHp(battlerTarget) || IsMultiHitMove(move) || gAiLogicData->abilities[battler] == ABILITY_PARENTAL_BOND)
return FALSE;
if (GetMoveStrikeCount(move) > 1 && !(effect == EFFECT_DRAGON_DARTS && !HasTwoOpponents(battler)))
if (GetMoveStrikeCount(move) > 1 && !(AI_GetBattlerMoveTargetType(battler, move) == TARGET_SMART && !HasTwoOpponents(battler)))
return FALSE;
if (gAiLogicData->holdEffects[battlerTarget] == HOLD_EFFECT_FOCUS_SASH)
return TRUE;
@ -6289,6 +6288,7 @@ bool32 ShouldInstructPartner(u32 partner, u32 move)
switch (type)
{
case TARGET_SELECTED:
case TARGET_SMART:
case TARGET_DEPENDS:
case TARGET_RANDOM:
case TARGET_BOTH:
@ -6311,6 +6311,7 @@ bool32 CanMoveBeBouncedBack(u32 battler, u32 move)
switch (type)
{
case TARGET_SELECTED:
case TARGET_SMART:
case TARGET_OPPONENTS_FIELD:
case TARGET_BOTH:
return TRUE;

View File

@ -2017,7 +2017,7 @@ static void PlayerHandleChooseAction(u32 battler)
u32 move = GetChosenMoveFromPosition(B_POSITION_PLAYER_RIGHT);
StringAppend(gStringVar1, GetMoveName(move));
enum MoveTarget moveTarget = GetBattlerMoveTargetType(B_POSITION_PLAYER_RIGHT, move);
if (moveTarget == TARGET_SELECTED)
if (moveTarget == TARGET_SELECTED || moveTarget == TARGET_SMART)
{
if (gAiBattleData->chosenTarget[B_POSITION_PLAYER_RIGHT] == B_POSITION_OPPONENT_LEFT)
StringAppend(gStringVar1, COMPOUND_STRING(" -{UP_ARROW}"));

View File

@ -300,7 +300,7 @@ u16 ChooseMoveAndTargetInBattlePalace(u32 battler)
if (moveTarget == TARGET_USER || moveTarget == TARGET_USER_OR_ALLY)
chosenMoveIndex |= (battler << 8);
else if (moveTarget == TARGET_SELECTED)
else if (moveTarget == TARGET_SELECTED || moveTarget == TARGET_SMART)
chosenMoveIndex |= GetBattlePalaceTarget(battler);
else
chosenMoveIndex |= (GetBattlerAtPosition(BATTLE_OPPOSITE(GetBattlerSide(battler))) << 8);
@ -322,6 +322,7 @@ static u8 GetBattlePalaceMoveGroup(u8 battler, u16 move)
switch (GetBattlerMoveTargetType(battler, move))
{
case TARGET_SELECTED:
case TARGET_SMART:
case TARGET_OPPONENT:
case TARGET_RANDOM:
case TARGET_BOTH:

View File

@ -783,8 +783,9 @@ static enum MoveEndResult MoveEnd_MultihitMove(void)
&& gMultiHitCounter
&& !(moveEffect == EFFECT_PRESENT && gBattleStruct->presentBasePower == 0)) // Parental Bond edge case
{
enum MoveTarget target = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
gMultiHitCounter--;
if (!IsBattlerAlive(gBattlerTarget) && moveEffect != EFFECT_DRAGON_DARTS)
if (!IsBattlerAlive(gBattlerTarget) && target != TARGET_SMART)
gMultiHitCounter = 0;
gBattleScripting.multihitString[4]++;
@ -798,7 +799,7 @@ static enum MoveEndResult MoveEnd_MultihitMove(void)
}
else
{
if (moveEffect == EFFECT_DRAGON_DARTS
if (target == TARGET_SMART
&& !IsAffectedByFollowMe(gBattlerAttacker, GetBattlerSide(gBattlerTarget), gCurrentMove)
&& !(gBattleStruct->moveResultFlags[BATTLE_PARTNER(gBattlerTarget)] & MOVE_RESULT_MISSED) // didn't miss the other target
&& CanTargetPartner(gBattlerAttacker, gBattlerTarget)

View File

@ -955,6 +955,7 @@ static bool32 NoTargetPresent(u8 battler, u32 move)
switch (GetBattlerMoveTargetType(battler, move))
{
case TARGET_SELECTED:
case TARGET_SMART:
case TARGET_DEPENDS:
case TARGET_RANDOM:
if (!IsBattlerAlive(gBattlerTarget))
@ -1388,7 +1389,7 @@ static void AccuracyCheck(bool32 recalcDragonDarts, const u8 *nextInstr, const u
if (holdEffectAtk == HOLD_EFFECT_BLUNDER_POLICY)
gBattleStruct->blunderPolicy = TRUE; // Only activates from missing through acc/evasion checks
if (effect == EFFECT_DRAGON_DARTS
if (moveTarget == TARGET_SMART
&& !IsAffectedByFollowMe(gBattlerAttacker, GetBattlerSide(battlerDef), gCurrentMove)
&& !recalcDragonDarts // So we don't jump back and forth between targets
&& CanTargetPartner(gBattlerAttacker, battlerDef)

View File

@ -331,17 +331,6 @@ static bool32 ShouldTeraShellDistortTypeMatchups(u32 move, u32 battlerDef, enum
return FALSE;
}
static inline bool32 IsDragonDartsSecondHit(u32 effect)
{
if (effect != EFFECT_DRAGON_DARTS)
return FALSE;
if (gMultiHitCounter == 1)
return TRUE;
return FALSE;
}
bool32 IsUnnerveBlocked(u32 battler, u32 itemId)
{
if (GetItemPocket(itemId) != POCKET_BERRIES)
@ -381,13 +370,24 @@ static bool32 IsUnnerveAbilityOnOpposingSide(u32 battler)
return FALSE;
}
static inline bool32 IsDragonDartsSecondHit(u32 battlerAtk, u32 move)
{
if (GetBattlerMoveTargetType(battlerAtk, move) != TARGET_SMART)
return FALSE;
if (gMultiHitCounter < GetMoveStrikeCount(move))
return TRUE;
return FALSE;
}
bool32 IsAffectedByFollowMe(u32 battlerAtk, u32 defSide, u32 move)
{
enum Ability ability = GetBattlerAbility(battlerAtk);
enum BattleMoveEffects effect = GetMoveEffect(move);
if (gSideTimers[defSide].followmeTimer == 0
|| (!IsBattlerAlive(gSideTimers[defSide].followmeTarget) && !IsDragonDartsSecondHit(effect))
|| (!IsBattlerAlive(gSideTimers[defSide].followmeTarget) && !IsDragonDartsSecondHit(battlerAtk, move))
|| effect == EFFECT_SNIPE_SHOT
|| effect == EFFECT_SKY_DROP
|| IsAbilityAndRecord(battlerAtk, ability, ABILITY_PROPELLER_TAIL)
@ -423,7 +423,7 @@ bool32 HandleMoveTargetRedirection(void)
}
if (IsAffectedByFollowMe(gBattlerAttacker, side, gCurrentMove)
&& (moveTarget == TARGET_SELECTED || moveEffect == EFFECT_REFLECT_DAMAGE)
&& (moveTarget == TARGET_SELECTED || moveTarget == TARGET_SMART || moveEffect == EFFECT_REFLECT_DAMAGE)
&& !IsBattlerAlly(gBattlerAttacker, gSideTimers[side].followmeTarget))
{
gBattleStruct->moveTarget[gBattlerAttacker] = gBattlerTarget = gSideTimers[side].followmeTarget; // follow me moxie fix
@ -2959,7 +2959,7 @@ static enum MoveCanceler CancelerMultihitMoves(struct BattleContext *ctx)
{
gMultiHitCounter = GetMoveStrikeCount(ctx->move);
if (GetMoveEffect(ctx->move) == EFFECT_DRAGON_DARTS
if (GetBattlerMoveTargetType(ctx->battlerAtk, ctx->move) == TARGET_SMART
&& !IsAffectedByFollowMe(ctx->battlerAtk, GetBattlerSide(ctx->battlerDef), ctx->move)
&& CanTargetPartner(ctx->battlerAtk, ctx->battlerDef)
&& TargetFullyImmuneToCurrMove(ctx->battlerAtk, ctx->battlerDef))
@ -11204,7 +11204,12 @@ void UpdateStallMons(void)
{
if (IsBattlerTurnDamaged(gBattlerTarget) || IsBattlerProtected(gBattlerAttacker, gBattlerTarget, gCurrentMove) || GetMoveCategory(gCurrentMove) == DAMAGE_CATEGORY_STATUS)
return;
if (!IsDoubleBattle() || GetMoveTarget(gCurrentMove) == TARGET_SELECTED)
enum MoveTarget target = GetBattlerMoveTargetType(gBattlerAttacker, gCurrentMove);
if (!IsDoubleBattle()
|| target == TARGET_SELECTED
|| target == TARGET_SMART)
{
enum Type moveType = GetBattleMoveType(gCurrentMove); // Probably doesn't handle dynamic move types right now
enum Ability abilityAtk = GetBattlerAbility(gBattlerAttacker);

View File

@ -5435,6 +5435,7 @@ static void SetMoveTargetPosition(u16 move)
break;
case TARGET_OPPONENT:
case TARGET_SELECTED:
case TARGET_SMART:
case TARGET_RANDOM:
case TARGET_BOTH:
case TARGET_FOES_AND_ALLY:

View File

@ -2239,12 +2239,6 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
.battleTvScore = 0, // TODO: Assign points
},
[EFFECT_DRAGON_DARTS] =
{
.battleScript = BattleScript_EffectHit,
.battleTvScore = 0, // TODO: Assign points
},
[EFFECT_SHELL_SIDE_ARM] =
{
.battleScript = BattleScript_EffectHit,

View File

@ -17875,12 +17875,12 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
.description = COMPOUND_STRING(
"The user attacks twice. Two\n"
"targets are hit once each."),
.effect = EFFECT_DRAGON_DARTS,
.effect = EFFECT_HIT,
.power = 50,
.type = TYPE_DRAGON,
.accuracy = 100,
.pp = 10,
.target = TARGET_SELECTED,
.target = TARGET_SMART,
.priority = 0,
.category = DAMAGE_CATEGORY_PHYSICAL,
.strikeCount = 2,

View File

@ -380,7 +380,7 @@ DOUBLE_BATTLE_TEST("Commander Tatsugiri does not attack if Dondozo faints the sa
DOUBLE_BATTLE_TEST("Commander Tatsugiri does not get hit by Dragon Darts when a commanded Dondozo faints")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_DRAGON_DARTS) == EFFECT_DRAGON_DARTS);
ASSUME(GetMoveTarget(MOVE_DRAGON_DARTS) == TARGET_SMART);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_DONDOZO) { HP(1); }
PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); }
@ -402,7 +402,7 @@ DOUBLE_BATTLE_TEST("Commander Tatsugiri does not get hit by Dragon Darts when co
PARAMETRIZE { targetPlayerRight = TRUE; }
PARAMETRIZE { targetPlayerRight = FALSE; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_DRAGON_DARTS) == EFFECT_DRAGON_DARTS);
ASSUME(GetMoveTarget(MOVE_DRAGON_DARTS) == TARGET_SMART);
PLAYER(SPECIES_TATSUGIRI) { Ability(ABILITY_COMMANDER); }
PLAYER(SPECIES_DONDOZO);
OPPONENT(SPECIES_WOBBUFFET);

View File

@ -378,7 +378,7 @@ DOUBLE_BATTLE_TEST("Neutralizing Gas is active for the duration of a Spread Move
DOUBLE_BATTLE_TEST("Neutralizing Gas is active until the last Dragon Darts hit even if Neutralizing Gas is no longer on the field")
{
GIVEN {
ASSUME(GetMoveEffect(MOVE_DRAGON_DARTS) == EFFECT_DRAGON_DARTS);
ASSUME(GetMoveTarget(MOVE_DRAGON_DARTS) == TARGET_SMART);
PLAYER(SPECIES_WEEZING) { HP(1); Ability(ABILITY_NEUTRALIZING_GAS); }
PLAYER(SPECIES_GOLEM) { HP(2); MaxHP(2); Ability(ABILITY_STURDY); }
OPPONENT(SPECIES_BASCULEGION) { Ability(ABILITY_MOLD_BREAKER); }

View File

@ -121,8 +121,8 @@ static u32 GetParametrizedLevel(u32 move, u32 variation)
static bool32 GetParametrizedShinyness(u32 move, u32 variation)
{
if ((gMovesInfo[move].effect == EFFECT_DRAGON_DARTS && variation == 2)
|| (move == MOVE_SYRUP_BOMB && variation == 1)
if ((GetMoveAnimationScript(move) == gBattleAnimMove_DragonDarts && variation == 2)
|| (GetMoveAnimationScript(move) == gBattleAnimMove_SyrupBomb && variation == 1)
)
return TRUE;
return FALSE;
@ -194,7 +194,7 @@ static u32 GetVariationsNumber(u32 move, bool8 isDouble)
variationsNumber = 4;
else if (gMovesInfo[move].effect == EFFECT_SPIT_UP
|| gMovesInfo[move].effect == EFFECT_SWALLOW
|| gMovesInfo[move].effect == EFFECT_DRAGON_DARTS
|| GetMoveAnimationScript(move) == gBattleAnimMove_DragonDarts
|| move == MOVE_SEISMIC_TOSS)
variationsNumber = 3;
else if (gMovesInfo[move].effect == EFFECT_CURSE

View File

@ -3,7 +3,7 @@
ASSUMPTIONS
{
ASSUME(GetMoveEffect(MOVE_DRAGON_DARTS) == EFFECT_DRAGON_DARTS);
ASSUME(GetMoveTarget(MOVE_DRAGON_DARTS) == TARGET_SMART);
}
SINGLE_BATTLE_TEST("Dragon Darts strikes twice")

View File

@ -2576,7 +2576,7 @@ s32 MoveGetTarget(s32 battlerId, u32 moveId, struct MoveContext *ctx, u32 source
{
target = BATTLE_OPPOSITE(battlerId);
}
else if (moveTarget == TARGET_SELECTED || moveTarget == TARGET_OPPONENT)
else if (moveTarget == TARGET_SELECTED || moveTarget == TARGET_SMART || moveTarget == TARGET_OPPONENT)
{
// In AI Doubles not specified target allows any target for EXPECT_MOVE.
if (GetBattleTest()->type != BATTLE_TEST_AI_DOUBLES)