Improved AI for status curing; trainer items, Purify, Smelling Salts, Sparkling Aria (#7853)
This commit is contained in:
parent
768c403850
commit
f0444f0d3f
@ -132,6 +132,8 @@ bool32 ShouldUseRecoilMove(u32 battlerAtk, u32 battlerDef, u32 recoilDmg, u32 mo
|
||||
bool32 ShouldAbsorb(u32 battlerAtk, u32 battlerDef, u32 move, s32 damage);
|
||||
bool32 ShouldRecover(u32 battlerAtk, u32 battlerDef, u32 move, u32 healPercent);
|
||||
bool32 ShouldSetScreen(u32 battlerAtk, u32 battlerDef, enum BattleMoveEffects moveEffect);
|
||||
bool32 ShouldCureStatus(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData);
|
||||
bool32 ShouldCureStatusWithItem(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData);
|
||||
enum AIPivot ShouldPivot(u32 battlerAtk, u32 battlerDef, enum Ability defAbility, u32 move, u32 moveIndex);
|
||||
bool32 IsRecycleEncouragedItem(u32 item);
|
||||
bool32 ShouldRestoreHpBerry(u32 battlerAtk, u32 item);
|
||||
|
||||
@ -2194,10 +2194,13 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
ADJUST_SCORE(-10);
|
||||
else if (battlerDef == BATTLE_PARTNER(battlerAtk))
|
||||
break; //Always heal your ally
|
||||
else if (AI_BattlerAtMaxHp(battlerAtk))
|
||||
ADJUST_SCORE(-10);
|
||||
else if (aiData->hpPercents[battlerAtk] >= 90)
|
||||
ADJUST_SCORE(-8); //No point in healing, but should at least do it if nothing better
|
||||
else if (!ShouldCureStatus(battlerAtk, battlerDef, aiData))
|
||||
{
|
||||
if (AI_BattlerAtMaxHp(battlerAtk))
|
||||
ADJUST_SCORE(-10);
|
||||
else if (aiData->hpPercents[battlerAtk] >= 90)
|
||||
ADJUST_SCORE(-8); //No point in healing, but should at least do it if nothing better
|
||||
}
|
||||
break;
|
||||
case EFFECT_RECOIL_IF_MISS:
|
||||
if (aiData->abilities[battlerAtk] != ABILITY_MAGIC_GUARD && gAiLogicData->moveAccuracy[battlerAtk][battlerDef][gAiThinkingStruct->movesetIndex] < 75
|
||||
@ -3602,8 +3605,18 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
||||
if (gBattleMons[battlerAtkPartner].status1 & STATUS1_ANY)
|
||||
{
|
||||
if (gBattleMons[battlerAtkPartner].status1 & STATUS1_CAN_MOVE)
|
||||
{
|
||||
if (ShouldCureStatus(battlerAtk, battlerAtkPartner, aiData))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
}
|
||||
else
|
||||
{
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
}
|
||||
|
||||
if ((!IsBattlerAlive(LEFT_FOE(battlerAtk)) || ShouldRecover(battlerAtk, LEFT_FOE(battlerAtk), move, 50))
|
||||
&& (!IsBattlerAlive(RIGHT_FOE(battlerAtk)) || ShouldRecover(battlerAtk, RIGHT_FOE(battlerAtk), move, 50)))
|
||||
RETURN_SCORE_PLUS(WEAK_EFFECT);
|
||||
RETURN_SCORE_PLUS(GOOD_EFFECT);
|
||||
}
|
||||
break;
|
||||
case EFFECT_SWAGGER:
|
||||
@ -4557,6 +4570,15 @@ static s32 AI_CalcMoveEffectScore(u32 battlerAtk, u32 battlerDef, u32 move, stru
|
||||
if (ShouldUseWishAromatherapy(battlerAtk, battlerDef, move))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
break;
|
||||
case EFFECT_PURIFY:
|
||||
if (gBattleMons[battlerDef].status1 & STATUS1_ANY)
|
||||
{
|
||||
if (ShouldCureStatus(battlerAtk, battlerDef, aiData))
|
||||
ADJUST_SCORE(GOOD_EFFECT);
|
||||
if (ShouldRecover(battlerAtk, battlerDef, move, 50))
|
||||
RETURN_SCORE_PLUS(WEAK_EFFECT);
|
||||
}
|
||||
break;
|
||||
case EFFECT_CURSE:
|
||||
if (IS_BATTLER_OF_TYPE(battlerAtk, TYPE_GHOST))
|
||||
{
|
||||
@ -5909,6 +5931,17 @@ static s32 AI_CalcAdditionalEffectScore(u32 battlerAtk, u32 battlerDef, u32 move
|
||||
if (ShouldSetScreen(battlerAtk, battlerDef, EFFECT_AURORA_VEIL))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
break;
|
||||
case MOVE_EFFECT_REMOVE_STATUS:
|
||||
if (gBattleMons[battlerDef].status1 & GetMoveEffectArg_Status(move))
|
||||
{
|
||||
if (ShouldCureStatus(battlerAtk, battlerDef, aiData))
|
||||
ADJUST_SCORE(DECENT_EFFECT);
|
||||
else if (aiData->holdEffects[battlerDef] == HOLD_EFFECT_FLAME_ORB || aiData->holdEffects[battlerDef] == HOLD_EFFECT_TOXIC_ORB)
|
||||
ADJUST_SCORE(WEAK_EFFECT);
|
||||
else
|
||||
ADJUST_SCORE(BAD_EFFECT);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@ -2505,18 +2505,13 @@ static bool32 ShouldUseItem(u32 battler)
|
||||
shouldUse = AI_ShouldHeal(battler, healAmount);
|
||||
break;
|
||||
case EFFECT_ITEM_CURE_STATUS:
|
||||
if (itemEffects[3] & ITEM3_SLEEP && gBattleMons[battler].status1 & STATUS1_SLEEP)
|
||||
shouldUse = TRUE;
|
||||
if (itemEffects[3] & ITEM3_POISON && gBattleMons[battler].status1 & STATUS1_PSN_ANY)
|
||||
shouldUse = TRUE;
|
||||
if (itemEffects[3] & ITEM3_BURN && gBattleMons[battler].status1 & STATUS1_BURN)
|
||||
shouldUse = TRUE;
|
||||
if (itemEffects[3] & ITEM3_FREEZE && gBattleMons[battler].status1 & STATUS1_ICY_ANY)
|
||||
shouldUse = TRUE;
|
||||
if (itemEffects[3] & ITEM3_PARALYSIS && gBattleMons[battler].status1 & STATUS1_PARALYSIS)
|
||||
shouldUse = TRUE;
|
||||
if (itemEffects[3] & ITEM3_CONFUSION && gBattleMons[battler].volatiles.confusionTurns > 0)
|
||||
shouldUse = TRUE;
|
||||
if ((itemEffects[3] & ITEM3_SLEEP && gBattleMons[battler].status1 & STATUS1_SLEEP)
|
||||
|| (itemEffects[3] & ITEM3_POISON && gBattleMons[battler].status1 & STATUS1_PSN_ANY)
|
||||
|| (itemEffects[3] & ITEM3_BURN && gBattleMons[battler].status1 & STATUS1_BURN)
|
||||
|| (itemEffects[3] & ITEM3_FREEZE && gBattleMons[battler].status1 & STATUS1_ICY_ANY)
|
||||
|| (itemEffects[3] & ITEM3_PARALYSIS && gBattleMons[battler].status1 & STATUS1_PARALYSIS)
|
||||
|| (itemEffects[3] & ITEM3_CONFUSION && gBattleMons[battler].volatiles.confusionTurns > 0))
|
||||
shouldUse = ShouldCureStatusWithItem(battler, battler, gAiLogicData);
|
||||
break;
|
||||
case EFFECT_ITEM_INCREASE_STAT:
|
||||
case EFFECT_ITEM_INCREASE_ALL_STATS:
|
||||
|
||||
@ -3931,6 +3931,102 @@ bool32 ShouldSetScreen(u32 battlerAtk, u32 battlerDef, enum BattleMoveEffects mo
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static bool32 ShouldCureStatusInternal(u32 battlerAtk, u32 battlerDef, bool32 usingItem, struct AiLogicData *aiData)
|
||||
{
|
||||
bool32 targetingSelf = (battlerAtk == battlerDef);
|
||||
bool32 targetingAlly = IsTargetingPartner(battlerAtk, battlerDef);
|
||||
u32 status = gBattleMons[battlerDef].status1;
|
||||
|
||||
if (status & STATUS1_SLEEP)
|
||||
{
|
||||
if (targetingAlly || targetingSelf)
|
||||
{
|
||||
if (HasMoveWithEffect(battlerDef, EFFECT_SLEEP_TALK) || HasMoveWithEffect(battlerDef, EFFECT_SNORE))
|
||||
return FALSE;
|
||||
else
|
||||
return usingItem || targetingAlly;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (status & STATUS1_FREEZE)
|
||||
{
|
||||
if (targetingAlly || targetingSelf)
|
||||
{
|
||||
if (HasThawingMove(battlerDef))
|
||||
return FALSE;
|
||||
return usingItem || targetingAlly;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 isHarmless = FALSE;
|
||||
|
||||
if (DoesBattlerBenefitFromAllVolatileStatus(battlerDef, aiData->abilities[battlerDef]))
|
||||
isHarmless = TRUE;
|
||||
|
||||
if (status & STATUS1_PSN_ANY)
|
||||
{
|
||||
if (aiData->holdEffects[battlerDef] == HOLD_EFFECT_TOXIC_ORB)
|
||||
return FALSE;
|
||||
|
||||
if (aiData->abilities[battlerDef] == ABILITY_POISON_HEAL)
|
||||
isHarmless = TRUE;
|
||||
|
||||
if (aiData->abilities[battlerDef] == ABILITY_TOXIC_BOOST && !isHarmless)
|
||||
{
|
||||
if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL))
|
||||
isHarmless = TRUE;
|
||||
else if (!(targetingSelf || targetingAlly) && !HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL))
|
||||
isHarmless = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
if (status & STATUS1_BURN)
|
||||
{
|
||||
if (aiData->holdEffects[battlerDef] == HOLD_EFFECT_FLAME_ORB)
|
||||
return FALSE;
|
||||
|
||||
if (aiData->abilities[battlerDef] == ABILITY_FLARE_BOOST && !isHarmless)
|
||||
{
|
||||
if (HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_SPECIAL))
|
||||
isHarmless = TRUE;
|
||||
else if (!(targetingSelf || targetingAlly) && !HasMoveWithCategory(battlerDef, DAMAGE_CATEGORY_PHYSICAL))
|
||||
isHarmless = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if (status & STATUS1_PARALYSIS)
|
||||
if (status & STATUS1_FROSTBITE)
|
||||
*/
|
||||
|
||||
if (isHarmless)
|
||||
{
|
||||
if (targetingSelf || targetingAlly)
|
||||
return FALSE;
|
||||
else
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (targetingSelf || targetingAlly)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
bool32 ShouldCureStatus(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData)
|
||||
{
|
||||
return ShouldCureStatusInternal(battlerAtk, battlerDef, FALSE, aiData);
|
||||
}
|
||||
|
||||
bool32 ShouldCureStatusWithItem(u32 battlerAtk, u32 battlerDef, struct AiLogicData *aiData)
|
||||
{
|
||||
return ShouldCureStatusInternal(battlerAtk, battlerDef, TRUE, aiData);
|
||||
}
|
||||
|
||||
// Partner Logic
|
||||
bool32 IsBattle1v1()
|
||||
{
|
||||
|
||||
@ -439,3 +439,22 @@ AI_DOUBLE_BATTLE_TEST("AI sees type-changing moves as the correct type")
|
||||
TURN { NOT_EXPECT_MOVE(opponentLeft, fieldStatus); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI uses Sparkling Aria to cure an enemy with Guts")
|
||||
{
|
||||
u32 ability;
|
||||
|
||||
PARAMETRIZE { ability = ABILITY_GUTS; }
|
||||
PARAMETRIZE { ability = ABILITY_BULLETPROOF; }
|
||||
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_OMNISCIENT);
|
||||
PLAYER(SPECIES_URSALUNA) { Ability(ability); Moves(MOVE_HEADLONG_RUSH, MOVE_CELEBRATE); Status1(STATUS1_BURN); }
|
||||
OPPONENT(SPECIES_PRIMARINA) { Moves(MOVE_SPARKLING_ARIA, MOVE_SCALD); }
|
||||
} WHEN {
|
||||
if (ability == ABILITY_GUTS)
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_SPARKLING_ARIA); }
|
||||
else
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_SCALD); }
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,5 +22,45 @@ AI_DOUBLE_BATTLE_TEST("AI uses Purify")
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI uses Purify to heal an enemy with Guts")
|
||||
{
|
||||
u32 ability;
|
||||
|
||||
PARAMETRIZE { ability = ABILITY_GUTS; }
|
||||
PARAMETRIZE { ability = ABILITY_BULLETPROOF; }
|
||||
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_OMNISCIENT);
|
||||
PLAYER(SPECIES_URSALUNA) { Ability(ability); Moves(MOVE_HEADLONG_RUSH, MOVE_CELEBRATE); Status1(STATUS1_BURN); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_HEADBUTT, MOVE_PURIFY); }
|
||||
} WHEN {
|
||||
if (ability == ABILITY_GUTS)
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_PURIFY); }
|
||||
else
|
||||
TURN { NOT_EXPECT_MOVE(opponent, MOVE_PURIFY); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_DOUBLE_BATTLE_TEST("AI does not use Purify to heal an ally with Guts")
|
||||
{
|
||||
u32 ability;
|
||||
|
||||
PARAMETRIZE { ability = ABILITY_GUTS; }
|
||||
PARAMETRIZE { ability = ABILITY_BULLETPROOF; }
|
||||
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_OMNISCIENT);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_HEADBUTT, MOVE_PURIFY); }
|
||||
OPPONENT(SPECIES_URSALUNA) { Ability(ability); Moves(MOVE_HEADLONG_RUSH); Status1(STATUS1_BURN); }
|
||||
} WHEN {
|
||||
if (ability == ABILITY_GUTS)
|
||||
TURN { NOT_EXPECT_MOVE(opponentLeft, MOVE_PURIFY); }
|
||||
else
|
||||
TURN { EXPECT_MOVE(opponentLeft, MOVE_PURIFY, target: opponentRight); }
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("TODO: Write Purify (Move Effect) test titles")
|
||||
TO_DO_BATTLE_TEST("Purify doesn't heal HP if the target has Comatose")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user