AI can use Z-status moves (#7666)
This commit is contained in:
parent
cb2754a186
commit
c3dec7d030
@ -110,4 +110,8 @@
|
||||
#define POWER_SPLIT_ALLY_PERCENTAGE 150
|
||||
#define POWER_SPLIT_ENEMY_PERCENTAGE 50
|
||||
|
||||
// HP thresholds to use a status z-move.
|
||||
#define Z_EFFECT_FOLLOW_ME_THRESHOLD 30
|
||||
#define Z_EFFECT_RESTORE_HP_THRESHOLD 60
|
||||
|
||||
#endif // GUARD_CONFIG_AI_H
|
||||
|
||||
@ -1985,7 +1985,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
ADJUST_SCORE(-10);
|
||||
if (aiData->abilities[battlerAtk] == ABILITY_CONTRARY)
|
||||
ADJUST_SCORE(-10);
|
||||
else if (aiData->hpPercents[battlerAtk] <= 60)
|
||||
else if (aiData->hpPercents[battlerAtk] <= 60 && (GetActiveGimmick(battlerAtk) != GIMMICK_Z_MOVE || GetMoveZEffect(move) == Z_EFFECT_NONE))
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_FUTURE_SIGHT:
|
||||
@ -2760,6 +2760,9 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
case EFFECT_HOLD_HANDS:
|
||||
case EFFECT_CELEBRATE:
|
||||
case EFFECT_HAPPY_HOUR:
|
||||
case EFFECT_LAST_RESORT:
|
||||
if (gBattleStruct->gimmick.usableGimmick[battlerAtk] == GIMMICK_Z_MOVE && ShouldUseZMove(battlerAtk, battlerDef, move))
|
||||
break;
|
||||
ADJUST_SCORE(-10);
|
||||
break;
|
||||
case EFFECT_INSTRUCT:
|
||||
@ -4443,7 +4446,8 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
case EFFECT_HOLD_HANDS:
|
||||
case EFFECT_CELEBRATE:
|
||||
case EFFECT_HAPPY_HOUR:
|
||||
//todo - check z splash, z celebrate, z happy hour (lol)
|
||||
if (gBattleStruct->gimmick.usableGimmick[battlerAtk] == GIMMICK_Z_MOVE && ShouldUseZMove(battlerAtk, battlerDef, move))
|
||||
ADJUST_SCORE(BEST_EFFECT);
|
||||
break;
|
||||
case EFFECT_TELEPORT: // Either remove or add better logic
|
||||
if (!(gBattleTypeFlags & BATTLE_TYPE_TRAINER) || !IsOnPlayerSide(battlerAtk))
|
||||
|
||||
@ -4847,15 +4847,97 @@ bool32 AI_MoveMakesContact(u32 ability, enum ItemHoldEffect holdEffect, u32 move
|
||||
bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, u32 chosenMove)
|
||||
{
|
||||
// simple logic. just upgrades chosen move to z move if possible, unless regular move would kill opponent
|
||||
if ((IsDoubleBattle()) && battlerDef == BATTLE_PARTNER(battlerAtk))
|
||||
if ((IsDoubleBattle()) && battlerDef == BATTLE_PARTNER(battlerAtk) && !(GetBattlerMoveTargetType(battlerAtk, chosenMove) & MOVE_TARGET_ALLY))
|
||||
return FALSE; // don't use z move on partner
|
||||
if (HasTrainerUsedGimmick(battlerAtk, GIMMICK_Z_MOVE))
|
||||
return FALSE; // can't use z move twice
|
||||
|
||||
if (IsViableZMove(battlerAtk, chosenMove))
|
||||
{
|
||||
uq4_12_t effectiveness;
|
||||
u32 zMove = GetUsableZMove(battlerAtk, chosenMove);
|
||||
|
||||
if (IsBattleMoveStatus(chosenMove))
|
||||
{
|
||||
u8 zEffect = GetMoveZEffect(chosenMove);
|
||||
enum StatChange statChange = 0;
|
||||
|
||||
if (zEffect == Z_EFFECT_CURSE)
|
||||
{
|
||||
if (IS_BATTLER_OF_TYPE(battlerAtk, TYPE_GHOST))
|
||||
zEffect = Z_EFFECT_RECOVER_HP;
|
||||
else
|
||||
zEffect = Z_EFFECT_ATK_UP_1;
|
||||
}
|
||||
|
||||
switch (zEffect)
|
||||
{
|
||||
case Z_EFFECT_NONE:
|
||||
return FALSE;
|
||||
case Z_EFFECT_RESET_STATS:
|
||||
if (CountNegativeStatStages(battlerAtk) > 1)
|
||||
return TRUE;
|
||||
break;
|
||||
case Z_EFFECT_ALL_STATS_UP_1:
|
||||
if (AreBattlersStatsMaxed(battlerAtk))
|
||||
return FALSE;
|
||||
return IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_ATK) > 0
|
||||
|| IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPATK) > 0;
|
||||
break;
|
||||
case Z_EFFECT_BOOST_CRITS:
|
||||
return TRUE;
|
||||
case Z_EFFECT_FOLLOW_ME:
|
||||
return HasPartnerIgnoreFlags(battlerAtk) && (GetHealthPercentage(battlerAtk) <= Z_EFFECT_FOLLOW_ME_THRESHOLD || GetBestNoOfHitsToKO(battlerDef, battlerAtk, AI_DEFENDING) == 1);
|
||||
break;
|
||||
case Z_EFFECT_RECOVER_HP:
|
||||
return gAiLogicData->hpPercents[battlerAtk] <= Z_EFFECT_RESTORE_HP_THRESHOLD;
|
||||
case Z_EFFECT_RESTORE_REPLACEMENT_HP:
|
||||
break;
|
||||
case Z_EFFECT_ACC_UP_1:
|
||||
case Z_EFFECT_ACC_UP_2:
|
||||
case Z_EFFECT_ACC_UP_3:
|
||||
statChange = STAT_CHANGE_ACC;
|
||||
break;
|
||||
case Z_EFFECT_EVSN_UP_1:
|
||||
case Z_EFFECT_EVSN_UP_2:
|
||||
case Z_EFFECT_EVSN_UP_3:
|
||||
statChange = STAT_CHANGE_EVASION;
|
||||
break;
|
||||
case Z_EFFECT_ATK_UP_1:
|
||||
case Z_EFFECT_DEF_UP_1:
|
||||
case Z_EFFECT_SPD_UP_1:
|
||||
case Z_EFFECT_SPATK_UP_1:
|
||||
case Z_EFFECT_SPDEF_UP_1:
|
||||
statChange = STAT_CHANGE_ATK + zEffect - Z_EFFECT_ATK_UP_1;
|
||||
break;
|
||||
case Z_EFFECT_ATK_UP_2:
|
||||
case Z_EFFECT_DEF_UP_2:
|
||||
case Z_EFFECT_SPD_UP_2:
|
||||
case Z_EFFECT_SPATK_UP_2:
|
||||
case Z_EFFECT_SPDEF_UP_2:
|
||||
statChange = STAT_CHANGE_ATK_2 + zEffect - Z_EFFECT_ATK_UP_2;
|
||||
break;
|
||||
case Z_EFFECT_ATK_UP_3:
|
||||
case Z_EFFECT_DEF_UP_3:
|
||||
case Z_EFFECT_SPD_UP_3:
|
||||
case Z_EFFECT_SPATK_UP_3:
|
||||
case Z_EFFECT_SPDEF_UP_3:
|
||||
statChange = STAT_CHANGE_ATK_2 + zEffect - Z_EFFECT_ATK_UP_3;
|
||||
break;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (statChange != 0 && IncreaseStatUpScore(battlerAtk, battlerDef, statChange) > 0)
|
||||
return TRUE;
|
||||
}
|
||||
else if (GetMoveEffect(zMove) == EFFECT_EXTREME_EVOBOOST)
|
||||
{
|
||||
return (!AreBattlersStatsMaxed(battlerAtk) && (IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_ATK_2) || IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_SPATK_2)));
|
||||
}
|
||||
else if (!IsBattleMoveStatus(chosenMove) && IsBattleMoveStatus(zMove))
|
||||
return FALSE;
|
||||
|
||||
uq4_12_t effectiveness;
|
||||
struct SimulatedDamage dmg;
|
||||
|
||||
if (gBattleMons[battlerDef].ability == ABILITY_DISGUISE
|
||||
@ -4867,11 +4949,6 @@ bool32 ShouldUseZMove(u32 battlerAtk, u32 battlerDef, u32 chosenMove)
|
||||
&& gBattleMons[battlerDef].species == SPECIES_EISCUE_ICE && IsBattleMovePhysical(chosenMove))
|
||||
return FALSE; // Don't waste a Z-Move busting Ice Face
|
||||
|
||||
if (IsBattleMoveStatus(chosenMove) && !IsBattleMoveStatus(zMove))
|
||||
return FALSE;
|
||||
else if (!IsBattleMoveStatus(chosenMove) && IsBattleMoveStatus(zMove))
|
||||
return FALSE;
|
||||
|
||||
dmg = AI_CalcDamageSaveBattlers(chosenMove, battlerAtk, battlerDef, &effectiveness, NO_GIMMICK, NO_GIMMICK);
|
||||
|
||||
if (!IsBattleMoveStatus(chosenMove) && dmg.minimum >= gBattleMons[battlerDef].hp)
|
||||
|
||||
@ -32,3 +32,94 @@ AI_SINGLE_BATTLE_TEST("AI does not use damaging Z-moves if the player would fain
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_QUICK_ATTACK, gimmick: GIMMICK_NONE); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI uses Z-Moves -- Z-Splash")
|
||||
{
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT );
|
||||
ASSUME(GetMoveType(MOVE_QUICK_ATTACK) == TYPE_NORMAL);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); Moves(MOVE_POUND, MOVE_SPLASH); }
|
||||
} WHEN {
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_SPLASH, gimmick: GIMMICK_Z_MOVE);
|
||||
SCORE_GT_VAL(opponent, MOVE_SPLASH, AI_SCORE_DEFAULT); }
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_POUND, gimmick: GIMMICK_NONE);
|
||||
SCORE_EQ_VAL(opponent, MOVE_SPLASH, 90); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI uses Z-Moves -- Z-Happy Hour")
|
||||
{
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT );
|
||||
ASSUME(GetMoveType(MOVE_QUICK_ATTACK) == TYPE_NORMAL);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); Moves(MOVE_POUND, MOVE_HAPPY_HOUR); }
|
||||
} WHEN {
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_HAPPY_HOUR, gimmick: GIMMICK_Z_MOVE);
|
||||
SCORE_GT_VAL(opponent, MOVE_HAPPY_HOUR, AI_SCORE_DEFAULT); }
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_POUND, gimmick: GIMMICK_NONE);
|
||||
SCORE_EQ_VAL(opponent, MOVE_HAPPY_HOUR, 90); }
|
||||
}
|
||||
}
|
||||
|
||||
// Last Resort itself is missing logic!
|
||||
AI_SINGLE_BATTLE_TEST("AI uses Z-Moves -- Extreme Evoboost")
|
||||
{
|
||||
KNOWN_FAILING;
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT );
|
||||
ASSUME(GetMoveType(MOVE_QUICK_ATTACK) == TYPE_NORMAL);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_EEVEE) { Item(ITEM_EEVIUM_Z); Moves(MOVE_POUND, MOVE_LAST_RESORT); }
|
||||
} WHEN {
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_LAST_RESORT, gimmick: GIMMICK_Z_MOVE); }
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_POUND, gimmick: GIMMICK_NONE);
|
||||
SCORE_EQ_VAL(opponent, MOVE_LAST_RESORT, 80); }
|
||||
// Uncomment when Last Resort works correctly.
|
||||
// TURN { EXPECT_MOVE(opponent, MOVE_LAST_RESORT, gimmick: GIMMICK_NONE); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI uses Z-Moves -- 10,000,000 Volt Thunderbolt")
|
||||
{
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT );
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_PIKACHU_PARTNER) { Item(ITEM_PIKASHUNIUM_Z); Moves(MOVE_THUNDERBOLT); }
|
||||
} WHEN {
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_THUNDERBOLT, gimmick: GIMMICK_Z_MOVE); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI uses Z-Moves -- Z-Destiny Bond is not used in singles")
|
||||
{
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT );
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_GHOSTIUM_Z); Moves(MOVE_DESTINY_BOND); }
|
||||
} WHEN {
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_DESTINY_BOND, gimmick: GIMMICK_NONE); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_DOUBLE_BATTLE_TEST("AI uses Z-Moves -- Z-Destiny Bond is used when about to die")
|
||||
{
|
||||
u32 currentHP;
|
||||
PARAMETRIZE { currentHP = 1; }
|
||||
PARAMETRIZE { currentHP = 500; }
|
||||
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT );
|
||||
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE, MOVE_POUND); }
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { HP(currentHP); Item(ITEM_GHOSTIUM_Z); Moves(MOVE_DESTINY_BOND); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
if (currentHP == 1)
|
||||
TURN { EXPECT_MOVE(opponentLeft, MOVE_DESTINY_BOND, gimmick: GIMMICK_Z_MOVE); }
|
||||
else
|
||||
TURN { EXPECT_MOVE(opponentLeft, MOVE_DESTINY_BOND, gimmick: GIMMICK_NONE); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -114,3 +114,43 @@ SINGLE_BATTLE_TEST("Last Resort works with Sleep Talk")
|
||||
HP_BAR(opponent);
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI uses Last Resort - 2 moves")
|
||||
{
|
||||
KNOWN_FAILING;
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_LAST_RESORT, MOVE_SCRATCH); }
|
||||
} WHEN {
|
||||
TURN { NOT_EXPECT_MOVE(opponent, MOVE_LAST_RESORT); }
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_LAST_RESORT); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI uses Last Resort - 3 moves")
|
||||
{
|
||||
KNOWN_FAILING;
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_LAST_RESORT, MOVE_QUICK_ATTACK, MOVE_SCRATCH); }
|
||||
} WHEN {
|
||||
TURN { NOT_EXPECT_MOVE(opponent, MOVE_LAST_RESORT); }
|
||||
TURN { NOT_EXPECT_MOVE(opponent, MOVE_LAST_RESORT); }
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_LAST_RESORT); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI uses Last Resort - 4 moves")
|
||||
{
|
||||
KNOWN_FAILING;
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_LAST_RESORT, MOVE_QUICK_ATTACK, MOVE_SCRATCH, MOVE_GUST); }
|
||||
} WHEN {
|
||||
TURN { NOT_EXPECT_MOVE(opponent, MOVE_LAST_RESORT); }
|
||||
TURN { NOT_EXPECT_MOVE(opponent, MOVE_LAST_RESORT); }
|
||||
TURN { NOT_EXPECT_MOVE(opponent, MOVE_LAST_RESORT); }
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_LAST_RESORT); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user