Merge branch '_RHH/master' into _RHH/upcoming
# Conflicts: # include/battle.h # include/constants/battle_script_commands.h # include/constants/pokemon.h # src/battle_ai_util.c # src/battle_main.c # src/battle_util.c # test/battle/ai.c
This commit is contained in:
commit
06153e4280
32 changed files with 478 additions and 160 deletions
|
@ -1654,11 +1654,11 @@
|
|||
.macro trygulpmissile
|
||||
callnative BS_TryGulpMissile
|
||||
.endm
|
||||
|
||||
|
||||
.macro tryactivategulpmissile
|
||||
callnative BS_TryActivateGulpMissile
|
||||
.endm
|
||||
|
||||
|
||||
.macro tryquash failInstr:req
|
||||
callnative BS_TryQuash
|
||||
.4byte \failInstr
|
||||
|
@ -2228,10 +2228,6 @@
|
|||
.4byte \failInstr
|
||||
.endm
|
||||
|
||||
.macro shellsidearmcheck
|
||||
various BS_ATTACKER, VARIOUS_SHELL_SIDE_ARM_CHECK
|
||||
.endm
|
||||
|
||||
.macro jumpifteanoberry jumpInstr:req
|
||||
various BS_ATTACKER, VARIOUS_TEATIME_TARGETS
|
||||
.4byte \jumpInstr
|
||||
|
|
|
@ -28263,6 +28263,7 @@ Move_TECTONIC_RAGE::
|
|||
waitforvisualfinish
|
||||
call UnsetPsychicBg
|
||||
waitbgfadein
|
||||
clearmonbg_static ANIM_ATTACKER
|
||||
createvisualtask AnimTask_AllBattlersVisible, 0xA
|
||||
waitforvisualfinish
|
||||
end
|
||||
|
|
|
@ -804,10 +804,6 @@ BattleScript_FlingMissed:
|
|||
ppreduce
|
||||
goto BattleScript_MoveMissedPause
|
||||
|
||||
BattleScript_EffectShellSideArm::
|
||||
shellsidearmcheck
|
||||
goto BattleScript_EffectHit
|
||||
|
||||
BattleScript_EffectPhotonGeyser::
|
||||
setphotongeysercategory
|
||||
goto BattleScript_EffectHit
|
||||
|
@ -8468,7 +8464,7 @@ BattleScript_ScriptingAbilityStatRaise::
|
|||
call BattleScript_AbilityPopUp
|
||||
copybyte sSAVED_DMG, gBattlerAttacker
|
||||
copybyte gBattlerAttacker, sBATTLER
|
||||
statbuffchange STAT_CHANGE_NOT_PROTECT_AFFECTED | MOVE_EFFECT_CERTAIN, NULL
|
||||
statbuffchange MOVE_EFFECT_AFFECTS_USER | STAT_CHANGE_NOT_PROTECT_AFFECTED | MOVE_EFFECT_CERTAIN, NULL
|
||||
setgraphicalstatchangevalues
|
||||
playanimation BS_SCRIPTING, B_ANIM_STATS_CHANGE, sB_ANIM_ARG1
|
||||
waitanimation
|
||||
|
|
|
@ -826,6 +826,7 @@ struct BattleStruct
|
|||
u8 supremeOverlordCounter[MAX_BATTLERS_COUNT];
|
||||
u8 quickClawRandom[MAX_BATTLERS_COUNT];
|
||||
u8 quickDrawRandom[MAX_BATTLERS_COUNT];
|
||||
u8 shellSideArmCategory[MAX_BATTLERS_COUNT][MAX_BATTLERS_COUNT];
|
||||
u8 boosterEnergyActivates;
|
||||
u8 distortedTypeMatchups;
|
||||
};
|
||||
|
|
|
@ -808,7 +808,6 @@ extern const u8 BattleScript_EffectPlasmaFists[];
|
|||
extern const u8 BattleScript_EffectHyperspaceFury[];
|
||||
extern const u8 BattleScript_EffectAuraWheel[];
|
||||
extern const u8 BattleScript_EffectPhotonGeyser[];
|
||||
extern const u8 BattleScript_EffectShellSideArm[];
|
||||
extern const u8 BattleScript_EffectNoRetreat[];
|
||||
extern const u8 BattleScript_EffectTarShot[];
|
||||
extern const u8 BattleScript_EffectPoltergeist[];
|
||||
|
|
|
@ -164,7 +164,7 @@ u32 IsAbilityOnOpposingSide(u32 battler, u32 ability);
|
|||
u32 IsAbilityOnField(u32 ability);
|
||||
u32 IsAbilityOnFieldExcept(u32 battler, u32 ability);
|
||||
u32 IsAbilityPreventingEscape(u32 battler);
|
||||
bool32 IsBattlerProtected(u32 battler, u32 move);
|
||||
bool32 IsBattlerProtected(u32 battlerAtk, u32 battlerDef, u32 move);
|
||||
bool32 CanBattlerEscape(u32 battler); // no ability check
|
||||
void BattleScriptExecute(const u8 *BS_ptr);
|
||||
void BattleScriptPushCursorAndCallback(const u8 *BS_ptr);
|
||||
|
@ -216,6 +216,7 @@ bool32 IsBelchPreventingMove(u32 battler, u32 move);
|
|||
bool32 HasEnoughHpToEatBerry(u32 battler, u32 hpFraction, u32 itemId);
|
||||
bool32 IsPartnerMonFromSameTrainer(u32 battler);
|
||||
u8 GetCategoryBasedOnStats(u32 battler);
|
||||
void SetShellSideArmCategory(void);
|
||||
bool32 MoveIsAffectedBySheerForce(u32 move);
|
||||
bool32 TestIfSheerForceAffected(u32 battler, u16 move);
|
||||
void TryRestoreHeldItems(void);
|
||||
|
|
|
@ -302,7 +302,6 @@ enum {
|
|||
EFFECT_HYPERSPACE_FURY,
|
||||
EFFECT_AURA_WHEEL,
|
||||
EFFECT_PHOTON_GEYSER,
|
||||
EFFECT_SHELL_SIDE_ARM,
|
||||
EFFECT_TERRAIN_PULSE,
|
||||
EFFECT_NO_RETREAT,
|
||||
EFFECT_TAR_SHOT,
|
||||
|
|
|
@ -206,35 +206,34 @@
|
|||
#define VARIOUS_JUMP_IF_WEATHER_AFFECTED 114
|
||||
#define VARIOUS_JUMP_IF_LEAF_GUARD_PROTECTED 115
|
||||
#define VARIOUS_SET_ATTACKER_STICKY_WEB_USER 116
|
||||
#define VARIOUS_SHELL_SIDE_ARM_CHECK 117
|
||||
#define VARIOUS_TRY_NO_RETREAT 118
|
||||
#define VARIOUS_TRY_TAR_SHOT 119
|
||||
#define VARIOUS_CAN_TAR_SHOT_WORK 120
|
||||
#define VARIOUS_CHECK_POLTERGEIST 121
|
||||
#define VARIOUS_CUT_1_3_HP_RAISE_STATS 122
|
||||
#define VARIOUS_TRY_END_NEUTRALIZING_GAS 123
|
||||
#define VARIOUS_JUMP_IF_UNDER_200 124
|
||||
#define VARIOUS_SET_SKY_DROP 125
|
||||
#define VARIOUS_CLEAR_SKY_DROP 126
|
||||
#define VARIOUS_SKY_DROP_YAWN 127
|
||||
#define VARIOUS_CURE_CERTAIN_STATUSES 128
|
||||
#define VARIOUS_TRY_RESET_NEGATIVE_STAT_STAGES 129
|
||||
#define VARIOUS_JUMP_IF_LAST_USED_ITEM_BERRY 130
|
||||
#define VARIOUS_JUMP_IF_LAST_USED_ITEM_HOLD_EFFECT 131
|
||||
#define VARIOUS_SAVE_BATTLER_ITEM 132
|
||||
#define VARIOUS_RESTORE_BATTLER_ITEM 133
|
||||
#define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 134
|
||||
#define VARIOUS_SET_BEAK_BLAST 135
|
||||
#define VARIOUS_SWAP_SIDE_STATUSES 136
|
||||
#define VARIOUS_SWAP_STATS 137
|
||||
#define VARIOUS_TEATIME_INVUL 138
|
||||
#define VARIOUS_TEATIME_TARGETS 139
|
||||
#define VARIOUS_TRY_WIND_RIDER_POWER 140
|
||||
#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 141
|
||||
#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 142
|
||||
#define VARIOUS_STORE_HEALING_WISH 143
|
||||
#define VARIOUS_HIT_SWITCH_TARGET_FAILED 144
|
||||
#define VARIOUS_TRY_REVIVAL_BLESSING 145
|
||||
#define VARIOUS_TRY_NO_RETREAT 117
|
||||
#define VARIOUS_TRY_TAR_SHOT 118
|
||||
#define VARIOUS_CAN_TAR_SHOT_WORK 119
|
||||
#define VARIOUS_CHECK_POLTERGEIST 120
|
||||
#define VARIOUS_CUT_1_3_HP_RAISE_STATS 121
|
||||
#define VARIOUS_TRY_END_NEUTRALIZING_GAS 122
|
||||
#define VARIOUS_JUMP_IF_UNDER_200 123
|
||||
#define VARIOUS_SET_SKY_DROP 124
|
||||
#define VARIOUS_CLEAR_SKY_DROP 125
|
||||
#define VARIOUS_SKY_DROP_YAWN 126
|
||||
#define VARIOUS_CURE_CERTAIN_STATUSES 127
|
||||
#define VARIOUS_TRY_RESET_NEGATIVE_STAT_STAGES 128
|
||||
#define VARIOUS_JUMP_IF_LAST_USED_ITEM_BERRY 129
|
||||
#define VARIOUS_JUMP_IF_LAST_USED_ITEM_HOLD_EFFECT 130
|
||||
#define VARIOUS_SAVE_BATTLER_ITEM 131
|
||||
#define VARIOUS_RESTORE_BATTLER_ITEM 132
|
||||
#define VARIOUS_BATTLER_ITEM_TO_LAST_USED_ITEM 133
|
||||
#define VARIOUS_SET_BEAK_BLAST 134
|
||||
#define VARIOUS_SWAP_SIDE_STATUSES 135
|
||||
#define VARIOUS_SWAP_STATS 136
|
||||
#define VARIOUS_TEATIME_INVUL 137
|
||||
#define VARIOUS_TEATIME_TARGETS 138
|
||||
#define VARIOUS_TRY_WIND_RIDER_POWER 139
|
||||
#define VARIOUS_ACTIVATE_WEATHER_CHANGE_ABILITIES 140
|
||||
#define VARIOUS_ACTIVATE_TERRAIN_CHANGE_ABILITIES 141
|
||||
#define VARIOUS_STORE_HEALING_WISH 142
|
||||
#define VARIOUS_HIT_SWITCH_TARGET_FAILED 143
|
||||
#define VARIOUS_TRY_REVIVAL_BLESSING 144
|
||||
|
||||
// Cmd_manipulatedamage
|
||||
#define DMG_CHANGE_SIGN 0
|
||||
|
|
|
@ -290,8 +290,8 @@
|
|||
#define EVO_LEVEL_FOG 42 // Pokémon reaches the specified level during fog in the overworld
|
||||
#define EVO_MOVE_TWO_SEGMENT 43 // Pokémon levels up, knows specified move, has a personality value with a modulus of 0
|
||||
#define EVO_MOVE_THREE_SEGMENT 44 // Pokémon levels up, knows specified move, has a personality value with a modulus of 1-99
|
||||
#define EVO_LEVEL_FAMILY_OF_THREE 45 // Pokémon reaches the specified level with a personality value with a modulus of 0
|
||||
#define EVO_LEVEL_FAMILY_OF_FOUR 46 // Pokémon reaches the specified level with a personality value with a modulus of 1-99
|
||||
#define EVO_LEVEL_FAMILY_OF_THREE 45 // Pokémon reaches the specified level in battle with a personality value with a modulus of 0
|
||||
#define EVO_LEVEL_FAMILY_OF_FOUR 46 // Pokémon reaches the specified level in battle with a personality value with a modulus of 1-99
|
||||
#define EVO_USE_MOVE_TWENTY_TIMES 47 // Pokémon levels up after having used a move for at least 20 times
|
||||
#define EVO_RECOIL_DAMAGE_MALE 48 // Pokémon levels up after having suffered specified amount of non-fainting recoil damage as a male
|
||||
#define EVO_RECOIL_DAMAGE_FEMALE 49 // Pokémon levels up after having suffered specified amount of non-fainting recoil damage as a female
|
||||
|
@ -307,6 +307,7 @@
|
|||
#define EVO_MODE_ITEM_CHECK 4 // If an Everstone is being held, still want to show that the stone *could* be used on that Pokémon to evolve
|
||||
#define EVO_MODE_BATTLE_SPECIAL 5
|
||||
#define EVO_MODE_OVERWORLD_SPECIAL 6
|
||||
#define EVO_MODE_BATTLE_ONLY 7 // This mode is only used in battles to support Tandemaus' unique requirement
|
||||
|
||||
#define MON_PIC_WIDTH 64
|
||||
#define MON_PIC_HEIGHT 64
|
||||
|
|
|
@ -188,6 +188,8 @@ enum RandomTag
|
|||
RNG_QUICK_CLAW,
|
||||
RNG_TRACE,
|
||||
RNG_FICKLE_BEAM,
|
||||
RNG_AI_ABILITY,
|
||||
RNG_SHELL_SIDE_ARM,
|
||||
};
|
||||
|
||||
#define RandomWeighted(tag, ...) \
|
||||
|
|
|
@ -972,6 +972,19 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
|||
&& IsNonVolatileStatusMoveEffect(moveEffect))
|
||||
RETURN_SCORE_MINUS(10);
|
||||
break;
|
||||
case ABILITY_VOLT_ABSORB:
|
||||
case ABILITY_LIGHTNING_ROD:
|
||||
if (moveType == TYPE_ELECTRIC)
|
||||
RETURN_SCORE_MINUS(20);
|
||||
break;
|
||||
case ABILITY_STORM_DRAIN:
|
||||
if (moveType == TYPE_WATER)
|
||||
RETURN_SCORE_MINUS(20);
|
||||
break;
|
||||
case ABILITY_FLASH_FIRE:
|
||||
if (moveType == TYPE_FIRE)
|
||||
RETURN_SCORE_MINUS(20);
|
||||
break;
|
||||
} // def ability checks
|
||||
|
||||
// target partner ability checks & not attacking partner
|
||||
|
@ -2913,20 +2926,6 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
|||
RETURN_SCORE_PLUS(DECENT_EFFECT);
|
||||
}
|
||||
break;
|
||||
case ABILITY_DEFIANT:
|
||||
if (IsStatLoweringEffect(effect)
|
||||
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_ATK))
|
||||
{
|
||||
RETURN_SCORE_PLUS(WEAK_EFFECT);
|
||||
}
|
||||
break;
|
||||
case ABILITY_COMPETITIVE:
|
||||
if (IsStatLoweringEffect(effect)
|
||||
&& BattlerStatCanRise(battlerAtkPartner, atkPartnerAbility, STAT_SPATK))
|
||||
{
|
||||
RETURN_SCORE_PLUS(WEAK_EFFECT);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} // ability checks
|
||||
|
||||
|
@ -2950,7 +2949,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
|||
case EFFECT_SWAGGER:
|
||||
if (gBattleMons[battlerAtkPartner].statStages[STAT_ATK] < MAX_STAT_STAGE
|
||||
&& HasMoveWithCategory(battlerAtkPartner, DAMAGE_CATEGORY_PHYSICAL)
|
||||
&& (!AI_CanBeConfused(battlerAtk, battlerAtkPartner, move, TRUE)
|
||||
&& (!AI_CanBeConfused(battlerAtk, battlerAtkPartner, move, atkPartnerAbility)
|
||||
|| atkPartnerHoldEffect == HOLD_EFFECT_CURE_CONFUSION
|
||||
|| atkPartnerHoldEffect == HOLD_EFFECT_CURE_STATUS))
|
||||
{
|
||||
|
@ -2960,7 +2959,7 @@ static s32 AI_DoubleBattle(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
|
|||
case EFFECT_FLATTER:
|
||||
if (gBattleMons[battlerAtkPartner].statStages[STAT_SPATK] < MAX_STAT_STAGE
|
||||
&& HasMoveWithCategory(battlerAtkPartner, DAMAGE_CATEGORY_SPECIAL)
|
||||
&& (!AI_CanBeConfused(battlerAtk, battlerAtkPartner, move, TRUE)
|
||||
&& (!AI_CanBeConfused(battlerAtk, battlerAtkPartner, move, atkPartnerAbility)
|
||||
|| atkPartnerHoldEffect == HOLD_EFFECT_CURE_CONFUSION
|
||||
|| atkPartnerHoldEffect == HOLD_EFFECT_CURE_STATUS))
|
||||
{
|
||||
|
|
|
@ -521,8 +521,9 @@ struct SimulatedDamage AI_CalcDamage(u32 move, u32 battlerAtk, u32 battlerDef, u
|
|||
}
|
||||
else if (gMovesInfo[move].effect == EFFECT_PHOTON_GEYSER)
|
||||
gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == DAMAGE_CATEGORY_PHYSICAL);
|
||||
|
||||
if (gMovesInfo[move].effect == EFFECT_NATURE_POWER)
|
||||
else if (move == MOVE_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] == DAMAGE_CATEGORY_SPECIAL)
|
||||
gBattleStruct->swapDamageCategory = TRUE;
|
||||
else if (gMovesInfo[move].effect == EFFECT_NATURE_POWER)
|
||||
move = GetNaturePowerMove();
|
||||
|
||||
// Temporarily enable other gimmicks for damage calcs if planned
|
||||
|
@ -917,17 +918,18 @@ static bool32 AI_IsMoveEffectInMinus(u32 battlerAtk, u32 battlerDef, u32 move, s
|
|||
s32 AI_WhichMoveBetter(u32 move1, u32 move2, u32 battlerAtk, u32 battlerDef, s32 noOfHitsToKo)
|
||||
{
|
||||
bool32 effect1, effect2;
|
||||
s32 defAbility = AI_DATA->abilities[battlerDef];
|
||||
u32 defAbility = AI_DATA->abilities[battlerDef];
|
||||
u32 atkAbility = AI_DATA->abilities[battlerAtk];
|
||||
|
||||
// Check if physical moves hurt.
|
||||
if (AI_DATA->holdEffects[battlerAtk] != HOLD_EFFECT_PROTECTIVE_PADS
|
||||
if (AI_DATA->holdEffects[battlerAtk] != HOLD_EFFECT_PROTECTIVE_PADS && atkAbility != ABILITY_LONG_REACH
|
||||
&& (AI_DATA->holdEffects[battlerDef] == HOLD_EFFECT_ROCKY_HELMET
|
||||
|| defAbility == ABILITY_IRON_BARBS || defAbility == ABILITY_ROUGH_SKIN))
|
||||
{
|
||||
if (IS_MOVE_PHYSICAL(move1) && !IS_MOVE_PHYSICAL(move2))
|
||||
if (gMovesInfo[move1].makesContact && !gMovesInfo[move2].makesContact)
|
||||
return -1;
|
||||
if (gMovesInfo[move2].makesContact && !gMovesInfo[move1].makesContact)
|
||||
return 1;
|
||||
if (IS_MOVE_PHYSICAL(move2) && !IS_MOVE_PHYSICAL(move1))
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check additional effects.
|
||||
|
@ -1198,6 +1200,8 @@ u32 AI_GetBattlerAbility(u32 battler)
|
|||
// does NOT include ability suppression checks
|
||||
s32 AI_DecideKnownAbilityForTurn(u32 battlerId)
|
||||
{
|
||||
u32 validAbilities[NUM_ABILITY_SLOTS];
|
||||
u8 i, numValidAbilities = 0;
|
||||
u32 knownAbility = AI_GetBattlerAbility(battlerId);
|
||||
|
||||
// We've had ability overwritten by e.g. Worry Seed. It is not part of AI_PARTY in case of switching
|
||||
|
@ -1219,18 +1223,15 @@ s32 AI_DecideKnownAbilityForTurn(u32 battlerId)
|
|||
if (knownAbility == ABILITY_SHADOW_TAG || knownAbility == ABILITY_MAGNET_PULL || knownAbility == ABILITY_ARENA_TRAP)
|
||||
return knownAbility;
|
||||
|
||||
// Else, guess the ability
|
||||
if (gSpeciesInfo[gBattleMons[battlerId].species].abilities[0] != ABILITY_NONE)
|
||||
for (i = 0; i < NUM_ABILITY_SLOTS; i++)
|
||||
{
|
||||
u32 abilityGuess = ABILITY_NONE;
|
||||
while (abilityGuess == ABILITY_NONE)
|
||||
{
|
||||
abilityGuess = gSpeciesInfo[gBattleMons[battlerId].species].abilities[Random() % NUM_ABILITY_SLOTS];
|
||||
}
|
||||
|
||||
return abilityGuess;
|
||||
if (gSpeciesInfo[gBattleMons[battlerId].species].abilities[i] != ABILITY_NONE)
|
||||
validAbilities[numValidAbilities++] = gSpeciesInfo[gBattleMons[battlerId].species].abilities[i];
|
||||
}
|
||||
|
||||
if (numValidAbilities > 0)
|
||||
return validAbilities[RandomUniform(RNG_AI_ABILITY, 0, numValidAbilities - 1)];
|
||||
|
||||
return ABILITY_NONE; // Unknown.
|
||||
}
|
||||
|
||||
|
|
|
@ -1602,6 +1602,7 @@ static u32 GetBattlerMonData(u32 battler, struct Pokemon *party, u32 monId, u8 *
|
|||
battleMon.abilityNum = GetMonData(&party[monId], MON_DATA_ABILITY_NUM);
|
||||
battleMon.otId = GetMonData(&party[monId], MON_DATA_OT_ID);
|
||||
battleMon.metLevel = GetMonData(&party[monId], MON_DATA_MET_LEVEL);
|
||||
battleMon.isShiny = GetMonData(&party[monId], MON_DATA_IS_SHINY);
|
||||
GetMonData(&party[monId], MON_DATA_NICKNAME, nickname);
|
||||
StringCopy_Nickname(battleMon.nickname, nickname);
|
||||
GetMonData(&party[monId], MON_DATA_OT_NAME, battleMon.otName);
|
||||
|
|
|
@ -585,6 +585,7 @@ void BattleLoadMonSpriteGfx(struct Pokemon *mon, u32 battler)
|
|||
if (B_TRANSFORM_SHINY >= GEN_4)
|
||||
{
|
||||
currentPersonality = gTransformedPersonalities[battler];
|
||||
isShiny = gTransformedShininess[battler];
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -3420,7 +3420,7 @@ const u8* FaintClearSetData(u32 battler)
|
|||
gBattleStruct->zmove.toBeUsed[battler] = MOVE_NONE;
|
||||
gBattleStruct->zmove.effect = EFFECT_HIT;
|
||||
// Clear Dynamax data
|
||||
UndoDynamax(battler);
|
||||
UndoDynamax(battler);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
@ -3905,6 +3905,7 @@ static void TryDoEventsBeforeFirstTurn(void)
|
|||
|
||||
memset(gQueuedStatBoosts, 0, sizeof(gQueuedStatBoosts)); // erase all totem boosts just to be safe
|
||||
|
||||
SetShellSideArmCategory();
|
||||
SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers
|
||||
|
||||
if (gBattleTypeFlags & BATTLE_TYPE_ARENA)
|
||||
|
@ -3997,6 +3998,7 @@ void BattleTurnPassed(void)
|
|||
|
||||
*(&gBattleStruct->absentBattlerFlags) = gAbsentBattlerFlags;
|
||||
BattlePutTextOnWindow(gText_EmptyString3, B_WIN_MSG);
|
||||
SetShellSideArmCategory();
|
||||
SetAiLogicDataForTurn(AI_DATA); // get assumed abilities, hold effects, etc of all battlers
|
||||
gBattleMainFunc = HandleTurnActionSelectionState;
|
||||
|
||||
|
@ -5600,9 +5602,9 @@ static void TryEvolvePokemon(void)
|
|||
sTriedEvolving |= gBitTable[i];
|
||||
|
||||
if (species == SPECIES_NONE && (gLeveledUpInBattle & gBitTable[i]))
|
||||
{
|
||||
{
|
||||
gLeveledUpInBattle &= ~(gBitTable[i]);
|
||||
species = GetEvolutionTargetSpecies(&gPlayerParty[i], EVO_MODE_NORMAL, gLeveledUpInBattle, NULL);
|
||||
species = GetEvolutionTargetSpecies(&gPlayerParty[i], EVO_MODE_BATTLE_ONLY, gLeveledUpInBattle, NULL);
|
||||
}
|
||||
|
||||
if (species == SPECIES_NONE)
|
||||
|
|
|
@ -1400,7 +1400,7 @@ static void Cmd_attackcanceler(void)
|
|||
gBattlescriptCurrInstr = BattleScript_TookAttack;
|
||||
RecordAbilityBattle(gBattlerTarget, gLastUsedAbility);
|
||||
}
|
||||
else if (IsBattlerProtected(gBattlerTarget, gCurrentMove)
|
||||
else if (IsBattlerProtected(gBattlerAttacker, gBattlerTarget, gCurrentMove)
|
||||
&& (gCurrentMove != MOVE_CURSE || IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST))
|
||||
&& (!gBattleMoveEffects[gMovesInfo[gCurrentMove].effect].twoTurnEffect || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS))
|
||||
&& gMovesInfo[gCurrentMove].effect != EFFECT_SUCKER_PUNCH
|
||||
|
@ -1456,7 +1456,7 @@ static void Cmd_unused5(void)
|
|||
{
|
||||
CMD_ARGS(const u8 *failInstr);
|
||||
|
||||
if (IsBattlerProtected(gBattlerTarget, gCurrentMove))
|
||||
if (IsBattlerProtected(gBattlerAttacker, gBattlerTarget, gCurrentMove))
|
||||
{
|
||||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||||
JumpIfMoveFailed(sizeof(*cmd), MOVE_NONE);
|
||||
|
@ -1471,7 +1471,7 @@ static void Cmd_unused5(void)
|
|||
static bool8 JumpIfMoveAffectedByProtect(u16 move)
|
||||
{
|
||||
bool8 affected = FALSE;
|
||||
if (IsBattlerProtected(gBattlerTarget, move))
|
||||
if (IsBattlerProtected(gBattlerAttacker, gBattlerTarget, move))
|
||||
{
|
||||
gMoveResultFlags |= MOVE_RESULT_MISSED;
|
||||
JumpIfMoveFailed(7, move);
|
||||
|
@ -1961,6 +1961,8 @@ static void Cmd_damagecalc(void)
|
|||
u8 moveType;
|
||||
|
||||
GET_MOVE_TYPE(gCurrentMove, moveType);
|
||||
if (gBattleStruct->shellSideArmCategory[gBattlerAttacker][gBattlerTarget] == DAMAGE_CATEGORY_SPECIAL && gCurrentMove == MOVE_SHELL_SIDE_ARM)
|
||||
gBattleStruct->swapDamageCategory = TRUE;
|
||||
gBattleMoveDamage = CalculateMoveDamage(gCurrentMove, gBattlerAttacker, gBattlerTarget, moveType, 0, gIsCriticalHit, TRUE, TRUE);
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
|
@ -7023,8 +7025,12 @@ static void Cmd_openpartyscreen(void)
|
|||
if (gAbsentBattlerFlags & gBitTable[battlerOpposite])
|
||||
battlerOpposite ^= BIT_FLANK;
|
||||
|
||||
BtlController_EmitLinkStandbyMsg(battlerOpposite, BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE);
|
||||
MarkBattlerForControllerExec(battlerOpposite);
|
||||
// Make sure we're checking a valid battler. In edge case scenarios - battler could be absent and battlerOpposite would become a non-existent one softlocking the game.
|
||||
if (battlerOpposite < gBattlersCount)
|
||||
{
|
||||
BtlController_EmitLinkStandbyMsg(battlerOpposite, BUFFER_A, LINK_STANDBY_MSG_ONLY, FALSE);
|
||||
MarkBattlerForControllerExec(battlerOpposite);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10500,44 +10506,6 @@ static void Cmd_various(void)
|
|||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
return;
|
||||
}
|
||||
case VARIOUS_SHELL_SIDE_ARM_CHECK: // 0% chance GameFreak actually checks this way according to DaWobblefet, but this is the only functional explanation at the moment
|
||||
{
|
||||
VARIOUS_ARGS();
|
||||
|
||||
u32 attackerAtkStat = gBattleMons[gBattlerAttacker].attack;
|
||||
u32 targetDefStat = gBattleMons[gBattlerTarget].defense;
|
||||
u32 attackerSpAtkStat = gBattleMons[gBattlerAttacker].spAttack;
|
||||
u32 targetSpDefStat = gBattleMons[gBattlerTarget].spDefense;
|
||||
u8 statStage;
|
||||
u32 physical;
|
||||
u32 special;
|
||||
|
||||
gBattleStruct->swapDamageCategory = FALSE;
|
||||
|
||||
statStage = gBattleMons[gBattlerAttacker].statStages[STAT_ATK];
|
||||
attackerAtkStat *= gStatStageRatios[statStage][0];
|
||||
attackerAtkStat /= gStatStageRatios[statStage][1];
|
||||
|
||||
statStage = gBattleMons[gBattlerTarget].statStages[STAT_DEF];
|
||||
targetDefStat *= gStatStageRatios[statStage][0];
|
||||
targetDefStat /= gStatStageRatios[statStage][1];
|
||||
|
||||
physical = ((((2 * gBattleMons[gBattlerAttacker].level / 5 + 2) * gMovesInfo[gCurrentMove].power * attackerAtkStat) / targetDefStat) / 50);
|
||||
|
||||
statStage = gBattleMons[gBattlerAttacker].statStages[STAT_SPATK];
|
||||
attackerSpAtkStat *= gStatStageRatios[statStage][0];
|
||||
attackerSpAtkStat /= gStatStageRatios[statStage][1];
|
||||
|
||||
statStage = gBattleMons[gBattlerTarget].statStages[STAT_SPDEF];
|
||||
targetSpDefStat *= gStatStageRatios[statStage][0];
|
||||
targetSpDefStat /= gStatStageRatios[statStage][1];
|
||||
|
||||
special = ((((2 * gBattleMons[gBattlerAttacker].level / 5 + 2) * gMovesInfo[gCurrentMove].power * attackerSpAtkStat) / targetSpDefStat) / 50);
|
||||
|
||||
if (((physical > special) || (physical == special && (Random() % 2) == 0)))
|
||||
gBattleStruct->swapDamageCategory = TRUE;
|
||||
break;
|
||||
}
|
||||
case VARIOUS_JUMP_IF_LEAF_GUARD_PROTECTED:
|
||||
{
|
||||
VARIOUS_ARGS(const u8 *jumpInstr);
|
||||
|
@ -13916,7 +13884,7 @@ static void Cmd_trymemento(void)
|
|||
if (B_MEMENTO_FAIL >= GEN_4
|
||||
&& (gBattleCommunication[MISS_TYPE] == B_MSG_PROTECTED
|
||||
|| gStatuses3[gBattlerTarget] & STATUS3_SEMI_INVULNERABLE
|
||||
|| IsBattlerProtected(gBattlerTarget, gCurrentMove)
|
||||
|| IsBattlerProtected(gBattlerAttacker, gBattlerTarget, gCurrentMove)
|
||||
|| DoesSubstituteBlockMove(gBattlerAttacker, gBattlerTarget, gCurrentMove)))
|
||||
{
|
||||
// Failed, target was protected.
|
||||
|
@ -16187,6 +16155,7 @@ void BS_ItemRestoreHP(void)
|
|||
if (gBattleTypeFlags & BATTLE_TYPE_DOUBLE && battler != MAX_BATTLERS_COUNT)
|
||||
{
|
||||
gAbsentBattlerFlags &= ~gBitTable[battler];
|
||||
gBattleMons[battler].hp = hp;
|
||||
gBattleCommunication[MULTIUSE_STATE] = TRUE;
|
||||
}
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
|
|
|
@ -8343,7 +8343,7 @@ bool32 IsMoveMakingContact(u32 move, u32 battlerAtk)
|
|||
|
||||
if (!gMovesInfo[move].makesContact)
|
||||
{
|
||||
if (gMovesInfo[move].effect == EFFECT_SHELL_SIDE_ARM && gBattleStruct->swapDamageCategory)
|
||||
if (move == MOVE_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][gBattlerTarget] == DAMAGE_CATEGORY_SPECIAL)
|
||||
return TRUE;
|
||||
else
|
||||
return FALSE;
|
||||
|
@ -8359,59 +8359,59 @@ bool32 IsMoveMakingContact(u32 move, u32 battlerAtk)
|
|||
}
|
||||
}
|
||||
|
||||
bool32 IsBattlerProtected(u32 battler, u32 move)
|
||||
bool32 IsBattlerProtected(u32 battlerAtk, u32 battlerDef, u32 move)
|
||||
{
|
||||
// Decorate bypasses protect and detect, but not crafty shield
|
||||
if (move == MOVE_DECORATE)
|
||||
{
|
||||
if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_CRAFTY_SHIELD)
|
||||
if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_CRAFTY_SHIELD)
|
||||
return TRUE;
|
||||
else if (gProtectStructs[battler].protected)
|
||||
else if (gProtectStructs[battlerDef].protected)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Z-Moves and Max Moves bypass protection (except Max Guard).
|
||||
if ((IsMaxMove(move) || gBattleStruct->zmove.active)
|
||||
&& (!gProtectStructs[battler].maxGuarded
|
||||
&& (!gProtectStructs[battlerDef].maxGuarded
|
||||
|| gMovesInfo[move].argument == MAX_EFFECT_BYPASS_PROTECT))
|
||||
return FALSE;
|
||||
|
||||
// Max Guard is silly about the moves it blocks, including Teatime.
|
||||
if (gProtectStructs[battler].maxGuarded && IsMoveBlockedByMaxGuard(move))
|
||||
if (gProtectStructs[battlerDef].maxGuarded && IsMoveBlockedByMaxGuard(move))
|
||||
return TRUE;
|
||||
|
||||
// Protective Pads doesn't stop Unseen Fist from bypassing Protect effects, so IsMoveMakingContact() isn't used here.
|
||||
// This means extra logic is needed to handle Shell Side Arm.
|
||||
if (GetBattlerAbility(gBattlerAttacker) == ABILITY_UNSEEN_FIST
|
||||
&& (gMovesInfo[move].makesContact || (gMovesInfo[move].effect == EFFECT_SHELL_SIDE_ARM && gBattleStruct->swapDamageCategory))
|
||||
&& !gProtectStructs[battler].maxGuarded) // Max Guard cannot be bypassed by Unseen Fist
|
||||
&& (gMovesInfo[move].makesContact || (move == MOVE_SHELL_SIDE_ARM && gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] == DAMAGE_CATEGORY_SPECIAL))
|
||||
&& !gProtectStructs[battlerDef].maxGuarded) // Max Guard cannot be bypassed by Unseen Fist
|
||||
return FALSE;
|
||||
else if (gMovesInfo[move].ignoresProtect)
|
||||
return FALSE;
|
||||
else if (gProtectStructs[battler].protected)
|
||||
else if (gProtectStructs[battlerDef].protected)
|
||||
return TRUE;
|
||||
else if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_WIDE_GUARD
|
||||
&& GetBattlerMoveTargetType(gBattlerAttacker, move) & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY))
|
||||
else if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_WIDE_GUARD
|
||||
&& GetBattlerMoveTargetType(gBattlerAttacker, move) & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY))
|
||||
return TRUE;
|
||||
else if (gProtectStructs[battler].banefulBunkered)
|
||||
else if (gProtectStructs[battlerDef].banefulBunkered)
|
||||
return TRUE;
|
||||
else if (gProtectStructs[battler].burningBulwarked)
|
||||
else if (gProtectStructs[battlerDef].burningBulwarked)
|
||||
return TRUE;
|
||||
else if ((gProtectStructs[battler].obstructed || gProtectStructs[battler].silkTrapped) && !IS_MOVE_STATUS(move))
|
||||
else if ((gProtectStructs[battlerDef].obstructed || gProtectStructs[battlerDef].silkTrapped) && !IS_MOVE_STATUS(move))
|
||||
return TRUE;
|
||||
else if (gProtectStructs[battler].spikyShielded)
|
||||
else if (gProtectStructs[battlerDef].spikyShielded)
|
||||
return TRUE;
|
||||
else if (gProtectStructs[battler].kingsShielded && gMovesInfo[move].power != 0)
|
||||
else if (gProtectStructs[battlerDef].kingsShielded && gMovesInfo[move].power != 0)
|
||||
return TRUE;
|
||||
else if (gProtectStructs[battler].maxGuarded)
|
||||
else if (gProtectStructs[battlerDef].maxGuarded)
|
||||
return TRUE;
|
||||
else if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_QUICK_GUARD
|
||||
else if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_QUICK_GUARD
|
||||
&& GetChosenMovePriority(gBattlerAttacker) > 0)
|
||||
return TRUE;
|
||||
else if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_CRAFTY_SHIELD
|
||||
else if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_CRAFTY_SHIELD
|
||||
&& IS_MOVE_STATUS(move))
|
||||
return TRUE;
|
||||
else if (gSideStatuses[GetBattlerSide(battler)] & SIDE_STATUS_MAT_BLOCK
|
||||
else if (gSideStatuses[GetBattlerSide(battlerDef)] & SIDE_STATUS_MAT_BLOCK
|
||||
&& !IS_MOVE_STATUS(move))
|
||||
return TRUE;
|
||||
else
|
||||
|
@ -11566,6 +11566,54 @@ void RemoveBattlerType(u32 battler, u8 type)
|
|||
}
|
||||
}
|
||||
|
||||
void SetShellSideArmCategory(void)
|
||||
{
|
||||
u32 battlerAtk, battlerDef;
|
||||
u32 attackerAtkStat;
|
||||
u32 targetDefStat;
|
||||
u32 attackerSpAtkStat;
|
||||
u32 targetSpDefStat;
|
||||
u8 statStage;
|
||||
u32 physical;
|
||||
u32 special;
|
||||
|
||||
for (battlerAtk = 0; battlerAtk < gBattlersCount; battlerAtk++)
|
||||
{
|
||||
attackerAtkStat = gBattleMons[battlerAtk].attack;
|
||||
statStage = gBattleMons[battlerAtk].statStages[STAT_ATK];
|
||||
attackerAtkStat *= gStatStageRatios[statStage][0];
|
||||
attackerAtkStat /= gStatStageRatios[statStage][1];
|
||||
|
||||
attackerSpAtkStat = gBattleMons[battlerAtk].spAttack;
|
||||
statStage = gBattleMons[battlerAtk].statStages[STAT_SPATK];
|
||||
attackerSpAtkStat *= gStatStageRatios[statStage][0];
|
||||
attackerSpAtkStat /= gStatStageRatios[statStage][1];
|
||||
|
||||
for (battlerDef = 0; battlerDef < gBattlersCount; battlerDef++)
|
||||
{
|
||||
if (battlerAtk == battlerDef)
|
||||
continue;
|
||||
|
||||
targetDefStat = gBattleMons[battlerDef].defense;
|
||||
statStage = gBattleMons[battlerDef].statStages[STAT_DEF];
|
||||
targetDefStat *= gStatStageRatios[statStage][0];
|
||||
targetDefStat /= gStatStageRatios[statStage][1];
|
||||
|
||||
physical = ((((2 * gBattleMons[battlerAtk].level / 5 + 2) * gMovesInfo[MOVE_SHELL_SIDE_ARM].power * attackerAtkStat) / targetDefStat) / 50);
|
||||
|
||||
targetSpDefStat = gBattleMons[battlerDef].spDefense;
|
||||
statStage = gBattleMons[battlerDef].statStages[STAT_SPDEF];
|
||||
targetSpDefStat *= gStatStageRatios[statStage][0];
|
||||
targetSpDefStat /= gStatStageRatios[statStage][1];
|
||||
|
||||
special = ((((2 * gBattleMons[battlerAtk].level / 5 + 2) * gMovesInfo[MOVE_SHELL_SIDE_ARM].power * attackerSpAtkStat) / targetSpDefStat) / 50);
|
||||
|
||||
if (((physical > special) || (physical == special && RandomPercentage(RNG_SHELL_SIDE_ARM, 50))))
|
||||
gBattleStruct->shellSideArmCategory[battlerAtk][battlerDef] = DAMAGE_CATEGORY_SPECIAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool32 CanTargetPartner(u32 battlerAtk, u32 battlerDef)
|
||||
{
|
||||
return (gBattleTypeFlags & BATTLE_TYPE_DOUBLE
|
||||
|
|
|
@ -1934,12 +1934,6 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
|
|||
.battleTvScore = 0, // TODO: Assign points
|
||||
},
|
||||
|
||||
[EFFECT_SHELL_SIDE_ARM] =
|
||||
{
|
||||
.battleScript = BattleScript_EffectShellSideArm,
|
||||
.battleTvScore = 0, // TODO: Assign points
|
||||
},
|
||||
|
||||
[EFFECT_TERRAIN_PULSE] =
|
||||
{
|
||||
.battleScript = BattleScript_EffectHit,
|
||||
|
|
|
@ -18079,7 +18079,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
|
|||
.description = COMPOUND_STRING(
|
||||
"Deals better of physical and\n"
|
||||
"special damage. May poison."),
|
||||
.effect = EFFECT_SHELL_SIDE_ARM,
|
||||
.effect = EFFECT_HIT, // The effect is hardcoded to the move since SetShellSideArmCategory() can't be used with anything but Shell Side Arm because of the BP requirement
|
||||
.power = 90,
|
||||
.type = TYPE_POISON,
|
||||
.accuracy = 100,
|
||||
|
|
|
@ -4907,6 +4907,7 @@ const struct SpeciesInfo gSpeciesInfoGen2[] =
|
|||
.catchRate = 120,
|
||||
.expYield = (P_UPDATED_EXP_YIELDS >= GEN_5) ? 66 : 124,
|
||||
.evYield_Attack = 1,
|
||||
.itemCommon = ITEM_HONEY,
|
||||
.genderRatio = PERCENT_FEMALE(50),
|
||||
.eggCycles = 20,
|
||||
.friendship = STANDARD_FRIENDSHIP,
|
||||
|
|
|
@ -9768,6 +9768,7 @@ const struct SpeciesInfo gSpeciesInfoGen5[] =
|
|||
.catchRate = 45,
|
||||
.expYield = 172,
|
||||
.evYield_Attack = 2,
|
||||
.itemRare = ITEM_LEADERS_CREST,
|
||||
.genderRatio = PERCENT_FEMALE(50),
|
||||
.eggCycles = 20,
|
||||
.friendship = 35,
|
||||
|
|
|
@ -1380,7 +1380,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] =
|
|||
.catchRate = 45,
|
||||
.expYield = 167,
|
||||
.evYield_SpAttack = 2,
|
||||
.itemRare = ITEM_HONEY,
|
||||
.itemRare = ITEM_RED_NECTAR,
|
||||
.genderRatio = PERCENT_FEMALE(75),
|
||||
.eggCycles = 20,
|
||||
.friendship = STANDARD_FRIENDSHIP,
|
||||
|
@ -1444,7 +1444,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] =
|
|||
.catchRate = 45,
|
||||
.expYield = 167,
|
||||
.evYield_SpAttack = 2,
|
||||
.itemRare = ITEM_HONEY,
|
||||
.itemRare = ITEM_YELLOW_NECTAR,
|
||||
.genderRatio = PERCENT_FEMALE(75),
|
||||
.eggCycles = 20,
|
||||
.friendship = STANDARD_FRIENDSHIP,
|
||||
|
@ -1500,7 +1500,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] =
|
|||
.catchRate = 45,
|
||||
.expYield = 167,
|
||||
.evYield_SpAttack = 2,
|
||||
.itemRare = ITEM_HONEY,
|
||||
.itemRare = ITEM_PINK_NECTAR,
|
||||
.genderRatio = PERCENT_FEMALE(75),
|
||||
.eggCycles = 20,
|
||||
.friendship = STANDARD_FRIENDSHIP,
|
||||
|
@ -1556,7 +1556,7 @@ const struct SpeciesInfo gSpeciesInfoGen7[] =
|
|||
.catchRate = 45,
|
||||
.expYield = 167,
|
||||
.evYield_SpAttack = 2,
|
||||
.itemRare = ITEM_HONEY,
|
||||
.itemRare = ITEM_PURPLE_NECTAR,
|
||||
.genderRatio = PERCENT_FEMALE(75),
|
||||
.eggCycles = 20,
|
||||
.friendship = STANDARD_FRIENDSHIP,
|
||||
|
|
|
@ -2550,6 +2550,8 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
|
|||
.catchRate = 190,
|
||||
.expYield = 67,
|
||||
.evYield_SpDefense = 1,
|
||||
.itemCommon = ITEM_TINY_MUSHROOM,
|
||||
.itemRare = ITEM_BIG_MUSHROOM,
|
||||
.genderRatio = PERCENT_FEMALE(50),
|
||||
.eggCycles = 20,
|
||||
.friendship = STANDARD_FRIENDSHIP,
|
||||
|
@ -2604,6 +2606,8 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
|
|||
.catchRate = 90,
|
||||
.expYield = 180,
|
||||
.evYield_SpDefense = 2,
|
||||
.itemCommon = ITEM_TINY_MUSHROOM,
|
||||
.itemRare = ITEM_BIG_MUSHROOM,
|
||||
.genderRatio = PERCENT_FEMALE(50),
|
||||
.eggCycles = 20,
|
||||
.friendship = STANDARD_FRIENDSHIP,
|
||||
|
@ -4396,6 +4400,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
|
|||
.catchRate = 30,
|
||||
.expYield = 285,
|
||||
.evYield_Attack = 3,
|
||||
.itemRare = ITEM_BOOSTER_ENERGY,
|
||||
.genderRatio = MON_GENDERLESS,
|
||||
.eggCycles = 50,
|
||||
.friendship = 0,
|
||||
|
@ -4451,6 +4456,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
|
|||
.catchRate = 50,
|
||||
.expYield = 285,
|
||||
.evYield_HP = 3,
|
||||
.itemRare = ITEM_BOOSTER_ENERGY,
|
||||
.genderRatio = MON_GENDERLESS,
|
||||
.eggCycles = 50,
|
||||
.friendship = 0,
|
||||
|
@ -4506,6 +4512,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
|
|||
.catchRate = 50,
|
||||
.expYield = 285,
|
||||
.evYield_Attack = 3,
|
||||
.itemRare = ITEM_BOOSTER_ENERGY,
|
||||
.genderRatio = MON_GENDERLESS,
|
||||
.eggCycles = 50,
|
||||
.friendship = 0,
|
||||
|
@ -4563,6 +4570,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
|
|||
.evYield_Speed = 1,
|
||||
.evYield_SpAttack = 1,
|
||||
.evYield_SpDefense = 1,
|
||||
.itemRare = ITEM_BOOSTER_ENERGY,
|
||||
.genderRatio = MON_GENDERLESS,
|
||||
.eggCycles = 50,
|
||||
.friendship = 0,
|
||||
|
@ -4619,6 +4627,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
|
|||
.catchRate = 30,
|
||||
.expYield = 285,
|
||||
.evYield_Attack = 3,
|
||||
.itemRare = ITEM_BOOSTER_ENERGY,
|
||||
.genderRatio = MON_GENDERLESS,
|
||||
.eggCycles = 50,
|
||||
.friendship = 0,
|
||||
|
@ -4673,6 +4682,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
|
|||
.catchRate = 30,
|
||||
.expYield = 285,
|
||||
.evYield_SpAttack = 3,
|
||||
.itemRare = ITEM_BOOSTER_ENERGY,
|
||||
.genderRatio = MON_GENDERLESS,
|
||||
.eggCycles = 50,
|
||||
.friendship = 0,
|
||||
|
@ -4728,6 +4738,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
|
|||
.catchRate = 30,
|
||||
.expYield = 285,
|
||||
.evYield_Defense = 3,
|
||||
.itemRare = ITEM_BOOSTER_ENERGY,
|
||||
.genderRatio = MON_GENDERLESS,
|
||||
.eggCycles = 50,
|
||||
.friendship = 0,
|
||||
|
@ -4783,6 +4794,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
|
|||
.catchRate = 50,
|
||||
.expYield = 285,
|
||||
.evYield_Speed = 3,
|
||||
.itemRare = ITEM_BOOSTER_ENERGY,
|
||||
.genderRatio = MON_GENDERLESS,
|
||||
.eggCycles = 50,
|
||||
.friendship = 0,
|
||||
|
@ -4838,6 +4850,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
|
|||
.catchRate = 50,
|
||||
.expYield = 285,
|
||||
.evYield_Attack = 3,
|
||||
.itemRare = ITEM_BOOSTER_ENERGY,
|
||||
.genderRatio = MON_GENDERLESS,
|
||||
.eggCycles = 50,
|
||||
.friendship = 0,
|
||||
|
@ -4893,6 +4906,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
|
|||
.catchRate = 30,
|
||||
.expYield = 285,
|
||||
.evYield_SpAttack = 3,
|
||||
.itemRare = ITEM_BOOSTER_ENERGY,
|
||||
.genderRatio = MON_GENDERLESS,
|
||||
.eggCycles = 50,
|
||||
.friendship = 0,
|
||||
|
@ -4949,6 +4963,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
|
|||
.catchRate = 30,
|
||||
.expYield = 285,
|
||||
.evYield_SpAttack = 3,
|
||||
.itemRare = ITEM_BOOSTER_ENERGY,
|
||||
.genderRatio = MON_GENDERLESS,
|
||||
.eggCycles = 50,
|
||||
.friendship = 0,
|
||||
|
@ -5005,6 +5020,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
|
|||
.catchRate = 30,
|
||||
.expYield = 285,
|
||||
.evYield_Attack = 3,
|
||||
.itemRare = ITEM_BOOSTER_ENERGY,
|
||||
.genderRatio = MON_GENDERLESS,
|
||||
.eggCycles = 50,
|
||||
.friendship = 0,
|
||||
|
@ -5604,6 +5620,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
|
|||
.catchRate = 10,
|
||||
.expYield = 295,
|
||||
.evYield_Attack = 3,
|
||||
.itemRare = ITEM_BOOSTER_ENERGY,
|
||||
.genderRatio = MON_GENDERLESS,
|
||||
.eggCycles = 50,
|
||||
.friendship = 0,
|
||||
|
@ -5660,6 +5677,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
|
|||
.catchRate = 10,
|
||||
.expYield = 295,
|
||||
.evYield_Attack = 3,
|
||||
.itemRare = ITEM_BOOSTER_ENERGY,
|
||||
.genderRatio = MON_GENDERLESS,
|
||||
.eggCycles = 50,
|
||||
.friendship = 0,
|
||||
|
@ -5826,6 +5844,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
|
|||
.catchRate = 5,
|
||||
.expYield = 295,
|
||||
.evYield_SpAttack = 3,
|
||||
.itemRare = ITEM_BOOSTER_ENERGY,
|
||||
.genderRatio = MON_GENDERLESS,
|
||||
.eggCycles = 50,
|
||||
.friendship = 0,
|
||||
|
@ -5880,6 +5899,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
|
|||
.catchRate = 5,
|
||||
.expYield = 295,
|
||||
.evYield_Attack = 3,
|
||||
.itemRare = ITEM_BOOSTER_ENERGY,
|
||||
.genderRatio = MON_GENDERLESS,
|
||||
.eggCycles = 50,
|
||||
.friendship = 0,
|
||||
|
@ -6380,6 +6400,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
|
|||
.catchRate = 10,
|
||||
.expYield = 295,
|
||||
.evYield_Defense = 3,
|
||||
.itemRare = ITEM_BOOSTER_ENERGY,
|
||||
.genderRatio = MON_GENDERLESS,
|
||||
.eggCycles = 50,
|
||||
.friendship = 0,
|
||||
|
@ -6435,6 +6456,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
|
|||
.catchRate = 10,
|
||||
.expYield = 295,
|
||||
.evYield_SpAttack = 3,
|
||||
.itemRare = ITEM_BOOSTER_ENERGY,
|
||||
.genderRatio = MON_GENDERLESS,
|
||||
.eggCycles = 50,
|
||||
.friendship = 0,
|
||||
|
@ -6490,6 +6512,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
|
|||
.catchRate = 10,
|
||||
.expYield = 295,
|
||||
.evYield_Speed = 3,
|
||||
.itemRare = ITEM_BOOSTER_ENERGY,
|
||||
.genderRatio = MON_GENDERLESS,
|
||||
.eggCycles = 50,
|
||||
.friendship = 0,
|
||||
|
@ -6544,6 +6567,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
|
|||
.catchRate = 10,
|
||||
.expYield = 295,
|
||||
.evYield_SpAttack = 3,
|
||||
.itemRare = ITEM_BOOSTER_ENERGY,
|
||||
.genderRatio = MON_GENDERLESS,
|
||||
.eggCycles = 50,
|
||||
.friendship = 0,
|
||||
|
|
|
@ -3666,6 +3666,7 @@ void PokemonToBattleMon(struct Pokemon *src, struct BattlePokemon *dst)
|
|||
dst->type1 = gSpeciesInfo[dst->species].types[0];
|
||||
dst->type2 = gSpeciesInfo[dst->species].types[1];
|
||||
dst->type3 = TYPE_MYSTERY;
|
||||
dst->isShiny = IsMonShiny(src);
|
||||
dst->ability = GetAbilityBySpecies(dst->species, dst->abilityNum);
|
||||
GetMonData(src, MON_DATA_NICKNAME, nickname);
|
||||
StringCopy_Nickname(dst->nickname, nickname);
|
||||
|
@ -4448,6 +4449,7 @@ u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 mode, u16 evolutionItem, s
|
|||
switch (mode)
|
||||
{
|
||||
case EVO_MODE_NORMAL:
|
||||
case EVO_MODE_BATTLE_ONLY:
|
||||
level = GetMonData(mon, MON_DATA_LEVEL, 0);
|
||||
friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, 0);
|
||||
|
||||
|
@ -4536,11 +4538,11 @@ u16 GetEvolutionTargetSpecies(struct Pokemon *mon, u8 mode, u16 evolutionItem, s
|
|||
targetSpecies = evolutions[i].targetSpecies;
|
||||
break;
|
||||
case EVO_LEVEL_FAMILY_OF_FOUR:
|
||||
if (evolutions[i].param <= level && (personality % 100) != 0)
|
||||
if (mode == EVO_MODE_BATTLE_ONLY && evolutions[i].param <= level && (personality % 100) != 0)
|
||||
targetSpecies = evolutions[i].targetSpecies;
|
||||
break;
|
||||
case EVO_LEVEL_FAMILY_OF_THREE:
|
||||
if (evolutions[i].param <= level && (personality % 100) == 0)
|
||||
if (mode == EVO_MODE_BATTLE_ONLY && evolutions[i].param <= level && (personality % 100) == 0)
|
||||
targetSpecies = evolutions[i].targetSpecies;
|
||||
break;
|
||||
case EVO_BEAUTY:
|
||||
|
|
|
@ -136,7 +136,7 @@ DOUBLE_BATTLE_TEST("Prankster-affected moves that target all Pokémon are succes
|
|||
{
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_CAPTIVATE].target == MOVE_TARGET_BOTH);
|
||||
PLAYER(SPECIES_VOLBEAT) { Ability(ABILITY_PRANKSTER); }
|
||||
PLAYER(SPECIES_ILLUMISE) { Ability(ABILITY_PRANKSTER); }
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_UMBREON);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
|
|
|
@ -555,8 +555,9 @@ AI_SINGLE_BATTLE_TEST("AI will not choose Burn Up if the user lost the Fire typi
|
|||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI will choose Surf over Thunderbolt and Ice Beam if the opposing mon has Volt Absorb")
|
||||
AI_SINGLE_BATTLE_TEST("AI will only choose Surf 1/3 times if the opposing mon has Volt Absorb")
|
||||
{
|
||||
PASSES_RANDOMLY(1, 3, RNG_AI_ABILITY);
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_THUNDERBOLT].type == TYPE_ELECTRIC);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||
|
@ -564,6 +565,27 @@ AI_SINGLE_BATTLE_TEST("AI will choose Surf over Thunderbolt and Ice Beam if the
|
|||
OPPONENT(SPECIES_LANTURN) { Moves(MOVE_THUNDERBOLT, MOVE_ICE_BEAM, MOVE_SURF); }
|
||||
} WHEN {
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_SURF); }
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_SURF); }
|
||||
} SCENE {
|
||||
MESSAGE("Foe Lanturn used Surf!");
|
||||
MESSAGE("Foe Lanturn used Surf!");
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI will choose Thunderbolt then Surf 2/3 times if the opposing mon has Volt Absorb")
|
||||
{
|
||||
PASSES_RANDOMLY(2, 3, RNG_AI_ABILITY);
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_THUNDERBOLT].type == TYPE_ELECTRIC);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||
PLAYER(SPECIES_LANTURN) { Ability(ABILITY_VOLT_ABSORB); };
|
||||
OPPONENT(SPECIES_LANTURN) { Moves(MOVE_THUNDERBOLT, MOVE_ICE_BEAM, MOVE_SURF); }
|
||||
} WHEN {
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_THUNDERBOLT); }
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_SURF); }
|
||||
} SCENE {
|
||||
MESSAGE("Foe Lanturn used Thunderbolt!");
|
||||
MESSAGE("Foe Lanturn used Surf!");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -715,6 +737,45 @@ AI_SINGLE_BATTLE_TEST("AI calculates guaranteed criticals and detects critical i
|
|||
}
|
||||
}
|
||||
|
||||
AI_DOUBLE_BATTLE_TEST("AI recognizes Volt Absorb received from Trace")
|
||||
{
|
||||
GIVEN {
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||
PLAYER(SPECIES_MAGNETON);
|
||||
PLAYER(SPECIES_GARDEVOIR) { Ability(ABILITY_TRACE); }
|
||||
OPPONENT(SPECIES_JOLTEON) { Ability(ABILITY_VOLT_ABSORB); Moves(MOVE_THUNDER_WAVE, MOVE_THUNDERSHOCK, MOVE_WATER_GUN); }
|
||||
OPPONENT(SPECIES_JOLTEON) { Ability(ABILITY_VOLT_ABSORB); Moves(MOVE_THUNDER_WAVE, MOVE_THUNDERSHOCK, MOVE_WATER_GUN); }
|
||||
} WHEN {
|
||||
TURN { NOT_EXPECT_MOVE(opponentLeft, MOVE_THUNDERSHOCK); NOT_EXPECT_MOVE(opponentLeft, MOVE_THUNDER_WAVE); NOT_EXPECT_MOVE(opponentRight, MOVE_THUNDER_WAVE); }
|
||||
} THEN {
|
||||
EXPECT(gBattleResources->aiData->abilities[B_POSITION_PLAYER_RIGHT] == ABILITY_VOLT_ABSORB);
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI avoids contact moves against rocky helmet")
|
||||
{
|
||||
u32 item;
|
||||
|
||||
PARAMETRIZE { item = ITEM_NONE; }
|
||||
PARAMETRIZE { item = ITEM_ROCKY_HELMET; }
|
||||
|
||||
GIVEN {
|
||||
ASSUME(gMovesInfo[MOVE_BRANCH_POKE].makesContact);
|
||||
ASSUME(!gMovesInfo[MOVE_LEAFAGE].makesContact);
|
||||
ASSUME(gMovesInfo[MOVE_BRANCH_POKE].power == gMovesInfo[MOVE_LEAFAGE].power);
|
||||
ASSUME(gMovesInfo[MOVE_BRANCH_POKE].type == gMovesInfo[MOVE_LEAFAGE].type);
|
||||
ASSUME(gMovesInfo[MOVE_BRANCH_POKE].category == gMovesInfo[MOVE_LEAFAGE].category);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Item(item); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_BRANCH_POKE, MOVE_LEAFAGE); }
|
||||
} WHEN {
|
||||
if (item == ITEM_ROCKY_HELMET)
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_LEAFAGE); }
|
||||
else
|
||||
TURN { EXPECT_MOVES(opponent, MOVE_LEAFAGE, MOVE_BRANCH_POKE); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI uses a guaranteed KO move instead of the move with the highest expected damage")
|
||||
{
|
||||
u32 flags;
|
||||
|
|
|
@ -48,3 +48,28 @@ SINGLE_BATTLE_TEST("Mirror Herb copies all of Stuff Cheeks' stat boosts")
|
|||
EXPECT_EQ(player->statStages[STAT_DEF], opponent->statStages[STAT_DEF]);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Mirror Herb does not trigger for Ally's Soul Heart's stat raise")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_MIRROR_HERB); }
|
||||
PLAYER(SPECIES_WYNAUT) { Ability(ABILITY_SOUL_HEART); } // Raises Sp. Atk after fainting am on
|
||||
OPPONENT(SPECIES_WOBBUFFET) { HP(1); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(playerRight, MOVE_TACKLE, target:opponentLeft); }
|
||||
} SCENE {
|
||||
MESSAGE("Wynaut used Tackle!");
|
||||
MESSAGE("Foe Wobbuffet fainted!");
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
|
||||
NONE_OF {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
|
||||
MESSAGE("Wobbuffet used its Mirror Herb to mirror its opponent's stat changes!");
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
|
||||
}
|
||||
}
|
||||
THEN {
|
||||
EXPECT_EQ(playerRight->statStages[STAT_SPATK], DEFAULT_STAT_STAGE + 1);
|
||||
EXPECT_EQ(playerLeft->statStages[STAT_SPATK], DEFAULT_STAT_STAGE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,4 +73,30 @@ SINGLE_BATTLE_TEST("Max Honey restores a fainted battler's HP fully")
|
|||
}
|
||||
}
|
||||
|
||||
// Note: this test is oddly specific with implicit moves/speeds, because I had errors/invalids without them.
|
||||
DOUBLE_BATTLE_TEST("Revive works for a partner in a double battle")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(gItemsInfo[ITEM_REVIVE].battleUsage == EFFECT_ITEM_REVIVE);
|
||||
PLAYER(SPECIES_WYNAUT) { HP(1); MaxHP(200); Moves(MOVE_IRON_DEFENSE, MOVE_CELEBRATE); Speed(5); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { HP(1); Speed(4); }
|
||||
OPPONENT(SPECIES_ABRA) { Speed(3); Moves(MOVE_TACKLE, MOVE_PSYCHIC, MOVE_CELEBRATE); }
|
||||
OPPONENT(SPECIES_KADABRA) { Speed(2); Moves(MOVE_TACKLE, MOVE_PSYCHIC, MOVE_CELEBRATE, MOVE_EXPLOSION); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponentRight, MOVE_PSYCHIC, target:playerLeft); MOVE(playerLeft, MOVE_CELEBRATE); } // Wynaut faints
|
||||
TURN { USE_ITEM(playerRight, ITEM_REVIVE, partyIndex: 0); MOVE(opponentRight, MOVE_PSYCHIC, target:playerRight); } // Wynaut gets revived, Wobb faints
|
||||
// Wynaut is functionally back
|
||||
TURN { MOVE(opponentLeft, MOVE_TACKLE, target:playerLeft); }
|
||||
TURN { MOVE(opponentRight, MOVE_TACKLE, target:playerLeft); }
|
||||
TURN { MOVE(opponentRight, MOVE_EXPLOSION); } // Everyone dies, the test can finish.
|
||||
} SCENE {
|
||||
MESSAGE("Wynaut fainted!");
|
||||
MESSAGE("You used Revive!");
|
||||
// Switch-in animation
|
||||
MESSAGE("Wobbuffet fainted!");
|
||||
HP_BAR(playerLeft);
|
||||
HP_BAR(playerLeft);
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Revive won't restore a battler's HP if it hasn't fainted")
|
||||
|
|
|
@ -1,6 +1,30 @@
|
|||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gMovesInfo[MOVE_BATON_PASS].effect == EFFECT_BATON_PASS);
|
||||
}
|
||||
|
||||
// This softlocked the game before.
|
||||
SINGLE_BATTLE_TEST("Baton Pass used after Memento works correctly")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_CATERPIE);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_MEMENTO); SEND_OUT(player, 1); MOVE(opponent, MOVE_BATON_PASS); SEND_OUT(opponent, 1); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet used Memento!");
|
||||
MESSAGE("Wobbuffet fainted!");
|
||||
MESSAGE("Foe Wynaut used Baton Pass!");
|
||||
MESSAGE("2 sent out Caterpie!");
|
||||
MESSAGE("Go! Wobbuffet!");
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Baton Pass switches out the user");
|
||||
TO_DO_BATTLE_TEST("Baton Pass fails if there's no valid party Pokémon left");
|
||||
TO_DO_BATTLE_TEST("Baton Pass passes both positive and negative stat changes");
|
||||
|
|
100
test/battle/move_effect/shell_side_arm.c
Normal file
100
test/battle/move_effect/shell_side_arm.c
Normal file
|
@ -0,0 +1,100 @@
|
|||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
SINGLE_BATTLE_TEST("Shell Side Arm can be countered if it is physical")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_SHELL_SIDE_ARM); }
|
||||
OPPONENT(SPECIES_REGICE) { Defense(100); SpDefense(200); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SHELL_SIDE_ARM); MOVE(opponent, MOVE_COUNTER); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SIDE_ARM, player);
|
||||
HP_BAR(opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_COUNTER, opponent);
|
||||
HP_BAR(player);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Shell Side Arm can be mirror coated if it is special")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_SHELL_SIDE_ARM); }
|
||||
OPPONENT(SPECIES_REGIROCK) { Defense(200); SpDefense(100); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SHELL_SIDE_ARM); MOVE(opponent, MOVE_MIRROR_COAT); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SIDE_ARM, player);
|
||||
HP_BAR(opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRROR_COAT, opponent);
|
||||
HP_BAR(player);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Shell Side Arm does not change catogory mid-turn")
|
||||
{
|
||||
s16 damage[3];
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Moves(MOVE_SHELL_SIDE_ARM); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Defense(100); SpDefense(120); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_SHELL_SIDE_ARM); }
|
||||
TURN { MOVE(opponent, MOVE_LIGHT_SCREEN); MOVE(player, MOVE_SHELL_SIDE_ARM); }
|
||||
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player, MOVE_SHELL_SIDE_ARM); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SIDE_ARM, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_LIGHT_SCREEN, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SIDE_ARM, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SIDE_ARM, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[2]);
|
||||
} THEN {
|
||||
EXPECT_EQ(damage[0], damage[1]);
|
||||
EXPECT_EQ(damage[1], damage[2]);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Shell Side Arm is choosing it's type for each battler on the field")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(10); Moves(MOVE_SHELL_SIDE_ARM); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(20); }
|
||||
OPPONENT(SPECIES_REGIROCK) { Speed(30); Defense(200); SpDefense(100); }
|
||||
OPPONENT(SPECIES_REGICE) { Speed(30); Defense(100); SpDefense(200); }
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_SHELL_SIDE_ARM, target: opponentRight); MOVE(opponentRight, MOVE_COUNTER); }
|
||||
TURN { MOVE(playerLeft, MOVE_SHELL_SIDE_ARM, target: opponentLeft); MOVE(opponentLeft, MOVE_MIRROR_COAT); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SIDE_ARM, playerLeft);
|
||||
HP_BAR(opponentRight);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_COUNTER, opponentRight);
|
||||
HP_BAR(playerLeft);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SIDE_ARM, playerLeft);
|
||||
HP_BAR(opponentLeft);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRROR_COAT, opponentLeft);
|
||||
HP_BAR(playerLeft);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Shell Side Arm does not change category mid-turn")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(10); Moves(MOVE_SHELL_SIDE_ARM); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(20); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(30); Defense(200); SpDefense(190); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(40); }
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_SHELL_SIDE_ARM, target: opponentLeft);
|
||||
MOVE(opponentRight, MOVE_LIGHT_SCREEN);
|
||||
MOVE(opponentLeft, MOVE_MIRROR_COAT);
|
||||
}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_LIGHT_SCREEN, opponentRight);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHELL_SIDE_ARM, playerLeft);
|
||||
HP_BAR(opponentLeft);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_MIRROR_COAT, opponentLeft);
|
||||
HP_BAR(playerLeft);
|
||||
}
|
||||
}
|
|
@ -94,3 +94,47 @@ TEST("Form change targets have the appropriate species flags")
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST("No species has two evolutions that use the evolution tracker")
|
||||
{
|
||||
u32 i;
|
||||
u32 species = SPECIES_NONE;
|
||||
u32 evolutionTrackerEvolutions;
|
||||
bool32 hasGenderBasedRecoil;
|
||||
const struct Evolution *evolutions;
|
||||
|
||||
for (i = 0; i < NUM_SPECIES; i++)
|
||||
{
|
||||
if (GetSpeciesEvolutions(i) != NULL) PARAMETRIZE { species = i; }
|
||||
}
|
||||
|
||||
evolutionTrackerEvolutions = 0;
|
||||
hasGenderBasedRecoil = FALSE;
|
||||
evolutions = GetSpeciesEvolutions(species);
|
||||
|
||||
for (i = 0; evolutions[i].method != EVOLUTIONS_END; i++)
|
||||
{
|
||||
if (evolutions[i].method == EVO_LEVEL_MOVE_TWENTY_TIMES
|
||||
#ifdef EVO_DEFEAT_WITH_ITEM
|
||||
|| evolutions[i].method == EVO_DEFEAT_WITH_ITEM
|
||||
#endif //EVO_DEFEAT_WITH_ITEM
|
||||
#ifdef EVO_OVERWORLD_STEPS
|
||||
|| evolutions[i].method == EVO_OVERWORLD_STEPS
|
||||
#endif //EVO_OVERWORLD_STEPS
|
||||
)
|
||||
evolutionTrackerEvolutions++;
|
||||
|
||||
if (evolutions[i].method == EVO_LEVEL_RECOIL_DAMAGE_MALE
|
||||
|| evolutions[i].method == EVO_LEVEL_RECOIL_DAMAGE_FEMALE)
|
||||
{
|
||||
// Special handling for these since they can be combined as the evolution tracker field is used for the same purpose
|
||||
if (!hasGenderBasedRecoil)
|
||||
{
|
||||
hasGenderBasedRecoil = TRUE;
|
||||
evolutionTrackerEvolutions++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EXPECT(evolutionTrackerEvolutions < 2);
|
||||
}
|
||||
|
|
|
@ -1553,7 +1553,7 @@ void OpenPokemon(u32 sourceLine, u32 side, u32 species)
|
|||
DATA.currentSide = side;
|
||||
DATA.currentPartyIndex = *partySize;
|
||||
DATA.currentMon = &party[DATA.currentPartyIndex];
|
||||
DATA.gender = MON_MALE;
|
||||
DATA.gender = 0xFF; // Male
|
||||
DATA.nature = NATURE_HARDY;
|
||||
(*partySize)++;
|
||||
|
||||
|
|
Loading…
Reference in a new issue