mirror of
https://github.com/pret/pokeemerald.git
synced 2026-05-05 21:07:17 -05:00
AI_FLAG_ASSUME_STATUS_MOVES -- AI flag to randomly know some of the player's status moves (#7324)
This commit is contained in:
commit
d27884cbb8
|
|
@ -147,6 +147,13 @@ AI has full knowledge of player moves, abilities, and hold items, and can use th
|
|||
## `AI_FLAG_ASSUME_STAB`
|
||||
A significantly more restricted version of `AI_FLAG_OMNISCIENT`, the AI only knows the player's STAB moves, as their existence would be reasonable to assume in almost any case.
|
||||
|
||||
## `AI_FLAG_ASSUME_STATUS_MOVES`
|
||||
A more restricted version of `AI_FLAG_OMNISCIENT`. The AI has a _chance_ to know what status moves the player has, plus additionally Fake Out and fixed percentage moves like Super Fang. The intention is so that if the AI has a counterplay implemented, it will seem to have guessed if the player's pokemon has a move, without giving the AI perfect information. For example, with Omniscient set, the AI will not usually put a pokemon to sleep if it has Sleep Talk; with neither Assume Powerful Status nor Omniscient set, the AI will always assume the pokemon does not have Sleep Talk.
|
||||
|
||||
By default, there are three groups of higher likelihood status moves defined in `include/config/ai.h` under `ASSUME_STATUS_HIGH_ODDS`, `ASSUME_STATUS_MEDIUM_ODDS`, and `ASSUME_STATUS_LOW_ODDS`. Moves are sorted in `src/battle_ai_util.c` within `ShouldRecordStatusMove()`.
|
||||
|
||||
Any move that is not special cased is then potentially caught by `ASSUME_ALL_STATUS_ODDS`.
|
||||
|
||||
## `AI_FLAG_SMART_MON_CHOICES`
|
||||
Affects what the AI chooses to send out after a switch. AI will make smarter decisions when choosing which mon to send out mid-battle and after a KO, which are handled separately. Automatically included when `AI_FLAG_SMART_SWITCHING` is enabled.
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ bool32 IsAiVsAiBattle(void);
|
|||
bool32 BattlerHasAi(u32 battlerId);
|
||||
bool32 IsAiBattlerAware(u32 battlerId);
|
||||
bool32 IsAiBattlerAssumingStab(void);
|
||||
bool32 IsAiBattlerAssumingStatusMoves(void);
|
||||
bool32 ShouldRecordStatusMove(u32 move);
|
||||
void ClearBattlerMoveHistory(u32 battlerId);
|
||||
void RecordLastUsedMoveBy(u32 battlerId, u32 move);
|
||||
void RecordAllMoves(u32 battler);
|
||||
|
|
|
|||
|
|
@ -76,6 +76,14 @@
|
|||
// AI_FLAG_ASSUME_STAB settings
|
||||
#define ASSUME_STAB_SEES_ABILITY FALSE // Flag also gives omniscience for player's ability. Can use AI_FLAG_WEIGH_ABILITY_PREDICTION instead for smarter prediction without omniscience.
|
||||
|
||||
// AI_FLAG_ASSUME_STATUS_MOVES settings
|
||||
#define ASSUME_STATUS_MOVES_HAS_TUNING TRUE // Flag has varying rates for different kinds of status move.
|
||||
// Setting to false also means it will not alert on Fake Out or Super Fang.
|
||||
#define ASSUME_STATUS_HIGH_ODDS 90 // Chance for AI to see extremely likely moves for a pokemon to have, like Spore
|
||||
#define ASSUME_STATUS_MEDIUM_ODDS 70 // Chance for AI to see moderately likely moves for a pokemon to have, like Protect
|
||||
#define ASSUME_STATUS_LOW_ODDS 40 // Chance for AI to see niche moves a pokemon may have but probably won't, like Entrainment
|
||||
#define ASSUME_ALL_STATUS_ODDS 25 // Chance for the AI to see any kind of status move.
|
||||
|
||||
// AI_FLAG_SMART_SWITCHING settings
|
||||
#define SMART_SWITCHING_OMNISCIENT FALSE // AI will use omniscience for switching calcs, regardless of omniscience setting otherwise
|
||||
|
||||
|
|
|
|||
|
|
@ -34,8 +34,9 @@
|
|||
#define AI_FLAG_PREDICT_MOVE (1 << 26) // AI will predict the player's move based on what move it would use in the same situation. Recommend using AI_FLAG_OMNISCIENT
|
||||
#define AI_FLAG_SMART_TERA (1 << 27) // AI will make smarter decisions when choosing whether to terrastalize (default is to always tera whenever available).
|
||||
#define AI_FLAG_ASSUME_STAB (1 << 28) // AI knows player's STAB moves, but nothing else. Restricted version of AI_FLAG_OMNISCIENT.
|
||||
#define AI_FLAG_ASSUME_STATUS_MOVES (1 << 29) // AI has a chance to know certain non-damaging moves, and also Fake Out and Super Fang. Restricted version of AI_FLAG_OMNISCIENT.
|
||||
|
||||
#define AI_FLAG_COUNT 29
|
||||
#define AI_FLAG_COUNT 30
|
||||
|
||||
// Flags at and after 32 need different formatting, as in
|
||||
// #define AI_FLAG_PLACEHOLDER ((u64)1 << 32)
|
||||
|
|
|
|||
|
|
@ -202,6 +202,12 @@ enum RandomTag
|
|||
RNG_AI_BOOST_INTO_HAZE,
|
||||
RNG_HEALER,
|
||||
RNG_DEXNAV_ENCOUNTER_LEVEL,
|
||||
RNG_AI_ASSUME_STATUS_SLEEP,
|
||||
RNG_AI_ASSUME_STATUS_NONVOLATILE,
|
||||
RNG_AI_ASSUME_STATUS_HIGH_ODDS,
|
||||
RNG_AI_ASSUME_STATUS_MEDIUM_ODDS,
|
||||
RNG_AI_ASSUME_STATUS_LOW_ODDS,
|
||||
RNG_AI_ASSUME_ALL_STATUS,
|
||||
};
|
||||
|
||||
#define RandomWeighted(tag, ...) \
|
||||
|
|
|
|||
|
|
@ -546,6 +546,17 @@ void RecordMovesBasedOnStab(u32 battler)
|
|||
}
|
||||
}
|
||||
|
||||
void RecordStatusMoves(u32 battler)
|
||||
{
|
||||
u32 i;
|
||||
for (i = 0; i < MAX_MON_MOVES; i++)
|
||||
{
|
||||
u32 playerMove = gBattleMons[battler].moves[i];
|
||||
if (ShouldRecordStatusMove(playerMove))
|
||||
RecordKnownMove(battler, playerMove);
|
||||
}
|
||||
}
|
||||
|
||||
void SetBattlerAiData(u32 battler, struct AiLogicData *aiData)
|
||||
{
|
||||
u32 ability, holdEffect;
|
||||
|
|
@ -561,6 +572,9 @@ void SetBattlerAiData(u32 battler, struct AiLogicData *aiData)
|
|||
|
||||
if (IsAiBattlerAssumingStab())
|
||||
RecordMovesBasedOnStab(battler);
|
||||
|
||||
if (IsAiBattlerAssumingStatusMoves())
|
||||
RecordStatusMoves(battler);
|
||||
}
|
||||
|
||||
#define BYPASSES_ACCURACY_CALC 101 // 101 indicates for ai that the move will always hit
|
||||
|
|
|
|||
|
|
@ -141,6 +141,15 @@ bool32 IsAiBattlerAssumingStab()
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 IsAiBattlerAssumingStatusMoves()
|
||||
{
|
||||
if (gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_LEFT] & AI_FLAG_ASSUME_STATUS_MOVES
|
||||
|| gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_RIGHT] & AI_FLAG_ASSUME_STATUS_MOVES)
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
bool32 IsAiBattlerPredictingAbility(u32 battlerId)
|
||||
{
|
||||
if (gAiThinkingStruct->aiFlags[B_POSITION_OPPONENT_LEFT] & AI_FLAG_WEIGH_ABILITY_PREDICTION
|
||||
|
|
@ -249,6 +258,66 @@ void SaveBattlerData(u32 battlerId)
|
|||
gAiThinkingStruct->saved[battlerId].types[1] = gBattleMons[battlerId].types[1];
|
||||
}
|
||||
|
||||
bool32 ShouldRecordStatusMove(u32 move)
|
||||
{
|
||||
if (ASSUME_STATUS_MOVES_HAS_TUNING)
|
||||
{
|
||||
switch (GetMoveEffect(move))
|
||||
{
|
||||
// variable odds by additional effect
|
||||
case EFFECT_NON_VOLATILE_STATUS:
|
||||
if (GetMoveNonVolatileStatus(move) == MOVE_EFFECT_SLEEP && RandomPercentage(RNG_AI_ASSUME_STATUS_SLEEP, ASSUME_STATUS_HIGH_ODDS))
|
||||
return TRUE;
|
||||
else if (RandomPercentage(RNG_AI_ASSUME_STATUS_NONVOLATILE, ASSUME_STATUS_MEDIUM_ODDS))
|
||||
return TRUE;
|
||||
break;
|
||||
// High odds
|
||||
case EFFECT_AURORA_VEIL:
|
||||
case EFFECT_CHILLY_RECEPTION:
|
||||
case EFFECT_FIRST_TURN_ONLY:
|
||||
case EFFECT_FOLLOW_ME:
|
||||
case EFFECT_INSTRUCT:
|
||||
case EFFECT_JUNGLE_HEALING:
|
||||
case EFFECT_SHED_TAIL:
|
||||
return RandomPercentage(RNG_AI_ASSUME_STATUS_HIGH_ODDS, ASSUME_STATUS_HIGH_ODDS);
|
||||
// Medium odds
|
||||
case EFFECT_AFTER_YOU:
|
||||
case EFFECT_DOODLE:
|
||||
case EFFECT_ENCORE:
|
||||
case EFFECT_HAZE:
|
||||
case EFFECT_PARTING_SHOT:
|
||||
case EFFECT_PROTECT:
|
||||
case EFFECT_REST:
|
||||
case EFFECT_ROAR:
|
||||
case EFFECT_ROOST:
|
||||
case EFFECT_SLEEP_TALK:
|
||||
case EFFECT_TAUNT:
|
||||
case EFFECT_TAILWIND:
|
||||
case EFFECT_TRICK:
|
||||
case EFFECT_TRICK_ROOM:
|
||||
// defoggables / screens and hazards
|
||||
case EFFECT_LIGHT_SCREEN:
|
||||
case EFFECT_REFLECT:
|
||||
case EFFECT_SPIKES:
|
||||
case EFFECT_STEALTH_ROCK:
|
||||
case EFFECT_STICKY_WEB:
|
||||
case EFFECT_TOXIC_SPIKES:
|
||||
return RandomPercentage(RNG_AI_ASSUME_STATUS_MEDIUM_ODDS, ASSUME_STATUS_MEDIUM_ODDS);
|
||||
// Low odds
|
||||
case EFFECT_ENTRAINMENT:
|
||||
case EFFECT_FIXED_PERCENT_DAMAGE:
|
||||
case EFFECT_GASTRO_ACID:
|
||||
case EFFECT_IMPRISON:
|
||||
case EFFECT_TELEPORT:
|
||||
return RandomPercentage(RNG_AI_ASSUME_STATUS_LOW_ODDS, ASSUME_STATUS_LOW_ODDS);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return RandomPercentage(RNG_AI_ASSUME_ALL_STATUS, ASSUME_ALL_STATUS_ODDS) && IsBattleMoveStatus(move);
|
||||
}
|
||||
|
||||
static bool32 ShouldFailForIllusion(u32 illusionSpecies, u32 battlerId)
|
||||
{
|
||||
u32 i, j;
|
||||
|
|
|
|||
49
test/battle/ai/ai_assume_status_moves.c
Normal file
49
test/battle/ai/ai_assume_status_moves.c
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
#include "battle_ai_util.h"
|
||||
|
||||
AI_DOUBLE_BATTLE_TEST("AI_FLAG_ASSUME_STATUS_MOVES correctly records assumed status moves")
|
||||
{
|
||||
PASSES_RANDOMLY(ASSUME_STATUS_HIGH_ODDS, 100, RNG_AI_ASSUME_STATUS_HIGH_ODDS);
|
||||
PASSES_RANDOMLY(ASSUME_STATUS_MEDIUM_ODDS, 100, RNG_AI_ASSUME_STATUS_MEDIUM_ODDS);
|
||||
PASSES_RANDOMLY(ASSUME_STATUS_LOW_ODDS, 100, RNG_AI_ASSUME_STATUS_LOW_ODDS);
|
||||
PASSES_RANDOMLY(ASSUME_ALL_STATUS_ODDS, 100, RNG_AI_ASSUME_ALL_STATUS);
|
||||
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_ASSUME_STATUS_MOVES);
|
||||
PLAYER(SPECIES_TYPHLOSION) { Moves(MOVE_TACKLE, MOVE_COURT_CHANGE, MOVE_FAKE_OUT); }
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_HAIL, MOVE_SHED_TAIL, MOVE_THUNDERBOLT); }
|
||||
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_TACKLE); }
|
||||
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_TACKLE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_TACKLE, target:opponentLeft); MOVE(playerRight, MOVE_THUNDERBOLT, target:opponentRight); }
|
||||
} THEN {
|
||||
EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][0], MOVE_TACKLE);
|
||||
EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][1], MOVE_COURT_CHANGE);
|
||||
EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][2], MOVE_FAKE_OUT);
|
||||
EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_LEFT][3], MOVE_NONE);
|
||||
|
||||
EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][0], MOVE_HAIL);
|
||||
EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][1], MOVE_SHED_TAIL);
|
||||
EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][2], MOVE_THUNDERBOLT);
|
||||
EXPECT_EQ(gBattleHistory->usedMoves[B_POSITION_PLAYER_RIGHT][3], MOVE_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI_FLAG_ASSUME_STATUS_MOVES changes behavior")
|
||||
{
|
||||
if (ASSUME_STATUS_MOVES_HAS_TUNING)
|
||||
PASSES_RANDOMLY(ASSUME_STATUS_MEDIUM_ODDS, 100, RNG_AI_ASSUME_STATUS_MEDIUM_ODDS);
|
||||
else
|
||||
PASSES_RANDOMLY(ASSUME_ALL_STATUS_ODDS, 100, RNG_AI_ASSUME_ALL_STATUS);
|
||||
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_ASSUME_STATUS_MOVES);
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_REST, MOVE_HEADBUTT); }
|
||||
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_WORRY_SEED, MOVE_HEADBUTT); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_HEADBUTT); EXPECT_MOVE(opponent, MOVE_WORRY_SEED); }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user