Merge branch '_RHH/master' into _RHH/upcoming

This commit is contained in:
Eduardo Quezada 2025-02-02 14:18:13 -03:00
commit 64c5113b23
19 changed files with 450 additions and 60 deletions

View File

@ -1727,11 +1727,6 @@
.4byte \failInstr
.endm
.macro tryhitswitchtarget failInstr:req
callnative BS_TryHitSwitchTarget
.4byte \failInstr
.endm
.macro setmagiccoattarget
callnative BS_SetMagicCoatTarget
.endm

View File

@ -1806,25 +1806,17 @@ BattleScript_EffectFinalGambit::
tryfaintmon BS_ATTACKER
goto BattleScript_MoveEnd
BattleScript_EffectHitSwitchTarget::
call BattleScript_EffectHit_Ret
tryfaintmon BS_TARGET
jumpiffainted BS_TARGET, TRUE, BattleScript_MoveEnd
jumpifability BS_TARGET, ABILITY_SUCTION_CUPS, BattleScript_AbilityPreventsPhasingOut
jumpifability BS_TARGET, ABILITY_GUARD_DOG, BattleScript_MoveEnd
jumpifstatus3 BS_TARGET, STATUS3_ROOTED, BattleScript_PrintMonIsRooted
jumpiftargetdynamaxed BattleScript_HitSwitchTargetDynamaxed
tryhitswitchtarget BattleScript_MoveEnd
BattleScript_TryHitSwitchTarget::
forcerandomswitch BattleScript_HitSwitchTargetForceRandomSwitchFailed
goto BattleScript_MoveEnd
return
BattleScript_HitSwitchTargetDynamaxed:
BattleScript_HitSwitchTargetDynamaxed::
printstring STRINGID_MOVEBLOCKEDBYDYNAMAX
waitmessage B_WAIT_TIME_LONG
BattleScript_HitSwitchTargetForceRandomSwitchFailed:
hitswitchtargetfailed
setbyte sSWITCH_CASE, B_SWITCH_NORMAL
goto BattleScript_MoveEnd
return
BattleScript_EffectToxicThread::
setstatchanger STAT_SPEED, 1, TRUE
@ -6722,6 +6714,12 @@ BattleScript_PrintMonIsRooted::
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_PrintMonIsRootedRet::
pause B_WAIT_TIME_SHORT
printstring STRINGID_PKMNANCHOREDITSELF
waitmessage B_WAIT_TIME_LONG
return
BattleScript_AtkDefDown::
setbyte sSTAT_ANIM_PLAYED, FALSE
playstatchangeanimation BS_ATTACKER, BIT_DEF | BIT_ATK, STAT_CHANGE_CANT_PREVENT | STAT_CHANGE_NEGATIVE | STAT_CHANGE_MULTIPLE_STATS
@ -7766,8 +7764,12 @@ BattleScript_IntimidateEffect:
printstring STRINGID_PKMNCUTSATTACKWITH
BattleScript_IntimidateEffect_WaitString:
waitmessage B_WAIT_TIME_LONG
saveattacker
savetarget
copybyte sBATTLER, gBattlerTarget
call BattleScript_TryIntimidateHoldEffects
restoreattacker
restoretarget
BattleScript_IntimidateLoopIncrement:
addbyte gBattlerTarget, 1
jumpifbytenotequal gBattlerTarget, gBattlersCount, BattleScript_IntimidateLoop
@ -8202,6 +8204,13 @@ BattleScript_AbilityPreventsPhasingOut::
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_AbilityPreventsPhasingOutRet::
pause B_WAIT_TIME_SHORT
call BattleScript_AbilityPopUp
printstring STRINGID_PKMNANCHORSITSELFWITH
waitmessage B_WAIT_TIME_LONG
return
BattleScript_AbilityNoStatLoss::
pause B_WAIT_TIME_SHORT
call BattleScript_AbilityPopUp

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 B

View File

@ -782,6 +782,7 @@ struct BattleStruct
u8 ballSwapped:1; // Used for the last used ball feature
u8 throwingPokeBall:1;
u8 ballSpriteIds[2]; // item gfx, window gfx
u8 moveInfoSpriteId; // move info, window gfx
u8 appearedInBattle; // Bitfield to track which Pokemon appeared in battle. Used for Burmy's form change
u8 skyDropTargets[MAX_BATTLERS_COUNT]; // For Sky Drop, to account for if multiple Pokemon use Sky Drop in a double battle.
// 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.

View File

@ -128,5 +128,7 @@ void SwapBallToDisplay(bool32 sameBall);
void ArrowsChangeColorLastBallCycle(bool32 showArrows);
void UpdateAbilityPopup(u8 battlerId);
void CategoryIcons_LoadSpritesGfx(void);
void TryToAddMoveInfoWindow(void);
void TryToHideMoveInfoWindow(void);
#endif // GUARD_BATTLE_INTERFACE_H

View File

@ -770,7 +770,10 @@ extern const u8 BattleScript_EffectDefenseUp3[];
extern const u8 BattleScript_EffectNobleRoar[];
extern const u8 BattleScript_EffectVenomDrench[];
extern const u8 BattleScript_EffectToxicThread[];
extern const u8 BattleScript_EffectHitSwitchTarget[];
extern const u8 BattleScript_TryHitSwitchTarget[];
extern const u8 BattleScript_HitSwitchTargetDynamaxed[];
extern const u8 BattleScript_AbilityPreventsPhasingOutRet[];
extern const u8 BattleScript_PrintMonIsRootedRet[];
extern const u8 BattleScript_EffectFinalGambit[];
extern const u8 BattleScript_EffectAutotomize[];
extern const u8 BattleScript_EffectCopycat[];

View File

@ -274,6 +274,7 @@ enum MoveEndEffects
MOVEEND_ITEM_EFFECTS_TARGET,
MOVEEND_MOVE_EFFECTS2,
MOVEEND_ITEM_EFFECTS_ALL,
MOVEEND_HIT_SWITCH_TARGET,
MOVEEND_KINGSROCK, // These item effects will occur each strike of a multi-hit move
MOVEEND_NUM_HITS,
MOVEEND_SUBSTITUTE,

View File

@ -669,6 +669,7 @@ void HandleInputChooseMove(u32 battler)
if (JOY_NEW(A_BUTTON) && !gBattleStruct->descriptionSubmenu)
{
TryToHideMoveInfoWindow();
PlaySE(SE_SELECT);
moveTarget = GetBattlerMoveTargetType(battler, moveInfo->moves[gMoveSelectionCursor[battler]]);
@ -779,6 +780,7 @@ void HandleInputChooseMove(u32 battler)
BtlController_EmitTwoReturnValues(battler, BUFFER_B, 10, 0xFFFF);
HideGimmickTriggerSprite();
PlayerBufferExecCompleted(battler);
TryToHideMoveInfoWindow();
}
}
else if (JOY_NEW(DPAD_LEFT) && !gBattleStruct->zmove.viewing)
@ -878,7 +880,7 @@ void HandleInputChooseMove(u32 battler)
MoveSelectionDisplayMoveType(battler);
}
}
else if (JOY_NEW(B_MOVE_DESCRIPTION_BUTTON) && B_MOVE_DESCRIPTION_BUTTON != B_LAST_USED_BALL_BUTTON)
else if (JOY_NEW(B_MOVE_DESCRIPTION_BUTTON))
{
gBattleStruct->descriptionSubmenu = TRUE;
MoveSelectionDisplayMoveDescription(battler);
@ -2127,6 +2129,7 @@ void PlayerHandleChooseMove(u32 battler)
InitMoveSelectionsVarsAndStrings(battler);
gBattleStruct->gimmick.playerSelect = FALSE;
TryToAddMoveInfoWindow();
AssignUsableZMoves(battler, moveInfo->moves);
gBattleStruct->zmove.viable = (gBattleStruct->zmove.possibleZMoves[battler] & (1u << gMoveSelectionCursor[battler])) != 0;

View File

@ -206,6 +206,7 @@ static void Task_FreeAbilityPopUpGfx(u8);
static void SpriteCB_LastUsedBall(struct Sprite *);
static void SpriteCB_LastUsedBallWin(struct Sprite *);
static void SpriteCB_MoveInfoWin(struct Sprite *sprite);
static const struct OamData sOamData_64x32 =
{
@ -732,6 +733,7 @@ u8 CreateBattlerHealthboxSprites(u8 battlerId)
gBattleStruct->ballSpriteIds[0] = MAX_SPRITES;
gBattleStruct->ballSpriteIds[1] = MAX_SPRITES;
gBattleStruct->moveInfoSpriteId = MAX_SPRITES;
return healthboxLeftSpriteId;
}
@ -2886,6 +2888,36 @@ static const struct SpriteTemplate sSpriteTemplate_LastUsedBallWindow =
.callback = SpriteCB_LastUsedBallWin
};
#define MOVE_INFO_WINDOW_TAG 0xE722
static const struct OamData sOamData_MoveInfoWindow =
{
.y = 0,
.affineMode = 0,
.objMode = 0,
.mosaic = 0,
.bpp = 0,
.shape = SPRITE_SHAPE(32x32),
.x = 0,
.matrixNum = 0,
.size = SPRITE_SIZE(32x32),
.tileNum = 0,
.priority = 1,
.paletteNum = 0,
.affineParam = 0,
};
static const struct SpriteTemplate sSpriteTemplate_MoveInfoWindow =
{
.tileTag = MOVE_INFO_WINDOW_TAG,
.paletteTag = ABILITY_POP_UP_TAG,
.oam = &sOamData_MoveInfoWindow,
.anims = gDummySpriteAnimTable,
.images = NULL,
.affineAnims = gDummySpriteAffineAnimTable,
.callback = SpriteCB_MoveInfoWin
};
#if B_LAST_USED_BALL_BUTTON == R_BUTTON && B_LAST_USED_BALL_CYCLE == TRUE
static const u8 ALIGNED(4) sLastUsedBallWindowGfx[] = INCBIN_U8("graphics/battle_interface/last_used_ball_r_cycle.4bpp");
#elif B_LAST_USED_BALL_CYCLE == TRUE
@ -2900,6 +2932,17 @@ static const struct SpriteSheet sSpriteSheet_LastUsedBallWindow =
sLastUsedBallWindowGfx, sizeof(sLastUsedBallWindowGfx), LAST_BALL_WINDOW_TAG
};
#if B_MOVE_DESCRIPTION_BUTTON == R_BUTTON
static const u8 sMoveInfoWindowGfx[] = INCBIN_U8("graphics/battle_interface/move_info_window_r.4bpp");
#else
static const u8 sMoveInfoWindowGfx[] = INCBIN_U8("graphics/battle_interface/move_info_window_l.4bpp");
#endif
static const struct SpriteSheet sSpriteSheet_MoveInfoWindow =
{
sMoveInfoWindowGfx, sizeof(sMoveInfoWindowGfx), MOVE_INFO_WINDOW_TAG
};
#define LAST_USED_BALL_X_F 14
#define LAST_USED_BALL_X_0 -14
#define LAST_USED_BALL_Y ((IsDoubleBattle()) ? 78 : 68)
@ -2958,7 +3001,7 @@ void TryAddLastUsedBallItemSprites(void)
gBattleStruct->ballSpriteIds[0] = AddItemIconSprite(102, 102, gBallToDisplay);
gSprites[gBattleStruct->ballSpriteIds[0]].x = LAST_USED_BALL_X_0;
gSprites[gBattleStruct->ballSpriteIds[0]].y = LAST_USED_BALL_Y;
gSprites[gBattleStruct->ballSpriteIds[0]].sHide = FALSE; // restore
gSprites[gBattleStruct->ballSpriteIds[0]].sHide = FALSE;
gLastUsedBallMenuPresent = TRUE;
gSprites[gBattleStruct->ballSpriteIds[0]].callback = SpriteCB_LastUsedBall;
}
@ -2973,7 +3016,8 @@ void TryAddLastUsedBallItemSprites(void)
gBattleStruct->ballSpriteIds[1] = CreateSprite(&sSpriteTemplate_LastUsedBallWindow,
LAST_BALL_WIN_X_0,
LAST_USED_WIN_Y, 5);
gSprites[gBattleStruct->ballSpriteIds[1]].sHide = FALSE; // restore
gSprites[gBattleStruct->ballSpriteIds[1]].sHide = FALSE;
gSprites[gBattleStruct->moveInfoSpriteId].sHide = TRUE;
gLastUsedBallMenuPresent = TRUE;
}
if (B_LAST_USED_BALL_CYCLE == TRUE)
@ -2996,6 +3040,32 @@ static void DestroyLastUsedBallGfx(struct Sprite *sprite)
gBattleStruct->ballSpriteIds[0] = MAX_SPRITES;
}
void TryToAddMoveInfoWindow(void)
{
LoadSpritePalette(&sSpritePalette_AbilityPopUp);
if (GetSpriteTileStartByTag(MOVE_INFO_WINDOW_TAG) == 0xFFFF)
LoadSpriteSheet(&sSpriteSheet_MoveInfoWindow);
if (gBattleStruct->moveInfoSpriteId == MAX_SPRITES)
{
gBattleStruct->moveInfoSpriteId = CreateSprite(&sSpriteTemplate_MoveInfoWindow, LAST_BALL_WIN_X_0, LAST_USED_WIN_Y + 32, 6);
gSprites[gBattleStruct->moveInfoSpriteId].sHide = FALSE;
}
}
void TryToHideMoveInfoWindow(void)
{
gSprites[gBattleStruct->moveInfoSpriteId].sHide = TRUE;
}
static void DestroyMoveInfoWinGfx(struct Sprite *sprite)
{
FreeSpriteTilesByTag(MOVE_INFO_WINDOW_TAG);
FreeSpritePaletteByTag(ABILITY_POP_UP_TAG);
DestroySprite(sprite);
gBattleStruct->moveInfoSpriteId = MAX_SPRITES;
}
static void SpriteCB_LastUsedBallWin(struct Sprite *sprite)
{
if (sprite->sHide)
@ -3033,6 +3103,23 @@ static void SpriteCB_LastUsedBall(struct Sprite *sprite)
}
}
static void SpriteCB_MoveInfoWin(struct Sprite *sprite)
{
if (sprite->sHide)
{
if (sprite->x != LAST_BALL_WIN_X_0)
sprite->x--;
if (sprite->x == LAST_BALL_WIN_X_0)
DestroyMoveInfoWinGfx(sprite);
}
else
{
if (sprite->x != LAST_BALL_WIN_X_F)
sprite->x++;
}
}
static void TryHideOrRestoreLastUsedBall(u8 caseId)
{
if (B_LAST_USED_BALL == FALSE)
@ -3044,16 +3131,16 @@ static void TryHideOrRestoreLastUsedBall(u8 caseId)
{
case 0: // hide
if (gBattleStruct->ballSpriteIds[0] != MAX_SPRITES)
gSprites[gBattleStruct->ballSpriteIds[0]].sHide = TRUE; // hide
gSprites[gBattleStruct->ballSpriteIds[0]].sHide = TRUE;
if (gBattleStruct->ballSpriteIds[1] != MAX_SPRITES)
gSprites[gBattleStruct->ballSpriteIds[1]].sHide = TRUE; // hide
gSprites[gBattleStruct->ballSpriteIds[1]].sHide = TRUE;
gLastUsedBallMenuPresent = FALSE;
break;
case 1: // restore
if (gBattleStruct->ballSpriteIds[0] != MAX_SPRITES)
gSprites[gBattleStruct->ballSpriteIds[0]].sHide = FALSE; // restore
gSprites[gBattleStruct->ballSpriteIds[0]].sHide = FALSE;
if (gBattleStruct->ballSpriteIds[1] != MAX_SPRITES)
gSprites[gBattleStruct->ballSpriteIds[1]].sHide = FALSE; // restore
gSprites[gBattleStruct->ballSpriteIds[1]].sHide = FALSE;
gLastUsedBallMenuPresent = TRUE;
break;
}

View File

@ -5829,11 +5829,12 @@ u32 GetDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler, u8 *ateBoost)
u32 moveType = GetMoveType(move);
u32 moveEffect = GetMoveEffect(move);
u32 species, heldItem, holdEffect, ability, type1, type2, type3;
bool32 monInBattle = gMain.inBattle && gPartyMenu.menuType != PARTY_MENU_TYPE_IN_BATTLE;
if (move == MOVE_STRUGGLE)
return TYPE_NORMAL;
if (gMain.inBattle)
if (monInBattle)
{
species = gBattleMons[battler].species;
heldItem = gBattleMons[battler].item;
@ -5857,18 +5858,21 @@ u32 GetDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler, u8 *ateBoost)
switch (moveEffect)
{
case EFFECT_WEATHER_BALL:
if (gMain.inBattle && HasWeatherEffect())
if (monInBattle)
{
if (gBattleWeather & B_WEATHER_RAIN && holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA)
return TYPE_WATER;
else if (gBattleWeather & B_WEATHER_SANDSTORM)
return TYPE_ROCK;
else if (gBattleWeather & B_WEATHER_SUN && holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA)
return TYPE_FIRE;
else if (gBattleWeather & (B_WEATHER_SNOW | B_WEATHER_HAIL))
return TYPE_ICE;
else
return moveType;
if (HasWeatherEffect())
{
if (gBattleWeather & B_WEATHER_RAIN && holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA)
return TYPE_WATER;
else if (gBattleWeather & B_WEATHER_SANDSTORM)
return TYPE_ROCK;
else if (gBattleWeather & B_WEATHER_SUN && holdEffect != HOLD_EFFECT_UTILITY_UMBRELLA)
return TYPE_FIRE;
else if (gBattleWeather & (B_WEATHER_SNOW | B_WEATHER_HAIL))
return TYPE_ICE;
else
return moveType;
}
}
else
{
@ -5895,7 +5899,7 @@ u32 GetDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler, u8 *ateBoost)
case EFFECT_HIDDEN_POWER:
{
u32 typeBits = 0;
if (gMain.inBattle)
if (monInBattle)
{
typeBits = ((gBattleMons[battler].hpIV & 1) << 0)
| ((gBattleMons[battler].attackIV & 1) << 1)
@ -5974,7 +5978,7 @@ u32 GetDynamicMoveType(struct Pokemon *mon, u32 move, u32 battler, u8 *ateBoost)
else
return moveType;
case EFFECT_TERRAIN_PULSE:
if (gMain.inBattle)
if (monInBattle)
{
if (IsBattlerTerrainAffected(battler, STATUS_FIELD_TERRAIN_ANY))
{

View File

@ -6303,6 +6303,43 @@ static void Cmd_moveend(void)
else
gBattleScripting.moveendState++;
break;
case MOVEEND_HIT_SWITCH_TARGET:
if (gMovesInfo[gCurrentMove].effect == EFFECT_HIT_SWITCH_TARGET
&& !(gHitMarker & HITMARKER_UNABLE_TO_USE_MOVE)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& IsBattlerAlive(gBattlerTarget)
&& IsBattlerAlive(gBattlerAttacker)
&& gSpecialStatuses[gBattlerAttacker].parentalBondState != PARENTAL_BOND_1ST_HIT)
{
u32 targetAbility = GetBattlerAbility(gBattlerTarget);
if (targetAbility == ABILITY_GUARD_DOG)
{
gBattleScripting.moveendState++;
break;
}
effect = TRUE;
BattleScriptPushCursor();
if (targetAbility == ABILITY_SUCTION_CUPS)
{
gBattlescriptCurrInstr = BattleScript_AbilityPreventsPhasingOutRet;
}
else if (gStatuses3[gBattlerTarget] & STATUS3_ROOTED)
{
gBattlescriptCurrInstr = BattleScript_PrintMonIsRootedRet;
}
else if (GetActiveGimmick(gBattlerTarget) == GIMMICK_DYNAMAX)
{
gBattlescriptCurrInstr = BattleScript_HitSwitchTargetDynamaxed;
}
else
{
gBattleScripting.switchCase = B_SWITCH_HIT;
gBattlescriptCurrInstr = BattleScript_TryHitSwitchTarget;
}
}
gBattleScripting.moveendState++;
break;
case MOVEEND_KINGSROCK: // King's rock
// These effects will occur at each hit in a multi-strike move
if (ItemBattleEffects(ITEMEFFECT_KINGSROCK, 0, FALSE))
@ -17683,26 +17720,6 @@ void BS_JumpIfBlockedBySoundproof(void)
}
}
void BS_TryHitSwitchTarget(void)
{
NATIVE_ARGS(const u8 *failInstr);
if (IsBattlerAlive(gBattlerAttacker)
&& IsBattlerAlive(gBattlerTarget)
&& !(gBattleStruct->moveResultFlags[gBattlerTarget] & MOVE_RESULT_NO_EFFECT)
&& IsBattlerTurnDamaged(gBattlerTarget)
&& gSpecialStatuses[gBattlerAttacker].parentalBondState != PARENTAL_BOND_1ST_HIT
&& GetBattlerAbility(gBattlerTarget) != ABILITY_GUARD_DOG)
{
gBattleScripting.switchCase = B_SWITCH_HIT;
gBattlescriptCurrInstr = cmd->nextInstr;
}
else
{
gBattlescriptCurrInstr = cmd->failInstr;
}
}
void BS_SetMagicCoatTarget(void)
{
NATIVE_ARGS();

View File

@ -1592,7 +1592,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_HIT_SWITCH_TARGET] =
{
.battleScript = BattleScript_EffectHitSwitchTarget,
.battleScript = BattleScript_EffectHit,
.battleTvScore = 0, // TODO: Assign points
},

View File

@ -2155,6 +2155,20 @@ static const u16 sGimmighoulFormSpeciesIdTable[] = {
};
#endif //P_FAMILY_GIMMIGHOUL
#if P_FAMILY_POLTCHAGEIST
static const u16 sPoltchageistFormSpeciesIdTable[] = {
SPECIES_POLTCHAGEIST_COUNTERFEIT,
SPECIES_POLTCHAGEIST_ARTISAN,
FORM_SPECIES_END,
};
static const u16 sSinistchaFormSpeciesIdTable[] = {
SPECIES_SINISTCHA_UNREMARKABLE,
SPECIES_SINISTCHA_MASTERPIECE,
FORM_SPECIES_END,
};
#endif //P_FAMILY_POLTCHAGEIST
#if P_FAMILY_OGERPON
static const u16 sOgerponFormSpeciesIdTable[] = {
SPECIES_OGERPON_TEAL,

View File

@ -7091,6 +7091,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
.levelUpLearnset = sPoltchageistLevelUpLearnset,
.teachableLearnset = sPoltchageistTeachableLearnset,
.evolutions = EVOLUTION({EVO_ITEM, ITEM_UNREMARKABLE_TEACUP, SPECIES_SINISTCHA_UNREMARKABLE}),
.formSpeciesIdTable = sPoltchageistFormSpeciesIdTable,
},
[SPECIES_POLTCHAGEIST_ARTISAN] =
{
@ -7154,6 +7155,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
.levelUpLearnset = sPoltchageistLevelUpLearnset,
.teachableLearnset = sPoltchageistTeachableLearnset,
.evolutions = EVOLUTION({EVO_ITEM, ITEM_MASTERPIECE_TEACUP, SPECIES_SINISTCHA_MASTERPIECE}),
.formSpeciesIdTable = sPoltchageistFormSpeciesIdTable,
},
[SPECIES_SINISTCHA_UNREMARKABLE] =
@ -7217,6 +7219,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
)
.levelUpLearnset = sSinistchaLevelUpLearnset,
.teachableLearnset = sSinistchaTeachableLearnset,
.formSpeciesIdTable = sSinistchaFormSpeciesIdTable,
},
[SPECIES_SINISTCHA_MASTERPIECE] =
{
@ -7279,6 +7282,7 @@ const struct SpeciesInfo gSpeciesInfoGen9[] =
)
.levelUpLearnset = sSinistchaLevelUpLearnset,
.teachableLearnset = sSinistchaTeachableLearnset,
.formSpeciesIdTable = sSinistchaFormSpeciesIdTable,
},
#endif //P_FAMILY_POLTCHAGEIST

View File

@ -0,0 +1,116 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Big Pecks prevents Defense stage reduction from moves")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_LEER].effect == EFFECT_DEFENSE_DOWN);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_PIDGEY) { Ability(ABILITY_BIG_PECKS); }
} WHEN {
TURN { MOVE(player, MOVE_LEER); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_BIG_PECKS);
MESSAGE("The opposing Pidgey's Big Pecks prevents Defense loss!");
}
}
SINGLE_BATTLE_TEST("Big Pecks is ignored by Mold Breaker")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_LEER].effect == EFFECT_DEFENSE_DOWN);
PLAYER(SPECIES_PINSIR) { Ability(ABILITY_MOLD_BREAKER); }
OPPONENT(SPECIES_PIDGEY) { Ability(ABILITY_BIG_PECKS); }
} WHEN {
TURN { MOVE(player, MOVE_LEER); }
} SCENE {
ABILITY_POPUP(player, ABILITY_MOLD_BREAKER);
MESSAGE("Pinsir breaks the mold!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_LEER, player);
MESSAGE("The opposing Pidgey's Defense fell!");
NONE_OF {
ABILITY_POPUP(opponent, ABILITY_BIG_PECKS);
MESSAGE("The opposing Pidgey's Big Pecks prevents Defense loss!");
}
}
}
SINGLE_BATTLE_TEST("Big Pecks doesn't prevent Defense stage reduction from moves used by the user")
{
GIVEN {
ASSUME(MoveHasAdditionalEffectSelf(MOVE_SUPERPOWER, MOVE_EFFECT_ATK_DEF_DOWN) == TRUE);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_PIDGEY) { Ability(ABILITY_BIG_PECKS); }
} WHEN {
TURN { MOVE(opponent, MOVE_SUPERPOWER); }
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUPERPOWER, opponent);
MESSAGE("The opposing Pidgey's Attack fell!");
MESSAGE("The opposing Pidgey's Defense fell!");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1);
}
}
SINGLE_BATTLE_TEST("Big Pecks doesn't prevent Topsy-Turvy")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_HARDEN].effect == EFFECT_DEFENSE_UP);
ASSUME(gMovesInfo[MOVE_TOPSY_TURVY].effect == EFFECT_TOPSY_TURVY);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_PIDGEY) { Ability(ABILITY_BIG_PECKS); }
} WHEN {
TURN { MOVE(opponent, MOVE_HARDEN); MOVE(player, MOVE_TOPSY_TURVY); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_HARDEN, opponent);
MESSAGE("The opposing Pidgey's Defense rose!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_TOPSY_TURVY, player);
MESSAGE("All stat changes on the opposing Pidgey were inverted!");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1);
}
}
SINGLE_BATTLE_TEST("Big Pecks doesn't prevent Spectral Thief from resetting positive Defense stage changes")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_HARDEN].effect == EFFECT_DEFENSE_UP);
ASSUME(MoveHasAdditionalEffect(MOVE_SPECTRAL_THIEF, MOVE_EFFECT_SPECTRAL_THIEF));
ASSUME(gMovesInfo[MOVE_SOAK].effect == EFFECT_SOAK);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_PIDGEY) { Ability(ABILITY_BIG_PECKS); }
} WHEN {
TURN { MOVE(opponent, MOVE_CELEBRATE); MOVE(player,MOVE_SOAK); }
TURN { MOVE(opponent, MOVE_HARDEN); MOVE(player, MOVE_SPECTRAL_THIEF); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_HARDEN, opponent);
MESSAGE("The opposing Pidgey's Defense rose!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SPECTRAL_THIEF, player);
MESSAGE("Wobbuffet stole the target's boosted stats!");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE);
}
}
SINGLE_BATTLE_TEST("Big Pecks doesn't prevent receiving negative Defense stage changes from Baton Pass")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_LEER].effect == EFFECT_DEFENSE_DOWN);
ASSUME(gMovesInfo[MOVE_BATON_PASS].effect == EFFECT_BATON_PASS);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_PIDGEY) { Ability(ABILITY_BIG_PECKS); }
} WHEN {
TURN { MOVE(player, MOVE_LEER);
MOVE(opponent, MOVE_BATON_PASS);
SEND_OUT(opponent, 1);
}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_LEER, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BATON_PASS, opponent);
MESSAGE("2 sent out Pidgey!");
} THEN {
EXPECT_EQ(opponent->statStages[STAT_DEF], DEFAULT_STAT_STAGE - 1);
}
}

View File

@ -351,3 +351,27 @@ SINGLE_BATTLE_TEST("Intimidate activates when it's no longer affected by Neutral
}
}
DOUBLE_BATTLE_TEST("Intimidate will correctly decrease the attack of the second mon after Protosynthesis activated")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_EJECT_PACK); }
PLAYER(SPECIES_WYNAUT);
PLAYER(SPECIES_WALKING_WAKE) { Ability(ABILITY_PROTOSYNTHESIS); Item(ITEM_BOOSTER_ENERGY); }
OPPONENT(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_EKANS) { Ability(ABILITY_INTIMIDATE); }
} WHEN {
TURN { SWITCH(opponentLeft, 2); SEND_OUT(playerLeft, 2); }
} SCENE {
ABILITY_POPUP(opponentLeft, ABILITY_INTIMIDATE);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_HELD_ITEM_EFFECT, playerLeft);
ABILITY_POPUP(playerLeft, ABILITY_PROTOSYNTHESIS);
NONE_OF {
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentLeft);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponentRight);
}
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, playerRight);
}
}

View File

@ -0,0 +1,61 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Water Compaction raises Defense 2 stages when hit by a water type move")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_WATER_GUN].type == TYPE_WATER);
PLAYER(SPECIES_SANDYGAST) { Ability(ABILITY_WATER_COMPACTION); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_WATER_GUN); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent);
ABILITY_POPUP(player, ABILITY_WATER_COMPACTION);
} THEN {
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 2);
}
}
SINGLE_BATTLE_TEST("Water Compaction raises Defense 2 stages on each hit of a multi-hit Water type move")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_SURGING_STRIKES].type == TYPE_WATER);
ASSUME(gMovesInfo[MOVE_SURGING_STRIKES].strikeCount == 3);
PLAYER(SPECIES_SANDYGAST) { Ability(ABILITY_WATER_COMPACTION); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_SURGING_STRIKES); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SURGING_STRIKES, opponent);
ABILITY_POPUP(player, ABILITY_WATER_COMPACTION);
MESSAGE("Sandygast's Defense sharply rose!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SURGING_STRIKES, opponent);
ABILITY_POPUP(player, ABILITY_WATER_COMPACTION);
MESSAGE("Sandygast's Defense sharply rose!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SURGING_STRIKES, opponent);
ABILITY_POPUP(player, ABILITY_WATER_COMPACTION);
MESSAGE("Sandygast's Defense sharply rose!");
} THEN {
EXPECT_EQ(player->statStages[STAT_DEF], DEFAULT_STAT_STAGE + 6);
}
}
SINGLE_BATTLE_TEST("Water Compaction does not affect damage taken from Water type moves", s16 damage)
{
u16 ability;
PARAMETRIZE { ability = ABILITY_SAND_VEIL; }
PARAMETRIZE { ability = ABILITY_WATER_COMPACTION; }
GIVEN {
ASSUME(gMovesInfo[MOVE_WATER_GUN].type == TYPE_WATER);
PLAYER(SPECIES_SANDYGAST) { Ability(ability); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_WATER_GUN); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_WATER_GUN, opponent);
HP_BAR(player, captureDamage: &results[i].damage);
} FINALLY {
EXPECT_EQ(results[0].damage, results[1].damage);
}
}

View File

@ -69,3 +69,52 @@ SINGLE_BATTLE_TEST("Dragon Tail does not fail if replacements fainted")
NOT MESSAGE("But it failed!");
}
}
SINGLE_BATTLE_TEST("Dragon Tail switches the target after Rocky Helmet and Iron Barbs")
{
PASSES_RANDOMLY(1, 2, RNG_FORCE_RANDOM_SWITCH);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_TOGEDEMARU) { Ability(ABILITY_IRON_BARBS); Item(ITEM_ROCKY_HELMET); }
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_CHARMANDER);
} WHEN {
TURN { MOVE(player, MOVE_DRAGON_TAIL); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_TAIL, player);
HP_BAR(player);
MESSAGE("Wobbuffet was hurt by the opposing Togedemaru's Iron Barbs!");
HP_BAR(player);
MESSAGE("Wobbuffet was hurt by the opposing Togedemaru's Rocky Helmet!");
MESSAGE("The opposing Charmander was dragged out!");
}
}
SINGLE_BATTLE_TEST("Dragon Tail effect will fails against Guard Dog ability")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_OKIDOGI) { Ability(ABILITY_GUARD_DOG); }
OPPONENT(SPECIES_CHARMANDER);
} WHEN {
TURN { MOVE(player, MOVE_DRAGON_TAIL); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_TAIL, player);
NOT MESSAGE("The opposing Charmander was dragged out!");
}
}
SINGLE_BATTLE_TEST("Dragon Tail effect will fails against Suction Cups ability")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_OCTILLERY) { Ability(ABILITY_SUCTION_CUPS); }
OPPONENT(SPECIES_CHARMANDER);
} WHEN {
TURN { MOVE(player, MOVE_DRAGON_TAIL); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAGON_TAIL, player);
MESSAGE("The opposing Octillery anchors itself with Suction Cups!");
NOT MESSAGE("The opposing Charmander was dragged out!");
}
}