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

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

* use bitfield

* battler fix

* fields
This commit is contained in:
Alex 2023-12-12 00:08:44 +01:00 committed by GitHub
parent 882c5529e0
commit 44e81d4f4c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 223 additions and 22 deletions

View file

@ -113,6 +113,7 @@ struct DisableStruct
u8 steelSurgeDone:1;
u8 weatherAbilityDone:1;
u8 terrainAbilityDone:1;
u8 usedProteanLibero:1;
};
struct ProtectStruct
@ -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.
@ -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,

View file

@ -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.
@ -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.

View file

@ -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;
@ -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();
@ -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.
{

View file

@ -4704,9 +4704,12 @@ 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);
@ -4714,9 +4717,12 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32
}
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);
@ -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++;
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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 {
@ -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!");
@ -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;
@ -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;