Improved logic for Guard Split and Power Split. (#7298)

This commit is contained in:
surskitty
2025-07-09 18:11:39 -04:00
committed by GitHub
parent d15c490223
commit 95a02dddb9
3 changed files with 155 additions and 26 deletions

View File

@@ -79,4 +79,10 @@
#define FRIENDLY_FIRE_NORMAL_THRESHOLD 3
#define FRIENDLY_FIRE_CONSERVATIVE_THRESHOLD 4
// AI's desired stat changes for Guard Split and Power Split, treated as %
#define GUARD_SPLIT_ALLY_PERCENTAGE 200
#define GUARD_SPLIT_ENEMY_PERCENTAGE 50
#define POWER_SPLIT_ALLY_PERCENTAGE 150
#define POWER_SPLIT_ENEMY_PERCENTAGE 50
#endif // GUARD_CONFIG_AI_H

View File

@@ -2455,11 +2455,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
}
break;
case EFFECT_POWER_SPLIT:
if (IsTargetingPartner(battlerAtk, battlerDef))
{
ADJUST_SCORE(-10);
}
else
if (!IsTargetingPartner(battlerAtk, battlerDef))
{
u32 atkAttack = gBattleMons[battlerAtk].attack;
u32 defAttack = gBattleMons[battlerDef].attack;
@@ -2472,11 +2468,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
}
break;
case EFFECT_GUARD_SPLIT:
if (IsTargetingPartner(battlerAtk, battlerDef))
{
ADJUST_SCORE(-10);
}
else
if (!IsTargetingPartner(battlerAtk, battlerDef))
{
u32 atkDefense = gBattleMons[battlerAtk].defense;
u32 defDefense = gBattleMons[battlerDef].defense;
@@ -3530,6 +3522,55 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
&& gBattleMons[battlerAtkPartner].hp < gBattleMons[battlerAtkPartner].maxHP / 2)
RETURN_SCORE_PLUS(WEAK_EFFECT);
break;
case EFFECT_SPEED_SWAP:
break;
case EFFECT_GUARD_SPLIT:
{
u32 atkDefense = gBattleMons[battlerAtk].defense;
u32 defDefense = gBattleMons[battlerDef].defense;
u32 atkSpDefense = gBattleMons[battlerAtk].spDefense;
u32 defSpDefense = gBattleMons[battlerDef].spDefense;
// It's actually * 100 and / 2
u32 newDefense = (atkDefense + defDefense) * 50;
u32 newSpDefense = (atkSpDefense + defSpDefense) * 50;
// We want to massively raise our defense.
if (newDefense > atkDefense * GUARD_SPLIT_ALLY_PERCENTAGE)
ADJUST_AND_RETURN_SCORE(GOOD_EFFECT);
if (newSpDefense > atkSpDefense * GUARD_SPLIT_ALLY_PERCENTAGE)
ADJUST_AND_RETURN_SCORE(GOOD_EFFECT);
if (newDefense > defDefense * GUARD_SPLIT_ALLY_PERCENTAGE)
ADJUST_AND_RETURN_SCORE(GOOD_EFFECT);
if (newSpDefense > defSpDefense * GUARD_SPLIT_ALLY_PERCENTAGE)
ADJUST_AND_RETURN_SCORE(GOOD_EFFECT);
ADJUST_SCORE(WORST_EFFECT);
break;
}
case EFFECT_POWER_SPLIT:
{
u32 atkAttack = gBattleMons[battlerAtk].attack;
u32 defAttack = gBattleMons[battlerDef].attack;
u32 atkSpAttack = gBattleMons[battlerAtk].spAttack;
u32 defSpAttack = gBattleMons[battlerDef].spAttack;
// * 100 and / 2
u32 newAttack = (atkAttack + defAttack) * 50;
u32 newSpAtk = (atkSpAttack + defSpAttack) * 50;
if (HasMoveWithCategory(battlerAtk, DAMAGE_CATEGORY_PHYSICAL) && newAttack > atkAttack * POWER_SPLIT_ALLY_PERCENTAGE)
ADJUST_AND_RETURN_SCORE(GOOD_EFFECT);
if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL) && newAttack > defAttack * POWER_SPLIT_ALLY_PERCENTAGE)
ADJUST_AND_RETURN_SCORE(GOOD_EFFECT);
if (HasMoveWithCategory(battlerAtk, DAMAGE_CATEGORY_SPECIAL) && newSpAtk > atkSpAttack * POWER_SPLIT_ALLY_PERCENTAGE)
ADJUST_AND_RETURN_SCORE(GOOD_EFFECT);
if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL) && newSpAtk > defSpAttack * POWER_SPLIT_ALLY_PERCENTAGE)
ADJUST_AND_RETURN_SCORE(GOOD_EFFECT);
ADJUST_SCORE(WORST_EFFECT);
break;
}
default:
break;
} // attacker move effects
@@ -4721,26 +4762,54 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
if (gBattleMons[battlerDef].speed > gBattleMons[battlerAtk].speed)
ADJUST_SCORE(DECENT_EFFECT);
break;
case EFFECT_GUARD_SPLIT:
{
u32 newDefense = (gBattleMons[battlerAtk].defense + gBattleMons[battlerDef].defense) / 2;
u32 newSpDef = (gBattleMons[battlerAtk].spDefense + gBattleMons[battlerDef].spDefense) / 2;
case EFFECT_GUARD_SPLIT:
{
u32 atkDefense = gBattleMons[battlerAtk].defense;
u32 defDefense = gBattleMons[battlerDef].defense;
u32 atkSpDefense = gBattleMons[battlerAtk].spDefense;
u32 defSpDefense = gBattleMons[battlerDef].spDefense;
if ((newDefense > gBattleMons[battlerAtk].defense && newSpDef >= gBattleMons[battlerAtk].spDefense)
|| (newSpDef > gBattleMons[battlerAtk].spDefense && newDefense >= gBattleMons[battlerAtk].defense))
ADJUST_SCORE(DECENT_EFFECT);
}
// It's actually * 100 and / 2;
u32 newDefense = (atkDefense + defDefense) * 50;
u32 newSpDefense = (atkSpDefense + defSpDefense) * 50;
// We want to massively raise our defense.
if (newDefense > atkDefense * GUARD_SPLIT_ALLY_PERCENTAGE)
ADJUST_AND_RETURN_SCORE(GOOD_EFFECT);
if (newSpDefense > atkSpDefense * GUARD_SPLIT_ALLY_PERCENTAGE)
ADJUST_AND_RETURN_SCORE(GOOD_EFFECT);
// We also want to massively lower theirs.
if (newDefense < defDefense * GUARD_SPLIT_ENEMY_PERCENTAGE)
ADJUST_AND_RETURN_SCORE(GOOD_EFFECT);
if (newSpDefense < defSpDefense * GUARD_SPLIT_ENEMY_PERCENTAGE)
ADJUST_AND_RETURN_SCORE(GOOD_EFFECT);
ADJUST_SCORE(WORST_EFFECT);
break;
}
case EFFECT_POWER_SPLIT:
{
u32 newAttack = (gBattleMons[battlerAtk].attack + gBattleMons[battlerDef].attack) / 2;
u32 newSpAtk = (gBattleMons[battlerAtk].spAttack + gBattleMons[battlerDef].spAttack) / 2;
{
u32 atkAttack = gBattleMons[battlerAtk].attack;
u32 defAttack = gBattleMons[battlerDef].attack;
u32 atkSpAttack = gBattleMons[battlerAtk].spAttack;
u32 defSpAttack = gBattleMons[battlerDef].spAttack;
if ((newAttack > gBattleMons[battlerAtk].attack && newSpAtk >= gBattleMons[battlerAtk].spAttack)
|| (newSpAtk > gBattleMons[battlerAtk].spAttack && newAttack >= gBattleMons[battlerAtk].attack))
ADJUST_SCORE(DECENT_EFFECT);
}
break;
// It's actually * 100 and / 2
u32 newAttack = (atkAttack + defAttack) * 50;
u32 newSpAtk = (atkSpAttack + defSpAttack) * 50;
if (HasMoveWithCategory(battlerAtk, DAMAGE_CATEGORY_PHYSICAL) && newAttack > atkAttack * POWER_SPLIT_ALLY_PERCENTAGE)
ADJUST_AND_RETURN_SCORE(GOOD_EFFECT);
if (HasMoveWithCategory(battlerAtk, DAMAGE_CATEGORY_SPECIAL) && newSpAtk > atkSpAttack * POWER_SPLIT_ALLY_PERCENTAGE)
ADJUST_AND_RETURN_SCORE(GOOD_EFFECT);
if (newAttack < defAttack * POWER_SPLIT_ENEMY_PERCENTAGE)
ADJUST_AND_RETURN_SCORE(GOOD_EFFECT);
if (newSpAtk < defSpAttack * POWER_SPLIT_ENEMY_PERCENTAGE)
ADJUST_AND_RETURN_SCORE(GOOD_EFFECT);
ADJUST_SCORE(WORST_EFFECT);
}
case EFFECT_ELECTRIC_TERRAIN:
case EFFECT_MISTY_TERRAIN:
if (gStatuses3[battlerAtk] & STATUS3_YAWN && IsBattlerGrounded(battlerAtk))

View File

@@ -410,3 +410,57 @@ AI_DOUBLE_BATTLE_TEST("AI prioritizes Skill Swapping Contrary to allied mons tha
}
}
AI_DOUBLE_BATTLE_TEST("AI uses Guard Split to improve its stats")
{
u32 player, opponent;
PARAMETRIZE { player = SPECIES_SHUCKLE; opponent = SPECIES_PHEROMOSA; }
PARAMETRIZE { player = SPECIES_PHEROMOSA; opponent = SPECIES_SHUCKLE; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_GUARD_SPLIT) == EFFECT_GUARD_SPLIT);
ASSUME(gSpeciesInfo[SPECIES_PHEROMOSA].baseDefense < gSpeciesInfo[SPECIES_WOBBUFFET].baseDefense);
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].baseDefense < gSpeciesInfo[SPECIES_SHUCKLE].baseDefense);
ASSUME(gSpeciesInfo[SPECIES_PHEROMOSA].baseSpDefense < gSpeciesInfo[SPECIES_WOBBUFFET].baseSpDefense);
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].baseSpDefense < gSpeciesInfo[SPECIES_SHUCKLE].baseSpDefense);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_DOUBLE_BATTLE);
PLAYER(player);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_GUARD_SPLIT, MOVE_NIGHT_SHADE); }
OPPONENT(opponent);
} WHEN {
if (player == SPECIES_SHUCKLE)
TURN { EXPECT_MOVE(opponentLeft, MOVE_GUARD_SPLIT, target:playerLeft); }
else
TURN { EXPECT_MOVE(opponentLeft, MOVE_GUARD_SPLIT, target:opponentRight); }
}
}
AI_DOUBLE_BATTLE_TEST("AI uses Power Split to improve its stats")
{
u32 player, opponent;
PARAMETRIZE { player = SPECIES_SHUCKLE; opponent = SPECIES_PHEROMOSA; }
PARAMETRIZE { player = SPECIES_PHEROMOSA; opponent = SPECIES_SHUCKLE; }
GIVEN {
ASSUME(GetMoveEffect(MOVE_POWER_SPLIT) == EFFECT_POWER_SPLIT);
ASSUME(gSpeciesInfo[SPECIES_PHEROMOSA].baseAttack > gSpeciesInfo[SPECIES_WOBBUFFET].baseAttack);
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].baseAttack > gSpeciesInfo[SPECIES_SHUCKLE].baseAttack);
ASSUME(gSpeciesInfo[SPECIES_PHEROMOSA].baseSpAttack > gSpeciesInfo[SPECIES_WOBBUFFET].baseSpAttack);
ASSUME(gSpeciesInfo[SPECIES_WOBBUFFET].baseSpAttack > gSpeciesInfo[SPECIES_SHUCKLE].baseSpAttack);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_DOUBLE_BATTLE);
PLAYER(player);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_POWER_SPLIT, MOVE_TACKLE, MOVE_ROUND); }
OPPONENT(opponent) { Moves(MOVE_TACKLE, MOVE_ROUND); }
} WHEN {
if (player == SPECIES_PHEROMOSA)
TURN { EXPECT_MOVE(opponentLeft, MOVE_POWER_SPLIT, target:playerLeft); }
else
TURN { EXPECT_MOVE(opponentLeft, MOVE_POWER_SPLIT, target:opponentRight); }
}
}