Feature/ai/wide guard quick guard singles (#7086)
Co-authored-by: Alex <93446519+AlexOn1ine@users.noreply.github.com>
This commit is contained in:
parent
872bb8785b
commit
f4ff5e6d26
@ -167,7 +167,8 @@ bool32 IsMoveRedirectionPrevented(u32 battlerAtk, u32 move, u32 atkAbility);
|
||||
bool32 IsMoveEncouragedToHit(u32 battlerAtk, u32 battlerDef, u32 move);
|
||||
bool32 IsHazardMove(u32 move);
|
||||
bool32 IsTwoTurnNotSemiInvulnerableMove(u32 battlerAtk, u32 move);
|
||||
void ProtectChecks(u32 battlerAtk, u32 battlerDef, u32 move, u32 predictedMove, s32 *score);
|
||||
bool32 IsBattlerDamagedByStatus(u32 battler);
|
||||
s32 ProtectChecks(u32 battlerAtk, u32 battlerDef, u32 move, u32 predictedMove);
|
||||
bool32 ShouldSetSandstorm(u32 battler, u32 ability, enum ItemHoldEffect holdEffect);
|
||||
bool32 ShouldSetHail(u32 battler, u32 ability, enum ItemHoldEffect holdEffect);
|
||||
bool32 ShouldSetSnow(u32 battler, u32 ability, enum ItemHoldEffect holdEffect);
|
||||
|
||||
@ -2202,7 +2202,17 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
switch (protectMethod)
|
||||
{
|
||||
case PROTECT_QUICK_GUARD:
|
||||
if (GetMovePriority(predictedMove) <= 0)
|
||||
{
|
||||
ADJUST_SCORE(-10);
|
||||
decreased = TRUE;
|
||||
}
|
||||
case PROTECT_WIDE_GUARD:
|
||||
if(!(GetBattlerMoveTargetType(battlerAtk, predictedMove) & (MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_BOTH)))
|
||||
{
|
||||
ADJUST_SCORE(-10);
|
||||
decreased = TRUE;
|
||||
}
|
||||
case PROTECT_CRAFTY_SHIELD:
|
||||
if (!isDoubleBattle)
|
||||
{
|
||||
@ -4288,28 +4298,30 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
{
|
||||
case PROTECT_QUICK_GUARD:
|
||||
if (predictedMove != MOVE_NONE && GetMovePriority(predictedMove) > 0)
|
||||
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
|
||||
{
|
||||
ADJUST_SCORE(ProtectChecks(battlerAtk, battlerDef, move, predictedMove));
|
||||
}
|
||||
break;
|
||||
case PROTECT_WIDE_GUARD:
|
||||
if (predictedMove != MOVE_NONE && GetBattlerMoveTargetType(battlerDef, predictedMove) & (MOVE_TARGET_FOES_AND_ALLY | MOVE_TARGET_BOTH))
|
||||
{
|
||||
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
|
||||
ADJUST_SCORE(ProtectChecks(battlerAtk, battlerDef, move, predictedMove));
|
||||
}
|
||||
else if (isDoubleBattle && GetBattlerMoveTargetType(BATTLE_PARTNER(battlerAtk), aiData->partnerMove) & MOVE_TARGET_FOES_AND_ALLY)
|
||||
{
|
||||
if (aiData->abilities[battlerAtk] != ABILITY_TELEPATHY)
|
||||
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
|
||||
ADJUST_SCORE(ProtectChecks(battlerAtk, battlerDef, move, predictedMove));
|
||||
}
|
||||
break;
|
||||
case PROTECT_CRAFTY_SHIELD:
|
||||
if (predictedMove != MOVE_NONE && IsBattleMoveStatus(predictedMove) && !(GetBattlerMoveTargetType(battlerDef, predictedMove) & MOVE_TARGET_USER))
|
||||
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
|
||||
ADJUST_SCORE(ProtectChecks(battlerAtk, battlerDef, move, predictedMove));
|
||||
break;
|
||||
|
||||
case PROTECT_MAT_BLOCK:
|
||||
if (gDisableStructs[battlerAtk].isFirstTurn && predictedMove != MOVE_NONE
|
||||
&& !IsBattleMoveStatus(predictedMove) && !(GetBattlerMoveTargetType(battlerDef, predictedMove) & MOVE_TARGET_USER))
|
||||
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
|
||||
ADJUST_SCORE(ProtectChecks(battlerAtk, battlerDef, move, predictedMove));
|
||||
break;
|
||||
case PROTECT_KINGS_SHIELD:
|
||||
if (aiData->abilities[battlerAtk] == ABILITY_STANCE_CHANGE //Special logic for Aegislash
|
||||
@ -4321,7 +4333,7 @@ static u32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
}
|
||||
//fallthrough
|
||||
default: // protect
|
||||
ProtectChecks(battlerAtk, battlerDef, move, predictedMove, &score);
|
||||
ADJUST_SCORE(ProtectChecks(battlerAtk, battlerDef, move, predictedMove));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1893,8 +1893,19 @@ bool32 ShouldSetSnow(u32 battler, u32 ability, enum ItemHoldEffect holdEffect)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
void ProtectChecks(u32 battlerAtk, u32 battlerDef, u32 move, u32 predictedMove, s32 *score)
|
||||
bool32 IsBattlerDamagedByStatus(u32 battler)
|
||||
{
|
||||
return gBattleMons[battler].status1 & (STATUS1_BURN | STATUS1_FROSTBITE | STATUS1_POISON | STATUS1_TOXIC_POISON)
|
||||
|| gBattleMons[battler].status2 & (STATUS2_WRAPPED | STATUS2_NIGHTMARE | STATUS2_CURSED)
|
||||
|| gStatuses3[battler] & (STATUS3_PERISH_SONG | STATUS3_LEECHSEED)
|
||||
|| gStatuses4[battler] & (STATUS4_SALT_CURE)
|
||||
|| gSideStatuses[GetBattlerSide(battler)] & (SIDE_STATUS_SEA_OF_FIRE | SIDE_STATUS_DAMAGE_NON_TYPES);
|
||||
}
|
||||
|
||||
s32 ProtectChecks(u32 battlerAtk, u32 battlerDef, u32 move, u32 predictedMove)
|
||||
{
|
||||
s32 score = 0;
|
||||
|
||||
// TODO more sophisticated logic
|
||||
u32 uses = gDisableStructs[battlerAtk].protectUses;
|
||||
|
||||
@ -1907,29 +1918,29 @@ void ProtectChecks(u32 battlerAtk, u32 battlerDef, u32 move, u32 predictedMove,
|
||||
if (uses == 0)
|
||||
{
|
||||
if (predictedMove != MOVE_NONE && predictedMove != 0xFFFF && !IsBattleMoveStatus(predictedMove))
|
||||
ADJUST_SCORE_PTR(DECENT_EFFECT);
|
||||
score += DECENT_EFFECT;
|
||||
else if (Random() % 256 < 100)
|
||||
ADJUST_SCORE_PTR(WEAK_EFFECT);
|
||||
score += WEAK_EFFECT;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsDoubleBattle())
|
||||
ADJUST_SCORE_PTR(-(2 * min(uses, 3)));
|
||||
score -= (2 * min(uses, 3));
|
||||
else
|
||||
ADJUST_SCORE_PTR(-(min(uses, 3)));
|
||||
score -= (min(uses, 3));
|
||||
}
|
||||
|
||||
if (gBattleMons[battlerAtk].status1 & (STATUS1_PSN_ANY | STATUS1_BURN | STATUS1_FROSTBITE)
|
||||
|| gBattleMons[battlerAtk].status2 & (STATUS2_CURSED | STATUS2_INFATUATION)
|
||||
|| gStatuses3[battlerAtk] & (STATUS3_PERISH_SONG | STATUS3_LEECHSEED | STATUS3_YAWN))
|
||||
if (IsBattlerDamagedByStatus(battlerAtk))
|
||||
{
|
||||
ADJUST_SCORE_PTR(-1);
|
||||
score -= 1;
|
||||
}
|
||||
|
||||
if (gBattleMons[battlerDef].status1 & STATUS1_TOXIC_POISON
|
||||
|| gBattleMons[battlerDef].status2 & (STATUS2_CURSED | STATUS2_INFATUATION)
|
||||
|| gStatuses3[battlerDef] & (STATUS3_PERISH_SONG | STATUS3_LEECHSEED | STATUS3_YAWN))
|
||||
ADJUST_SCORE_PTR(DECENT_EFFECT);
|
||||
if (IsBattlerDamagedByStatus(battlerDef))
|
||||
{
|
||||
score += DECENT_EFFECT;
|
||||
}
|
||||
|
||||
return score;
|
||||
}
|
||||
|
||||
// stat stages
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
#include "constants/battle_ai.h"
|
||||
#include "constants/moves.h"
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
#include "battle_ai_util.h"
|
||||
#include "test/test.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
@ -253,3 +256,25 @@ AI_SINGLE_BATTLE_TEST("AI prioritizes Pursuit if it would KO opponent")
|
||||
TURN { SWITCH(player, 1); EXPECT_MOVE(opponent, MOVE_PURSUIT); SEND_OUT(player, 1); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI uses Quick Guard against Quick Attack when opponent would die of poison")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ZUBAT) { Moves(MOVE_QUICK_ATTACK); Status1(STATUS1_TOXIC_POISON); }
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT | AI_FLAG_PREDICT_MOVE);
|
||||
OPPONENT(SPECIES_RATTATA) { Moves(MOVE_QUICK_GUARD, MOVE_TACKLE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_QUICK_ATTACK); EXPECT_MOVE(opponent, MOVE_QUICK_GUARD); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI uses Wide Guard against Earthquake when opponent would die of poison")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ZUBAT) { Moves(MOVE_EARTHQUAKE); Status1(STATUS1_TOXIC_POISON); }
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT | AI_FLAG_PREDICT_MOVE);
|
||||
OPPONENT(SPECIES_RATTATA) { Moves(MOVE_WIDE_GUARD, MOVE_TACKLE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_EARTHQUAKE); EXPECT_MOVE(opponent, MOVE_WIDE_GUARD); }
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user