Rename EFFECT_FAKE_OUT to EFFECT_FIRST_TURN_ONLY (#4081)

* Splits First Impression effect from Fake Out

* Fix test failing

* rename EFFECT_FAKE_OUT

* use moveeffect chance for fake out and priority field for first impression

* rename rest of fake out

* messed up merge

* remove useful comment

---------

Co-authored-by: Eduardo Quezada D'Ottone <eduardo602002@gmail.com>
This commit is contained in:
Alex 2024-01-31 13:32:58 +01:00 committed by GitHub
parent 7afa20029c
commit 6d3fa525d5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 76 additions and 40 deletions

View file

@ -4834,7 +4834,7 @@ BattleScript_AlreadyAtFullHp::
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_EffectFakeOut::
BattleScript_EffectFirstTurnOnly::
attackcanceler
jumpifnotfirstturn BattleScript_FailedFromAtkString
goto BattleScript_EffectHit

View file

@ -29,7 +29,7 @@ bool32 IsBattlerTrapped(u32 battler, bool32 switching);
s32 AI_WhoStrikesFirst(u32 battlerAI, u32 battler2, u32 moveConsidered);
bool32 CanTargetFaintAi(u32 battlerDef, u32 battlerAtk);
u32 NoOfHitsForTargetToFaintAI(u32 battlerDef, u32 battlerAtk);
u32 GetBestDmgMoveFromTarget(u32 battlerAtk, u32 battlerDef);
u32 GetBestDmgMoveFromBattler(u32 battlerAtk, u32 battlerDef);
bool32 CanTargetMoveFaintAi(u32 move, u32 battlerDef, u32 battlerAtk, u32 nHits);
bool32 CanTargetFaintAiWithMod(u32 battlerDef, u32 battlerAtk, s32 hpMod, s32 dmgMod);
s32 AI_DecideKnownAbilityForTurn(u32 battlerId);

View file

@ -648,7 +648,7 @@ extern const u8 BattleScript_EffectBeatUp[];
extern const u8 BattleScript_EffectSemiInvulnerable[];
extern const u8 BattleScript_EffectDefenseCurl[];
extern const u8 BattleScript_EffectSoftboiled[];
extern const u8 BattleScript_EffectFakeOut[];
extern const u8 BattleScript_EffectFirstTurnOnly[];
extern const u8 BattleScript_EffectUproar[];
extern const u8 BattleScript_EffectStockpile[];
extern const u8 BattleScript_EffectSpitUp[];

View file

@ -128,7 +128,7 @@ enum {
EFFECT_SEMI_INVULNERABLE,
EFFECT_DEFENSE_CURL,
EFFECT_SOFTBOILED, // differences vs Recover - can be used outside of battle to restore HP
EFFECT_FAKE_OUT,
EFFECT_FIRST_TURN_ONLY,
EFFECT_UPROAR,
EFFECT_STOCKPILE,
EFFECT_SPIT_UP,

View file

@ -880,6 +880,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
break;
case ABILITY_DAZZLING:
case ABILITY_QUEENLY_MAJESTY:
case ABILITY_ARMOR_TAIL:
if (atkPriority > 0)
RETURN_SCORE_MINUS(10);
break;
@ -979,6 +980,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
break;
case ABILITY_DAZZLING:
case ABILITY_QUEENLY_MAJESTY:
case ABILITY_ARMOR_TAIL:
if (atkPriority > 0)
RETURN_SCORE_MINUS(10);
break;
@ -1706,7 +1708,7 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
case EFFECT_TELEPORT:
ADJUST_SCORE(-10);
break;
case EFFECT_FAKE_OUT:
case EFFECT_FIRST_TURN_ONLY:
if (!gDisableStructs[battlerAtk].isFirstTurn)
ADJUST_SCORE(-10);
break;
@ -3818,14 +3820,11 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(DECENT_EFFECT);
IncreaseStatUpScore(battlerAtk, battlerDef, STAT_CHANGE_DEF, &score);
break;
case EFFECT_FAKE_OUT:
if (move == MOVE_FAKE_OUT) // filter out first impression
{
if (ShouldFakeOut(battlerAtk, battlerDef, move))
ADJUST_SCORE(BEST_EFFECT);
else
ADJUST_SCORE(-10);
}
case EFFECT_FIRST_TURN_ONLY:
if (ShouldFakeOut(battlerAtk, battlerDef, move))
ADJUST_SCORE(GOOD_EFFECT);
else if (gMovesInfo[move].priority >= 1 && gDisableStructs[battlerAtk].isFirstTurn && GetBestDmgMoveFromBattler(battlerAtk, battlerDef) == move)
ADJUST_SCORE(BEST_EFFECT);
break;
case EFFECT_STOCKPILE:
if (aiData->abilities[battlerAtk] == ABILITY_CONTRARY)
@ -4623,7 +4622,7 @@ static u32 AI_CalcMoveScore(u32 battlerAtk, u32 battlerDef, u32 move)
ADJUST_SCORE(GOOD_EFFECT);
break;
case MOVE_EFFECT_THROAT_CHOP:
if (gMovesInfo[GetBestDmgMoveFromTarget(battlerDef, battlerAtk)].soundMove)
if (gMovesInfo[GetBestDmgMoveFromBattler(battlerDef, battlerAtk)].soundMove)
{
if (AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_FASTER)
ADJUST_SCORE(GOOD_EFFECT);

View file

@ -105,7 +105,7 @@ static bool32 HasBadOdds(u32 battler, bool32 emitResult)
|| aiMoveEffect == EFFECT_SPIKES || aiMoveEffect == EFFECT_TOXIC_SPIKES || aiMoveEffect == EFFECT_STEALTH_ROCK || aiMoveEffect == EFFECT_STICKY_WEB || aiMoveEffect == EFFECT_LEECH_SEED
|| aiMoveEffect == EFFECT_EXPLOSION
|| aiMoveEffect == EFFECT_SLEEP || aiMoveEffect == EFFECT_YAWN || aiMoveEffect == EFFECT_TOXIC || aiMoveEffect == EFFECT_WILL_O_WISP || aiMoveEffect == EFFECT_PARALYZE
|| aiMoveEffect == EFFECT_TRICK || aiMoveEffect == EFFECT_TRICK_ROOM || aiMoveEffect== EFFECT_WONDER_ROOM || aiMoveEffect == EFFECT_PSYCHO_SHIFT || aiMoveEffect == EFFECT_FAKE_OUT
|| aiMoveEffect == EFFECT_TRICK || aiMoveEffect == EFFECT_TRICK_ROOM || aiMoveEffect== EFFECT_WONDER_ROOM || aiMoveEffect == EFFECT_PSYCHO_SHIFT || aiMoveEffect == EFFECT_FIRST_TURN_ONLY
)
{
hasStatusMove = TRUE;

View file

@ -867,20 +867,20 @@ u32 NoOfHitsForTargetToFaintAI(u32 battlerDef, u32 battlerAtk)
return leastNumberOfHits;
}
u32 GetBestDmgMoveFromTarget(u32 battlerDef, u32 battlerAtk)
u32 GetBestDmgMoveFromBattler(u32 battlerAtk, u32 battlerDef)
{
u32 i;
u32 move = 0;
u32 bestDmg = 0;
u32 unusable = AI_DATA->moveLimitations[battlerDef];
u16 *moves = GetMovesArray(battlerDef);
u32 unusable = AI_DATA->moveLimitations[battlerAtk];
u16 *moves = GetMovesArray(battlerAtk);
for (i = 0; i < MAX_MON_MOVES; i++)
{
if (moves[i] != MOVE_NONE && moves[i] != MOVE_UNAVAILABLE && !(unusable & gBitTable[i])
&& bestDmg < AI_DATA->simulatedDmg[battlerDef][battlerAtk][i])
&& bestDmg < AI_DATA->simulatedDmg[battlerAtk][battlerDef][i])
{
bestDmg = AI_DATA->simulatedDmg[battlerDef][battlerAtk][i];
bestDmg = AI_DATA->simulatedDmg[battlerAtk][battlerDef][i];
move = moves[i];
}
}
@ -2724,7 +2724,7 @@ bool32 ShouldTrap(u32 battlerAtk, u32 battlerDef, u32 move)
bool32 ShouldFakeOut(u32 battlerAtk, u32 battlerDef, u32 move)
{
if (!gDisableStructs[battlerAtk].isFirstTurn
if ((!gDisableStructs[battlerAtk].isFirstTurn && MoveHasMoveEffectWithChance(move, MOVE_EFFECT_FLINCH, 100))
|| AI_DATA->abilities[battlerAtk] == ABILITY_GORILLA_TACTICS
|| AI_DATA->holdEffects[battlerAtk] == HOLD_EFFECT_CHOICE_BAND
|| AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_COVERT_CLOAK
@ -3418,7 +3418,7 @@ void IncreaseParalyzeScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score)
if ((defSpeed >= atkSpeed && defSpeed / 2 < atkSpeed) // You'll go first after paralyzing foe
|| HasMoveEffectANDArg(battlerAtk, EFFECT_DOUBLE_POWER_ON_ARG_STATUS, STATUS1_PARALYSIS)
|| (HasMoveWithMoveEffectExcept(battlerAtk, MOVE_EFFECT_FLINCH, EFFECT_FAKE_OUT)) // filter out Fake Out
|| (HasMoveWithMoveEffectExcept(battlerAtk, MOVE_EFFECT_FLINCH, EFFECT_FIRST_TURN_ONLY)) // filter out Fake Out
|| gBattleMons[battlerDef].status2 & STATUS2_INFATUATION
|| gBattleMons[battlerDef].status2 & STATUS2_CONFUSION)
ADJUST_SCORE_PTR(GOOD_EFFECT);
@ -3459,7 +3459,7 @@ void IncreaseConfusionScore(u32 battlerAtk, u32 battlerDef, u32 move, s32 *score
{
if (gBattleMons[battlerDef].status1 & STATUS1_PARALYSIS
|| gBattleMons[battlerDef].status2 & STATUS2_INFATUATION
|| (AI_DATA->abilities[battlerAtk] == ABILITY_SERENE_GRACE && HasMoveWithMoveEffectExcept(battlerAtk, MOVE_EFFECT_FLINCH, EFFECT_FAKE_OUT)))
|| (AI_DATA->abilities[battlerAtk] == ABILITY_SERENE_GRACE && HasMoveWithMoveEffectExcept(battlerAtk, MOVE_EFFECT_FLINCH, EFFECT_FIRST_TURN_ONLY)))
ADJUST_SCORE_PTR(GOOD_EFFECT);
else
ADJUST_SCORE_PTR(DECENT_EFFECT);

View file

@ -363,7 +363,7 @@ void BattleArena_AddMindPoints(u8 battler)
// All moves with power == 0 give 0 points, with the following exceptions:
// - Protect, Detect, and Endure subtract 1 point
if (gMovesInfo[gCurrentMove].effect == EFFECT_FAKE_OUT
if (gMovesInfo[gCurrentMove].effect == EFFECT_FIRST_TURN_ONLY
|| gMovesInfo[gCurrentMove].effect == EFFECT_PROTECT
|| gMovesInfo[gCurrentMove].effect == EFFECT_ENDURE)
{

View file

@ -817,9 +817,9 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
.encourageEncore = TRUE,
},
[EFFECT_FAKE_OUT] =
[EFFECT_FIRST_TURN_ONLY] =
{
.battleScript = BattleScript_EffectFakeOut,
.battleScript = BattleScript_EffectFirstTurnOnly,
.battleTvScore = 4,
.encourageEncore = TRUE,
},

View file

@ -6188,7 +6188,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
"that causes flinching."),
.priority = B_UPDATED_MOVE_DATA >= GEN_5 ? 3 : 1,
.makesContact = B_UPDATED_MOVE_DATA >= GEN_4,
.effect = EFFECT_FAKE_OUT,
.effect = EFFECT_FIRST_TURN_ONLY,
.power = 40,
.type = TYPE_NORMAL,
.accuracy = 100,
@ -14987,7 +14987,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.description = COMPOUND_STRING(
"Hits hard and first.\n"
"Only works first turn."),
.effect = EFFECT_FAKE_OUT,
.effect = EFFECT_FIRST_TURN_ONLY,
.power = 90,
.type = TYPE_BUG,
.accuracy = 100,

View file

@ -569,7 +569,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI considers hazard damage whe
OPPONENT(SPECIES_TYPHLOSION) { Speed(200); Moves(MOVE_FLAMETHROWER); SpAttack(317); SpDefense(207); MaxHP(297); } // Outspeends and 2HKOs Meganium
} WHEN {
TURN { MOVE(player, MOVE_STEALTH_ROCK) ;}
TURN { MOVE(player, MOVE_SURF) ; EXPECT_SEND_OUT(opponent, aiIsSmart ? 2 : 1); } // AI sends out Typhlosion to get the KO with the flag rather than Charizard
TURN { MOVE(player, MOVE_SURF); EXPECT_SEND_OUT(opponent, aiIsSmart ? 2 : 1); } // AI sends out Typhlosion to get the KO with the flag rather than Charizard
}
}
@ -593,7 +593,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Mid-battle switches prioritize
OPPONENT(SPECIES_LOMBRE) { Level(30); Moves(move2); Speed(4); }
OPPONENT(SPECIES_HARIYAMA) { Level(30); Moves(MOVE_VITAL_THROW); Speed(4); }
} WHEN {
TURN { MOVE(player, MOVE_GROWL) ; EXPECT_SWITCH(opponent, expectedIndex); }
TURN { MOVE(player, MOVE_GROWL); EXPECT_SWITCH(opponent, expectedIndex); }
}
}
@ -606,7 +606,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Mid-battle switches prioritize
OPPONENT(SPECIES_ARON) { Level(30); Moves(MOVE_HEADBUTT); Speed(4); } // Mid battle, AI sends out Aron
OPPONENT(SPECIES_ELECTRODE) { Level(30); Moves(MOVE_CHARGE_BEAM); Speed(6); }
} WHEN {
TURN { MOVE(player, MOVE_WING_ATTACK) ; EXPECT_SWITCH(opponent, 1); }
TURN { MOVE(player, MOVE_WING_ATTACK); EXPECT_SWITCH(opponent, 1); }
}
}
@ -619,7 +619,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: Post-KO switches prioritize of
OPPONENT(SPECIES_ARON) { Level(30); Moves(MOVE_HEADBUTT); Speed(4); } // Mid battle, AI sends out Aron
OPPONENT(SPECIES_ELECTRODE) { Level(30); Moves(MOVE_CHARGE_BEAM); Speed(6); }
} WHEN {
TURN { MOVE(player, MOVE_WING_ATTACK) ; EXPECT_SEND_OUT(opponent, 2); }
TURN { MOVE(player, MOVE_WING_ATTACK); EXPECT_SEND_OUT(opponent, 2); }
}
}
@ -631,8 +631,8 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI switches out after sufficient
OPPONENT(SPECIES_GRIMER) { Level(30); Moves(MOVE_TACKLE); Speed(4); }
OPPONENT(SPECIES_PONYTA) { Level(30); Moves(MOVE_HEADBUTT); Speed(4); }
} WHEN {
TURN { MOVE(player, MOVE_CHARM) ;}
TURN { MOVE(player, MOVE_TACKLE) ; EXPECT_SWITCH(opponent, 1); }
TURN { MOVE(player, MOVE_CHARM); }
TURN { MOVE(player, MOVE_TACKLE); EXPECT_SWITCH(opponent, 1); }
}
}
@ -640,8 +640,8 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will not switch out if Pokemo
{
u32 move1;
PARAMETRIZE{move1 = MOVE_TACKLE; }
PARAMETRIZE{move1 = MOVE_RAPID_SPIN; }
PARAMETRIZE{ move1 = MOVE_TACKLE; }
PARAMETRIZE{ move1 = MOVE_RAPID_SPIN; }
GIVEN {
ASSUME(gMovesInfo[MOVE_TACKLE].category == DAMAGE_CATEGORY_PHYSICAL);
@ -653,9 +653,9 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will not switch out if Pokemo
OPPONENT(SPECIES_GRIMER) { Level(30); Moves(MOVE_TACKLE); Item(ITEM_FOCUS_SASH); Speed(4); }
OPPONENT(SPECIES_PONYTA) { Level(30); Moves(MOVE_HEADBUTT, move1); Speed(4); }
} WHEN {
TURN { MOVE(player, MOVE_STEALTH_ROCK) ;}
TURN { MOVE(player, MOVE_EARTHQUAKE) ;}
TURN { MOVE(player, MOVE_CHARM) ;}
TURN { MOVE(player, MOVE_STEALTH_ROCK); }
TURN { MOVE(player, MOVE_EARTHQUAKE); }
TURN { MOVE(player, MOVE_CHARM); }
TURN { // If the AI has a mon that can remove hazards, don't prevent them switching out
MOVE(player, MOVE_CHARM);
if (move1 == MOVE_RAPID_SPIN)
@ -665,3 +665,40 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will not switch out if Pokemo
}
}
}
AI_SINGLE_BATTLE_TEST("First Impression is preferred on the first turn of the species if it's the best dmg move")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_FIRST_IMPRESSION].effect == EFFECT_FIRST_TURN_ONLY);
ASSUME(gMovesInfo[MOVE_FIRST_IMPRESSION].power == 90);
ASSUME(gMovesInfo[MOVE_LUNGE].power == 80);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
PLAYER(SPECIES_KANGASKHAN);
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_FIRST_IMPRESSION, MOVE_LUNGE); }
} WHEN {
TURN { EXPECT_MOVE(opponent, MOVE_FIRST_IMPRESSION); }
TURN { EXPECT_MOVE(opponent, MOVE_LUNGE); }
}
}
AI_SINGLE_BATTLE_TEST("First Impression is not chosen if it's blocked by certain abilities")
{
u16 species;
u16 ability;
PARAMETRIZE { species = SPECIES_BRUXISH; ability = ABILITY_DAZZLING; }
PARAMETRIZE { species = SPECIES_FARIGIRAF; ability = ABILITY_ARMOR_TAIL; }
PARAMETRIZE { species = SPECIES_TSAREENA; ability = ABILITY_QUEENLY_MAJESTY; }
KNOWN_FAILING; // Fails because the Omniscient flag is currently broken. It should pass after it is fixed
GIVEN {
ASSUME(gMovesInfo[MOVE_FIRST_IMPRESSION].effect == EFFECT_FIRST_TURN_ONLY);
ASSUME(gMovesInfo[MOVE_FIRST_IMPRESSION].power == 90);
ASSUME(gMovesInfo[MOVE_LUNGE].power == 80);
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
PLAYER(species) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_FIRST_IMPRESSION, MOVE_LUNGE); }
} WHEN {
TURN { EXPECT_MOVE(opponent, MOVE_LUNGE); }
}
}

View file

@ -58,7 +58,7 @@ SINGLE_BATTLE_TEST("(DYNAMAX) Dynamax expires after three turns", u16 hp)
SINGLE_BATTLE_TEST("(DYNAMAX) Dynamaxed Pokemon cannot be flinched")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_FAKE_OUT].effect == EFFECT_FAKE_OUT);
ASSUME(gMovesInfo[MOVE_FAKE_OUT].effect == EFFECT_FIRST_TURN_ONLY);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {