Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Increase score of secondary effects only at 100% chance #3583

Merged
merged 3 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion data/battle_scripts_1.s
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ gBattleScriptsForMoveEffects::
.4byte BattleScript_EffectHit @ EFFECT_IVY_CUDGEL
.4byte BattleScript_EffectMaxMove @ EFFECT_MAX_MOVE
.4byte BattleScript_EffectGlaiveRush @ EFFECT_GLAIVE_RUSH
.4byte BattleScript_EffectBrickBreak @ EFFECT_RAGING_BULL
.4byte BattleScript_EffectBrickBreak @ EFFECT_RAGING_BULL

BattleScript_EffectGlaiveRush::
call BattleScript_EffectHit_Ret
Expand Down
1 change: 1 addition & 0 deletions include/battle_ai_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ bool32 AI_IsAbilityOnSide(u32 battlerId, u32 ability);
bool32 AI_MoveMakesContact(u32 ability, u32 holdEffect, u32 move);
u32 AI_GetBattlerMoveTargetType(u32 battlerId, u32 move);
bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, u32 chosenMove);
u32 AI_CalcSecondaryEffectChance(u32 battler, u32 secondaryEffectChance);

// stat stage checks
bool32 AnyStatIsRaised(u32 battlerId);
Expand Down
23 changes: 8 additions & 15 deletions src/battle_ai_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -3211,13 +3211,12 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
struct AiLogicData *aiData = AI_DATA;
u32 movesetIndex = AI_THINKING_STRUCT->movesetIndex;
u32 effectiveness = aiData->effectiveness[battlerAtk][battlerDef][movesetIndex];
u32 secondaryEffectChance = AI_CalcSecondaryEffectChance(battlerAtk, gBattleMoves[move].secondaryEffectChance);
s8 atkPriority = GetMovePriority(battlerAtk, move);
u32 predictedMove = aiData->predictedMoves[battlerDef];
u32 predictedMoveSlot = GetMoveSlot(GetMovesArray(battlerDef), predictedMove);
bool32 isDoubleBattle = IsValidDoubleBattle(battlerAtk);
u32 i;
// We only check for moves that have a 20% chance or more for their secondary effect to happen because moves with a smaller chance are rather worthless. We don't want the AI to use those.
bool32 sereneGraceBoost = (aiData->abilities[battlerAtk] == ABILITY_SERENE_GRACE && (gBattleMoves[move].secondaryEffectChance >= 20 && gBattleMoves[move].secondaryEffectChance < 100));

// The AI should understand that while Dynamaxed, status moves function like Protect.
if (IsDynamaxed(battlerAtk) && gBattleMoves[move].split == SPLIT_STATUS)
Expand Down Expand Up @@ -3646,24 +3645,18 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
case EFFECT_PARALYZE:
IncreaseParalyzeScore(battlerAtk, battlerDef, move, &score);
break;
case EFFECT_SPEED_DOWN_HIT:
if (!ShouldLowerSpeed(battlerAtk, battlerDef, aiData->abilities[battlerDef]))
break;
case EFFECT_ATTACK_DOWN_HIT:
case EFFECT_DEFENSE_DOWN_HIT:
case EFFECT_SPECIAL_ATTACK_DOWN_HIT:
case EFFECT_SPECIAL_DEFENSE_DOWN_HIT:
case EFFECT_ACCURACY_DOWN_HIT:
case EFFECT_EVASION_DOWN_HIT:
if (sereneGraceBoost && aiData->abilities[battlerDef] != ABILITY_CONTRARY)
if (secondaryEffectChance >= 100 && aiData->abilities[battlerDef] != ABILITY_CONTRARY)
ADJUST_SCORE(2);
break;
case EFFECT_SPEED_DOWN_HIT:
if (ShouldLowerSpeed(battlerAtk, battlerDef, aiData->abilities[battlerDef]))
{
if (sereneGraceBoost && aiData->abilities[battlerDef] != ABILITY_CONTRARY)
ADJUST_SCORE(5);
else
ADJUST_SCORE(2);
}
break;
case EFFECT_SUBSTITUTE:
if (gStatuses3[battlerDef] & STATUS3_PERISH_SONG)
ADJUST_SCORE(3);
Expand Down Expand Up @@ -3789,7 +3782,7 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
ADJUST_SCORE(1);
break;
case EFFECT_SPEED_UP_HIT:
if (sereneGraceBoost && aiData->abilities[battlerDef] != ABILITY_CONTRARY && !AI_STRIKES_FIRST(battlerAtk, battlerDef, move))
if (secondaryEffectChance >= 100 && aiData->abilities[battlerDef] != ABILITY_CONTRARY && !AI_STRIKES_FIRST(battlerAtk, battlerDef, move))
ADJUST_SCORE(3);
break;
case EFFECT_DESTINY_BOND:
Expand Down Expand Up @@ -4048,11 +4041,11 @@ static s32 AI_CheckViability(u32 battlerAtk, u32 battlerDef, u32 move, s32 score
}
break;
case EFFECT_ATTACK_UP_HIT:
if (sereneGraceBoost)
if (secondaryEffectChance >= 100)
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_ATK, &score);
break;
case EFFECT_SPECIAL_ATTACK_UP_HIT:
if (sereneGraceBoost)
if (secondaryEffectChance >= 100)
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_SPATK, &score);
break;
case EFFECT_FELL_STINGER:
Expand Down
8 changes: 8 additions & 0 deletions src/battle_ai_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -3806,3 +3806,11 @@ bool32 AI_IsBattlerAsleepOrComatose(u32 battlerId)
{
return (gBattleMons[battlerId].status1 & STATUS1_SLEEP) || AI_DATA->abilities[battlerId] == ABILITY_COMATOSE;
}

u32 AI_CalcSecondaryEffectChance(u32 battler, u32 secondaryEffectChance)
{
if (AI_DATA->abilities[battler] == ABILITY_SERENE_GRACE)
secondaryEffectChance *= 2;

return secondaryEffectChance;
}
24 changes: 24 additions & 0 deletions test/battle/ai_check_viability.c
Original file line number Diff line number Diff line change
Expand Up @@ -165,3 +165,27 @@ AI_SINGLE_BATTLE_TEST("AI can choose Counter or Mirror Coat if the predicted mov
MESSAGE("Foe Wobbuffet fainted!");
}
}

AI_SINGLE_BATTLE_TEST("AI chooses moves with secondary effect that have a 100% chance to trigger")
{
u16 ability;

PARAMETRIZE { ability = ABILITY_NONE; }
PARAMETRIZE { ability = ABILITY_SERENE_GRACE; }

GIVEN {
AI_LOG;
ASSUME(gBattleMoves[MOVE_SHADOW_BALL].effect == EFFECT_SPECIAL_DEFENSE_DOWN_HIT);
ASSUME(gBattleMoves[MOVE_SHADOW_BALL].secondaryEffectChance == 20);
ASSUME(gBattleMoves[MOVE_LUSTER_PURGE].effect == EFFECT_SPECIAL_DEFENSE_DOWN_HIT);
ASSUME(gBattleMoves[MOVE_LUSTER_PURGE].secondaryEffectChance == 50);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_REGICE);
OPPONENT(SPECIES_REGIROCK) { Ability(ability); Moves(MOVE_SHADOW_BALL, MOVE_LUSTER_PURGE); }
} WHEN {
if (ability == ABILITY_NONE)
TURN { EXPECT_MOVE(opponent, MOVE_SHADOW_BALL); }
else
TURN { EXPECT_MOVES(opponent, MOVE_LUSTER_PURGE); }
}
}
20 changes: 20 additions & 0 deletions test/battle/move_effect/relic_song.c
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,23 @@ SINGLE_BATTLE_TEST("Relic Song loses the form-changing effect with Sheer Force")
EXPECT_EQ(player->species, SPECIES_MELOETTA_ARIA);
}
}

SINGLE_BATTLE_TEST("Relic Song transforms Meloetta after Magician was activated")
{
GIVEN {
ASSUME(P_GEN_6_POKEMON == TRUE);
PLAYER(SPECIES_MELOETTA_ARIA);
OPPONENT(SPECIES_DELPHOX) { Ability(ABILITY_MAGICIAN); Item(ITEM_POTION); }
} WHEN {
TURN { MOVE(opponent, MOVE_SKILL_SWAP); MOVE(player, MOVE_RELIC_SONG); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SKILL_SWAP, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_RELIC_SONG, player);
HP_BAR(opponent);
ABILITY_POPUP(player, ABILITY_MAGICIAN);
MESSAGE("Meloetta stole Foe Delphox's Potion!");
MESSAGE("Meloetta transformed!");
} THEN {
EXPECT_EQ(player->species, SPECIES_MELOETTA_PIROUETTE);
}
}
Loading