27/11/25 Master to upcoming merge
This commit is contained in:
commit
68300e0f9a
3
.gitattributes
vendored
3
.gitattributes
vendored
@ -18,6 +18,9 @@ Makefile text eol=lf
|
||||
*.md text eol=lf
|
||||
*.ps1 text eol=crlf
|
||||
*.yml text eol=lf
|
||||
*.party text eol=lf
|
||||
*.cfg text eol=lf
|
||||
*.ld text eol=lf
|
||||
|
||||
*.png binary
|
||||
*.bin binary
|
||||
|
||||
@ -1405,9 +1405,10 @@
|
||||
.4byte \restoreBattlerInstr
|
||||
.endm
|
||||
|
||||
.macro itemcurestatus jumpInstr:req
|
||||
.macro itemcurestatus jumpInstr:req, restoreBattlerInstr:req
|
||||
callnative BS_ItemCureStatus
|
||||
.4byte \jumpInstr
|
||||
.4byte \restoreBattlerInstr
|
||||
.endm
|
||||
|
||||
.macro itemincreasestat
|
||||
@ -1774,8 +1775,8 @@
|
||||
.4byte \failInstr
|
||||
.endm
|
||||
|
||||
.macro jumpifcriticalhit failInstr:req
|
||||
callnative BS_JumpIfCriticalHit
|
||||
.macro jumpifnotcriticalhit failInstr:req
|
||||
callnative BS_JumpIfNotCriticalHit
|
||||
.4byte \failInstr
|
||||
.endm
|
||||
|
||||
|
||||
@ -3738,35 +3738,16 @@ BattleScript_DoEffectTeleport::
|
||||
goto BattleScript_MoveEnd
|
||||
|
||||
BattleScript_EffectBeatUp::
|
||||
jumpifgenconfiglowerthan GEN_CONFIG_BEAT_UP, GEN_5, BattleScript_EffectBeatUpGen3
|
||||
goto BattleScript_EffectHit
|
||||
|
||||
BattleScript_EffectBeatUpGen3:
|
||||
attackcanceler
|
||||
accuracycheck BattleScript_MoveMissedPause, ACC_CURR_MOVE
|
||||
pause B_WAIT_TIME_SHORT
|
||||
setbyte gBattleCommunication, 0
|
||||
BattleScript_BeatUpLoop::
|
||||
movevaluescleanup
|
||||
trydobeatup BattleScript_BeatUpEnd, BattleScript_ButItFailed
|
||||
trydobeatup BattleScript_MoveEnd, BattleScript_ButItFailed
|
||||
printstring STRINGID_PKMNATTACK
|
||||
critcalc
|
||||
jumpifcriticalhit BattleScript_BeatUpAttack
|
||||
manipulatedamage DMG_DOUBLED
|
||||
BattleScript_BeatUpAttack::
|
||||
adjustdamage
|
||||
attackanimation
|
||||
waitanimation
|
||||
effectivenesssound
|
||||
hitanimation BS_TARGET
|
||||
waitstate
|
||||
healthbarupdate BS_TARGET, MOVE_DAMAGE_HP_UPDATE
|
||||
datahpupdate BS_TARGET, MOVE_DAMAGE_HP_UPDATE
|
||||
critmessage
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
resultmessage
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
tryfaintmon BS_TARGET
|
||||
moveendto MOVEEND_NEXT_TARGET
|
||||
goto BattleScript_BeatUpLoop
|
||||
BattleScript_BeatUpEnd::
|
||||
end
|
||||
goto BattleScript_HitFromCritCalc
|
||||
|
||||
BattleScript_EffectDefenseCurl::
|
||||
attackcanceler
|
||||
|
||||
@ -74,6 +74,10 @@ BattleScript_ItemRestoreHP_Party::
|
||||
return
|
||||
|
||||
BattleScript_ItemRestoreHP_SendOutRevivedBattler:
|
||||
switchhandleorder BS_SCRIPTING, 0
|
||||
getswitchedmondata BS_SCRIPTING
|
||||
switchindataupdate BS_SCRIPTING
|
||||
trytoclearprimalweather
|
||||
switchinanim BS_SCRIPTING, FALSE, FALSE
|
||||
waitstate
|
||||
switchineffects BS_SCRIPTING
|
||||
@ -82,13 +86,18 @@ BattleScript_ItemRestoreHP_SendOutRevivedBattler:
|
||||
BattleScript_ItemCureStatus::
|
||||
call BattleScript_UseItemMessage
|
||||
BattleScript_ItemCureStatusAfterItemMsg:
|
||||
itemcurestatus BattleScript_ItemCureStatusEnd
|
||||
updatestatusicon BS_SCRIPTING
|
||||
itemcurestatus BattleScript_ItemCureStatusEnd, BattleScript_CureStatus_Battler
|
||||
printstring STRINGID_ITEMCUREDSPECIESSTATUS
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
BattleScript_ItemCureStatusEnd:
|
||||
end
|
||||
|
||||
BattleScript_CureStatus_Battler::
|
||||
updatestatusicon BS_SCRIPTING
|
||||
printstring STRINGID_ITEMCUREDSPECIESSTATUS
|
||||
waitmessage B_WAIT_TIME_LONG
|
||||
end
|
||||
|
||||
BattleScript_ItemHealAndCureStatus::
|
||||
call BattleScript_UseItemMessage
|
||||
itemrestorehp BattleScript_ItemCureStatusAfterItemMsg, BattleScript_ItemHealAndCureStatus_Battler
|
||||
|
||||
@ -138,7 +138,8 @@ struct DisableStruct
|
||||
u8 endured:1;
|
||||
u8 tryEjectPack:1;
|
||||
u8 octolockedBy:3;
|
||||
u8 padding:5;
|
||||
u8 paradoxBoostedStat:4;
|
||||
u8 padding2:1;
|
||||
};
|
||||
|
||||
// Fully Cleared each turn after end turn effects are done. A few things are cleared before end turn effects
|
||||
@ -193,14 +194,14 @@ struct SpecialStatus
|
||||
u8 afterYou:1;
|
||||
u8 enduredDamage:1;
|
||||
u8 dancerUsedMove:1;
|
||||
u8 padding:1;
|
||||
u8 padding1:1;
|
||||
// End of byte
|
||||
u8 switchInAbilityDone:1;
|
||||
u8 switchInItemDone:1;
|
||||
u8 instructedChosenTarget:3;
|
||||
u8 berryReduced:1;
|
||||
u8 announceNeutralizingGas:1; // See Cmd_switchineffects
|
||||
u8 neutralizingGasRemoved:1; // See VARIOUS_TRY_END_NEUTRALIZING_GAS
|
||||
u8 padding2:1;
|
||||
// End of byte
|
||||
u8 gemParam;
|
||||
// End of byte
|
||||
@ -213,7 +214,7 @@ struct SpecialStatus
|
||||
u8 criticalHit:1;
|
||||
// End of byte
|
||||
u8 dancerOriginalTarget:3;
|
||||
u8 unused:5;
|
||||
u8 padding3:5;
|
||||
// End of byte
|
||||
};
|
||||
|
||||
@ -740,7 +741,7 @@ struct BattleStruct
|
||||
u8 moveInfoSpriteId; // move info, window gfx
|
||||
u8 skyDropTargets[MAX_BATTLERS_COUNT]; // For Sky Drop, to account for if multiple Pokemon use Sky Drop in a double battle.
|
||||
// When using a move which hits multiple opponents which is then bounced by a target, we need to make sure, the move hits both opponents, the one with bounce, and the one without.
|
||||
u16 beatUpSpecies[PARTY_SIZE];
|
||||
u16 beatUpSpecies[PARTY_SIZE]; // Species for Gen5+ Beat Up, otherwise party indexes
|
||||
u8 attackerBeforeBounce:2;
|
||||
u8 beatUpSlot:3;
|
||||
u8 pledgeMove:1;
|
||||
|
||||
@ -66,7 +66,6 @@ bool32 TryResetBattlerStatChanges(u8 battler);
|
||||
bool32 CanCamouflage(u8 battlerId);
|
||||
void StealTargetItem(u8 battlerStealer, u8 battlerItem);
|
||||
u8 GetCatchingBattler(void);
|
||||
enum Stat GetHighestStatId(u32 battlerId);
|
||||
bool32 ProteanTryChangeType(u32 battler, enum Ability ability, u32 move, enum Type moveType);
|
||||
bool32 IsMoveNotAllowedInSkyBattles(u32 move);
|
||||
bool32 DoSwitchInAbilities(u32 battlerId);
|
||||
|
||||
@ -369,6 +369,10 @@ bool32 CanTargetPartner(u32 battlerAtk, u32 battlerDef);
|
||||
bool32 TargetFullyImmuneToCurrMove(u32 battlerAtk, u32 battlerDef);
|
||||
bool32 MoodyCantRaiseStat(u32 stat);
|
||||
bool32 MoodyCantLowerStat(u32 stat);
|
||||
bool32 IsBattlerTerrainAffected(u32 battler, enum Ability ability, enum HoldEffect holdEffect, u32 terrainFlag);
|
||||
u32 GetHighestStatId(u32 battler);
|
||||
u32 GetParadoxHighestStatId(u32 battler);
|
||||
u32 GetParadoxBoostedStatId(u32 battler);
|
||||
|
||||
bool32 CanBeSlept(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef, enum SleepClauseBlock isBlockedBySleepClause);
|
||||
bool32 CanBePoisoned(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum Ability abilityDef);
|
||||
@ -378,7 +382,6 @@ bool32 CanBeFrozen(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef);
|
||||
bool32 CanGetFrostbite(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef);
|
||||
bool32 CanSetNonVolatileStatus(u32 battlerAtk, u32 battlerDef, enum Ability abilityAtk, enum Ability abilityDef, enum MoveEffect secondaryMoveEffect, enum FunctionCallOption option);
|
||||
bool32 CanBeConfused(u32 battler);
|
||||
bool32 IsBattlerTerrainAffected(u32 battler, enum Ability ability, enum HoldEffect holdEffect, u32 terrainFlag);
|
||||
u32 GetBattlerAffectionHearts(u32 battler);
|
||||
void TryToRevertMimicryAndFlags(void);
|
||||
bool32 BattleArenaTurnEnd(void);
|
||||
|
||||
@ -96,10 +96,9 @@ enum CmdVarious
|
||||
|
||||
// Cmd_manipulatedamage
|
||||
#define DMG_CHANGE_SIGN 1
|
||||
#define DMG_DOUBLED 2
|
||||
#define DMG_1_8_TARGET_HP 3
|
||||
#define DMG_FULL_ATTACKER_HP 4
|
||||
#define DMG_BIG_ROOT 5
|
||||
#define DMG_1_8_TARGET_HP 2
|
||||
#define DMG_FULL_ATTACKER_HP 3
|
||||
#define DMG_BIG_ROOT 4
|
||||
|
||||
// Cmd_jumpifcantswitch
|
||||
#define SWITCH_IGNORE_ESCAPE_PREVENTION (1 << 7)
|
||||
|
||||
@ -48,6 +48,12 @@ enum GenConfigTag
|
||||
GEN_CONFIG_PARALYZE_ELECTRIC,
|
||||
GEN_CONFIG_BADGE_BOOST,
|
||||
GEN_CONFIG_LEAF_GUARD_PREVENTS_REST,
|
||||
GEN_CONFIG_BEAT_UP,
|
||||
GEN_CONFIG_WIDE_GUARD,
|
||||
GEN_CONFIG_QUICK_GUARD,
|
||||
GEN_CONFIG_DEFOG_EFFECT_CLEARING,
|
||||
GEN_CONFIG_BURN_HIT_THAW,
|
||||
GEN_CONFIG_BURN_FACADE_DMG,
|
||||
GEN_CONFIG_COUNT
|
||||
};
|
||||
|
||||
|
||||
@ -93,6 +93,9 @@
|
||||
#define ITEM_EFFECT_HEAL_PP 21
|
||||
#define ITEM_EFFECT_NONE 22
|
||||
|
||||
#define ITEM_FRIENDSHIP_MAPSEC_BONUS 1 // The amount of bonus friendship gained when an item is used on a Pokémon whose met location matches the current map section.
|
||||
#define ITEM_FRIENDSHIP_LUXURY_BONUS 1 // The amount of bonus friendship gained when a Pokémon is in the Luxury Ball.
|
||||
|
||||
// Since X item stat increases are now handled by battle scripts, the friendship increase effect is now handled by the battle controller in HandleAction_UseItem.
|
||||
#define X_ITEM_FRIENDSHIP_INCREASE 1 // The amount of friendship gained by using an X item on a Pokémon in battle.
|
||||
#define X_ITEM_MAX_FRIENDSHIP 200 // Friendship threshold at which Pokémon stop receiving a friendship increase from using X items on them in battle.
|
||||
|
||||
@ -51,6 +51,12 @@ static const u8 sGenerationalChanges[GEN_CONFIG_COUNT] =
|
||||
[GEN_CONFIG_PARALYZE_ELECTRIC] = B_PARALYZE_ELECTRIC,
|
||||
[GEN_CONFIG_BADGE_BOOST] = B_BADGE_BOOST,
|
||||
[GEN_CONFIG_LEAF_GUARD_PREVENTS_REST] = B_LEAF_GUARD_PREVENTS_REST,
|
||||
[GEN_CONFIG_BEAT_UP] = B_BEAT_UP,
|
||||
[GEN_CONFIG_WIDE_GUARD] = B_WIDE_GUARD,
|
||||
[GEN_CONFIG_QUICK_GUARD] = B_QUICK_GUARD,
|
||||
[GEN_CONFIG_DEFOG_EFFECT_CLEARING] = B_DEFOG_EFFECT_CLEARING,
|
||||
[GEN_CONFIG_BURN_HIT_THAW] = B_BURN_HIT_THAW,
|
||||
[GEN_CONFIG_BURN_FACADE_DMG] = B_BURN_FACADE_DMG,
|
||||
};
|
||||
|
||||
#if TESTING
|
||||
|
||||
@ -217,6 +217,7 @@ bool32 CheckBagHasSpace(u16 itemId, u16 count);
|
||||
u32 GetFreeSpaceForItemInBag(u16 itemId);
|
||||
bool32 AddBagItem(u16 itemId, u16 count);
|
||||
bool32 RemoveBagItem(u16 itemId, u16 count);
|
||||
void RemoveBagItemFromSlot(struct BagPocket *pocket, u16 slotId, u16 count);
|
||||
u8 CountUsedPCItemSlots(void);
|
||||
bool32 CheckPCHasItem(u16 itemId, u16 count);
|
||||
bool32 AddPCItem(u16 itemId, u16 count);
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#include "constants/battle.h"
|
||||
#include "constants/cries.h"
|
||||
#include "constants/form_change_types.h"
|
||||
#include "constants/hold_effects.h"
|
||||
#include "constants/items.h"
|
||||
#include "constants/map_groups.h"
|
||||
#include "constants/regions.h"
|
||||
@ -829,6 +830,7 @@ s32 GetBattlerMultiplayerId(u16 id);
|
||||
u8 GetTrainerEncounterMusicId(u16 trainerOpponentId);
|
||||
u16 ModifyStatByNature(u8 nature, u16 stat, enum Stat statIndex);
|
||||
void AdjustFriendship(struct Pokemon *mon, u8 event);
|
||||
u8 CalculateFriendshipBonuses(struct Pokemon *mon, u32 modifier, enum HoldEffect itemHoldEffect);
|
||||
void MonGainEVs(struct Pokemon *mon, u16 defeatedSpecies);
|
||||
u16 GetMonEVCount(struct Pokemon *mon);
|
||||
void RandomlyGivePartyPokerus(struct Pokemon *party);
|
||||
|
||||
@ -818,7 +818,7 @@ static inline void CalcDynamicMoveDamage(struct DamageContext *ctx, u16 *medianD
|
||||
median = maximum = minimum = max(0, gBattleMons[ctx->battlerDef].hp - gBattleMons[ctx->battlerAtk].hp);
|
||||
break;
|
||||
case EFFECT_BEAT_UP:
|
||||
if (B_BEAT_UP >= GEN_5)
|
||||
if (GetGenConfig(GEN_CONFIG_BEAT_UP) >= GEN_5)
|
||||
{
|
||||
u32 partyCount = CalculatePartyCount(GetBattlerParty(ctx->battlerAtk));
|
||||
u32 i;
|
||||
@ -1924,7 +1924,7 @@ bool32 IsHazardClearingMove(u32 move)
|
||||
case EFFECT_TIDY_UP:
|
||||
return TRUE;
|
||||
case EFFECT_DEFOG:
|
||||
if (B_DEFOG_EFFECT_CLEARING >= GEN_6)
|
||||
if (GetGenConfig(GEN_CONFIG_DEFOG_EFFECT_CLEARING) >= GEN_6)
|
||||
return TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -3132,24 +3132,7 @@ void UpdateFriendshipFromXItem(u32 battler)
|
||||
|
||||
if (friendship < X_ITEM_MAX_FRIENDSHIP)
|
||||
{
|
||||
if (GetItemHoldEffect(heldItem) == HOLD_EFFECT_FRIENDSHIP_UP)
|
||||
friendship += 150 * X_ITEM_FRIENDSHIP_INCREASE / 100;
|
||||
else
|
||||
friendship += X_ITEM_FRIENDSHIP_INCREASE;
|
||||
|
||||
u8 pokeball;
|
||||
gBattleResources->bufferA[battler][1] = REQUEST_POKEBALL_BATTLE;
|
||||
GetBattlerMonData(battler, party, gBattlerPartyIndexes[battler], &pokeball);
|
||||
|
||||
if (pokeball == BALL_LUXURY)
|
||||
friendship++;
|
||||
|
||||
u8 metLocation;
|
||||
gBattleResources->bufferA[battler][1] = REQUEST_MET_LOCATION_BATTLE;
|
||||
GetBattlerMonData(battler, party, gBattlerPartyIndexes[battler], &metLocation);
|
||||
|
||||
if (metLocation == GetCurrentRegionMapSectionId())
|
||||
friendship++;
|
||||
friendship += CalculateFriendshipBonuses(GetBattlerMon(battler), X_ITEM_FRIENDSHIP_INCREASE, GetItemHoldEffect(heldItem));
|
||||
|
||||
if (friendship > MAX_FRIENDSHIP)
|
||||
friendship = MAX_FRIENDSHIP;
|
||||
|
||||
@ -61,7 +61,8 @@ enum ItemEffect TryBoosterEnergy(u32 battler, enum Ability ability, ActivationTi
|
||||
if (((ability == ABILITY_PROTOSYNTHESIS) && !((gBattleWeather & B_WEATHER_SUN) && HasWeatherEffect()))
|
||||
|| ((ability == ABILITY_QUARK_DRIVE) && !(gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)))
|
||||
{
|
||||
PREPARE_STAT_BUFFER(gBattleTextBuff1, GetHighestStatId(battler));
|
||||
gDisableStructs[battler].paradoxBoostedStat = GetParadoxHighestStatId(battler);
|
||||
PREPARE_STAT_BUFFER(gBattleTextBuff1, gDisableStructs[battler].paradoxBoostedStat);
|
||||
gBattlerAbility = gBattleScripting.battler = battler;
|
||||
gDisableStructs[battler].boosterEnergyActivated = TRUE;
|
||||
RecordAbilityBattle(battler, ability);
|
||||
@ -205,6 +206,13 @@ static enum ItemEffect TryConsumeMirrorHerb(u32 battler, ActivationTiming timing
|
||||
static enum ItemEffect TryKingsRock(u32 battlerAtk, u32 battlerDef, u32 item)
|
||||
{
|
||||
enum ItemEffect effect = ITEM_NO_EFFECT;
|
||||
|
||||
if (!IsBattlerAlive(battlerDef)
|
||||
|| !IsBattlerTurnDamaged(battlerDef)
|
||||
|| MoveIgnoresKingsRock(gCurrentMove)
|
||||
|| MoveHasAdditionalEffect(gCurrentMove, MOVE_EFFECT_FLINCH))
|
||||
return effect;
|
||||
|
||||
enum Ability ability = GetBattlerAbility(battlerAtk);
|
||||
u32 holdEffectParam = GetItemHoldEffectParam(item);
|
||||
|
||||
@ -212,11 +220,7 @@ static enum ItemEffect TryKingsRock(u32 battlerAtk, u32 battlerDef, u32 item)
|
||||
holdEffectParam *= 2;
|
||||
if (gSideStatuses[GetBattlerSide(battlerAtk)] & SIDE_STATUS_RAINBOW && gCurrentMove != MOVE_SECRET_POWER)
|
||||
holdEffectParam *= 2;
|
||||
if (IsBattlerTurnDamaged(battlerDef)
|
||||
&& !MoveIgnoresKingsRock(gCurrentMove)
|
||||
&& IsBattlerAlive(battlerDef)
|
||||
&& RandomPercentage(RNG_HOLD_EFFECT_FLINCH, holdEffectParam)
|
||||
&& ability != ABILITY_STENCH)
|
||||
if (ability != ABILITY_STENCH && RandomPercentage(RNG_HOLD_EFFECT_FLINCH, holdEffectParam))
|
||||
{
|
||||
SetMoveEffect(battlerAtk, battlerDef, MOVE_EFFECT_FLINCH, gBattlescriptCurrInstr, NO_FLAGS);
|
||||
effect = ITEM_EFFECT_OTHER;
|
||||
|
||||
@ -4281,6 +4281,13 @@ static void HandleTurnActionSelectionState(void)
|
||||
gChosenMoveByBattler[battler] = gDisableStructs[battler].encoredMove;
|
||||
gBattleStruct->chosenMovePositions[battler] = gDisableStructs[battler].encoredMovePos;
|
||||
gBattleCommunication[battler] = STATE_WAIT_ACTION_CONFIRMED_STANDBY;
|
||||
if (gTestRunnerEnabled)
|
||||
{
|
||||
UNUSED enum Gimmick gimmick = GIMMICK_NONE;
|
||||
if (gBattleResources->bufferB[battler][2] & RET_GIMMICK)
|
||||
gimmick = gBattleStruct->gimmick.usableGimmick[battler];
|
||||
TestRunner_Battle_CheckChosenMove(battler, gDisableStructs[battler].encoredMove, gDisableStructs[battler].encoredMovePos, gimmick);
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
@ -4781,9 +4788,9 @@ u32 GetBattlerTotalSpeedStat(u32 battler, enum Ability ability, enum HoldEffect
|
||||
else if (ability == ABILITY_SLOW_START && gDisableStructs[battler].slowStartTimer != 0)
|
||||
speed /= 2;
|
||||
else if (ability == ABILITY_PROTOSYNTHESIS && !(gBattleMons[battler].volatiles.transformed) && ((gBattleWeather & B_WEATHER_SUN && HasWeatherEffect()) || gDisableStructs[battler].boosterEnergyActivated))
|
||||
speed = (GetHighestStatId(battler) == STAT_SPEED) ? (speed * 150) / 100 : speed;
|
||||
speed = (GetParadoxBoostedStatId(battler) == STAT_SPEED) ? (speed * 150) / 100 : speed;
|
||||
else if (ability == ABILITY_QUARK_DRIVE && !(gBattleMons[battler].volatiles.transformed) && (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN || gDisableStructs[battler].boosterEnergyActivated))
|
||||
speed = (GetHighestStatId(battler) == STAT_SPEED) ? (speed * 150) / 100 : speed;
|
||||
speed = (GetParadoxBoostedStatId(battler) == STAT_SPEED) ? (speed * 150) / 100 : speed;
|
||||
else if (ability == ABILITY_UNBURDEN && gDisableStructs[battler].unburdenActive)
|
||||
speed *= 2;
|
||||
|
||||
|
||||
@ -4819,8 +4819,6 @@ static void Cmd_getexp(void)
|
||||
// not sure why gf clears the item and ability here
|
||||
gBattleStruct->expOrderId = 0;
|
||||
gBattleStruct->teamGotExpMsgPrinted = FALSE;
|
||||
gBattleMons[gBattlerFainted].item = ITEM_NONE;
|
||||
gBattleMons[gBattlerFainted].ability = ABILITY_NONE;
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
break;
|
||||
@ -7918,10 +7916,9 @@ static bool32 DoSwitchInEffectsForBattler(u32 battler)
|
||||
u32 i = 0;
|
||||
u32 side = GetBattlerSide(battler);
|
||||
// Neutralizing Gas announces itself before hazards
|
||||
if (!gSpecialStatuses[battler].announceNeutralizingGas
|
||||
&& AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS_FIRST_TURN, battler, gBattleMons[battler].ability, 0, 0))
|
||||
if (AbilityBattleEffects(ABILITYEFFECT_NEUTRALIZINGGAS, battler, 0, 0, 0))
|
||||
{
|
||||
gSpecialStatuses[battler].announceNeutralizingGas = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
// Healing Wish activates before hazards.
|
||||
// Starting from Gen8 - it heals only pokemon which can be healed. In gens 5,6,7 the effect activates anyways.
|
||||
@ -9378,7 +9375,7 @@ static bool32 TryDefogClear(u32 battlerAtk, bool32 clear)
|
||||
DEFOG_CLEAR(SIDE_STATUS_AURORA_VEIL, auroraVeilTimer, BattleScript_SideStatusWoreOffReturn, MOVE_AURORA_VEIL);
|
||||
DEFOG_CLEAR(SIDE_STATUS_SAFEGUARD, safeguardTimer, BattleScript_SideStatusWoreOffReturn, MOVE_SAFEGUARD);
|
||||
}
|
||||
if (B_DEFOG_EFFECT_CLEARING >= GEN_6)
|
||||
if (GetGenConfig(GEN_CONFIG_DEFOG_EFFECT_CLEARING) >= GEN_6)
|
||||
{
|
||||
gBattlerAttacker = i; // For correct battle string. Ally's / Foe's
|
||||
if (DefogClearHazards(saveBattler, i, clear))
|
||||
@ -9390,7 +9387,7 @@ static bool32 TryDefogClear(u32 battlerAtk, bool32 clear)
|
||||
BattleScriptCall(BattleScript_FogEnded_Ret);
|
||||
return TRUE;
|
||||
}
|
||||
if (B_DEFOG_EFFECT_CLEARING >= GEN_8 && (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY))
|
||||
if (GetGenConfig(GEN_CONFIG_DEFOG_EFFECT_CLEARING) >= GEN_8 && (gFieldStatuses & STATUS_FIELD_TERRAIN_ANY))
|
||||
{
|
||||
RemoveAllTerrains();
|
||||
BattleScriptCall(BattleScript_TerrainEnds_Ret);
|
||||
@ -9464,27 +9461,6 @@ u32 IsAbilityStatusProtected(u32 battler, enum Ability ability)
|
||||
|| IsFlowerVeilProtected(battler);
|
||||
}
|
||||
|
||||
enum Stat GetHighestStatId(u32 battler)
|
||||
{
|
||||
enum Stat i;
|
||||
enum Stat highestId = STAT_ATK;
|
||||
u32 highestStat = gBattleMons[battler].attack;
|
||||
|
||||
for (i = STAT_DEF; i < NUM_STATS; i++)
|
||||
{
|
||||
u16 *statVal = &gBattleMons[battler].attack + (i - 1);
|
||||
if (*statVal > highestStat && i != STAT_SPEED)
|
||||
{
|
||||
highestStat = *statVal;
|
||||
highestId = i;
|
||||
}
|
||||
}
|
||||
if (gBattleMons[battler].speed > highestStat)
|
||||
highestId = STAT_SPEED;
|
||||
|
||||
return highestId;
|
||||
}
|
||||
|
||||
static bool32 IsRototillerAffected(u32 battler)
|
||||
{
|
||||
if (!IsBattlerAlive(battler))
|
||||
@ -9704,8 +9680,8 @@ static void Cmd_setprotectlike(void)
|
||||
notLastTurn = FALSE;
|
||||
|
||||
if ((sProtectSuccessRates[gDisableStructs[gBattlerAttacker].protectUses] >= RandomUniform(RNG_PROTECT_FAIL, 0, USHRT_MAX) && notLastTurn)
|
||||
|| (protectMethod == PROTECT_WIDE_GUARD && B_WIDE_GUARD != GEN_5)
|
||||
|| (protectMethod == PROTECT_QUICK_GUARD && B_QUICK_GUARD != GEN_5))
|
||||
|| (protectMethod == PROTECT_WIDE_GUARD && GetGenConfig(GEN_CONFIG_WIDE_GUARD) >= GEN_6)
|
||||
|| (protectMethod == PROTECT_QUICK_GUARD && GetGenConfig(GEN_CONFIG_QUICK_GUARD) >= GEN_6))
|
||||
{
|
||||
if (GetMoveEffect(gCurrentMove) == EFFECT_ENDURE)
|
||||
{
|
||||
@ -9891,9 +9867,6 @@ static void Cmd_manipulatedamage(void)
|
||||
case DMG_CHANGE_SIGN:
|
||||
gBattleStruct->passiveHpUpdate[gBattlerAttacker] *= -1;
|
||||
break;
|
||||
case DMG_DOUBLED:
|
||||
gBattleStruct->moveDamage[gBattlerTarget] *= 2;
|
||||
break;
|
||||
case DMG_1_8_TARGET_HP:
|
||||
SetPassiveDamageAmount(gBattlerTarget, GetNonDynamaxMaxHP(gBattlerTarget) / 8);
|
||||
break;
|
||||
@ -12446,48 +12419,20 @@ static void Cmd_setfutureattack(void)
|
||||
static void Cmd_trydobeatup(void)
|
||||
{
|
||||
CMD_ARGS(const u8 *endInstr, const u8 *failInstr);
|
||||
struct Pokemon *party = GetBattlerParty(gBattlerAttacker);
|
||||
|
||||
if (!IsBattlerAlive(gBattlerTarget))
|
||||
{
|
||||
gMultiHitCounter = 0;
|
||||
gBattlescriptCurrInstr = cmd->endInstr;
|
||||
}
|
||||
else if (gBattleStruct->beatUpSlot == 0 && gMultiHitCounter == 0)
|
||||
{
|
||||
gBattlescriptCurrInstr = cmd->failInstr;
|
||||
}
|
||||
else
|
||||
{
|
||||
u8 beforeLoop = gBattleCommunication[0];
|
||||
for (;gBattleCommunication[0] < PARTY_SIZE; gBattleCommunication[0]++)
|
||||
{
|
||||
if (GetMonData(&party[gBattleCommunication[0]], MON_DATA_HP)
|
||||
&& GetMonData(&party[gBattleCommunication[0]], MON_DATA_SPECIES_OR_EGG) != SPECIES_NONE
|
||||
&& GetMonData(&party[gBattleCommunication[0]], MON_DATA_SPECIES_OR_EGG) != SPECIES_EGG
|
||||
&& !GetMonData(&party[gBattleCommunication[0]], MON_DATA_STATUS))
|
||||
break;
|
||||
}
|
||||
|
||||
if (gBattleCommunication[0] < PARTY_SIZE)
|
||||
{
|
||||
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gBattlerAttacker, gBattleCommunication[0])
|
||||
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
|
||||
gBattleStruct->moveDamage[gBattlerTarget] = GetSpeciesBaseAttack(GetMonData(&party[gBattleCommunication[0]], MON_DATA_SPECIES));
|
||||
gBattleStruct->moveDamage[gBattlerTarget] *= GetMovePower(gCurrentMove);
|
||||
gBattleStruct->moveDamage[gBattlerTarget] *= (GetMonData(&party[gBattleCommunication[0]], MON_DATA_LEVEL) * 2 / 5 + 2);
|
||||
gBattleStruct->moveDamage[gBattlerTarget] /= GetSpeciesBaseDefense(gBattleMons[gBattlerTarget].species);
|
||||
gBattleStruct->moveDamage[gBattlerTarget] = (gBattleStruct->moveDamage[gBattlerTarget] / 50) + 2;
|
||||
if (gProtectStructs[gBattlerAttacker].helpingHand)
|
||||
gBattleStruct->moveDamage[gBattlerTarget] = gBattleStruct->moveDamage[gBattlerTarget] * 15 / 10;
|
||||
|
||||
gBattleCommunication[0]++;
|
||||
}
|
||||
else if (beforeLoop != 0)
|
||||
{
|
||||
gBattlescriptCurrInstr = cmd->endInstr;
|
||||
}
|
||||
else
|
||||
{
|
||||
gBattlescriptCurrInstr = cmd->failInstr;
|
||||
}
|
||||
PREPARE_MON_NICK_WITH_PREFIX_BUFFER(gBattleTextBuff1, gBattlerAttacker, gBattleStruct->beatUpSpecies[gBattleStruct->beatUpSlot])
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
}
|
||||
|
||||
@ -12929,7 +12874,7 @@ static void Cmd_setgastroacid(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gBattleMons[gBattlerTarget].ability == ABILITY_NEUTRALIZING_GAS)
|
||||
if (gDisableStructs[gBattlerTarget].neutralizingGas)
|
||||
gSpecialStatuses[gBattlerTarget].neutralizingGasRemoved = TRUE;
|
||||
|
||||
gBattleMons[gBattlerTarget].volatiles.gastroAcid = TRUE;
|
||||
@ -14457,7 +14402,7 @@ static void Cmd_tryoverwriteability(void)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gBattleMons[gBattlerTarget].ability == ABILITY_NEUTRALIZING_GAS)
|
||||
if (gDisableStructs[gBattlerTarget].neutralizingGas)
|
||||
gSpecialStatuses[gBattlerTarget].neutralizingGasRemoved = TRUE;
|
||||
|
||||
RemoveAbilityFlags(gBattlerTarget);
|
||||
@ -14724,7 +14669,7 @@ bool32 CanBurnHitThaw(u16 move)
|
||||
{
|
||||
u8 i;
|
||||
|
||||
if (B_BURN_HIT_THAW >= GEN_6)
|
||||
if (GetGenConfig(GEN_CONFIG_BURN_HIT_THAW) >= GEN_6)
|
||||
{
|
||||
u32 numAdditionalEffects = GetMoveAdditionalEffectCount(move);
|
||||
for (i = 0; i < numAdditionalEffects; i++)
|
||||
@ -14964,12 +14909,14 @@ void BS_ItemRestoreHP(void)
|
||||
hp += healAmount;
|
||||
SetMonData(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], MON_DATA_HP, &hp);
|
||||
|
||||
// Revived battlers on the field need to be brought back.
|
||||
if (IsDoubleBattle() && battler != MAX_BATTLERS_COUNT)
|
||||
u32 partner = BATTLE_PARTNER(gBattlerAttacker);
|
||||
// Absent battlers on the field need to be replaced
|
||||
if (IsDoubleBattle() && (gAbsentBattlerFlags & (1u << partner)))
|
||||
{
|
||||
gAbsentBattlerFlags &= ~(1u << battler);
|
||||
gBattleMons[battler].hp = hp;
|
||||
gAbsentBattlerFlags &= ~(1u << partner);
|
||||
gBattleCommunication[MULTIUSE_STATE] = TRUE;
|
||||
gBattleScripting.battler = partner;
|
||||
BtlController_EmitChosenMonReturnValue(partner, B_COMM_TO_ENGINE, gBattleStruct->itemPartyIndex[gBattlerAttacker], NULL);
|
||||
}
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
@ -14978,37 +14925,47 @@ void BS_ItemRestoreHP(void)
|
||||
|
||||
void BS_ItemCureStatus(void)
|
||||
{
|
||||
NATIVE_ARGS(const u8 *noStatusInstr);
|
||||
u32 battler = gBattlerAttacker;
|
||||
NATIVE_ARGS(const u8 *noStatusInstr, const u8 *restoreBattlerInstr);
|
||||
u32 targetBattler = MAX_BATTLERS_COUNT;
|
||||
bool32 statusChanged = FALSE;
|
||||
struct Pokemon *party = GetBattlerParty(gBattlerAttacker);
|
||||
|
||||
// Heal volatile conditions if battler is active.
|
||||
if (gBattleStruct->itemPartyIndex[gBattlerAttacker] == gBattlerPartyIndexes[gBattlerAttacker])
|
||||
statusChanged = ItemHealMonVolatile(battler, gLastUsedItem);
|
||||
{
|
||||
statusChanged = ItemHealMonVolatile(gBattlerAttacker, gLastUsedItem);
|
||||
targetBattler = gBattlerAttacker;
|
||||
}
|
||||
else if (IsDoubleBattle()
|
||||
&& gBattleStruct->itemPartyIndex[gBattlerAttacker] == gBattlerPartyIndexes[BATTLE_PARTNER(gBattlerAttacker)])
|
||||
{
|
||||
statusChanged = ItemHealMonVolatile(BATTLE_PARTNER(gBattlerAttacker), gLastUsedItem);
|
||||
targetBattler = BATTLE_PARTNER(gBattlerAttacker);
|
||||
}
|
||||
|
||||
// Heal Status1 conditions.
|
||||
if (!HealStatusConditions(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], GetItemStatus1Mask(gLastUsedItem), battler))
|
||||
if (!HealStatusConditions(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], GetItemStatus1Mask(gLastUsedItem), targetBattler))
|
||||
{
|
||||
statusChanged = TRUE;
|
||||
if (GetItemStatus1Mask(gLastUsedItem) & STATUS1_SLEEP)
|
||||
gBattleMons[battler].volatiles.nightmare = FALSE;
|
||||
if (ItemHasVolatileFlag(gLastUsedItem, VOLATILE_CONFUSION))
|
||||
gBattleMons[battler].volatiles.infiniteConfusion = FALSE;
|
||||
gBattleMons[targetBattler].volatiles.nightmare = FALSE;
|
||||
}
|
||||
|
||||
if (statusChanged)
|
||||
if (!statusChanged)
|
||||
{
|
||||
gBattlescriptCurrInstr = cmd->noStatusInstr;
|
||||
return;
|
||||
}
|
||||
|
||||
PREPARE_SPECIES_BUFFER(gBattleTextBuff1, GetMonData(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], MON_DATA_SPECIES));
|
||||
if (targetBattler == MAX_BATTLERS_COUNT)
|
||||
{
|
||||
gBattleScripting.battler = battler;
|
||||
PREPARE_SPECIES_BUFFER(gBattleTextBuff1, GetMonData(&party[gBattleStruct->itemPartyIndex[gBattlerAttacker]], MON_DATA_SPECIES));
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
else
|
||||
{
|
||||
gBattlescriptCurrInstr = cmd->noStatusInstr;
|
||||
gBattleScripting.battler = targetBattler;
|
||||
gBattlescriptCurrInstr = cmd->restoreBattlerInstr;
|
||||
}
|
||||
}
|
||||
|
||||
@ -16137,11 +16094,11 @@ void BS_JumpIfMoveResultFlags(void)
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
}
|
||||
|
||||
void BS_JumpIfCriticalHit(void)
|
||||
void BS_JumpIfNotCriticalHit(void)
|
||||
{
|
||||
NATIVE_ARGS(const u8 *jumpInstr);
|
||||
|
||||
if (gSpecialStatuses[gBattlerTarget].criticalHit)
|
||||
if (!gSpecialStatuses[gBattlerTarget].criticalHit)
|
||||
gBattlescriptCurrInstr = cmd->jumpInstr;
|
||||
else
|
||||
gBattlescriptCurrInstr = cmd->nextInstr;
|
||||
|
||||
@ -67,6 +67,8 @@ static void SetRandomMultiHitCounter();
|
||||
static bool32 IsNonVolatileStatusBlocked(u32 battlerDef, enum Ability abilityDef, enum Ability abilityAffected, const u8 *battleScript, enum FunctionCallOption option);
|
||||
static bool32 CanSleepDueToSleepClause(u32 battlerAtk, u32 battlerDef, enum FunctionCallOption option);
|
||||
static bool32 IsOpposingSideEmpty(u32 battler);
|
||||
static void ResetParadoxWeatherStat(u32 battler);
|
||||
static void ResetParadoxTerrainStat(u32 battler);
|
||||
static bool32 CanBattlerFormChange(u32 battler, enum FormChanges method);
|
||||
|
||||
// Submoves
|
||||
@ -257,7 +259,10 @@ bool32 EndOrContinueWeather(void)
|
||||
{
|
||||
gBattleWeather = B_WEATHER_NONE;
|
||||
for (u32 battler = 0; battler < gBattlersCount; battler++)
|
||||
{
|
||||
gDisableStructs[battler].weatherAbilityDone = FALSE;
|
||||
ResetParadoxWeatherStat(battler);
|
||||
}
|
||||
gBattleCommunication[MULTISTRING_CHOOSER] = sBattleWeatherInfo[currBattleWeather].endMessage;
|
||||
BattleScriptExecute(BattleScript_WeatherFaded);
|
||||
return TRUE;
|
||||
@ -273,12 +278,35 @@ bool32 EndOrContinueWeather(void)
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Gen5+
|
||||
static u32 CalcBeatUpPower(void)
|
||||
{
|
||||
u32 species = gBattleStruct->beatUpSpecies[gBattleStruct->beatUpSlot++];
|
||||
return (GetSpeciesBaseAttack(species) / 10) + 5;
|
||||
}
|
||||
|
||||
// Gen 3/4
|
||||
static s32 CalcBeatUpDamage(struct DamageContext *ctx)
|
||||
{
|
||||
u32 partyIndex = gBattleStruct->beatUpSpecies[gBattleStruct->beatUpSlot++];
|
||||
struct Pokemon *party = GetBattlerParty(ctx->battlerAtk);
|
||||
u32 species = GetMonData(&party[partyIndex], MON_DATA_SPECIES);
|
||||
u32 levelFactor = GetMonData(&party[partyIndex], MON_DATA_LEVEL) * 2 / 5 + 2;
|
||||
s32 dmg = GetSpeciesBaseAttack(species);
|
||||
|
||||
dmg *= GetMovePower(ctx->move);
|
||||
dmg *= levelFactor;
|
||||
dmg /= GetSpeciesBaseDefense(gBattleMons[ctx->battlerDef].species);
|
||||
dmg = (dmg / 50) + 2;
|
||||
|
||||
if (gProtectStructs[ctx->battlerAtk].helpingHand)
|
||||
dmg = dmg * 15 / 10;
|
||||
if (ctx->isCrit)
|
||||
dmg *= 2;
|
||||
|
||||
return dmg;
|
||||
}
|
||||
|
||||
static bool32 ShouldTeraShellDistortTypeMatchups(u32 move, u32 battlerDef, enum Ability abilityDef)
|
||||
{
|
||||
if (!gSpecialStatuses[battlerDef].distortedTypeMatchups
|
||||
@ -1777,6 +1805,7 @@ void TryToRevertMimicryAndFlags(void)
|
||||
for (u32 battler = 0; battler < gBattlersCount; battler++)
|
||||
{
|
||||
gDisableStructs[battler].terrainAbilityDone = FALSE;
|
||||
ResetParadoxTerrainStat(battler);
|
||||
if (IsAbilityAndRecord(battler, GetBattlerAbility(battler), ABILITY_MIMICRY))
|
||||
RESTORE_BATTLER_TYPE(battler);
|
||||
}
|
||||
@ -2870,11 +2899,13 @@ static enum MoveCanceler CancelerMultihitMoves(struct BattleContext *ctx)
|
||||
|
||||
PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 3, 0)
|
||||
}
|
||||
else if (B_BEAT_UP >= GEN_5 && GetMoveEffect(ctx->currentMove) == EFFECT_BEAT_UP)
|
||||
else if (GetMoveEffect(ctx->currentMove) == EFFECT_BEAT_UP)
|
||||
{
|
||||
struct Pokemon* party = GetBattlerParty(ctx->battlerAtk);
|
||||
int i;
|
||||
gBattleStruct->beatUpSlot = 0;
|
||||
gMultiHitCounter = 0;
|
||||
memset(gBattleStruct->beatUpSpecies, 0xFF, sizeof(gBattleStruct->beatUpSpecies));
|
||||
|
||||
for (i = 0; i < PARTY_SIZE; i++)
|
||||
{
|
||||
@ -2884,12 +2915,14 @@ static enum MoveCanceler CancelerMultihitMoves(struct BattleContext *ctx)
|
||||
&& !GetMonData(&party[i], MON_DATA_IS_EGG)
|
||||
&& !GetMonData(&party[i], MON_DATA_STATUS))
|
||||
{
|
||||
gBattleStruct->beatUpSpecies[gBattleStruct->beatUpSlot++] = species;
|
||||
if (GetGenConfig(GEN_CONFIG_BEAT_UP) >= GEN_5)
|
||||
gBattleStruct->beatUpSpecies[gMultiHitCounter] = species;
|
||||
else
|
||||
gBattleStruct->beatUpSpecies[gMultiHitCounter] = i;
|
||||
gMultiHitCounter++;
|
||||
}
|
||||
}
|
||||
|
||||
gBattleStruct->beatUpSlot = 0;
|
||||
PREPARE_BYTE_NUMBER_BUFFER(gBattleScripting.multihitString, 1, 0)
|
||||
}
|
||||
else
|
||||
@ -3183,7 +3216,10 @@ bool32 TryChangeBattleWeather(u32 battler, u32 battleWeatherId, u32 ability)
|
||||
{
|
||||
gBattleWeather = sBattleWeatherInfo[battleWeatherId].flag;
|
||||
for (u32 i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
gDisableStructs[i].weatherAbilityDone = FALSE;
|
||||
ResetParadoxWeatherStat(i);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
else
|
||||
@ -3197,7 +3233,10 @@ bool32 TryChangeBattleWeather(u32 battler, u32 battleWeatherId, u32 ability)
|
||||
else
|
||||
gWishFutureKnock.weatherDuration = 5;
|
||||
for (u32 i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
gDisableStructs[i].weatherAbilityDone = FALSE;
|
||||
ResetParadoxWeatherStat(i);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -3214,7 +3253,10 @@ bool32 TryChangeBattleTerrain(u32 battler, u32 statusFlag)
|
||||
gFieldStatuses &= ~STATUS_FIELD_TERRAIN_ANY;
|
||||
gFieldStatuses |= statusFlag;
|
||||
for (u32 i = 0; i < gBattlersCount; i++)
|
||||
{
|
||||
gDisableStructs[i].terrainAbilityDone = FALSE;
|
||||
ResetParadoxTerrainStat(i);
|
||||
}
|
||||
if (GetBattlerHoldEffect(battler) == HOLD_EFFECT_TERRAIN_EXTENDER)
|
||||
gFieldTimers.terrainTimer = 8;
|
||||
else
|
||||
@ -5493,7 +5535,7 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
|
||||
case ABILITYEFFECT_NEUTRALIZINGGAS:
|
||||
case ABILITYEFFECT_NEUTRALIZINGGAS_FIRST_TURN:
|
||||
// Prints message only. separate from ABILITYEFFECT_ON_SWITCHIN bc activates before entry hazards
|
||||
if (gBattleMons[battler].ability == ABILITY_NEUTRALIZING_GAS && !gDisableStructs[battler].neutralizingGas)
|
||||
if (gLastUsedAbility == ABILITY_NEUTRALIZING_GAS && !gDisableStructs[battler].neutralizingGas)
|
||||
{
|
||||
gDisableStructs[battler].neutralizingGas = TRUE;
|
||||
gBattlerAbility = battler;
|
||||
@ -5506,6 +5548,8 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
|
||||
}
|
||||
break;
|
||||
case ABILITYEFFECT_ON_WEATHER: // For ability effects that activate when the battle weather changes.
|
||||
if (!IsBattlerAlive(battler))
|
||||
return effect;
|
||||
gLastUsedAbility = GetBattlerAbility(battler);
|
||||
switch (gLastUsedAbility)
|
||||
{
|
||||
@ -5539,7 +5583,8 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
|
||||
&& !gDisableStructs[battler].boosterEnergyActivated)
|
||||
{
|
||||
gDisableStructs[battler].weatherAbilityDone = TRUE;
|
||||
PREPARE_STAT_BUFFER(gBattleTextBuff1, GetHighestStatId(battler));
|
||||
gDisableStructs[battler].paradoxBoostedStat = GetParadoxHighestStatId(battler);
|
||||
PREPARE_STAT_BUFFER(gBattleTextBuff1, gDisableStructs[battler].paradoxBoostedStat);
|
||||
gBattleScripting.battler = battler;
|
||||
BattleScriptPushCursorAndCallback(BattleScript_ProtosynthesisActivates);
|
||||
effect++;
|
||||
@ -5550,6 +5595,8 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
|
||||
}
|
||||
break;
|
||||
case ABILITYEFFECT_ON_TERRAIN: // For ability effects that activate when the field terrain changes.
|
||||
if (!IsBattlerAlive(battler))
|
||||
return effect;
|
||||
gLastUsedAbility = GetBattlerAbility(battler);
|
||||
switch (gLastUsedAbility)
|
||||
{
|
||||
@ -5570,7 +5617,8 @@ u32 AbilityBattleEffects(enum AbilityEffect caseID, u32 battler, enum Ability ab
|
||||
&& !gDisableStructs[battler].boosterEnergyActivated)
|
||||
{
|
||||
gDisableStructs[battler].terrainAbilityDone = TRUE;
|
||||
PREPARE_STAT_BUFFER(gBattleTextBuff1, GetHighestStatId(battler));
|
||||
gDisableStructs[battler].paradoxBoostedStat = GetParadoxHighestStatId(battler);
|
||||
PREPARE_STAT_BUFFER(gBattleTextBuff1, gDisableStructs[battler].paradoxBoostedStat);
|
||||
gBattlerAbility = gBattleScripting.battler = battler;
|
||||
BattleScriptPushCursorAndCallback(BattleScript_QuarkDriveActivates);
|
||||
effect++;
|
||||
@ -5684,7 +5732,7 @@ u32 GetBattlerAbilityInternal(u32 battler, u32 ignoreMoldBreaker, u32 noAbilityS
|
||||
|
||||
if (!hasAbilityShield
|
||||
&& IsNeutralizingGasOnField()
|
||||
&& !gDisableStructs[battler].neutralizingGas)
|
||||
&& (gBattleMons[battler].ability != ABILITY_NEUTRALIZING_GAS || gBattleMons[battler].volatiles.gastroAcid))
|
||||
return ABILITY_NONE;
|
||||
|
||||
if (CanBreakThroughAbility(gBattlerAttacker, battler, gBattleMons[gBattlerAttacker].ability, hasAbilityShield, ignoreMoldBreaker))
|
||||
@ -5805,6 +5853,108 @@ bool32 IsBattlerTerrainAffected(u32 battler, enum Ability ability, enum HoldEffe
|
||||
return IsBattlerGrounded(battler, ability, holdEffect);
|
||||
}
|
||||
|
||||
u32 GetHighestStatId(u32 battler)
|
||||
{
|
||||
u32 highestId = STAT_ATK;
|
||||
u32 highestStat = gBattleMons[battler].attack;
|
||||
|
||||
for (u32 stat = STAT_DEF; stat < NUM_STATS; stat++)
|
||||
{
|
||||
if (stat == STAT_SPEED)
|
||||
continue;
|
||||
|
||||
u16 *statVal = &gBattleMons[battler].attack + (stat - 1);
|
||||
if (*statVal > highestStat)
|
||||
{
|
||||
highestStat = *statVal;
|
||||
highestId = stat;
|
||||
}
|
||||
}
|
||||
|
||||
if (gBattleMons[battler].speed > highestStat)
|
||||
highestId = STAT_SPEED;
|
||||
|
||||
return highestId;
|
||||
}
|
||||
|
||||
static u32 GetStatValueWithStages(u32 battler, u32 stat)
|
||||
{
|
||||
u32 statValue;
|
||||
|
||||
switch (stat)
|
||||
{
|
||||
case STAT_ATK:
|
||||
statValue = gBattleMons[battler].attack;
|
||||
break;
|
||||
case STAT_DEF:
|
||||
statValue = gBattleMons[battler].defense;
|
||||
break;
|
||||
case STAT_SPATK:
|
||||
statValue = gBattleMons[battler].spAttack;
|
||||
break;
|
||||
case STAT_SPDEF:
|
||||
statValue = gBattleMons[battler].spDefense;
|
||||
break;
|
||||
case STAT_SPEED:
|
||||
statValue = gBattleMons[battler].speed;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
||||
statValue *= gStatStageRatios[gBattleMons[battler].statStages[stat]][0];
|
||||
statValue /= gStatStageRatios[gBattleMons[battler].statStages[stat]][1];
|
||||
|
||||
return statValue;
|
||||
}
|
||||
|
||||
u32 GetParadoxHighestStatId(u32 battler)
|
||||
{
|
||||
u32 highestId = STAT_ATK;
|
||||
u32 highestStat = GetStatValueWithStages(battler, STAT_ATK);
|
||||
|
||||
for (u32 stat = STAT_DEF; stat < NUM_STATS; stat++)
|
||||
{
|
||||
if (stat == STAT_SPEED)
|
||||
continue;
|
||||
|
||||
u32 statValue = GetStatValueWithStages(battler, stat);
|
||||
if (statValue > highestStat)
|
||||
{
|
||||
highestStat = statValue;
|
||||
highestId = stat;
|
||||
}
|
||||
}
|
||||
|
||||
u32 speed = GetStatValueWithStages(battler, STAT_SPEED);
|
||||
if (speed > highestStat)
|
||||
highestId = STAT_SPEED;
|
||||
|
||||
return highestId;
|
||||
}
|
||||
|
||||
static void ResetParadoxWeatherStat(u32 battler)
|
||||
{
|
||||
if (gBattleMons[battler].ability == ABILITY_PROTOSYNTHESIS
|
||||
&& !gDisableStructs[battler].boosterEnergyActivated)
|
||||
gDisableStructs[battler].paradoxBoostedStat = 0;
|
||||
}
|
||||
|
||||
static void ResetParadoxTerrainStat(u32 battler)
|
||||
{
|
||||
if (gBattleMons[battler].ability == ABILITY_QUARK_DRIVE
|
||||
&& !gDisableStructs[battler].boosterEnergyActivated)
|
||||
gDisableStructs[battler].paradoxBoostedStat = 0;
|
||||
}
|
||||
|
||||
u32 GetParadoxBoostedStatId(u32 battler)
|
||||
{
|
||||
if (gDisableStructs[battler].paradoxBoostedStat == 0)
|
||||
gDisableStructs[battler].paradoxBoostedStat = GetParadoxHighestStatId(battler);
|
||||
|
||||
return gDisableStructs[battler].paradoxBoostedStat;
|
||||
}
|
||||
|
||||
bool32 CanBeSlept(u32 battlerAtk, u32 battlerDef, enum Ability abilityDef, enum SleepClauseBlock isBlockedBySleepClause)
|
||||
{
|
||||
if (IsSleepClauseActiveForSide(GetBattlerSide(battlerDef)) && isBlockedBySleepClause != NOT_BLOCKED_BY_SLEEP_CLAUSE)
|
||||
@ -7036,7 +7186,7 @@ static inline u32 CalcMoveBasePower(struct DamageContext *ctx)
|
||||
basePower *= 2;
|
||||
break;
|
||||
case EFFECT_BEAT_UP:
|
||||
if (B_BEAT_UP >= GEN_5)
|
||||
if (GetGenConfig(GEN_CONFIG_BEAT_UP) >= GEN_5)
|
||||
basePower = CalcBeatUpPower();
|
||||
break;
|
||||
case EFFECT_PSYBLADE:
|
||||
@ -7304,7 +7454,7 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageContext *ctx)
|
||||
break;
|
||||
case ABILITY_PROTOSYNTHESIS:
|
||||
{
|
||||
enum Stat defHighestStat = GetHighestStatId(battlerDef);
|
||||
enum Stat defHighestStat = GetParadoxBoostedStatId(battlerDef);
|
||||
if (((ctx->weather & B_WEATHER_SUN && HasWeatherEffect()) || gDisableStructs[battlerDef].boosterEnergyActivated)
|
||||
&& ((IsBattleMovePhysical(move) && defHighestStat == STAT_DEF) || (IsBattleMoveSpecial(move) && defHighestStat == STAT_SPDEF))
|
||||
&& !(gBattleMons[battlerDef].volatiles.transformed))
|
||||
@ -7313,7 +7463,7 @@ static inline u32 CalcMoveBasePowerAfterModifiers(struct DamageContext *ctx)
|
||||
break;
|
||||
case ABILITY_QUARK_DRIVE:
|
||||
{
|
||||
u8 defHighestStat = GetHighestStatId(battlerDef);
|
||||
u32 defHighestStat = GetParadoxBoostedStatId(battlerDef);
|
||||
if ((gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN || gDisableStructs[battlerDef].boosterEnergyActivated)
|
||||
&& ((IsBattleMovePhysical(move) && defHighestStat == STAT_DEF) || (IsBattleMoveSpecial(move) && defHighestStat == STAT_SPDEF))
|
||||
&& !(gBattleMons[battlerDef].volatiles.transformed))
|
||||
@ -7604,7 +7754,7 @@ static inline u32 CalcAttackStat(struct DamageContext *ctx)
|
||||
case ABILITY_PROTOSYNTHESIS:
|
||||
if (!(gBattleMons[battlerAtk].volatiles.transformed))
|
||||
{
|
||||
enum Stat atkHighestStat = GetHighestStatId(battlerAtk);
|
||||
enum Stat atkHighestStat = GetParadoxBoostedStatId(battlerAtk);
|
||||
if (((ctx->weather & B_WEATHER_SUN) && HasWeatherEffect()) || gDisableStructs[battlerAtk].boosterEnergyActivated)
|
||||
{
|
||||
if ((IsBattleMovePhysical(move) && atkHighestStat == STAT_ATK) || (IsBattleMoveSpecial(move) && atkHighestStat == STAT_SPATK))
|
||||
@ -7615,7 +7765,7 @@ static inline u32 CalcAttackStat(struct DamageContext *ctx)
|
||||
case ABILITY_QUARK_DRIVE:
|
||||
if (!(gBattleMons[battlerAtk].volatiles.transformed))
|
||||
{
|
||||
enum Stat atkHighestStat = GetHighestStatId(battlerAtk);
|
||||
enum Stat atkHighestStat = GetParadoxBoostedStatId(battlerAtk);
|
||||
if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN || gDisableStructs[battlerAtk].boosterEnergyActivated)
|
||||
{
|
||||
if ((IsBattleMovePhysical(move) && atkHighestStat == STAT_ATK) || (IsBattleMoveSpecial(move) && atkHighestStat == STAT_SPATK))
|
||||
@ -7954,12 +8104,12 @@ static inline uq4_12_t GetBurnOrFrostBiteModifier(struct DamageContext *ctx)
|
||||
|
||||
if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_BURN
|
||||
&& IsBattleMovePhysical(ctx->move)
|
||||
&& (B_BURN_FACADE_DMG < GEN_6 || moveEffect != EFFECT_FACADE)
|
||||
&& (GetGenConfig(GEN_CONFIG_BURN_FACADE_DMG) < GEN_6 || moveEffect != EFFECT_FACADE)
|
||||
&& ctx->abilityAtk != ABILITY_GUTS)
|
||||
return UQ_4_12(0.5);
|
||||
if (gBattleMons[ctx->battlerAtk].status1 & STATUS1_FROSTBITE
|
||||
&& IsBattleMoveSpecial(ctx->move)
|
||||
&& (B_BURN_FACADE_DMG < GEN_6 || moveEffect != EFFECT_FACADE))
|
||||
&& (GetGenConfig(GEN_CONFIG_BURN_FACADE_DMG) < GEN_6 || moveEffect != EFFECT_FACADE))
|
||||
return UQ_4_12(0.5);
|
||||
return UQ_4_12(1.0);
|
||||
}
|
||||
@ -8319,6 +8469,12 @@ s32 DoFixedDamageMoveCalc(struct DamageContext *ctx)
|
||||
case EFFECT_FINAL_GAMBIT:
|
||||
dmg = GetNonDynamaxHP(ctx->battlerAtk);
|
||||
break;
|
||||
case EFFECT_BEAT_UP:
|
||||
if (GetGenConfig(GEN_CONFIG_BEAT_UP) < GEN_5)
|
||||
dmg = CalcBeatUpDamage(ctx);
|
||||
else
|
||||
return INT32_MAX;
|
||||
break;
|
||||
default:
|
||||
return INT32_MAX;
|
||||
}
|
||||
@ -9461,7 +9617,7 @@ enum DamageCategory GetBattleMoveCategory(u32 move)
|
||||
return DAMAGE_CATEGORY_STATUS;
|
||||
}
|
||||
|
||||
if (B_PHYSICAL_SPECIAL_SPLIT <= GEN_4)
|
||||
if (B_PHYSICAL_SPECIAL_SPLIT < GEN_4)
|
||||
return gTypesInfo[GetBattleMoveType(move)].damageCategory;
|
||||
|
||||
return GetMoveCategory(move);
|
||||
@ -10455,9 +10611,10 @@ bool32 ItemHealMonVolatile(u32 battler, u16 itemId)
|
||||
const u8 *effect = GetItemEffect(itemId);
|
||||
if (effect[3] & ITEM3_STATUS_ALL)
|
||||
{
|
||||
statusChanged = (gBattleMons[battler].volatiles.infatuation || gBattleMons[battler].volatiles.confusionTurns > 0);
|
||||
statusChanged = (gBattleMons[battler].volatiles.infatuation || gBattleMons[battler].volatiles.confusionTurns > 0 || gBattleMons[battler].volatiles.infiniteConfusion);
|
||||
gBattleMons[battler].volatiles.infatuation = 0;
|
||||
gBattleMons[battler].volatiles.confusionTurns = 0;
|
||||
gBattleMons[battler].volatiles.infiniteConfusion = FALSE;
|
||||
}
|
||||
else if (effect[0] & ITEM0_INFATUATION)
|
||||
{
|
||||
@ -10466,8 +10623,9 @@ bool32 ItemHealMonVolatile(u32 battler, u16 itemId)
|
||||
}
|
||||
else if (effect[3] & ITEM3_CONFUSION)
|
||||
{
|
||||
statusChanged = gBattleMons[battler].volatiles.confusionTurns > 0;
|
||||
statusChanged = (gBattleMons[battler].volatiles.confusionTurns > 0 || gBattleMons[battler].volatiles.infiniteConfusion);
|
||||
gBattleMons[battler].volatiles.confusionTurns = 0;
|
||||
gBattleMons[battler].volatiles.infiniteConfusion = FALSE;
|
||||
}
|
||||
|
||||
return statusChanged;
|
||||
|
||||
@ -791,7 +791,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
|
||||
|
||||
[EFFECT_BEAT_UP] =
|
||||
{
|
||||
.battleScript = (B_BEAT_UP >= GEN_5) ? BattleScript_EffectHit : BattleScript_EffectBeatUp,
|
||||
.battleScript = BattleScript_EffectBeatUp,
|
||||
.battleTvScore = 2,
|
||||
},
|
||||
|
||||
|
||||
@ -5722,7 +5722,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_ALL] =
|
||||
.ignoresProtect = TRUE,
|
||||
.ignoresSubstitute = TRUE,
|
||||
.mirrorMoveBanned = TRUE,
|
||||
.soundMove = B_UPDATED_MOVE_FLAGS != GEN_5,
|
||||
.soundMove = TRUE,
|
||||
.contestEffect = C_UPDATED_MOVE_EFFECTS >= GEN_6 ? CONTEST_EFFECT_AVOID_STARTLE : CONTEST_EFFECT_BETTER_IF_LAST,
|
||||
.contestCategory = CONTEST_CATEGORY_BEAUTY,
|
||||
.contestComboStarterId = COMBO_STARTER_HEAL_BELL,
|
||||
|
||||
@ -932,8 +932,8 @@ static void DexNavUpdateDirectionArrow(void)
|
||||
{
|
||||
u16 tileX = sDexNavSearchDataPtr->tileX;
|
||||
u16 tileY = sDexNavSearchDataPtr->tileY;
|
||||
u16 playerX = gSaveBlock1Ptr->pos.x + 7;
|
||||
u16 playerY = gSaveBlock1Ptr->pos.y + 7;
|
||||
u16 playerX = gSaveBlock1Ptr->pos.x + MAP_OFFSET;
|
||||
u16 playerY = gSaveBlock1Ptr->pos.y + MAP_OFFSET;
|
||||
u16 deltaX = abs(tileX - playerX);
|
||||
u16 deltaY = abs(tileY - playerY);
|
||||
const u8 *str;
|
||||
|
||||
@ -4313,8 +4313,8 @@ void GetObjectPosition(u16* xPointer, u16* yPointer, u32 localId, u32 useTemplat
|
||||
|
||||
objectId = GetObjectEventIdByLocalId(localId);
|
||||
objEvent = &gObjectEvents[objectId];
|
||||
*xPointer = objEvent->currentCoords.x - 7;
|
||||
*yPointer = objEvent->currentCoords.y - 7;
|
||||
*xPointer = objEvent->currentCoords.x - MAP_OFFSET;
|
||||
*yPointer = objEvent->currentCoords.y - MAP_OFFSET;
|
||||
}
|
||||
|
||||
bool32 CheckObjectAtXY(u32 x, u32 y)
|
||||
|
||||
@ -424,6 +424,13 @@ bool32 RemoveBagItem(u16 itemId, u16 count)
|
||||
return BagPocket_RemoveItem(&gBagPockets[GetItemPocket(itemId)], itemId, count);
|
||||
}
|
||||
|
||||
// Unsafe function: Only use with functions that already check the slot and count are valid
|
||||
void RemoveBagItemFromSlot(struct BagPocket *pocket, u16 slotId, u16 count)
|
||||
{
|
||||
struct ItemSlot itemSlot = BagPocket_GetSlotData(pocket, slotId);
|
||||
BagPocket_SetSlotItemIdAndCount(pocket, slotId, itemSlot.itemId, itemSlot.quantity - count);
|
||||
}
|
||||
|
||||
static u8 NONNULL BagPocket_CountUsedItemSlots(struct BagPocket *pocket)
|
||||
{
|
||||
u8 usedSlots = 0;
|
||||
|
||||
@ -170,6 +170,7 @@ static void PrintThereIsNoPokemon(u8);
|
||||
static void Task_ChooseHowManyToToss(u8);
|
||||
static void AskTossItems(u8);
|
||||
static void Task_RemoveItemFromBag(u8);
|
||||
static void Task_TossItemFromBag(u8 taskId);
|
||||
static void ItemMenu_Cancel(u8);
|
||||
static void HandleErrorMessage(u8);
|
||||
static void PrintItemCantBeHeld(u8);
|
||||
@ -1958,11 +1959,34 @@ static void ConfirmToss(u8 taskId)
|
||||
StringExpandPlaceholders(gStringVar4, gText_ThrewAwayVar2Var1s);
|
||||
FillWindowPixelBuffer(WIN_DESCRIPTION, PIXEL_FILL(0));
|
||||
BagMenu_Print(WIN_DESCRIPTION, FONT_NORMAL, gStringVar4, 3, 1, 0, 0, 0, COLORID_NORMAL);
|
||||
gTasks[taskId].func = Task_RemoveItemFromBag;
|
||||
if (CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE || FlagGet(FLAG_STORING_ITEMS_IN_PYRAMID_BAG) == TRUE)
|
||||
gTasks[taskId].func = Task_RemoveItemFromBag;
|
||||
else
|
||||
gTasks[taskId].func = Task_TossItemFromBag;
|
||||
}
|
||||
|
||||
static void Task_TossItemFromBag(u8 taskId)
|
||||
{
|
||||
s16 *data = gTasks[taskId].data;
|
||||
u16 *scrollPos = &gBagPosition.scrollPosition[gBagPosition.pocket];
|
||||
u16 *cursorPos = &gBagPosition.cursorPosition[gBagPosition.pocket];
|
||||
|
||||
if (JOY_NEW(A_BUTTON | B_BUTTON))
|
||||
{
|
||||
PlaySE(SE_SELECT);
|
||||
RemoveBagItemFromSlot(&gBagPockets[gBagPosition.pocket], *scrollPos + *cursorPos, tItemCount);
|
||||
DestroyListMenuTask(tListTaskId, scrollPos, cursorPos);
|
||||
UpdatePocketItemList(gBagPosition.pocket);
|
||||
UpdatePocketListPosition(gBagPosition.pocket);
|
||||
LoadBagItemListBuffers(gBagPosition.pocket);
|
||||
tListTaskId = ListMenuInit(&gMultiuseListMenuTemplate, *scrollPos, *cursorPos);
|
||||
ScheduleBgCopyTilemapToVram(0);
|
||||
ReturnToItemList(taskId);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove selected item(s) from the bag and update list
|
||||
// For when items are tossed or deposited
|
||||
// For when items are deposited
|
||||
static void Task_RemoveItemFromBag(u8 taskId)
|
||||
{
|
||||
s16 *data = gTasks[taskId].data;
|
||||
|
||||
@ -3082,6 +3082,10 @@ static void CB2_ShowPokemonSummaryScreen(void)
|
||||
UpdatePartyToBattleOrder();
|
||||
ShowPokemonSummaryScreen(SUMMARY_MODE_LOCK_MOVES, gPlayerParty, gPartyMenu.slotId, gPlayerPartyCount - 1, CB2_ReturnToPartyMenuFromSummaryScreen);
|
||||
}
|
||||
else if (gPartyMenu.menuType == PARTY_MENU_TYPE_CHOOSE_HALF)
|
||||
{
|
||||
ShowPokemonSummaryScreen(SUMMARY_MODE_LOCK_MOVES, gPlayerParty, gPartyMenu.slotId, gPlayerPartyCount - 1, CB2_ReturnToPartyMenuFromSummaryScreen);
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowPokemonSummaryScreen(SUMMARY_MODE_NORMAL, gPlayerParty, gPartyMenu.slotId, gPlayerPartyCount - 1, CB2_ReturnToPartyMenuFromSummaryScreen);
|
||||
|
||||
@ -2846,7 +2846,7 @@ u32 GetBoxMonData2(struct BoxPokemon *boxMon, s32 field)
|
||||
#define SET16(lhs) (lhs) = data[0] + (data[1] << 8)
|
||||
#define SET32(lhs) (lhs) = data[0] + (data[1] << 8) + (data[2] << 16) + (data[3] << 24)
|
||||
//
|
||||
// Prefer SET_BY_WIDTH for fields whose types might be extended (e.g.
|
||||
// Prefer SET_BY_WIDTH for fields whose types might be extended (e.g.
|
||||
// anything whose typedef is in gametypes.h).
|
||||
//
|
||||
#define SET_BY_WIDTH(lhs) \
|
||||
@ -3744,17 +3744,7 @@ bool8 ExecuteTableBasedItemEffect(struct Pokemon *mon, u16 item, u8 partyIndex,
|
||||
{ \
|
||||
friendshipChange = itemEffect[itemEffectParam]; \
|
||||
friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, NULL); \
|
||||
if (friendshipChange > 0 && holdEffect == HOLD_EFFECT_FRIENDSHIP_UP) \
|
||||
friendship += 150 * friendshipChange / 100; \
|
||||
else \
|
||||
friendship += friendshipChange; \
|
||||
if (friendshipChange > 0) \
|
||||
{ \
|
||||
if (GetMonData(mon, MON_DATA_POKEBALL, NULL) == ITEM_LUXURY_BALL) \
|
||||
friendship++; \
|
||||
if (GetMonData(mon, MON_DATA_MET_LOCATION, NULL) == GetCurrentRegionMapSectionId()) \
|
||||
friendship++; \
|
||||
} \
|
||||
friendship += CalculateFriendshipBonuses(mon,friendshipChange,holdEffect); \
|
||||
if (friendship < 0) \
|
||||
friendship = 0; \
|
||||
if (friendship > MAX_FRIENDSHIP) \
|
||||
@ -5290,7 +5280,7 @@ void AdjustFriendship(struct Pokemon *mon, u8 event)
|
||||
if (species && species != SPECIES_EGG)
|
||||
{
|
||||
u8 friendshipLevel = 0;
|
||||
s16 friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, 0);
|
||||
s32 friendship = GetMonData(mon, MON_DATA_FRIENDSHIP, 0);
|
||||
enum TrainerClassID opponentTrainerClass = GetTrainerClassFromId(TRAINER_BATTLE_PARAM.opponentA);
|
||||
|
||||
if (friendship > 99)
|
||||
@ -5316,18 +5306,7 @@ void AdjustFriendship(struct Pokemon *mon, u8 event)
|
||||
}
|
||||
|
||||
mod = sFriendshipEventModifiers[event][friendshipLevel];
|
||||
if (mod > 0 && holdEffect == HOLD_EFFECT_FRIENDSHIP_UP)
|
||||
// 50% increase, rounding down
|
||||
mod = (150 * mod) / 100;
|
||||
|
||||
friendship += mod;
|
||||
if (mod > 0)
|
||||
{
|
||||
if (GetMonData(mon, MON_DATA_POKEBALL, NULL) == ITEM_LUXURY_BALL)
|
||||
friendship++;
|
||||
if (GetMonData(mon, MON_DATA_MET_LOCATION, NULL) == GetCurrentRegionMapSectionId())
|
||||
friendship++;
|
||||
}
|
||||
friendship += CalculateFriendshipBonuses(mon,mod,holdEffect);
|
||||
|
||||
if (friendship < 0)
|
||||
friendship = 0;
|
||||
@ -5338,6 +5317,27 @@ void AdjustFriendship(struct Pokemon *mon, u8 event)
|
||||
}
|
||||
}
|
||||
|
||||
u8 CalculateFriendshipBonuses(struct Pokemon *mon, u32 modifier, enum HoldEffect itemHoldEffect)
|
||||
{
|
||||
u32 bonus = 0;
|
||||
|
||||
if ((modifier > 0) && (itemHoldEffect == HOLD_EFFECT_FRIENDSHIP_UP))
|
||||
bonus += 150 * modifier / 100;
|
||||
else
|
||||
bonus += modifier;
|
||||
|
||||
if (modifier == 0)
|
||||
return bonus;
|
||||
|
||||
if (GetMonData(mon, MON_DATA_POKEBALL, NULL) == ITEM_LUXURY_BALL)
|
||||
bonus += ITEM_FRIENDSHIP_LUXURY_BONUS;
|
||||
|
||||
if (GetMonData(mon, MON_DATA_MET_LOCATION, NULL) == GetCurrentRegionMapSectionId())
|
||||
bonus += ITEM_FRIENDSHIP_MAPSEC_BONUS;
|
||||
|
||||
return bonus;
|
||||
}
|
||||
|
||||
void MonGainEVs(struct Pokemon *mon, u16 defeatedSpecies)
|
||||
{
|
||||
u8 evs[NUM_STATS];
|
||||
|
||||
@ -3086,8 +3086,8 @@ bool8 ScrCmd_getobjectxy(struct ScriptContext *ctx)
|
||||
|
||||
bool8 ScrCmd_checkobjectat(struct ScriptContext *ctx)
|
||||
{
|
||||
u32 x = VarGet(ScriptReadHalfword(ctx)) + 7;
|
||||
u32 y = VarGet(ScriptReadHalfword(ctx)) + 7;
|
||||
u32 x = VarGet(ScriptReadHalfword(ctx)) + MAP_OFFSET;
|
||||
u32 y = VarGet(ScriptReadHalfword(ctx)) + MAP_OFFSET;
|
||||
u32 varId = ScriptReadHalfword(ctx);
|
||||
|
||||
Script_RequestEffects(SCREFF_V1);
|
||||
|
||||
@ -541,6 +541,9 @@ static u8 GetTrainerApproachDistance(struct ObjectEvent *trainerObj)
|
||||
PlayerGetDestCoords(&x, &y);
|
||||
if (trainerObj->trainerType == TRAINER_TYPE_NORMAL) // can only see in one direction
|
||||
{
|
||||
// Disable trainer approach while moving diagonally (usually moving on sideway stairs)
|
||||
if (trainerObj->facingDirection > DIR_EAST)
|
||||
return 0;
|
||||
approachDistance = sDirectionalApproachDistanceFuncs[trainerObj->facingDirection - 1](trainerObj, trainerObj->trainerRange_berryTreeId, x, y);
|
||||
return CheckPathBetweenTrainerAndPlayer(trainerObj, approachDistance, trainerObj->facingDirection);
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@
|
||||
#include "battle_setup.h"
|
||||
#include "random.h"
|
||||
#include "field_player_avatar.h"
|
||||
#include "fieldmap.h"
|
||||
#include "vs_seeker.h"
|
||||
#include "menu.h"
|
||||
#include "string_util.h"
|
||||
@ -390,8 +391,8 @@ static void GatherNearbyTrainerInfo(void)
|
||||
sVsSeeker->trainerInfo[vsSeekerObjectIdx].localId = templates[objectEventIdx].localId;
|
||||
TryGetObjectEventIdByLocalIdAndMap(templates[objectEventIdx].localId, gSaveBlock1Ptr->location.mapNum, gSaveBlock1Ptr->location.mapGroup, &objectEventId);
|
||||
sVsSeeker->trainerInfo[vsSeekerObjectIdx].objectEventId = objectEventId;
|
||||
sVsSeeker->trainerInfo[vsSeekerObjectIdx].xCoord = gObjectEvents[objectEventId].currentCoords.x - 7;
|
||||
sVsSeeker->trainerInfo[vsSeekerObjectIdx].yCoord = gObjectEvents[objectEventId].currentCoords.y - 7;
|
||||
sVsSeeker->trainerInfo[vsSeekerObjectIdx].xCoord = gObjectEvents[objectEventId].currentCoords.x - MAP_OFFSET;
|
||||
sVsSeeker->trainerInfo[vsSeekerObjectIdx].yCoord = gObjectEvents[objectEventId].currentCoords.y - MAP_OFFSET;
|
||||
sVsSeeker->trainerInfo[vsSeekerObjectIdx].graphicsId = templates[objectEventIdx].graphicsId;
|
||||
vsSeekerObjectIdx++;
|
||||
}
|
||||
@ -766,8 +767,8 @@ static bool8 IsTrainerVisibleOnScreen(struct VsSeekerTrainerInfo * trainerInfo)
|
||||
s16 y;
|
||||
|
||||
PlayerGetDestCoords(&x, &y);
|
||||
x -= 7;
|
||||
y -= 7;
|
||||
x -= MAP_OFFSET;
|
||||
y -= MAP_OFFSET;
|
||||
|
||||
if ( x - 7 <= trainerInfo->xCoord
|
||||
&& x + 7 >= trainerInfo->xCoord
|
||||
|
||||
@ -144,7 +144,7 @@ SINGLE_BATTLE_TEST("(Gulp Missile) Transformed Cramorant Gulping lowers defense
|
||||
PARAMETRIZE { species = SPECIES_CHATOT; ability = ABILITY_BIG_PECKS; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_CRAMORANT) { Ability(ABILITY_GULP_MISSILE); }
|
||||
OPPONENT(species) { Ability(ability); }
|
||||
OPPONENT(species) { Ability(ability); HP(9999); MaxHP(9999); } // In Gen 5 data, Surf would be enough to knock out Chatot
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SURF); MOVE(opponent, MOVE_SCRATCH); }
|
||||
} SCENE {
|
||||
|
||||
@ -5,12 +5,12 @@ SINGLE_BATTLE_TEST("Mirror Armor lowers a stat of the attacking Pokémon")
|
||||
{
|
||||
u16 move, statId;
|
||||
|
||||
PARAMETRIZE { move = MOVE_LEER; statId = STAT_DEF; }
|
||||
PARAMETRIZE { move = MOVE_GROWL; statId = STAT_ATK; }
|
||||
PARAMETRIZE { move = MOVE_LEER; statId = STAT_DEF; }
|
||||
PARAMETRIZE { move = MOVE_GROWL; statId = STAT_ATK; }
|
||||
PARAMETRIZE { move = MOVE_SWEET_SCENT; statId = STAT_EVASION; }
|
||||
PARAMETRIZE { move = MOVE_SAND_ATTACK; statId = STAT_ACC; }
|
||||
PARAMETRIZE { move = MOVE_CONFIDE; statId = STAT_SPATK; }
|
||||
PARAMETRIZE { move = MOVE_FAKE_TEARS; statId = STAT_SPDEF; }
|
||||
PARAMETRIZE { move = MOVE_CONFIDE; statId = STAT_SPATK; }
|
||||
PARAMETRIZE { move = MOVE_FAKE_TEARS; statId = STAT_SPDEF; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_CORVIKNIGHT) {Ability(ABILITY_MIRROR_ARMOR);}
|
||||
@ -29,7 +29,11 @@ SINGLE_BATTLE_TEST("Mirror Armor lowers a stat of the attacking Pokémon")
|
||||
MESSAGE("The opposing Wynaut's Attack fell!");
|
||||
break;
|
||||
case STAT_EVASION:
|
||||
MESSAGE("The opposing Wynaut's evasiveness harshly fell!");
|
||||
if (GetMoveEffect(move) == EFFECT_EVASION_DOWN_2) {
|
||||
MESSAGE("The opposing Wynaut's evasiveness harshly fell!");
|
||||
} else {
|
||||
MESSAGE("The opposing Wynaut's evasiveness fell!");
|
||||
}
|
||||
break;
|
||||
case STAT_ACC:
|
||||
MESSAGE("The opposing Wynaut's accuracy fell!");
|
||||
@ -43,7 +47,7 @@ SINGLE_BATTLE_TEST("Mirror Armor lowers a stat of the attacking Pokémon")
|
||||
}
|
||||
} THEN {
|
||||
EXPECT_EQ(player->statStages[statId], DEFAULT_STAT_STAGE);
|
||||
EXPECT_EQ(opponent->statStages[statId], (statId == STAT_SPDEF || statId == STAT_EVASION) ? DEFAULT_STAT_STAGE - 2 : DEFAULT_STAT_STAGE - 1);
|
||||
EXPECT_EQ(opponent->statStages[statId], (statId == STAT_SPDEF || (statId == STAT_EVASION && GetMoveEffect(move) == EFFECT_EVASION_DOWN_2)) ? DEFAULT_STAT_STAGE - 2 : DEFAULT_STAT_STAGE - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -219,3 +219,95 @@ SINGLE_BATTLE_TEST("Protosynthesis activates after weather was reset")
|
||||
ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Protosynthesis accounts for Sticky Web when determining the boosted stat")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(1); }
|
||||
PLAYER(SPECIES_FLUTTER_MANE) { Ability(ABILITY_PROTOSYNTHESIS); Attack(50); Defense(50); SpAttack(150); SpDefense(140); Speed(180); }
|
||||
OPPONENT(SPECIES_GALVANTULA) { Speed(60); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(1); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_STICKY_WEB); MOVE(player, MOVE_SUNNY_DAY); }
|
||||
TURN { SWITCH(player, 1); MOVE(opponent, MOVE_SPLASH); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_STICKY_WEB, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, player);
|
||||
ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS);
|
||||
MESSAGE("The harsh sunlight activated Flutter Mane's Protosynthesis!");
|
||||
MESSAGE("Flutter Mane's Sp. Atk was heightened!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Protosynthesis keeps its initial boosted stat after Speed is lowered")
|
||||
{
|
||||
s16 damage[2];
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_FLUTTER_MANE) { Ability(ABILITY_PROTOSYNTHESIS); Attack(10); Defense(10); SpAttack(150); SpDefense(120); Speed(180); Moves(MOVE_ROUND); }
|
||||
OPPONENT(SPECIES_NINETALES) { Ability(ABILITY_DROUGHT); Moves(MOVE_ICY_WIND, MOVE_CELEBRATE); Speed(100); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_ROUND); MOVE(opponent, MOVE_ICY_WIND); }
|
||||
TURN { MOVE(player, MOVE_ROUND); MOVE(opponent, MOVE_CELEBRATE); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(opponent, ABILITY_DROUGHT);
|
||||
ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ICY_WIND, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
} THEN {
|
||||
EXPECT_EQ(damage[0], damage[1]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Protosynthesis recalculates the boosted stat after Neutralizing Gas leaves the field")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_FLUTTER_MANE) { Ability(ABILITY_PROTOSYNTHESIS); Attack(10); Defense(10); SpAttack(150); SpDefense(120); Speed(180); }
|
||||
OPPONENT(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); Moves(MOVE_ICY_WIND); Speed(70); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(1); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SUNNY_DAY); MOVE(opponent, MOVE_ICY_WIND); }
|
||||
TURN { SWITCH(opponent, 1); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(opponent, ABILITY_NEUTRALIZING_GAS);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, player);
|
||||
NOT ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ICY_WIND, opponent);
|
||||
MESSAGE("The effects of the neutralizing gas wore off!");
|
||||
ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS);
|
||||
MESSAGE("The harsh sunlight activated Flutter Mane's Protosynthesis!");
|
||||
MESSAGE("Flutter Mane's Sp. Atk was heightened!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Protosynthesis retains its boosted stat after Neutralizing Gas briefly suppresses it")
|
||||
{
|
||||
s16 damage[2];
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_FLUTTER_MANE) { Ability(ABILITY_PROTOSYNTHESIS); Attack(10); Defense(10); SpAttack(150); SpDefense(120); Speed(180); Moves(MOVE_SUNNY_DAY, MOVE_ROUND, MOVE_CELEBRATE); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE); Speed(1); }
|
||||
OPPONENT(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); Moves(MOVE_CELEBRATE); Speed(70); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SUNNY_DAY); MOVE(opponent, MOVE_CELEBRATE); }
|
||||
TURN { MOVE(player, MOVE_ROUND); MOVE(opponent, MOVE_CELEBRATE); }
|
||||
TURN { MOVE(player, MOVE_CELEBRATE); SWITCH(opponent, 1); }
|
||||
TURN { MOVE(player, MOVE_CELEBRATE); SWITCH(opponent, 0); }
|
||||
TURN { MOVE(player, MOVE_ROUND); MOVE(opponent, MOVE_CELEBRATE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, player);
|
||||
ABILITY_POPUP(player, ABILITY_PROTOSYNTHESIS);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
ABILITY_POPUP(opponent, ABILITY_NEUTRALIZING_GAS);
|
||||
MESSAGE("Neutralizing gas filled the area!");
|
||||
MESSAGE("The effects of the neutralizing gas wore off!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
} THEN {
|
||||
EXPECT_EQ(damage[0], damage[1]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,6 +54,59 @@ SINGLE_BATTLE_TEST("Quark Drive boosts either Attack or Special Attack, not both
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Quark Drive keeps its initial boosted stat after Speed is lowered")
|
||||
{
|
||||
s16 damage[2];
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_IRON_MOTH) { Ability(ABILITY_QUARK_DRIVE); Attack(10); Defense(10); SpAttack(150); SpDefense(120); Speed(180); Moves(MOVE_ROUND, MOVE_CELEBRATE); }
|
||||
OPPONENT(SPECIES_TAPU_KOKO) { Ability(ABILITY_ELECTRIC_SURGE); Moves(MOVE_CELEBRATE, MOVE_ICY_WIND); Speed(100); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_ROUND); MOVE(opponent, MOVE_CELEBRATE); }
|
||||
TURN { MOVE(player, MOVE_CELEBRATE); MOVE(opponent, MOVE_ICY_WIND); }
|
||||
TURN { MOVE(player, MOVE_ROUND); MOVE(opponent, MOVE_CELEBRATE); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(opponent, ABILITY_ELECTRIC_SURGE);
|
||||
ABILITY_POPUP(player, ABILITY_QUARK_DRIVE);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ICY_WIND, opponent);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
} THEN {
|
||||
EXPECT_EQ(damage[0], damage[1]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Quark Drive retains its boosted stat after Neutralizing Gas briefly suppresses it")
|
||||
{
|
||||
s16 damage[2];
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_IRON_MOTH) { Ability(ABILITY_QUARK_DRIVE); Attack(10); Defense(10); SpAttack(150); SpDefense(120); Speed(180); Moves(MOVE_ELECTRIC_TERRAIN, MOVE_ROUND, MOVE_CELEBRATE); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_CELEBRATE); Speed(1); }
|
||||
OPPONENT(SPECIES_WEEZING) { Ability(ABILITY_NEUTRALIZING_GAS); Moves(MOVE_CELEBRATE); Speed(70); }
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_ELECTRIC_TERRAIN); MOVE(opponent, MOVE_CELEBRATE); }
|
||||
TURN { MOVE(player, MOVE_ROUND); MOVE(opponent, MOVE_CELEBRATE); }
|
||||
TURN { MOVE(player, MOVE_CELEBRATE); SWITCH(opponent, 1); }
|
||||
TURN { MOVE(player, MOVE_CELEBRATE); SWITCH(opponent, 0); }
|
||||
TURN { MOVE(player, MOVE_ROUND); MOVE(opponent, MOVE_CELEBRATE); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ELECTRIC_TERRAIN, player);
|
||||
ABILITY_POPUP(player, ABILITY_QUARK_DRIVE);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[0]);
|
||||
ABILITY_POPUP(opponent, ABILITY_NEUTRALIZING_GAS);
|
||||
MESSAGE("Neutralizing gas filled the area!");
|
||||
MESSAGE("The effects of the neutralizing gas wore off!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_ROUND, player);
|
||||
HP_BAR(opponent, captureDamage: &damage[1]);
|
||||
} THEN {
|
||||
EXPECT_EQ(damage[0], damage[1]);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Quark Drive ability pop up activates only once during the duration of electric terrain")
|
||||
{
|
||||
u16 turns;
|
||||
|
||||
@ -25,6 +25,7 @@ SINGLE_BATTLE_TEST("Synchronize will mirror back non volatile status back at opp
|
||||
SINGLE_BATTLE_TEST("Synchronize will still show up the ability pop up even if it fails")
|
||||
{
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_PARALYZE_ELECTRIC, GEN_6);
|
||||
ASSUME(MoveMakesContact(MOVE_TACKLE));
|
||||
PLAYER(SPECIES_PIKACHU) { Ability(ABILITY_STATIC); }
|
||||
OPPONENT(SPECIES_ABRA) { Ability(ABILITY_SYNCHRONIZE); }
|
||||
|
||||
@ -99,3 +99,28 @@ SINGLE_BATTLE_TEST("Unnerve activates only once per switch-in")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Unnerve stops applying on death but applies on revive")
|
||||
{
|
||||
u16 mon;
|
||||
u16 ability;
|
||||
PARAMETRIZE { mon = SPECIES_JOLTIK, ability = ABILITY_UNNERVE;}
|
||||
PARAMETRIZE { mon = SPECIES_CALYREX_ICE, ability = ABILITY_AS_ONE_ICE_RIDER; }
|
||||
GIVEN {
|
||||
ASSUME(gItemsInfo[ITEM_RAWST_BERRY].holdEffect == HOLD_EFFECT_CURE_BRN);
|
||||
ASSUME(gItemsInfo[ITEM_REVIVE].battleUsage == EFFECT_ITEM_REVIVE);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(mon) { Ability(ability); HP(1); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_RAWST_BERRY); Status1(STATUS1_BURN); }
|
||||
OPPONENT(SPECIES_WYNAUT) { Item(ITEM_RAWST_BERRY); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponentLeft, MOVE_SCRATCH, target: playerRight); }
|
||||
TURN { USE_ITEM(playerLeft, ITEM_REVIVE, partyIndex: 1); SKIP_TURN(playerRight); MOVE(opponentLeft, MOVE_WILL_O_WISP, target: opponentRight); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponentLeft);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponentLeft);
|
||||
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponentRight);
|
||||
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, opponentRight);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ AI_SINGLE_BATTLE_TEST("AI prefers Bubble over Water Gun if it's slower")
|
||||
PARAMETRIZE { speedPlayer = 10; speedAi = 200; }
|
||||
|
||||
GIVEN {
|
||||
ASSUME(GetMovePower(MOVE_WATER_GUN) == GetMovePower(MOVE_BUBBLE));
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||
PLAYER(SPECIES_SCIZOR) { Speed(speedPlayer); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Moves(MOVE_WATER_GUN, MOVE_BUBBLE); Speed(speedAi); }
|
||||
@ -34,6 +35,7 @@ AI_SINGLE_BATTLE_TEST("AI prefers Water Gun over Bubble if it knows that foe has
|
||||
PARAMETRIZE { abilityAI = ABILITY_MOXIE; }
|
||||
PARAMETRIZE { abilityAI = ABILITY_MOLD_BREAKER; } // Mold Breaker ignores Contrary.
|
||||
GIVEN {
|
||||
ASSUME(GetMovePower(MOVE_BUBBLE) == GetMovePower(MOVE_WATER_GUN));
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||
PLAYER(SPECIES_SHUCKLE) { Ability(ABILITY_CONTRARY); }
|
||||
OPPONENT(SPECIES_PINSIR) { Moves(MOVE_WATER_GUN, MOVE_BUBBLE); Ability(abilityAI); }
|
||||
@ -152,6 +154,7 @@ AI_SINGLE_BATTLE_TEST("AI prefers moves which deal more damage instead of moves
|
||||
ASSUME(GetMoveCategory(MOVE_SCALD) == DAMAGE_CATEGORY_SPECIAL);
|
||||
ASSUME(GetMoveCategory(MOVE_POISON_JAB) == DAMAGE_CATEGORY_PHYSICAL);
|
||||
ASSUME(GetMoveCategory(MOVE_WATER_GUN) == DAMAGE_CATEGORY_SPECIAL);
|
||||
ASSUME(GetSpeciesBaseAttack(SPECIES_NIDOQUEEN) == 92); // Gen 5's 82 Base Attack causes the test to fail
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||
PLAYER(SPECIES_TYPHLOSION) { Ability(abilityDef); }
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
@ -206,6 +209,9 @@ AI_SINGLE_BATTLE_TEST("AI prefers a weaker move over a one with a downside effec
|
||||
GIVEN {
|
||||
ASSUME(GetMoveCategory(MOVE_FLAMETHROWER) == DAMAGE_CATEGORY_SPECIAL); // Added because Typhlosion has to KO Wobbuffet
|
||||
ASSUME(GetMoveCategory(MOVE_OVERHEAT) == DAMAGE_CATEGORY_SPECIAL); // Added because Typhlosion has to KO Wobbuffet
|
||||
// With Gen 5 data, it chooses Overheat instead
|
||||
ASSUME(GetMovePower(MOVE_FLAMETHROWER) == 90); // In Gen 5, it's 95
|
||||
ASSUME(GetMovePower(MOVE_OVERHEAT) == 130); // In Gen 5, it's 140.
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||
PLAYER(SPECIES_WOBBUFFET) { HP(hp); }
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
@ -336,6 +342,7 @@ AI_SINGLE_BATTLE_TEST("AI won't use Solar Beam if there is no Sun up or the user
|
||||
GIVEN {
|
||||
ASSUME(GetMoveCategory(MOVE_SOLAR_BEAM) == DAMAGE_CATEGORY_SPECIAL);
|
||||
ASSUME(GetMoveCategory(MOVE_GRASS_PLEDGE) == DAMAGE_CATEGORY_SPECIAL);
|
||||
ASSUME(GetMovePower(MOVE_GRASS_PLEDGE) == 80); // Gen 5's 50 power causes the test to fail
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||
PLAYER(SPECIES_WOBBUFFET) { HP(211); }
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
@ -692,6 +699,7 @@ AI_SINGLE_BATTLE_TEST("AI won't use thawing moves if target is frozen unless it
|
||||
PARAMETRIZE { status = STATUS1_FROSTBITE; aiMove = MOVE_EMBER; aiFlags = AI_FLAG_CHECK_BAD_MOVE; }
|
||||
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_BURN_HIT_THAW, GEN_6); // In Gen 5, non-Fire burning moves didn't cause thawing
|
||||
ASSUME(GetMoveType(MOVE_EMBER) == TYPE_FIRE);
|
||||
ASSUME(GetMoveCategory(MOVE_TACKLE) == DAMAGE_CATEGORY_PHYSICAL);
|
||||
ASSUME(GetMoveCategory(MOVE_WATER_GUN) == DAMAGE_CATEGORY_SPECIAL);
|
||||
@ -933,6 +941,7 @@ AI_SINGLE_BATTLE_TEST("AI will see Magnitude damage")
|
||||
AI_SINGLE_BATTLE_TEST("AI will prefer resisted move over failing move")
|
||||
{
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, GEN_6);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY);
|
||||
PLAYER(SPECIES_ROSELIA) { Moves(MOVE_ABSORB); };
|
||||
OPPONENT(SPECIES_GLOOM) { Moves(MOVE_MEGA_DRAIN, MOVE_STUN_SPORE, MOVE_LEECH_SEED, MOVE_SYNTHESIS); }
|
||||
|
||||
@ -18,6 +18,7 @@ AI_SINGLE_BATTLE_TEST("AI sees increased base power of Facade")
|
||||
PARAMETRIZE { status1 = STATUS1_BURN; expectedMove = MOVE_FACADE; }
|
||||
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_BURN_FACADE_DMG, GEN_6);
|
||||
ASSUME(GetMoveEffect(MOVE_FACADE) == EFFECT_FACADE);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||
PLAYER(SPECIES_WOBBUFFET) { HP(60); }
|
||||
@ -162,6 +163,7 @@ AI_SINGLE_BATTLE_TEST("AI can choose Counter or Mirror Coat if the predicted mov
|
||||
ASSUME(GetMoveEffect(MOVE_MIRROR_COAT) == EFFECT_MIRROR_COAT);
|
||||
ASSUME(GetMoveCategory(MOVE_STRENGTH) == DAMAGE_CATEGORY_PHYSICAL);
|
||||
ASSUME(GetMoveCategory(MOVE_POWER_GEM) == DAMAGE_CATEGORY_SPECIAL);
|
||||
ASSUME(GetMovePower(MOVE_POWER_GEM) == 80); // Gen 5's 70 power causes the test to fail
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(1); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { HP(102); Speed(100); Moves(opponentMove, MOVE_STRENGTH); }
|
||||
|
||||
@ -590,6 +590,7 @@ AI_DOUBLE_BATTLE_TEST("AI uses After You to set up Trick Room")
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_AFTER_YOU) == EFFECT_AFTER_YOU);
|
||||
ASSUME(GetMoveEffect(MOVE_TRICK_ROOM) == EFFECT_TRICK_ROOM);
|
||||
ASSUME(IsHealingMove(MOVE_DRAINING_KISS)); // Doesn't have the Healing Move flag in Gen 5
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_DOUBLE_BATTLE);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(4); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(4); }
|
||||
@ -617,6 +618,7 @@ AI_DOUBLE_BATTLE_TEST("AI uses Trick Room intelligently")
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_AFTER_YOU) == EFFECT_AFTER_YOU);
|
||||
ASSUME(GetMoveEffect(MOVE_TRICK_ROOM) == EFFECT_TRICK_ROOM);
|
||||
ASSUME(IsHealingMove(MOVE_DRAINING_KISS)); // Doesn't have the Healing Move flag in Gen 5
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_TRY_TO_FAINT | AI_FLAG_CHECK_VIABILITY | AI_FLAG_DOUBLE_BATTLE);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(4); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { Speed(speed); }
|
||||
|
||||
@ -53,7 +53,7 @@ AI_SINGLE_BATTLE_TEST("AI sees on-field player ability correctly and does not se
|
||||
OPPONENT(SPECIES_BOMBIRDIER) {Level(42); Moves(MOVE_ROCK_SLIDE); }
|
||||
OPPONENT(SPECIES_IRON_THORNS) {Level(43); Moves(MOVE_SUPERCELL_SLAM, MOVE_ICE_PUNCH); }
|
||||
} WHEN {
|
||||
TURN {
|
||||
TURN {
|
||||
MOVE(player, MOVE_VOLT_SWITCH);
|
||||
SEND_OUT(player, 1);
|
||||
EXPECT_MOVE(opponent, MOVE_SCRATCH);
|
||||
@ -880,6 +880,7 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if it has bee
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_TOXIC) == EFFECT_NON_VOLATILE_STATUS);
|
||||
ASSUME(GetMoveNonVolatileStatus(MOVE_TOXIC) == MOVE_EFFECT_TOXIC);
|
||||
ASSUME(GetMovePower(MOVE_AURA_SPHERE) == 80); // Gen 5's 90 power causes too much damage
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING);
|
||||
PLAYER(SPECIES_ZIGZAGOON) { Moves(MOVE_SCRATCH, MOVE_CELEBRATE, MOVE_TOXIC, MOVE_AURA_SPHERE); }
|
||||
OPPONENT(SPECIES_ZIGZAGOON) { Moves(MOVE_SCRATCH); }
|
||||
@ -1184,6 +1185,8 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will stay in if Encore'd into
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_ENCORE) == EFFECT_ENCORE);
|
||||
ASSUME(GetSpeciesType(SPECIES_AZURILL, 0) == TYPE_NORMAL);
|
||||
ASSUME(GetSpeciesType(SPECIES_AZURILL, 1) == TYPE_FAIRY);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING);
|
||||
PLAYER(SPECIES_AZURILL) { Moves(MOVE_SCRATCH, MOVE_ENCORE); }
|
||||
OPPONENT(SPECIES_ODDISH) { Moves(MOVE_ACID); }
|
||||
@ -1195,6 +1198,22 @@ AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will stay in if Encore'd into
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if Encore'd into neutral move with good switchin 50% of the time")
|
||||
{
|
||||
PASSES_RANDOMLY(SHOULD_SWITCH_ENCORE_DAMAGE_PERCENTAGE, 100, RNG_AI_SWITCH_ENCORE);
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_ENCORE_TARGET, GEN_3);
|
||||
ASSUME(GetMoveEffect(MOVE_ENCORE) == EFFECT_ENCORE);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_SWITCHING);
|
||||
PLAYER(SPECIES_AZURILL) { Moves(MOVE_SCRATCH, MOVE_ENCORE); }
|
||||
OPPONENT(SPECIES_ODDISH) { Moves(MOVE_SCRATCH); }
|
||||
OPPONENT(SPECIES_ARON) { Moves(MOVE_METAL_CLAW); }
|
||||
} WHEN {
|
||||
TURN { EXPECT_MOVE(opponent, MOVE_SCRATCH); MOVE(player, MOVE_ENCORE); }
|
||||
TURN { MOVE(player, MOVE_SCRATCH); EXPECT_SWITCH(opponent, 1); }
|
||||
}
|
||||
}
|
||||
|
||||
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_SWITCHING: AI will switch out if Encore'd into neutral move with good switchin 50% of the time (Gen 5+)")
|
||||
{
|
||||
PASSES_RANDOMLY(SHOULD_SWITCH_ENCORE_DAMAGE_PERCENTAGE, 100, RNG_AI_SWITCH_ENCORE);
|
||||
GIVEN {
|
||||
@ -1573,6 +1592,8 @@ AI_SINGLE_BATTLE_TEST("AI_SMART_MON_CHOICES: AI sees its own weather setting abi
|
||||
AI_SINGLE_BATTLE_TEST("AI_FLAG_SMART_MON_CHOICES: AI will properly consider immunities when determining switchin type matchup")
|
||||
{
|
||||
GIVEN {
|
||||
ASSUME(GetSpeciesType(SPECIES_WHIMSICOTT, 0) == TYPE_GRASS);
|
||||
ASSUME(GetSpeciesType(SPECIES_WHIMSICOTT, 1) == TYPE_FAIRY); // Gen 5's pure Grass type makes the test fail
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_SMART_MON_CHOICES | AI_FLAG_OMNISCIENT);
|
||||
PLAYER(SPECIES_POLIWRATH) { Moves(MOVE_WATER_GUN, MOVE_KARATE_CHOP); }
|
||||
OPPONENT(SPECIES_ZIGZAGOON) { Level(1); Moves(MOVE_SCRATCH); }
|
||||
|
||||
@ -41,7 +41,7 @@ SINGLE_BATTLE_TEST("Damage calculation matches Gen5+")
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Damage calculation matches Gen5+ (Muscle Band, crit)")
|
||||
SINGLE_BATTLE_TEST("Damage calculation matches Gen6+ (Muscle Band, crit)")
|
||||
{
|
||||
s16 dmg;
|
||||
s16 expectedDamage;
|
||||
@ -62,6 +62,7 @@ SINGLE_BATTLE_TEST("Damage calculation matches Gen5+ (Muscle Band, crit)")
|
||||
PARAMETRIZE { expectedDamage = 276; }
|
||||
PARAMETRIZE { expectedDamage = 268; }
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_CRIT_MULTIPLIER, GEN_6);
|
||||
ASSUME(GetMoveCategory(MOVE_ICE_FANG) == DAMAGE_CATEGORY_PHYSICAL);
|
||||
PLAYER(SPECIES_GLACEON) { Level(75); Attack(123); Item(ITEM_MUSCLE_BAND); }
|
||||
OPPONENT(SPECIES_GARCHOMP) { Defense(163); }
|
||||
|
||||
@ -971,6 +971,7 @@ SINGLE_BATTLE_TEST("Dynamax: G-Max Stonesurge sets up Stealth Rocks")
|
||||
SINGLE_BATTLE_TEST("Dynamax: G-Max Steelsurge sets up sharp steel")
|
||||
{
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_DEFOG_EFFECT_CLEARING, GEN_6);
|
||||
ASSUME(MoveHasAdditionalEffect(MOVE_G_MAX_STEELSURGE, MOVE_EFFECT_STEELSURGE));
|
||||
PLAYER(SPECIES_COPPERAJAH) { GigantamaxFactor(TRUE); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
|
||||
@ -115,10 +115,16 @@ SINGLE_BATTLE_TEST("(Z-MOVE) Z_EFFECT_ALL_STATS_UP raises all of a battler's sta
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("(Z-MOVE) Z_EFFECT_BOOST_CRITS raises a battler's critical hit ratio")
|
||||
SINGLE_BATTLE_TEST("(Z-MOVE) Z_EFFECT_BOOST_CRITS raises a battler's critical hit ratio by 2 stages")
|
||||
{
|
||||
PASSES_RANDOMLY(1, 2, RNG_CRITICAL_HIT);
|
||||
u32 genConfig = 0, chance;
|
||||
for (u32 j = GEN_1; j <= GEN_5; j++)
|
||||
PARAMETRIZE { genConfig = j; chance = 4; } // 25%
|
||||
for (u32 j = GEN_6; j <= GEN_9; j++)
|
||||
PARAMETRIZE { genConfig = j; chance = 2; } // 50%
|
||||
PASSES_RANDOMLY(1, chance, RNG_CRITICAL_HIT);
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_CRIT_CHANCE, genConfig);
|
||||
ASSUME(GetMoveType(MOVE_FORESIGHT) == TYPE_NORMAL);
|
||||
ASSUME(GetMoveZEffect(MOVE_FORESIGHT) == Z_EFFECT_BOOST_CRITS);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_NORMALIUM_Z); }
|
||||
|
||||
@ -252,3 +252,54 @@ SINGLE_BATTLE_TEST("Berserk Gene does not cause an infinite loop")
|
||||
MESSAGE("Using Berserk Gene, the Attack of the opposing Wobbuffet sharply rose!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Berserker Gene confusion can be healed with bag items")
|
||||
{
|
||||
u16 item;
|
||||
PARAMETRIZE { item = ITEM_FULL_HEAL; }
|
||||
PARAMETRIZE { item = ITEM_HEAL_POWDER; }
|
||||
PARAMETRIZE { item = ITEM_PEWTER_CRUNCHIES; }
|
||||
PARAMETRIZE { item = ITEM_LAVA_COOKIE; }
|
||||
PARAMETRIZE { item = ITEM_RAGE_CANDY_BAR; }
|
||||
PARAMETRIZE { item = ITEM_OLD_GATEAU; }
|
||||
PARAMETRIZE { item = ITEM_CASTELIACONE; }
|
||||
PARAMETRIZE { item = ITEM_LUMIOSE_GALETTE; }
|
||||
PARAMETRIZE { item = ITEM_SHALOUR_SABLE; }
|
||||
PARAMETRIZE { item = ITEM_BIG_MALASADA; }
|
||||
PARAMETRIZE { item = ITEM_JUBILIFE_MUFFIN; }
|
||||
GIVEN {
|
||||
ASSUME(gItemsInfo[item].battleUsage == EFFECT_ITEM_CURE_STATUS);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_BERSERK_GENE);};
|
||||
OPPONENT(SPECIES_GENGAR);
|
||||
} WHEN {
|
||||
TURN { USE_ITEM(player, item, partyIndex: 0); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
|
||||
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, player);
|
||||
MESSAGE("Wobbuffet had its status healed!");
|
||||
} THEN {
|
||||
EXPECT(player->volatiles.infiniteConfusion == 0);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Berserker Gene confusion can be healed with used held items")
|
||||
{
|
||||
u16 item;
|
||||
PARAMETRIZE { item = ITEM_PERSIM_BERRY; }
|
||||
PARAMETRIZE { item = ITEM_LUM_BERRY; }
|
||||
|
||||
GIVEN {
|
||||
ASSUME(gItemsInfo[ITEM_PERSIM_BERRY].holdEffect == HOLD_EFFECT_CURE_CONFUSION);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_BERSERK_GENE);};
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(item);};
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_COVET, WITH_RNG(RNG_CONFUSION, FALSE)); }
|
||||
TURN {}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
|
||||
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_CONFUSION, player);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
|
||||
} THEN {
|
||||
EXPECT(player->volatiles.infiniteConfusion == 0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -65,6 +65,29 @@ SINGLE_BATTLE_TEST("Booster Energy will activate Protosynthesis after harsh sunl
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Booster Energy's Protosynthesis boost is preserved when weather changes")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_RAGING_BOLT) { Attack(110); Defense(100); Speed(100); SpAttack(100); SpDefense(100); Ability(ABILITY_PROTOSYNTHESIS); Item(ITEM_BOOSTER_ENERGY); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(50); Moves(MOVE_SUNNY_DAY, MOVE_CELEBRATE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_SUNNY_DAY); }
|
||||
TURN { }
|
||||
TURN { }
|
||||
TURN { }
|
||||
TURN { }
|
||||
TURN { }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
|
||||
MESSAGE("Raging Bolt used its Booster Energy to activate Protosynthesis!");
|
||||
MESSAGE("Raging Bolt's Attack was heightened!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUNNY_DAY, opponent);
|
||||
MESSAGE("The sunlight faded.");
|
||||
} THEN {
|
||||
EXPECT(gDisableStructs[B_POSITION_PLAYER_LEFT].paradoxBoostedStat == STAT_ATK);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Booster Energy activates Protosynthesis and increases highest stat")
|
||||
{
|
||||
u32 attack, defense, speed, spAttack, spDefense;
|
||||
@ -131,6 +154,29 @@ SINGLE_BATTLE_TEST("Booster Energy activates Quark Drive and increases highest s
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Booster Energy's Quark Drive boost is preserved when terrain changes")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_IRON_MOTH) { Attack(110); Defense(100); Speed(100); SpAttack(100); SpDefense(100); Ability(ABILITY_QUARK_DRIVE); Item(ITEM_BOOSTER_ENERGY); Moves(MOVE_CELEBRATE); }
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Speed(50); Moves(MOVE_GRASSY_TERRAIN, MOVE_CELEBRATE); }
|
||||
} WHEN {
|
||||
TURN { MOVE(opponent, MOVE_GRASSY_TERRAIN); }
|
||||
TURN { }
|
||||
TURN { }
|
||||
TURN { }
|
||||
TURN { }
|
||||
TURN { }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, player);
|
||||
MESSAGE("Iron Moth used its Booster Energy to activate Quark Drive!");
|
||||
MESSAGE("Iron Moth's Attack was heightened!");
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_GRASSY_TERRAIN, opponent);
|
||||
MESSAGE("The grass disappeared from the battlefield.");
|
||||
} THEN {
|
||||
EXPECT(gDisableStructs[B_POSITION_PLAYER_LEFT].paradoxBoostedStat == STAT_ATK);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Booster Energy increases special attack by 30% if it is the highest stat", s16 damage)
|
||||
{
|
||||
u32 species;
|
||||
|
||||
36
test/battle/hold_effect/kings_rock.c
Normal file
36
test/battle/hold_effect/kings_rock.c
Normal file
@ -0,0 +1,36 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
ASSUMPTIONS
|
||||
{
|
||||
ASSUME(gItemsInfo[ITEM_KINGS_ROCK].holdEffect == HOLD_EFFECT_FLINCH);
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Kings Rock holder will flinch the target 10% of the time")
|
||||
{
|
||||
PASSES_RANDOMLY(10, 100, RNG_HOLD_EFFECT_FLINCH);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_KINGS_ROCK); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_SCRATCH); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, player);
|
||||
MESSAGE("The opposing Wobbuffet flinched and couldn't move!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Kings Rock does not increase flinch chance of a move that has the flinch effect")
|
||||
{
|
||||
PASSES_RANDOMLY(30, 100, RNG_SECONDARY_EFFECT);
|
||||
GIVEN {
|
||||
ASSUME(MoveHasAdditionalEffect(MOVE_HEADBUTT, MOVE_EFFECT_FLINCH));
|
||||
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_KINGS_ROCK); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_HEADBUTT); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_HEADBUTT, player);
|
||||
MESSAGE("The opposing Wobbuffet flinched and couldn't move!");
|
||||
}
|
||||
}
|
||||
@ -46,8 +46,10 @@ SINGLE_BATTLE_TEST("Safety Goggles blocks damage from Sandstorm")
|
||||
|
||||
SINGLE_BATTLE_TEST("Safety Goggles blocks Effect Spore's effect")
|
||||
{
|
||||
KNOWN_FAILING;
|
||||
PASSES_RANDOMLY(100, 100, RNG_EFFECT_SPORE);
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_POWDER_GRASS, GEN_5); // Setting it to Gen 6 causes it to pass
|
||||
ASSUME(MoveMakesContact(MOVE_SCRATCH));
|
||||
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_SAFETY_GOGGLES); }
|
||||
OPPONENT(SPECIES_BRELOOM) { Ability(ABILITY_EFFECT_SPORE); }
|
||||
|
||||
@ -31,6 +31,29 @@ SINGLE_BATTLE_TEST("Antidote heals a battler from being poisoned")
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Antidote heals a battler from being poisoned (doubles)")
|
||||
{
|
||||
u32 index;
|
||||
struct BattlePokemon *user = NULL;
|
||||
struct BattlePokemon *target = NULL;
|
||||
PARAMETRIZE { index = 0; user = playerRight; target = playerLeft;}
|
||||
PARAMETRIZE { index = 1; user = playerLeft; target = playerRight;}
|
||||
PARAMETRIZE { index = 0; user = playerLeft; target = playerLeft;}
|
||||
PARAMETRIZE { index = 1; user = playerRight; target = playerRight; }
|
||||
|
||||
GIVEN {
|
||||
ASSUME(gItemsInfo[ITEM_ANTIDOTE].battleUsage == EFFECT_ITEM_CURE_STATUS);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Status1(STATUS1_POISON); }
|
||||
PLAYER(SPECIES_WYNAUT) { }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { USE_ITEM(user, ITEM_ANTIDOTE, partyIndex: index); }
|
||||
} THEN {
|
||||
EXPECT_EQ(target->status1, STATUS1_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Antidote heals a battler from being badly poisoned")
|
||||
{
|
||||
GIVEN {
|
||||
@ -135,6 +158,43 @@ SINGLE_BATTLE_TEST("Full Heal heals a battler from any primary status")
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Full Heal heals a battler from any primary status (doubles)")
|
||||
{
|
||||
u32 statusParameters[7] =
|
||||
{
|
||||
STATUS1_SLEEP,
|
||||
STATUS1_POISON,
|
||||
STATUS1_BURN,
|
||||
STATUS1_FREEZE,
|
||||
STATUS1_PARALYSIS,
|
||||
STATUS1_TOXIC_POISON,
|
||||
STATUS1_FROSTBITE
|
||||
};
|
||||
|
||||
u16 status = 0;
|
||||
u32 index = 0;
|
||||
struct BattlePokemon *user = NULL;
|
||||
struct BattlePokemon *target = NULL;
|
||||
for (u32 j = 0; j < 7; j++)
|
||||
{
|
||||
PARAMETRIZE {status = statusParameters[j]; user = playerRight; target = playerLeft; index = 0;}
|
||||
PARAMETRIZE {status = statusParameters[j]; user = playerLeft; target = playerRight; index = 1;}
|
||||
PARAMETRIZE {status = statusParameters[j]; user = playerLeft; target = playerLeft; index = 0;}
|
||||
PARAMETRIZE {status = statusParameters[j]; user = playerRight; target = playerRight; index = 1;}
|
||||
}
|
||||
GIVEN {
|
||||
ASSUME(gItemsInfo[ITEM_FULL_HEAL].battleUsage == EFFECT_ITEM_CURE_STATUS);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { USE_ITEM(user, ITEM_FULL_HEAL, partyIndex: index); }
|
||||
} THEN {
|
||||
EXPECT_EQ(target->status1, STATUS1_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Heal Powder heals a battler from any primary status")
|
||||
{
|
||||
u16 status;
|
||||
@ -158,6 +218,43 @@ SINGLE_BATTLE_TEST("Heal Powder heals a battler from any primary status")
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Heal Powder heals a battler from any primary status (doubles)")
|
||||
{
|
||||
u32 statusParameters[7] =
|
||||
{
|
||||
STATUS1_SLEEP,
|
||||
STATUS1_POISON,
|
||||
STATUS1_BURN,
|
||||
STATUS1_FREEZE,
|
||||
STATUS1_PARALYSIS,
|
||||
STATUS1_TOXIC_POISON,
|
||||
STATUS1_FROSTBITE
|
||||
};
|
||||
|
||||
u16 status = 0;
|
||||
u32 index = 0;
|
||||
struct BattlePokemon *user = NULL;
|
||||
struct BattlePokemon *target = NULL;
|
||||
for (u32 j = 0; j < 7; j++)
|
||||
{
|
||||
PARAMETRIZE {status = statusParameters[j]; user = playerRight; target = playerLeft; index = 0;}
|
||||
PARAMETRIZE {status = statusParameters[j]; user = playerLeft; target = playerRight; index = 1;}
|
||||
PARAMETRIZE {status = statusParameters[j]; user = playerLeft; target = playerLeft; index = 0;}
|
||||
PARAMETRIZE {status = statusParameters[j]; user = playerRight; target = playerRight; index = 1;}
|
||||
}
|
||||
GIVEN {
|
||||
ASSUME(gItemsInfo[ITEM_HEAL_POWDER].battleUsage == EFFECT_ITEM_CURE_STATUS);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Status1(status); }
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { USE_ITEM(user, ITEM_HEAL_POWDER, partyIndex: index); }
|
||||
} THEN {
|
||||
EXPECT_EQ(target->status1, STATUS1_NONE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Pewter Crunchies heals a battler from any primary status")
|
||||
{
|
||||
u16 status;
|
||||
|
||||
@ -43,8 +43,8 @@ SINGLE_BATTLE_TEST("Full Restore restores a party members HP and cures any prima
|
||||
PARAMETRIZE { status = STATUS1_SLEEP; }
|
||||
PARAMETRIZE { status = STATUS1_NONE; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { HP(1); MaxHP(300); Status1(status); }
|
||||
PLAYER(SPECIES_WYNAUT) { HP(1); MaxHP(300); Status1(status); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { HP(100); MaxHP(300); Status1(status); }
|
||||
PLAYER(SPECIES_WYNAUT) { HP(100); MaxHP(300); Status1(status); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { USE_ITEM(player, ITEM_FULL_RESTORE, partyIndex: 1); }
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include "global.h"
|
||||
#include "overworld.h"
|
||||
#include "test/battle.h"
|
||||
#include "constants/item_effects.h"
|
||||
|
||||
@ -262,12 +263,13 @@ SINGLE_BATTLE_TEST("Max Mushrooms raises battler's Speed stat", s16 damage)
|
||||
SINGLE_BATTLE_TEST("Using X items in battle raises Friendship", s16 damage)
|
||||
{
|
||||
u32 startingFriendship;
|
||||
u8 metLocation = MAPSEC_NONE;
|
||||
u8 metLocation = GetCurrentRegionMapSectionId() + 1;
|
||||
|
||||
PARAMETRIZE { startingFriendship = 0; }
|
||||
PARAMETRIZE { startingFriendship = X_ITEM_MAX_FRIENDSHIP; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Friendship(startingFriendship); };
|
||||
// Set met location to MAPSEC_NONE to avoid getting the friendship boost
|
||||
// Set met location to currentMapSec + 1 to avoid getting the friendship boost
|
||||
// from being met in the current map section
|
||||
SetMonData(&PLAYER_PARTY[0], MON_DATA_MET_LOCATION, &metLocation);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
@ -280,3 +282,25 @@ SINGLE_BATTLE_TEST("Using X items in battle raises Friendship", s16 damage)
|
||||
EXPECT_EQ(player->friendship, X_ITEM_FRIENDSHIP_INCREASE);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Using X items in battle where Pokemon was met raises Friendship with a bonus", s16 damage)
|
||||
{
|
||||
u32 startingFriendship;
|
||||
u8 metLocation = GetCurrentRegionMapSectionId();
|
||||
|
||||
PARAMETRIZE { startingFriendship = 0; }
|
||||
PARAMETRIZE { startingFriendship = X_ITEM_MAX_FRIENDSHIP; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Friendship(startingFriendship); };
|
||||
// Set met location to currentMapSec to get the friendship boost
|
||||
SetMonData(&PLAYER_PARTY[0], MON_DATA_MET_LOCATION, &metLocation);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { USE_ITEM(player, ITEM_X_ACCURACY); MOVE(opponent, MOVE_CELEBRATE); }
|
||||
} THEN {
|
||||
if (startingFriendship == X_ITEM_MAX_FRIENDSHIP)
|
||||
EXPECT_EQ(player->friendship, X_ITEM_MAX_FRIENDSHIP);
|
||||
else
|
||||
EXPECT_EQ(player->friendship, (ITEM_FRIENDSHIP_MAPSEC_BONUS + X_ITEM_FRIENDSHIP_INCREASE));
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,4 +99,128 @@ DOUBLE_BATTLE_TEST("Revive works for a partner in a double battle")
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Revive can trigger switch-in abilities")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ARBOK) { Ability(ABILITY_INTIMIDATE); HP(1); }
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponentLeft, MOVE_SCRATCH, target: playerLeft); }
|
||||
TURN { USE_ITEM(playerRight, ITEM_REVIVE, partyIndex: 0); SKIP_TURN(playerLeft); }
|
||||
} SCENE {
|
||||
ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
|
||||
ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
|
||||
} THEN {
|
||||
EXPECT_EQ(opponentLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 2);
|
||||
EXPECT_EQ(opponentRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 2);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Revive does reset abilities")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_ARBOK) { Ability(ABILITY_INTIMIDATE); HP(1); }
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponentRight, MOVE_WORRY_SEED, target: playerLeft); MOVE(opponentLeft, MOVE_SCRATCH, target: playerLeft); }
|
||||
TURN { USE_ITEM(playerRight, ITEM_REVIVE, partyIndex: 0); SKIP_TURN(playerLeft); MOVE(opponentRight, MOVE_SPORE, target: playerLeft);}
|
||||
} SCENE {
|
||||
ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
|
||||
ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
|
||||
} THEN {
|
||||
EXPECT_EQ(opponentLeft->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 2);
|
||||
EXPECT_EQ(opponentRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE - 2);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Revive does not grant a mon its pre-death status condition")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT) { Status1(STATUS1_SLEEP); HP(1); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponentLeft, MOVE_SCRATCH, target: playerRight); }
|
||||
TURN { USE_ITEM(playerLeft, ITEM_REVIVE, partyIndex: 1); SKIP_TURN(playerRight); }
|
||||
} THEN {
|
||||
EXPECT_EQ(opponentRight->status1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Revive does not grant a mon its pre-death stat change")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT) { HP(1); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(playerRight, MOVE_SWORDS_DANCE); MOVE(opponentLeft, MOVE_SCRATCH, target: playerRight); }
|
||||
TURN { USE_ITEM(playerLeft, ITEM_REVIVE, partyIndex: 1); SKIP_TURN(playerRight); }
|
||||
} THEN {
|
||||
EXPECT_EQ(playerRight->statStages[STAT_ATK], DEFAULT_STAT_STAGE);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Revive does not grant a mon its pre-death types")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT) { HP(1); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_POOCHYENA);
|
||||
} WHEN {
|
||||
TURN { MOVE(playerRight, MOVE_REFLECT_TYPE, target: opponentRight); MOVE(opponentLeft, MOVE_PSYSHOCK, target: playerRight); MOVE(opponentRight, MOVE_SCRATCH, target: playerRight); }
|
||||
TURN { USE_ITEM(playerLeft, ITEM_MAX_REVIVE, partyIndex: 1); SKIP_TURN(playerRight); MOVE(opponentLeft, MOVE_PSYCHIC, target: playerRight);}
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_REFLECT_TYPE, playerRight);
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYSHOCK, opponentLeft);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_PSYCHIC, opponentLeft);
|
||||
} THEN {
|
||||
EXPECT_LT(playerRight->hp, playerRight->maxHP);
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Revive force revived pokemon to replace absent battler immediately", s16 damage)
|
||||
{
|
||||
u32 ability;
|
||||
|
||||
PARAMETRIZE { ability = ABILITY_INTIMIDATE; }
|
||||
PARAMETRIZE { ability = ABILITY_SHED_SKIN; }
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WYNAUT) { HP(1); }
|
||||
PLAYER(SPECIES_WOBBUFFET) { };
|
||||
PLAYER(SPECIES_ARBOK) { Ability(ability); HP(0) ;} ;
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(opponentLeft, MOVE_SCRATCH, target: playerLeft);}
|
||||
TURN { USE_ITEM(playerRight, ITEM_REVIVE, partyIndex: 2); SKIP_TURN(playerLeft); MOVE(opponentRight, MOVE_SCRATCH, target: playerRight); }
|
||||
} SCENE {
|
||||
if (ability == ABILITY_INTIMIDATE)
|
||||
{
|
||||
ABILITY_POPUP(playerLeft, ABILITY_INTIMIDATE);
|
||||
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
|
||||
}
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponentRight);
|
||||
HP_BAR(playerRight, captureDamage: &results[i].damage);
|
||||
} FINALLY {
|
||||
EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.5), results[1].damage);
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Revive won't restore a battler's HP if it hasn't fainted")
|
||||
|
||||
@ -1,10 +1,18 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
// General
|
||||
// TODO: Beat Up's strikes have each an independent chance of a critical hit
|
||||
// Unconfirmed by Bulbapedia
|
||||
// - Technician interacion
|
||||
|
||||
SINGLE_BATTLE_TEST("Beat Up hits the target for each non-fainted, non-statused member in the party")
|
||||
{
|
||||
u32 gen;
|
||||
PARAMETRIZE { gen = GEN_3; }
|
||||
PARAMETRIZE { gen = GEN_5; }
|
||||
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_BEAT_UP, gen);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
PLAYER(SPECIES_PICHU)
|
||||
@ -21,37 +29,367 @@ SINGLE_BATTLE_TEST("Beat Up hits the target for each non-fainted, non-statused m
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAT_UP, player);
|
||||
MESSAGE("The Pokémon was hit 4 time(s)!");
|
||||
} THEN {
|
||||
EXPECT_EQ(gBattleStruct->beatUpSpecies[0], SPECIES_WOBBUFFET);
|
||||
EXPECT_EQ(gBattleStruct->beatUpSpecies[1], SPECIES_WYNAUT);
|
||||
EXPECT_EQ(gBattleStruct->beatUpSpecies[2], SPECIES_PICHU);
|
||||
EXPECT_EQ(gBattleStruct->beatUpSpecies[3], SPECIES_RAICHU);
|
||||
if (gen == GEN_5) {
|
||||
EXPECT_EQ(gBattleStruct->beatUpSpecies[0], SPECIES_WOBBUFFET);
|
||||
EXPECT_EQ(gBattleStruct->beatUpSpecies[1], SPECIES_WYNAUT);
|
||||
EXPECT_EQ(gBattleStruct->beatUpSpecies[2], SPECIES_PICHU);
|
||||
EXPECT_EQ(gBattleStruct->beatUpSpecies[3], SPECIES_RAICHU);
|
||||
}
|
||||
else {
|
||||
EXPECT_EQ(gBattleStruct->beatUpSpecies[0], 0);
|
||||
EXPECT_EQ(gBattleStruct->beatUpSpecies[1], 1);
|
||||
EXPECT_EQ(gBattleStruct->beatUpSpecies[2], 2);
|
||||
EXPECT_EQ(gBattleStruct->beatUpSpecies[3], 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TO_DO_BATTLE_TEST("Beat Up doesn't consider Comatose as a status")
|
||||
TO_DO_BATTLE_TEST("Beat Up's strikes have each an independent chance of a critical hit");
|
||||
SINGLE_BATTLE_TEST("Beat Up doesn't consider Comatose as a status")
|
||||
{
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_KOMALA) { Ability(ABILITY_COMATOSE); }
|
||||
PLAYER(SPECIES_WYNAUT) { HP(0); }
|
||||
PLAYER(SPECIES_WYNAUT) { Status1(STATUS1_POISON); }
|
||||
PLAYER(SPECIES_WYNAUT) { Status1(STATUS1_SLEEP); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BEAT_UP); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAT_UP, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAT_UP, player);
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAT_UP, player);
|
||||
MESSAGE("The Pokémon was hit 2 time(s)!");
|
||||
}
|
||||
}
|
||||
|
||||
// B_BEAT_UP Gen2-4
|
||||
TO_DO_BATTLE_TEST("Beat Up lists each party member's name");
|
||||
TO_DO_BATTLE_TEST("Beat Up's damage is typeless");
|
||||
TO_DO_BATTLE_TEST("Beat Up's damage doesn't consider STAB");
|
||||
TO_DO_BATTLE_TEST("Beat Up's last strike-only can trigger King's Rock");
|
||||
TO_DO_BATTLE_TEST("Beat Up's base power is the same for each strike");
|
||||
TO_DO_BATTLE_TEST("Beat Up's damage is determined by each striking Pokémon's base attack and level and the target's defense");
|
||||
TO_DO_BATTLE_TEST("Beat Up ignores stat stage changes"); //eg. Swords Dance
|
||||
TO_DO_BATTLE_TEST("Beat Up ignores Huge Power");
|
||||
TO_DO_BATTLE_TEST("Beat Up ignores Choice Band");
|
||||
SINGLE_BATTLE_TEST("Beat Up doesn't list party member's name (Gen5+)")
|
||||
{
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_BEAT_UP, GEN_5);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BEAT_UP); }
|
||||
} SCENE {
|
||||
NONE_OF {
|
||||
MESSAGE("Wobbuffet's attack!");
|
||||
MESSAGE("Wynaut's attack!");
|
||||
}
|
||||
MESSAGE("The Pokémon was hit 2 time(s)!");
|
||||
}
|
||||
}
|
||||
|
||||
// B_BEAT_UP Gen5+
|
||||
TO_DO_BATTLE_TEST("Beat Up doesn't list party member's name");
|
||||
TO_DO_BATTLE_TEST("Beat Up's damage is Dark-typed");
|
||||
TO_DO_BATTLE_TEST("Beat Up's damage receives STAB");
|
||||
TO_DO_BATTLE_TEST("Beat Up's can trigger King's Rock on all strikes");
|
||||
TO_DO_BATTLE_TEST("Beat Up's base power is determined by each striking Pokémon");
|
||||
TO_DO_BATTLE_TEST("Beat Up's damage is determined by the user's attack and the target's defense");
|
||||
TO_DO_BATTLE_TEST("Beat Up's damage considers stat stage changes"); //eg. Swords Dance
|
||||
TO_DO_BATTLE_TEST("Beat Up's damage considers Huge Power");
|
||||
TO_DO_BATTLE_TEST("Beat Up's damage considers Choice Band");
|
||||
SINGLE_BATTLE_TEST("Beat Up's damage is Dark-typed (Gen5+)", s16 damage)
|
||||
{
|
||||
bool32 targetIsFairy;
|
||||
PARAMETRIZE { targetIsFairy = FALSE; }
|
||||
PARAMETRIZE { targetIsFairy = TRUE; }
|
||||
|
||||
// Unconfirmed by Bulbapedia
|
||||
// - Technician interacion
|
||||
ASSUME(GetMoveType(MOVE_BEAT_UP) == TYPE_DARK);
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_BEAT_UP, GEN_5);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(targetIsFairy ? SPECIES_SYLVEON : SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BEAT_UP); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAT_UP, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} THEN {
|
||||
if (targetIsFairy)
|
||||
EXPECT_LT(results[i].damage, results[0].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Beat Up's base power is determined by each striking Pokémon (Gen5+)")
|
||||
{
|
||||
s16 firstHit, secondHit;
|
||||
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_BEAT_UP, GEN_5);
|
||||
PLAYER(SPECIES_SHUCKLE);
|
||||
PLAYER(SPECIES_DEOXYS_ATTACK);
|
||||
PLAYER(SPECIES_WYNAUT) { HP(0); }
|
||||
PLAYER(SPECIES_WYNAUT) { HP(0); }
|
||||
PLAYER(SPECIES_WYNAUT) { HP(0); }
|
||||
OPPONENT(SPECIES_BLISSEY);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BEAT_UP); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAT_UP, player);
|
||||
HP_BAR(opponent, captureDamage: &firstHit);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAT_UP, player);
|
||||
HP_BAR(opponent, captureDamage: &secondHit);
|
||||
} THEN {
|
||||
EXPECT_LT(firstHit, secondHit);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Beat Up's damage considers stat stage changes (Gen5+)", s16 damage)
|
||||
{
|
||||
bool32 boosted;
|
||||
PARAMETRIZE { boosted = FALSE; }
|
||||
PARAMETRIZE { boosted = TRUE; }
|
||||
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_BEAT_UP, GEN_5);
|
||||
PLAYER(SPECIES_UMBREON);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { if (boosted) { MOVE(player, MOVE_SWORDS_DANCE); } else { MOVE(player, MOVE_CELEBRATE); } }
|
||||
TURN { MOVE(player, MOVE_BEAT_UP); }
|
||||
} SCENE {
|
||||
if (boosted)
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAT_UP, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} THEN {
|
||||
if (boosted)
|
||||
EXPECT_GT(results[i].damage, results[0].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Beat Up's damage considers Huge Power and Choice Band (Gen5+)", s16 damage)
|
||||
{
|
||||
u16 ability;
|
||||
u16 item;
|
||||
|
||||
PARAMETRIZE { ability = ABILITY_THICK_FAT; item = ITEM_NONE; }
|
||||
PARAMETRIZE { ability = ABILITY_HUGE_POWER; item = ITEM_NONE; }
|
||||
PARAMETRIZE { ability = ABILITY_THICK_FAT; item = ITEM_CHOICE_BAND; }
|
||||
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_BEAT_UP, GEN_5);
|
||||
PLAYER(SPECIES_AZUMARILL) { Ability(ability); Item(item); Moves(MOVE_BEAT_UP); }
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BEAT_UP); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAT_UP, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} THEN {
|
||||
if (i == 1)
|
||||
EXPECT_GT(results[i].damage, results[0].damage);
|
||||
if (i == 2)
|
||||
EXPECT_GT(results[i].damage, results[0].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Beat Up lists each party member's name")
|
||||
{
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_BEAT_UP, GEN_3);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
PLAYER(SPECIES_WYNAUT) { HP(0); }
|
||||
PLAYER(SPECIES_WYNAUT) { Status1(STATUS1_POISON); }
|
||||
PLAYER(SPECIES_PIKACHU);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BEAT_UP); }
|
||||
} SCENE {
|
||||
MESSAGE("Wobbuffet's attack!");
|
||||
MESSAGE("Wynaut's attack!");
|
||||
NOT MESSAGE("Wynaut's attack!");
|
||||
MESSAGE("Pikachu's attack!");
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Beat Up's damage is typeless", s16 damage)
|
||||
{
|
||||
u16 defender = SPECIES_WOBBUFFET;
|
||||
u16 type1, type2;
|
||||
|
||||
PARAMETRIZE { defender = SPECIES_BLISSEY; } // Normal
|
||||
PARAMETRIZE { defender = SPECIES_MACHAMP; } // Fighting
|
||||
PARAMETRIZE { defender = SPECIES_TORNADUS; } // Flying
|
||||
PARAMETRIZE { defender = SPECIES_GRIMER; } // Poison
|
||||
PARAMETRIZE { defender = SPECIES_SANDSHREW; } // Ground
|
||||
PARAMETRIZE { defender = SPECIES_NOSEPASS; } // Rock
|
||||
PARAMETRIZE { defender = SPECIES_CATERPIE; } // Bug
|
||||
PARAMETRIZE { defender = SPECIES_DUSKULL; } // Ghost
|
||||
PARAMETRIZE { defender = SPECIES_REGISTEEL; } // Steel
|
||||
PARAMETRIZE { defender = SPECIES_CHIMCHAR; } // Fire
|
||||
PARAMETRIZE { defender = SPECIES_WARTORTLE; } // Water
|
||||
PARAMETRIZE { defender = SPECIES_TANGELA; } // Grass
|
||||
PARAMETRIZE { defender = SPECIES_PIKACHU; } // Electric
|
||||
PARAMETRIZE { defender = SPECIES_ABRA; } // Psychic
|
||||
PARAMETRIZE { defender = SPECIES_SNORUNT; } // Ice
|
||||
PARAMETRIZE { defender = SPECIES_BAGON; } // Dragon
|
||||
PARAMETRIZE { defender = SPECIES_UMBREON; } // Dark
|
||||
PARAMETRIZE { defender = SPECIES_SYLVEON; } // Fairy
|
||||
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_BEAT_UP, GEN_3);
|
||||
type1 = GetSpeciesType(defender, 0);
|
||||
type2 = GetSpeciesType(defender, 1);
|
||||
ASSUME(type2 == type1 || type2 == TYPE_MYSTERY); // Ensure monotype targets
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(defender);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BEAT_UP); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAT_UP, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
NONE_OF {
|
||||
MESSAGE("It's super effective!");
|
||||
MESSAGE("It's not very effective...");
|
||||
MESSAGE("It doesn't affect");
|
||||
}
|
||||
} THEN {
|
||||
EXPECT_GT(results[i].damage, 0);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Beat Up's damage doesn't consider STAB")
|
||||
{
|
||||
s16 damage;
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_BEAT_UP, GEN_3);
|
||||
damage = 0;
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT) { HP(0); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BEAT_UP); }
|
||||
} SCENE {
|
||||
HP_BAR(opponent, captureDamage: &damage);
|
||||
} THEN {
|
||||
// Raw damage: baseAtk 33 * basePower 1 * levelFactor ((100 * 2 / 5) + 2 = 42) = 1386
|
||||
// Divide by baseDef 58 -> 23 (floor); 23/50 + 2 = 2;
|
||||
u16 expected = 2;
|
||||
EXPECT_EQ(damage, expected);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Beat Up's base power is the same for each strike")
|
||||
{
|
||||
s16 firstHit, secondHit;
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_BEAT_UP, GEN_3);
|
||||
firstHit = 0;
|
||||
secondHit = 0;
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
PLAYER(SPECIES_WYNAUT) { HP(0); }
|
||||
PLAYER(SPECIES_WYNAUT) { HP(0); }
|
||||
PLAYER(SPECIES_WYNAUT) { HP(0); }
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BEAT_UP); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAT_UP, player);
|
||||
HP_BAR(opponent, captureDamage: &firstHit);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAT_UP, player);
|
||||
HP_BAR(opponent, captureDamage: &secondHit);
|
||||
} THEN {
|
||||
EXPECT_EQ(firstHit, secondHit);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Beat Up's damage is determined by each striking Pokémon's base attack and level and the target's defense")
|
||||
{
|
||||
s16 shuckleHit, deoxysHit;
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_BEAT_UP, GEN_3);
|
||||
shuckleHit = 0;
|
||||
deoxysHit = 0;
|
||||
PLAYER(SPECIES_SHUCKLE);
|
||||
PLAYER(SPECIES_DEOXYS_ATTACK);
|
||||
PLAYER(SPECIES_WYNAUT) { HP(0); }
|
||||
PLAYER(SPECIES_WYNAUT) { HP(0); }
|
||||
PLAYER(SPECIES_WYNAUT) { HP(0); }
|
||||
OPPONENT(SPECIES_BLISSEY);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BEAT_UP); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAT_UP, player);
|
||||
HP_BAR(opponent, captureDamage: &shuckleHit);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAT_UP, player);
|
||||
HP_BAR(opponent, captureDamage: &deoxysHit);
|
||||
} THEN {
|
||||
// Shuckle: baseAtk 10 * basePower 1 * levelFactor 42 = 420; / baseDef 10 -> 42; 42/50 + 2 = 2
|
||||
u16 shuckleDmg = 2;
|
||||
// Deoxys-A: baseAtk 180 * basePower 1 * levelFactor 42 = 7560; / baseDef 10 -> 756; 756/50 + 2 = 17
|
||||
u16 deoxysDmg = 17;
|
||||
EXPECT_EQ(shuckleHit, shuckleDmg);
|
||||
EXPECT_EQ(deoxysHit, deoxysDmg);
|
||||
EXPECT_LT(shuckleHit, deoxysHit);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Beat Up ignores stat stage changes", s16 damage)
|
||||
{
|
||||
bool32 boosted;
|
||||
PARAMETRIZE { boosted = FALSE; }
|
||||
PARAMETRIZE { boosted = TRUE; }
|
||||
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_BEAT_UP, GEN_3);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { if (boosted) { MOVE(player, MOVE_SWORDS_DANCE); } else { MOVE(player, MOVE_CELEBRATE); } }
|
||||
TURN { MOVE(player, MOVE_BEAT_UP); }
|
||||
} SCENE {
|
||||
if (boosted)
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_SWORDS_DANCE, player);
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAT_UP, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} THEN {
|
||||
if (boosted)
|
||||
EXPECT_EQ(results[i].damage, results[0].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Beat Up ignores Huge Power", s16 damage)
|
||||
{
|
||||
u16 ability;
|
||||
|
||||
PARAMETRIZE { ability = ABILITY_THICK_FAT; }
|
||||
PARAMETRIZE { ability = ABILITY_HUGE_POWER; }
|
||||
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_BEAT_UP, GEN_3);
|
||||
PLAYER(SPECIES_AZUMARILL) { Ability(ability); Moves(MOVE_BEAT_UP); }
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BEAT_UP); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAT_UP, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} THEN {
|
||||
if (ability == ABILITY_HUGE_POWER)
|
||||
EXPECT_EQ(results[i].damage, results[0].damage);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Beat Up ignores Choice Band", s16 damage)
|
||||
{
|
||||
u16 item;
|
||||
|
||||
PARAMETRIZE { item = ITEM_NONE; }
|
||||
PARAMETRIZE { item = ITEM_CHOICE_BAND; }
|
||||
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_BEAT_UP, GEN_3);
|
||||
PLAYER(SPECIES_URSARING) { Item(item); Moves(MOVE_BEAT_UP); }
|
||||
PLAYER(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_BEAT_UP); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_BEAT_UP, player);
|
||||
HP_BAR(opponent, captureDamage: &results[i].damage);
|
||||
} THEN {
|
||||
if (item == ITEM_CHOICE_BAND)
|
||||
EXPECT_EQ(results[i].damage, results[0].damage);
|
||||
}
|
||||
}
|
||||
|
||||
@ -334,7 +334,11 @@ DOUBLE_BATTLE_TEST("Defog removes Aurora Veil from target's side", s16 damagePhy
|
||||
|
||||
DOUBLE_BATTLE_TEST("Defog removes everything it can")
|
||||
{
|
||||
u32 config;
|
||||
PARAMETRIZE { config = GEN_5; }
|
||||
PARAMETRIZE { config = GEN_6; }
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_DEFOG_EFFECT_CLEARING, config);
|
||||
ASSUME(GetMoveEffect(MOVE_HAIL) == EFFECT_HAIL);
|
||||
ASSUME(GetSpeciesType(SPECIES_GLALIE, 0) == TYPE_ICE);
|
||||
PLAYER(SPECIES_GLALIE) { Speed(4); }
|
||||
@ -363,7 +367,7 @@ DOUBLE_BATTLE_TEST("Defog removes everything it can")
|
||||
MESSAGE("Your team's Aurora Veil wore off!");
|
||||
MESSAGE("Your team's Safeguard wore off!");
|
||||
|
||||
if (B_DEFOG_EFFECT_CLEARING >= GEN_6) {
|
||||
if (config == GEN_6) {
|
||||
MESSAGE("The spikes disappeared from the ground around your team!");
|
||||
MESSAGE("The sticky web has disappeared from the ground around your team!");
|
||||
MESSAGE("The poison spikes disappeared from the ground around your team!");
|
||||
@ -376,17 +380,31 @@ DOUBLE_BATTLE_TEST("Defog removes everything it can")
|
||||
MESSAGE("The pointed stones disappeared from around the opposing team!");
|
||||
}
|
||||
} THEN {
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[0][0], HAZARDS_NONE);
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[0][1], HAZARDS_NONE);
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[0][2], HAZARDS_NONE);
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[0][3], HAZARDS_NONE);
|
||||
if (config == GEN_6) {
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[0][0], HAZARDS_NONE);
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[0][1], HAZARDS_NONE);
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[0][2], HAZARDS_NONE);
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[0][3], HAZARDS_NONE);
|
||||
} else {
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[0][0], HAZARDS_STICKY_WEB);
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[0][1], HAZARDS_SPIKES);
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[0][2], HAZARDS_STEALTH_ROCK);
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[0][3], HAZARDS_TOXIC_SPIKES);
|
||||
}
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[0][4], HAZARDS_NONE);
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[0][5], HAZARDS_NONE);
|
||||
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[1][0], HAZARDS_NONE);
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[1][1], HAZARDS_NONE);
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[1][2], HAZARDS_NONE);
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[1][3], HAZARDS_NONE);
|
||||
if (config == GEN_6) {
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[1][0], HAZARDS_NONE);
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[1][1], HAZARDS_NONE);
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[1][2], HAZARDS_NONE);
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[1][3], HAZARDS_NONE);
|
||||
} else {
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[1][0], HAZARDS_STICKY_WEB);
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[1][1], HAZARDS_SPIKES);
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[1][2], HAZARDS_TOXIC_SPIKES);
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[1][3], HAZARDS_STEALTH_ROCK);
|
||||
}
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[1][4], HAZARDS_NONE);
|
||||
EXPECT_EQ(gBattleStruct->hazardsQueue[1][5], HAZARDS_NONE);
|
||||
}
|
||||
|
||||
@ -118,6 +118,9 @@ AI_SINGLE_BATTLE_TEST("AI won't use status moves if the player's best attacking
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_FOCUS_PUNCH) == EFFECT_FOCUS_PUNCH);
|
||||
ASSUME(GetMoveCategory(MOVE_SWORDS_DANCE) == DAMAGE_CATEGORY_STATUS);
|
||||
// If Clefable is Normal-type, it will always use Play Rough.
|
||||
ASSUME(GetSpeciesType(SPECIES_CLEFABLE, 0) == TYPE_FAIRY);
|
||||
ASSUME(GetSpeciesType(SPECIES_CLEFABLE, 1) == TYPE_FAIRY);
|
||||
AI_FLAGS(AI_FLAG_CHECK_BAD_MOVE | AI_FLAG_CHECK_VIABILITY | AI_FLAG_TRY_TO_FAINT | AI_FLAG_OMNISCIENT);
|
||||
PLAYER(SPECIES_SNORLAX) { Moves(MOVE_FOCUS_PUNCH, MOVE_POUND); }
|
||||
OPPONENT(SPECIES_CLEFABLE) { Moves(MOVE_PLAY_ROUGH, MOVE_SWORDS_DANCE); }
|
||||
|
||||
@ -96,8 +96,8 @@ SINGLE_BATTLE_TEST("Glaive Rush doesn't affect the user if the effect is blocked
|
||||
{
|
||||
u32 species;
|
||||
|
||||
PARAMETRIZE { species = SPECIES_CLEFAIRY; }
|
||||
PARAMETRIZE { species = SPECIES_SHELLOS; } // Closest mon in both Defense and Sp. Defense
|
||||
PARAMETRIZE { species = SPECIES_FIDOUGH; }
|
||||
PARAMETRIZE { species = SPECIES_MAGNEMITE; } // Closest mon in both Defense and Sp. Defense
|
||||
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
@ -105,7 +105,7 @@ SINGLE_BATTLE_TEST("Glaive Rush doesn't affect the user if the effect is blocked
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_GLAIVE_RUSH); MOVE(opponent, MOVE_SCRATCH); }
|
||||
} SCENE {
|
||||
if (species == SPECIES_CLEFAIRY)
|
||||
if (species == SPECIES_FIDOUGH)
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_GLAIVE_RUSH, player);
|
||||
else
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_GLAIVE_RUSH, player);
|
||||
|
||||
@ -166,3 +166,53 @@ SINGLE_BATTLE_TEST("Heal Bell cures a Soundproof user (Gen5, Gen8+)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Aromatherapy cure Soundproof battlers regardless of config")
|
||||
{
|
||||
u32 ability, config;
|
||||
|
||||
PARAMETRIZE { ability = ABILITY_SOUNDPROOF; config = GEN_4; }
|
||||
PARAMETRIZE { ability = ABILITY_SOUNDPROOF; config = GEN_5; }
|
||||
PARAMETRIZE { ability = ABILITY_SOUNDPROOF; config = GEN_6; }
|
||||
PARAMETRIZE { ability = ABILITY_SOUNDPROOF; config = GEN_8; }
|
||||
|
||||
GIVEN {
|
||||
ASSUME(!IsSoundMove(MOVE_AROMATHERAPY));
|
||||
WITH_CONFIG(GEN_CONFIG_HEAL_BELL_SOUNDPROOF, config);
|
||||
PLAYER(SPECIES_WOBBUFFET) { Ability(ability); Status1(STATUS1_POISON); };
|
||||
PLAYER(SPECIES_EXPLOUD) { Ability(ability); Status1(STATUS1_POISON); }
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(playerLeft, MOVE_AROMATHERAPY, target: playerLeft); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_AROMATHERAPY, playerLeft);
|
||||
NONE_OF {
|
||||
MESSAGE("Exploud was hurt by its poisoning!");
|
||||
MESSAGE("Wobbuffet was hurt by its poisoning!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Aromatherapy cures inactive Soundproof Pokemon regardless of config")
|
||||
{
|
||||
u32 config, ability;
|
||||
|
||||
PARAMETRIZE { config = GEN_4, ability = ABILITY_SOUNDPROOF; }
|
||||
PARAMETRIZE { config = GEN_5, ability = ABILITY_SOUNDPROOF; }
|
||||
|
||||
GIVEN {
|
||||
ASSUME(!IsSoundMove(MOVE_AROMATHERAPY));
|
||||
WITH_CONFIG(GEN_CONFIG_HEAL_BELL_SOUNDPROOF, config);
|
||||
PLAYER(SPECIES_WOBBUFFET) { }
|
||||
PLAYER(SPECIES_EXPLOUD) { Ability(ability); Status1(STATUS1_POISON); }
|
||||
OPPONENT(SPECIES_WYNAUT);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_AROMATHERAPY, target: player); }
|
||||
TURN { SWITCH(player, 1); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_AROMATHERAPY, player);
|
||||
SEND_IN_MESSAGE("Exploud");
|
||||
NOT MESSAGE("Exploud was hurt by its poisoning!");
|
||||
}
|
||||
}
|
||||
|
||||
@ -13,7 +13,7 @@ SINGLE_BATTLE_TEST("Last Respects power is multiplied by the amount of fainted m
|
||||
PARAMETRIZE { faintCount = 1; }
|
||||
PARAMETRIZE { faintCount = 2; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_GOLEM); // Not Wobbuffet to omit type effectiveness
|
||||
PLAYER(SPECIES_HITMONLEE); // Not Wobbuffet to omit type effectiveness
|
||||
PLAYER(SPECIES_GEODUDE);
|
||||
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_LEPPA_BERRY); Moves(MOVE_RECYCLE, MOVE_NONE, MOVE_NONE, MOVE_NONE); }
|
||||
} WHEN {
|
||||
@ -44,7 +44,7 @@ SINGLE_BATTLE_TEST("Last Respects power is multiplied by the amount of fainted m
|
||||
PARAMETRIZE { faintCount = 2; }
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_LEPPA_BERRY); Moves(MOVE_RECYCLE, MOVE_NONE, MOVE_NONE, MOVE_NONE); }
|
||||
OPPONENT(SPECIES_GOLEM); // Not Wobbuffet to omit type effectiveness
|
||||
OPPONENT(SPECIES_HITMONLEE); // Not Wobbuffet to omit type effectiveness
|
||||
OPPONENT(SPECIES_GEODUDE);
|
||||
} WHEN {
|
||||
for (j = 0; j < faintCount; j++)
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#include "global.h"
|
||||
#include "test/battle.h"
|
||||
|
||||
TO_DO_BATTLE_TEST("TODO: Write Max Moves (Move Effect) test titles")
|
||||
// Tested in the dynamax.c test file
|
||||
|
||||
@ -398,12 +398,15 @@ DOUBLE_BATTLE_TEST("Protect: Wide Guard protects self and ally from multi-target
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Protect: Wide Guard can not fail on consecutive turns")
|
||||
DOUBLE_BATTLE_TEST("Protect: Wide Guard can not fail on consecutive turns (Gen6+)")
|
||||
{
|
||||
u8 turns;
|
||||
u32 turns, config, passes;
|
||||
|
||||
PASSES_RANDOMLY(2, 2);
|
||||
PARAMETRIZE { config = GEN_5; passes = 0; }
|
||||
PARAMETRIZE { config = GEN_6; passes = 2; }
|
||||
PASSES_RANDOMLY(passes, 2);
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_WIDE_GUARD, config);
|
||||
ASSUME(GetMoveTarget(MOVE_HYPER_VOICE) == MOVE_TARGET_BOTH);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
@ -431,8 +434,8 @@ DOUBLE_BATTLE_TEST("Protect: Quick Guard protects self and ally from priority mo
|
||||
u16 move = MOVE_NONE;
|
||||
struct BattlePokemon *targetOpponent = NULL;
|
||||
|
||||
PARAMETRIZE { move = MOVE_SCRATCH; targetOpponent = opponentLeft; }
|
||||
PARAMETRIZE { move = MOVE_SCRATCH; targetOpponent = opponentRight; }
|
||||
PARAMETRIZE { move = MOVE_SCRATCH; targetOpponent = opponentLeft; }
|
||||
PARAMETRIZE { move = MOVE_SCRATCH; targetOpponent = opponentRight; }
|
||||
PARAMETRIZE { move = MOVE_QUICK_ATTACK; targetOpponent = opponentLeft; }
|
||||
PARAMETRIZE { move = MOVE_QUICK_ATTACK; targetOpponent = opponentRight; }
|
||||
|
||||
@ -461,12 +464,15 @@ DOUBLE_BATTLE_TEST("Protect: Quick Guard protects self and ally from priority mo
|
||||
}
|
||||
}
|
||||
|
||||
DOUBLE_BATTLE_TEST("Protect: Quick Guard can not fail on consecutive turns")
|
||||
DOUBLE_BATTLE_TEST("Protect: Quick Guard can not fail on consecutive turns (Gen6+)")
|
||||
{
|
||||
u8 turns;
|
||||
u32 turns, config, passes;
|
||||
|
||||
PASSES_RANDOMLY(2, 2);
|
||||
PARAMETRIZE { config = GEN_5; passes = 0; }
|
||||
PARAMETRIZE { config = GEN_6; passes = 2; }
|
||||
PASSES_RANDOMLY(passes, 2);
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_QUICK_GUARD, config);
|
||||
ASSUME(GetMovePriority(MOVE_QUICK_ATTACK) == 1);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
|
||||
@ -10,6 +10,7 @@ DOUBLE_BATTLE_TEST("Speed Down: Cotton Spore does not fail if it is blocked by o
|
||||
|
||||
GIVEN {
|
||||
ASSUME(GetMoveEffect(MOVE_COTTON_SPORE) == EFFECT_SPEED_DOWN_2);
|
||||
ASSUME(GetMoveTarget(MOVE_COTTON_SPORE) == MOVE_TARGET_BOTH);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_SHELLDER) { Ability(abilityOne); }
|
||||
|
||||
@ -68,6 +68,7 @@ SINGLE_BATTLE_TEST("Upper Hand succeeds if the target's move is boosted in prior
|
||||
GIVEN {
|
||||
ASSUME(GetMoveCategory(MOVE_DRAINING_KISS) == DAMAGE_CATEGORY_SPECIAL);
|
||||
ASSUME(GetMovePriority(MOVE_DRAINING_KISS) == 0);
|
||||
ASSUME(IsHealingMove(MOVE_DRAINING_KISS)); // Doesn't have the Healing Move flag in Gen 5
|
||||
PLAYER(SPECIES_MIENSHAO) { Speed(10); }
|
||||
OPPONENT(SPECIES_COMFEY) { Speed(5); Ability(ABILITY_TRIAGE); }
|
||||
} WHEN {
|
||||
@ -85,6 +86,7 @@ SINGLE_BATTLE_TEST("Upper Hand fails if the target moves first")
|
||||
GIVEN {
|
||||
ASSUME(GetMoveCategory(MOVE_DRAINING_KISS) == DAMAGE_CATEGORY_SPECIAL);
|
||||
ASSUME(GetMovePriority(MOVE_DRAINING_KISS) == 0);
|
||||
ASSUME(IsHealingMove(MOVE_DRAINING_KISS)); // Doesn't have the Healing Move flag in Gen 5
|
||||
PLAYER(SPECIES_MIENSHAO) { Speed(5); }
|
||||
OPPONENT(SPECIES_COMFEY) { Speed(10); Ability(ABILITY_TRIAGE); }
|
||||
} WHEN {
|
||||
|
||||
@ -63,6 +63,7 @@ SINGLE_BATTLE_TEST("Dire Claw cannot poison/paralyze poison/electric types respe
|
||||
|
||||
SINGLE_BATTLE_TEST("Dire Claw cannot poison/paralyze/cause to fall asleep Pokémon with abilities preventing respective statuses")
|
||||
{
|
||||
KNOWN_FAILING;
|
||||
u8 statusAnim;
|
||||
u16 species;
|
||||
enum Ability ability;
|
||||
@ -76,6 +77,7 @@ SINGLE_BATTLE_TEST("Dire Claw cannot poison/paralyze/cause to fall asleep Pokém
|
||||
PARAMETRIZE { statusAnim = B_ANIM_STATUS_SLP; rng = MOVE_EFFECT_SLEEP; species = SPECIES_HYPNO; ability = ABILITY_INSOMNIA; }
|
||||
|
||||
GIVEN {
|
||||
WITH_CONFIG(GEN_CONFIG_PARALYZE_ELECTRIC, GEN_5); // To prevent Electric paralysis immunity from affecting the test
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(species) { Ability(ability); }
|
||||
} WHEN {
|
||||
|
||||
@ -69,3 +69,17 @@ SINGLE_BATTLE_TEST("Protect always works when used after flinching")
|
||||
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_HEADBUTT, opponent);
|
||||
}
|
||||
}
|
||||
|
||||
SINGLE_BATTLE_TEST("Headbutt flinches 30% of the time")
|
||||
{
|
||||
PASSES_RANDOMLY(30, 100, RNG_SECONDARY_EFFECT);
|
||||
GIVEN {
|
||||
PLAYER(SPECIES_WOBBUFFET);
|
||||
OPPONENT(SPECIES_WOBBUFFET);
|
||||
} WHEN {
|
||||
TURN { MOVE(player, MOVE_HEADBUTT); }
|
||||
} SCENE {
|
||||
ANIMATION(ANIM_TYPE_MOVE, MOVE_HEADBUTT, player);
|
||||
MESSAGE("The opposing Wobbuffet flinched and couldn't move!");
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user