Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Gen 9 configs for Protean/Libero, Intrepid Sword and Dauntless Sword #3614

Merged
merged 6 commits into from
Dec 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 13 additions & 10 deletions include/battle.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ struct DisableStruct
u8 steelSurgeDone:1;
u8 weatherAbilityDone:1;
u8 terrainAbilityDone:1;
u8 usedProteanLibero:1;
};

struct ProtectStruct
Expand Down Expand Up @@ -699,10 +700,10 @@ struct BattleStruct
u16 changedSpecies[NUM_BATTLE_SIDES][PARTY_SIZE]; // For forms when multiple mons can change into the same pokemon.
u8 quickClawBattlerId;
struct LostItem itemLost[PARTY_SIZE]; // Player's team that had items consumed or stolen (two bytes per party member)
u8 blunderPolicy:1; // should blunder policy activate
u8 swapDamageCategory:1; // Photon Geyser, Shell Side Arm, Light That Burns the Sky
u8 forcedSwitch:4; // For each battler
u8 switchInAbilityPostponed:4; // To not activate against an empty field, each bit for battler
u8 blunderPolicy:1; // should blunder policy activate
u8 swapDamageCategory:1; // Photon Geyser, Shell Side Arm, Light That Burns the Sky
u8 ballSpriteIds[2]; // item gfx, window gfx
u8 appearedInBattle; // Bitfield to track which Pokemon appeared in battle. Used for Burmy's form change
u8 skyDropTargets[MAX_BATTLERS_COUNT]; // For Sky Drop, to account for if multiple Pokemon use Sky Drop in a double battle.
Expand All @@ -721,22 +722,24 @@ struct BattleStruct
uq4_12_t supremeOverlordModifier[MAX_BATTLERS_COUNT];
u8 itemPartyIndex[MAX_BATTLERS_COUNT];
u8 itemMoveIndex[MAX_BATTLERS_COUNT];
bool8 trainerSlideHalfHpMsgDone;
u8 trainerSlideFirstCriticalHitMsgState:2;
u8 trainerSlideFirstSuperEffectiveHitMsgState:2;
u8 trainerSlideFirstSTABMoveMsgState:2;
u8 trainerSlidePlayerMonUnaffectedMsgState:2;
bool8 trainerSlideMegaEvolutionMsgDone;
bool8 trainerSlideZMoveMsgDone;
bool8 trainerSlideBeforeFirstTurnMsgDone;
bool8 trainerSlideDynamaxMsgDone;
u8 trainerSlideHalfHpMsgDone:1;
u8 trainerSlideMegaEvolutionMsgDone:1;
u8 trainerSlideZMoveMsgDone:1;
u8 trainerSlideBeforeFirstTurnMsgDone:1;
u8 trainerSlideDynamaxMsgDone:1;
u8 pledgeMove:1;
u8 isSkyBattle:1;
u32 aiDelayTimer; // Counts number of frames AI takes to choose an action.
u32 aiDelayFrames; // Number of frames it took to choose an action.
bool8 transformZeroToHero[PARTY_SIZE][NUM_BATTLE_SIDES];
u8 pledgeMove:1;
bool8 isSkyBattle:1;
u8 timesGotHit[NUM_BATTLE_SIDES][PARTY_SIZE];
u8 enduredDamage;
u8 transformZeroToHero[NUM_BATTLE_SIDES];
u8 intrepidSwordBoost[NUM_BATTLE_SIDES];
u8 dauntlessShieldBoost[NUM_BATTLE_SIDES];
};

// The palaceFlags member of struct BattleStruct contains 1 flag per move to indicate which moves the AI should consider,
Expand Down
6 changes: 6 additions & 0 deletions include/config/battle.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@
#define B_TRANSFORM_SHINY GEN_LATEST // In Gen4+, Transform will copy the shiny state of the opponent instead of maintaining its own shiny state.
#define B_TRANSFORM_FORM_CHANGES GEN_LATEST // In Gen5+, Transformed Pokemon cannot change forms.

#define B_WIDE_GUARD GEN_LATEST // In Gen5 only, Quick Guard has a chance to fail if used consecutively.
#define B_QUICK_GUARD GEN_LATEST // In Gen5 only, Wide Guard has a chance to fail if used consecutively.

// Ability settings
#define B_EXPANDED_ABILITY_NAMES TRUE // If TRUE, ability names are increased from 12 characters to 16 characters.
#define B_ABILITY_WEATHER GEN_LATEST // In Gen6+, ability-induced weather lasts 5 turns. Before, it lasted until the battle ended or until it was changed by a move or a different weather-affecting ability.
Expand All @@ -130,6 +133,9 @@
#define B_TRANSISTOR_BOOST GEN_LATEST // In Gen9+, Transistor will only boost Electric-type moves by 1.3x as opposed to 1.5x.
#define B_ILLUMINATE_EFFECT GEN_LATEST // In Gen9+, Illuminate prevents accuracy reductions and ignores the target's evasion.
#define B_WEAK_ARMOR_SPEED GEN_LATEST // In Gen7+, Weak Armor raises Speed by 2 stages instead of 1 when hit by a physical move.
#define B_PROTEAN_LIBERO GEN_LATEST // In Gen7+, Weak Armor raises Speed by 2 stages instead of 1 when hit by a physical move.
#define B_INTREPID_SWORD GEN_LATEST // In Gen9+, Intrepid Sword raises Attack by one stage only once per Battle.
#define B_DAUNTLESS_SHIELD GEN_LATEST // In Gen9+, Dauntless Shield raises Defense by one stage only once per Battle.

// Item settings
#define B_HP_BERRIES GEN_LATEST // In Gen4+, berries which restore HP activate immediately after HP drops to half. In Gen3, the effect occurs at the end of the turn.
Expand Down
9 changes: 7 additions & 2 deletions src/battle_script_commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -1244,11 +1244,12 @@ static bool32 TryAegiFormChange(void)
bool32 ProteanTryChangeType(u32 battler, u32 ability, u32 move, u32 moveType)
{
if ((ability == ABILITY_PROTEAN || ability == ABILITY_LIBERO)
&& !gDisableStructs[gBattlerAttacker].usedProteanLibero
&& (gBattleMons[battler].type1 != moveType || gBattleMons[battler].type2 != moveType
|| (gBattleMons[battler].type3 != moveType && gBattleMons[battler].type3 != TYPE_MYSTERY))
&& move != MOVE_STRUGGLE)
{
SET_BATTLER_TYPE(gBattlerAttacker, moveType);
SET_BATTLER_TYPE(battler, moveType);
return TRUE;
}
return FALSE;
Expand Down Expand Up @@ -1324,6 +1325,8 @@ static void Cmd_attackcanceler(void)
// Check Protean activation.
if (ProteanTryChangeType(gBattlerAttacker, attackerAbility, gCurrentMove, moveType))
{
if (B_PROTEAN_LIBERO == GEN_9)
gDisableStructs[gBattlerAttacker].usedProteanLibero = TRUE;
PREPARE_TYPE_BUFFER(gBattleTextBuff1, moveType);
gBattlerAbility = gBattlerAttacker;
BattleScriptPushCursor();
Expand Down Expand Up @@ -10639,7 +10642,9 @@ static void Cmd_setprotectlike(void)
if (gCurrentTurnActionNumber == (gBattlersCount - 1))
notLastTurn = FALSE;

if (sProtectSuccessRates[gDisableStructs[gBattlerAttacker].protectUses] >= Random() && notLastTurn)
if ((sProtectSuccessRates[gDisableStructs[gBattlerAttacker].protectUses] >= Random() && notLastTurn)
|| (gCurrentMove == MOVE_WIDE_GUARD && B_WIDE_GUARD != GEN_5)
|| (gCurrentMove == MOVE_QUICK_GUARD && B_QUICK_GUARD != GEN_5))
{
if (!gBattleMoves[gCurrentMove].argument) // Protects one mon only.
{
Expand Down
14 changes: 10 additions & 4 deletions src/battle_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -4704,19 +4704,25 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
break;
case ABILITY_INTREPID_SWORD:
if (!gSpecialStatuses[battler].switchInAbilityDone && CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN))
if (!gSpecialStatuses[battler].switchInAbilityDone && CompareStat(battler, STAT_ATK, MAX_STAT_STAGE, CMP_LESS_THAN)
&& !(gBattleStruct->intrepidSwordBoost[GetBattlerSide(battler)] & gBitTable[gBattlerPartyIndexes[battler]]))
{
gBattlerAttacker = battler;
if (B_INTREPID_SWORD == GEN_9)
gBattleStruct->intrepidSwordBoost[GetBattlerSide(battler)] |= gBitTable[gBattlerPartyIndexes[battler]];
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
SET_STATCHANGER(STAT_ATK, 1, FALSE);
BattleScriptPushCursorAndCallback(BattleScript_BattlerAbilityStatRaiseOnSwitchIn);
effect++;
}
break;
case ABILITY_DAUNTLESS_SHIELD:
if (!gSpecialStatuses[battler].switchInAbilityDone && CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN))
if (!gSpecialStatuses[battler].switchInAbilityDone && CompareStat(battler, STAT_DEF, MAX_STAT_STAGE, CMP_LESS_THAN)
&& !(gBattleStruct->dauntlessShieldBoost[GetBattlerSide(battler)] & gBitTable[gBattlerPartyIndexes[battler]]))
{
gBattlerAttacker = battler;
if (B_DAUNTLESS_SHIELD == GEN_9)
gBattleStruct->dauntlessShieldBoost[GetBattlerSide(battler)] |= gBitTable[gBattlerPartyIndexes[battler]];
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
SET_STATCHANGER(STAT_DEF, 1, FALSE);
BattleScriptPushCursorAndCallback(BattleScript_BattlerAbilityStatRaiseOnSwitchIn);
Expand Down Expand Up @@ -4816,10 +4822,10 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32

if (!gSpecialStatuses[battler].switchInAbilityDone
&& GetMonData(mon, MON_DATA_SPECIES) == SPECIES_PALAFIN_HERO
&& !gBattleStruct->transformZeroToHero[gBattlerPartyIndexes[battler]][side])
&& !(gBattleStruct->transformZeroToHero[side] & gBitTable[gBattlerPartyIndexes[battler]]))
{
gSpecialStatuses[battler].switchInAbilityDone = TRUE;
gBattleStruct->transformZeroToHero[gBattlerPartyIndexes[battler]][side] = TRUE;
gBattleStruct->transformZeroToHero[side] |= gBitTable[gBattlerPartyIndexes[battler]];
BattleScriptPushCursorAndCallback(BattleScript_ZeroToHeroActivates);
effect++;
}
Expand Down
47 changes: 47 additions & 0 deletions test/battle/ability/dauntless_shield.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include "global.h"
#include "test/battle.h"

ASSUMPTIONS
{
ASSUME(P_GEN_8_POKEMON == TRUE);
ASSUME(B_PROTEAN_LIBERO == GEN_9);
}

SINGLE_BATTLE_TEST("Dauntless Shield raises Attack by one stage")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ZAMAZENTA) { Ability(ABILITY_DAUNTLESS_SHIELD); }
} WHEN {
TURN { }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_DAUNTLESS_SHIELD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Zamazenta's Dauntless Shield raised its Defense!");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 1);
}
}

SINGLE_BATTLE_TEST("Dauntless Shield raises Attack by one stage only once per battle")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ZAMAZENTA) { Ability(ABILITY_DAUNTLESS_SHIELD); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { SWITCH(opponent, 1); }
TURN { SWITCH(opponent, 0); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_DAUNTLESS_SHIELD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Zamazenta's Dauntless Shield raised its Defense!");
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_DAUNTLESS_SHIELD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Zamazenta's Dauntless Shield raised its Defense!");
}
} THEN {
EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
}
}
47 changes: 47 additions & 0 deletions test/battle/ability/intrepid_sword.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#include "global.h"
#include "test/battle.h"

ASSUMPTIONS
{
ASSUME(P_GEN_8_POKEMON == TRUE);
ASSUME(B_INTREPID_SWORD == GEN_9);
}

SINGLE_BATTLE_TEST("Intrepid Sword raises Attack by one stage")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); }
} WHEN {
TURN { }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_INTREPID_SWORD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Zacian's Intrepid Sword raised its Attack!");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE + 1);
}
}

SINGLE_BATTLE_TEST("Intrepid Sword raises Attack by one stage only once per battle")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_ZACIAN) { Ability(ABILITY_INTREPID_SWORD); }
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { SWITCH(opponent, 1); }
TURN { SWITCH(opponent, 0); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_INTREPID_SWORD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Zacian's Intrepid Sword raised its Attack!");
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_INTREPID_SWORD);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Zacian's Intrepid Sword raised its Attack!");
}
} THEN {
EXPECT_EQ(opponent->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
}
}
34 changes: 34 additions & 0 deletions test/battle/ability/protean.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#include "global.h"
#include "test/battle.h"

ASSUMPTIONS
{
ASSUME(B_PROTEAN_LIBERO == GEN_9);
}

SINGLE_BATTLE_TEST("Protean changes the type of the user only once per switch in")
{
GIVEN {
PLAYER(SPECIES_REGIROCK);
OPPONENT(SPECIES_KECLEON) { Ability(ABILITY_PROTEAN); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_WATER_GUN); }
TURN { MOVE(opponent, MOVE_TACKLE); }
TURN { SWITCH(opponent, 1); }
TURN { SWITCH(opponent, 0); }
TURN { MOVE(opponent, MOVE_WATER_GUN); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_PROTEAN);
MESSAGE("Foe Kecleon transformed into the Water type!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent);
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_PROTEAN);
MESSAGE("Foe Kecleon transformed into the Normal type!");
}
ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, opponent);
ABILITY_POPUP(opponent, ABILITY_PROTEAN);
MESSAGE("Foe Kecleon transformed into the Water type!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent);
}
}
65 changes: 59 additions & 6 deletions test/battle/move_effect/protect.c
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,13 @@ SINGLE_BATTLE_TEST("Spiky Shield does 1/8 dmg of max hp of attackers making cont
u16 usedMove = MOVE_NONE;
u16 hp = 400, maxHp = 400;

PARAMETRIZE { usedMove = MOVE_TACKLE; hp = 1;}
PARAMETRIZE { usedMove = MOVE_TACKLE;}
PARAMETRIZE { usedMove = MOVE_LEER;}
PARAMETRIZE { usedMove = MOVE_WATER_GUN;}
PARAMETRIZE { usedMove = MOVE_TACKLE; hp = 1; }
PARAMETRIZE { usedMove = MOVE_TACKLE; }
PARAMETRIZE { usedMove = MOVE_LEER; }
PARAMETRIZE { usedMove = MOVE_WATER_GUN; }

GIVEN {
PLAYER(SPECIES_WOBBUFFET) {HP(hp); MaxHP(maxHp); }
PLAYER(SPECIES_WOBBUFFET) { HP(hp); MaxHP(maxHp); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
Expand Down Expand Up @@ -295,7 +295,7 @@ DOUBLE_BATTLE_TEST("Wide Guard protects self and ally from multi-target moves")
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_WIDE_GUARD); MOVE(playerLeft, move, target:opponentLeft); }
TURN { MOVE(opponentLeft, MOVE_WIDE_GUARD); MOVE(playerLeft, move, target: opponentLeft); }
TURN {}
} SCENE {
MESSAGE("Foe Wobbuffet used Wide Guard!");
Expand All @@ -320,6 +320,34 @@ DOUBLE_BATTLE_TEST("Wide Guard protects self and ally from multi-target moves")
}
}

DOUBLE_BATTLE_TEST("Wide Guard can not fail on consecutive turns")
{
u8 turns;

PASSES_RANDOMLY(2, 2);
GIVEN {
ASSUME(gBattleMoves[MOVE_HYPER_VOICE].target == MOVE_TARGET_BOTH);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_WIDE_GUARD); MOVE(playerLeft, MOVE_HYPER_VOICE, target: opponentLeft); }
TURN { MOVE(opponentLeft, MOVE_WIDE_GUARD); MOVE(playerLeft, MOVE_HYPER_VOICE, target: opponentLeft); }
TURN {}
} SCENE {
for (turns = 0; turns < 2; turns++) {
MESSAGE("Foe Wobbuffet used Wide Guard!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_WIDE_GUARD, opponentLeft);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_HYPER_VOICE, playerLeft);
MESSAGE("Foe Wobbuffet protected itself!");
NOT HP_BAR(opponentLeft);
MESSAGE("Foe Wobbuffet protected itself!");
NOT HP_BAR(opponentRight);
}
}
}

DOUBLE_BATTLE_TEST("Quick Guard protects self and ally from priority moves")
{
u16 move = MOVE_NONE;
Expand Down Expand Up @@ -355,6 +383,31 @@ DOUBLE_BATTLE_TEST("Quick Guard protects self and ally from priority moves")
}
}

DOUBLE_BATTLE_TEST("Quick Guard can not fail on consecutive turns")
{
u8 turns;

PASSES_RANDOMLY(2, 2);
GIVEN {
ASSUME(gBattleMoves[MOVE_QUICK_ATTACK].priority == 1);
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponentLeft, MOVE_QUICK_GUARD); MOVE(playerLeft, MOVE_QUICK_ATTACK, target: opponentRight); }
TURN { MOVE(opponentLeft, MOVE_QUICK_GUARD); MOVE(playerLeft, MOVE_QUICK_ATTACK, target: opponentRight); }
} SCENE {
for (turns = 0; turns < 2; turns++) {
MESSAGE("Foe Wobbuffet used Quick Guard!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_GUARD, opponentLeft);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_QUICK_ATTACK, playerLeft);
MESSAGE("Foe Wobbuffet protected itself!");
NOT HP_BAR(opponentRight);
}
}
}

DOUBLE_BATTLE_TEST("Crafty Shield protects self and ally from status moves")
{
u16 move = MOVE_NONE;
Expand Down
Loading