This commit is contained in:
Alex 2026-03-21 21:41:41 +01:00 committed by GitHub
commit 7a9363a3f0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 205 additions and 92 deletions

View File

@ -743,10 +743,6 @@
.byte B_SCR_OP_NORMALISEBUFFS
.endm
.macro setbide
.byte B_SCR_OP_SETBIDE
.endm
.macro twoturnmoveschargestringandanimation
.byte B_SCR_OP_TWOTURNMOVESCHARGESTRINGANDANIMATION
.4byte 1f @animation then attack string
@ -801,10 +797,6 @@
.4byte \failInstr
.endm
.macro copybidedmg
.byte B_SCR_OP_COPYBIDEDMG
.endm
.macro animatewildpokemonafterfailedpokeball battler:req
.byte B_SCR_OP_ANIMATEWILDPOKEMONAFTERFAILEDPOKEBALL
.byte \battler
@ -1208,7 +1200,7 @@
.byte B_SCR_OP_TRY_CONFUSION_AFTER_SKY_DROP
.byte \battler
.endm
.macro callnative func:req
.byte B_SCR_OP_CALLNATIVE
.4byte \func

View File

@ -2519,11 +2519,9 @@ BattleScript_EffectHaze::
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_EffectBide::
attackcanceler
BattleScript_SetUpBide::
attackanimation
waitanimation
setbide
goto BattleScript_MoveEnd
BattleScript_EffectRoar::
@ -4592,29 +4590,11 @@ BattleScript_BideStoringEnergy::
goto BattleScript_MoveEnd
BattleScript_BideAttack::
attackcanceler
clearvolatile BS_ATTACKER, VOLATILE_MULTIPLETURNS
printstring STRINGID_PKMNUNLEASHEDENERGY
waitmessage B_WAIT_TIME_LONG
accuracycheck BattleScript_MoveMissed
typecalc
clearmoveresultflags MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE
copybidedmg
setbyte sB_ANIM_TURN, 1
attackanimation
waitanimation
effectivenesssound
hitanimation BS_TARGET
waitstate
healthbarupdate BS_TARGET, MOVE_DAMAGE_HP_UPDATE
datahpupdate BS_TARGET, MOVE_DAMAGE_HP_UPDATE
resultmessage
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
return
BattleScript_BideNoEnergyToAttack::
attackcanceler
clearvolatile BS_ATTACKER, VOLATILE_MULTIPLETURNS
printstring STRINGID_PKMNUNLEASHEDENERGY
waitmessage B_WAIT_TIME_LONG
goto BattleScript_ButItFailed

View File

@ -579,7 +579,7 @@ extern const u8 BattleScript_EffectSpecialDefenseDown[];
extern const u8 BattleScript_EffectAccuracyDown[];
extern const u8 BattleScript_EffectEvasionDown[];
extern const u8 BattleScript_EffectHaze[];
extern const u8 BattleScript_EffectBide[];
extern const u8 BattleScript_SetUpBide[];
extern const u8 BattleScript_EffectRoar[];
extern const u8 BattleScript_EffectHit[];
extern const u8 BattleScript_EffectConversion[];

View File

@ -133,7 +133,6 @@ enum BattleScriptOpcode
B_SCR_OP_STOCKPILETOHPHEAL,
B_SCR_OP_STATBUFFCHANGE,
B_SCR_OP_NORMALISEBUFFS,
B_SCR_OP_SETBIDE,
B_SCR_OP_TWOTURNMOVESCHARGESTRINGANDANIMATION,
B_SCR_OP_TRYNONVOLATILESTATUS,
B_SCR_OP_INITMULTIHITSTRING,
@ -143,7 +142,6 @@ enum BattleScriptOpcode
B_SCR_OP_SETLIGHTSCREEN,
B_SCR_OP_TRYKO,
B_SCR_OP_CHECKNONVOLATILETRIGGER,
B_SCR_OP_COPYBIDEDMG,
B_SCR_OP_ANIMATEWILDPOKEMONAFTERFAILEDPOKEBALL,
B_SCR_OP_TRYINFATUATING,
B_SCR_OP_UPDATESTATUSICON,
@ -262,6 +260,8 @@ enum BattleScriptOpcode
B_SCR_OP_UNUSED_29,
B_SCR_OP_UNUSED_30,
B_SCR_OP_UNUSED_31,
B_SCR_OP_UNUSED_32,
B_SCR_OP_UNUSED_33,
B_SCR_OP_CALLNATIVE,
};

View File

@ -287,6 +287,48 @@ static enum CancelerResult CancelerFocusPreGen5(struct BattleContext *ctx)
return CANCELER_RESULT_SUCCESS;
}
static enum CancelerResult CancelerBide(struct BattleContext *ctx)
{
if (GetMoveEffect(ctx->move) != EFFECT_BIDE)
return CANCELER_RESULT_SUCCESS;
if (gBattleMons[ctx->battlerAtk].volatiles.bideTurns)
{
if (--gBattleMons[ctx->battlerAtk].volatiles.bideTurns)
{
gBattlescriptCurrInstr = BattleScript_BideStoringEnergy;
return CANCELER_RESULT_BREAK; // Jump to moveend
}
else if (gBideDmg[ctx->battlerAtk])
{
gBattlerTarget = gBideTarget[ctx->battlerAtk];
gBattleMons[ctx->battlerAtk].volatiles.multipleTurns = FALSE;
if (!IsBattlerAlive(gBattlerTarget))
gBattlerTarget = GetBattleMoveTarget(gCurrentMove, TARGET_SELECTED);
gBattleStruct->battlerState[ctx->battlerAtk].targetsDone[gBattlerTarget] = FALSE;
BattleScriptCall(BattleScript_BideAttack);
return CANCELER_RESULT_BREAK;
}
else
{
gBattleMons[ctx->battlerAtk].volatiles.multipleTurns = FALSE;
gBattlescriptCurrInstr = BattleScript_BideNoEnergyToAttack;
return CANCELER_RESULT_FAILURE;
}
}
else
{
gBattleMons[gBattlerAttacker].volatiles.multipleTurns = TRUE;
gLockedMoves[gBattlerAttacker] = gCurrentMove;
gBideDmg[gBattlerAttacker] = 0;
gBattleMons[gBattlerAttacker].volatiles.bideTurns = 2;
gBattlescriptCurrInstr = BattleScript_SetUpBide;
return CANCELER_RESULT_BREAK; // Jump to moveend
}
return CANCELER_RESULT_SUCCESS;
}
static enum CancelerResult CancelerFocusGen5(struct BattleContext *ctx)
{
if (GetConfig(B_FOCUS_PUNCH_FAILURE) >= GEN_5)
@ -613,6 +655,9 @@ static enum CancelerResult CancelerStanceChangeTwo(struct BattleContext *ctx)
static enum CancelerResult CancelerAttackstring(struct BattleContext *ctx)
{
if (gBattleMons[ctx->battlerAtk].volatiles.bideTurns)
return CANCELER_RESULT_SUCCESS;
BattleScriptCall(BattleScript_Attackstring);
if (!gSpecialStatuses[gBattlerAttacker].dancerUsedMove)
{
@ -935,6 +980,7 @@ static enum CancelerResult CancelerPPDeduction(struct BattleContext *ctx)
if (gBattleMons[ctx->battlerAtk].volatiles.multipleTurns
|| gSpecialStatuses[ctx->battlerAtk].dancerUsedMove
|| gBattleStruct->bouncedMoveIsUsed
|| gBattleMons[ctx->battlerAtk].volatiles.bideTurns
|| ctx->move == MOVE_STRUGGLE)
return CANCELER_RESULT_SUCCESS;
@ -1064,34 +1110,6 @@ static bool32 ShouldSkipFailureCheckOnBattler(enum BattlerId battlerAtk, enum Ba
return FALSE;
}
static enum CancelerResult CancelerBide(struct BattleContext *ctx)
{
if (gBattleMons[ctx->battlerAtk].volatiles.bideTurns)
{
if (--gBattleMons[ctx->battlerAtk].volatiles.bideTurns)
{
gBattlescriptCurrInstr = BattleScript_BideStoringEnergy;
}
else
{
if (gBideDmg[ctx->battlerAtk])
{
gCurrentMove = MOVE_BIDE;
gBattlerTarget = gBideTarget[ctx->battlerAtk];
if (!IsBattlerAlive(gBattlerTarget))
gBattlerTarget = GetBattleMoveTarget(MOVE_BIDE, TARGET_SELECTED);
gBattlescriptCurrInstr = BattleScript_BideAttack;
}
else
{
gBattlescriptCurrInstr = BattleScript_BideNoEnergyToAttack;
return CANCELER_RESULT_FAILURE;
}
}
}
return CANCELER_RESULT_BREAK; // Jumps to a different script but no failure
}
static enum CancelerResult CancelerMoveFailure(struct BattleContext *ctx)
{
const u8 *battleScript = NULL;
@ -1550,12 +1568,13 @@ static enum CancelerResult HandleSkyDropResult(struct BattleContext *ctx)
static enum CancelerResult CancelerCharging(struct BattleContext *ctx)
{
if (!gBattleMoveEffects[GetMoveEffect(ctx->move)].twoTurnEffect)
return CANCELER_RESULT_SUCCESS;
enum CancelerResult result = CANCELER_RESULT_SUCCESS;
if (GetMoveEffect(ctx->move) == EFFECT_SKY_DROP)
if (!gBattleMoveEffects[GetMoveEffect(ctx->move)].twoTurnEffect)
{
result = CANCELER_RESULT_SUCCESS;
}
else if (GetMoveEffect(ctx->move) == EFFECT_SKY_DROP)
{
result = HandleSkyDropResult(ctx);
}

View File

@ -483,7 +483,6 @@ static void Cmd_stockpiletobasedamage(void);
static void Cmd_stockpiletohpheal(void);
static void Cmd_statbuffchange(void);
static void Cmd_normalisebuffs(void);
static void Cmd_setbide(void);
static void Cmd_twoturnmoveschargestringandanimation(void);
static void Cmd_trynonvolatilestatus(void);
static void Cmd_initmultihitstring(void);
@ -493,7 +492,6 @@ static void Cmd_givepaydaymoney(void);
static void Cmd_setlightscreen(void);
static void Cmd_tryKO(void);
static void Cmd_checknonvolatiletrigger(void);
static void Cmd_copybidedmg(void);
static void Cmd_animatewildpokemonafterfailedpokeball(void);
static void Cmd_tryinfatuating(void);
static void Cmd_updatestatusicon(void);
@ -712,7 +710,6 @@ void (*const gBattleScriptingCommandsTable[])(void) =
[B_SCR_OP_STOCKPILETOHPHEAL] = Cmd_stockpiletohpheal,
[B_SCR_OP_STATBUFFCHANGE] = Cmd_statbuffchange,
[B_SCR_OP_NORMALISEBUFFS] = Cmd_normalisebuffs,
[B_SCR_OP_SETBIDE] = Cmd_setbide,
[B_SCR_OP_TWOTURNMOVESCHARGESTRINGANDANIMATION] = Cmd_twoturnmoveschargestringandanimation,
[B_SCR_OP_TRYNONVOLATILESTATUS] = Cmd_trynonvolatilestatus,
[B_SCR_OP_INITMULTIHITSTRING] = Cmd_initmultihitstring,
@ -722,7 +719,6 @@ void (*const gBattleScriptingCommandsTable[])(void) =
[B_SCR_OP_SETLIGHTSCREEN] = Cmd_setlightscreen,
[B_SCR_OP_TRYKO] = Cmd_tryKO,
[B_SCR_OP_CHECKNONVOLATILETRIGGER] = Cmd_checknonvolatiletrigger,
[B_SCR_OP_COPYBIDEDMG] = Cmd_copybidedmg,
[B_SCR_OP_ANIMATEWILDPOKEMONAFTERFAILEDPOKEBALL] = Cmd_animatewildpokemonafterfailedpokeball,
[B_SCR_OP_TRYINFATUATING] = Cmd_tryinfatuating,
[B_SCR_OP_UPDATESTATUSICON] = Cmd_updatestatusicon,
@ -837,6 +833,8 @@ void (*const gBattleScriptingCommandsTable[])(void) =
[B_SCR_OP_UNUSED_29] = Cmd_dummy,
[B_SCR_OP_UNUSED_30] = Cmd_dummy,
[B_SCR_OP_UNUSED_31] = Cmd_dummy,
[B_SCR_OP_UNUSED_32] = Cmd_dummy,
[B_SCR_OP_UNUSED_33] = Cmd_dummy,
[B_SCR_OP_CALLNATIVE] = Cmd_callnative,
};
@ -8066,18 +8064,6 @@ static void Cmd_normalisebuffs(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_setbide(void)
{
CMD_ARGS();
gBattleMons[gBattlerAttacker].volatiles.multipleTurns = TRUE;
gLockedMoves[gBattlerAttacker] = gCurrentMove;
gBideDmg[gBattlerAttacker] = 0;
gBattleMons[gBattlerAttacker].volatiles.bideTurns = 2;
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_twoturnmoveschargestringandanimation(void)
{
CMD_ARGS(const u8 *animationThenStringPtr);
@ -8459,13 +8445,6 @@ static void Cmd_checknonvolatiletrigger(void)
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_copybidedmg(void)
{
CMD_ARGS();
gBattleStruct->moveDamage[gBattlerTarget] = gBideDmg[gBattlerAttacker] * 2;
gBattlescriptCurrInstr = cmd->nextInstr;
}
static void Cmd_animatewildpokemonafterfailedpokeball(void)
{
CMD_ARGS(u8 battler);

View File

@ -7686,6 +7686,9 @@ s32 DoFixedDamageMoveCalc(struct BattleContext *ctx)
case EFFECT_OHKO:
dmg = gBattleMons[ctx->battlerDef].hp;
break;
case EFFECT_BIDE:
dmg = gBideDmg[ctx->battlerAtk] * 2;
break;
default:
break;
}

View File

@ -170,7 +170,7 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
[EFFECT_BIDE] =
{
.battleScript = BattleScript_EffectBide,
.battleScript = BattleScript_EffectHit,
.battleTvScore = 5,
.battleFactoryStyle = FACTORY_STYLE_HIGH_RISK,
},

View File

@ -22,9 +22,11 @@ SINGLE_BATTLE_TEST("Bide deals twice the taken damage over two turns")
ANIMATION(ANIM_TYPE_MOVE, MOVE_BIDE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
HP_BAR(player, captureDamage: &damage1);
NOT MESSAGE("Wobbuffet used Bide!");
MESSAGE("Wobbuffet is storing energy!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_SCRATCH, opponent);
HP_BAR(player, captureDamage: &damage2);
NOT MESSAGE("Wobbuffet used Bide!");
MESSAGE("Wobbuffet unleashed its energy!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_BIDE, player);
HP_BAR(opponent, captureDamage: &bideDamage);
@ -33,6 +35,144 @@ SINGLE_BATTLE_TEST("Bide deals twice the taken damage over two turns")
}
}
TO_DO_BATTLE_TEST("Bide hits the last Pokémon that attacked the user, even allies");
TO_DO_BATTLE_TEST("Bide has +1 priority if called via a different move"); // Gen 5 onwards
SINGLE_BATTLE_TEST("Bide fails if no damage has been dealt to the user")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BIDE); }
TURN { SKIP_TURN(player); }
TURN { SKIP_TURN(player); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BIDE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
MESSAGE("Wobbuffet is storing energy!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
MESSAGE("Wobbuffet unleashed its energy!");
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BIDE, player);
HP_BAR(opponent);
}
}
}
DOUBLE_BATTLE_TEST("Bide hits the last Pokémon that attacked the user, even allies")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WYNAUT);
} WHEN {
TURN { MOVE(playerLeft, MOVE_BIDE); MOVE(playerRight, MOVE_POUND, target: playerLeft); }
TURN { SKIP_TURN(playerLeft); MOVE(playerRight, MOVE_POUND, target: playerLeft); }
TURN { SKIP_TURN(playerLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BIDE, playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_POUND, playerRight);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_BIDE, playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_POUND, playerRight);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BIDE, playerLeft);
HP_BAR(playerRight);
}
}
SINGLE_BATTLE_TEST("Bide is blocked by Dazzling when it unleashes the attack")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_BRUXISH) { Ability(ABILITY_DAZZLING); }
} WHEN {
TURN { MOVE(player, MOVE_BIDE); MOVE(opponent, MOVE_POUND); }
TURN { SKIP_TURN(player); MOVE(opponent, MOVE_POUND); }
TURN { SKIP_TURN(player); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BIDE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_POUND, opponent);
HP_BAR(player);
MESSAGE("Wobbuffet is storing energy!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_POUND, opponent);
HP_BAR(player);
MESSAGE("Wobbuffet unleashed its energy!");
ABILITY_POPUP(opponent, ABILITY_DAZZLING);
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BIDE, player);
HP_BAR(opponent);
}
}
}
DOUBLE_BATTLE_TEST("Bide is blocked by partner Dazzling")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_BRUXISH) { Ability(ABILITY_DAZZLING); }
} WHEN {
TURN { MOVE(playerLeft, MOVE_BIDE); MOVE(opponentLeft, MOVE_POUND, target: playerLeft); }
TURN { SKIP_TURN(playerLeft); MOVE(opponentLeft, MOVE_POUND, target: playerLeft); }
TURN { SKIP_TURN(playerLeft); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BIDE, playerLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_POUND, opponentLeft);
ANIMATION(ANIM_TYPE_MOVE, MOVE_POUND, opponentLeft);
MESSAGE("Wobbuffet unleashed its energy!");
ABILITY_POPUP(opponentRight, ABILITY_DAZZLING);
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BIDE, playerLeft);
HP_BAR(opponentLeft);
}
}
}
// Redundant?
SINGLE_BATTLE_TEST("Bide fails if 0 total damage was dealt to the user")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_BIDE); }
TURN { SKIP_TURN(player); }
TURN { SKIP_TURN(player); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BIDE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
NOT HP_BAR(player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_CELEBRATE, opponent);
NOT HP_BAR(player);
MESSAGE("Wobbuffet unleashed its energy!");
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BIDE, player);
HP_BAR(opponent);
}
}
}
SINGLE_BATTLE_TEST("Bide doesn't deal back damage taken by user's Substitute")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SUBSTITUTE); }
TURN { MOVE(player, MOVE_BIDE); MOVE(opponent, MOVE_POUND); }
TURN { SKIP_TURN(player); MOVE(opponent, MOVE_POUND); }
TURN { SKIP_TURN(player); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_BIDE, player);
ANIMATION(ANIM_TYPE_MOVE, MOVE_POUND, opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_POUND, opponent);
MESSAGE("Wobbuffet unleashed its energy!");
NONE_OF {
ANIMATION(ANIM_TYPE_MOVE, MOVE_BIDE, player);
HP_BAR(opponent);
}
}
}
TO_DO_BATTLE_TEST("Bide has +1 priority on following turns if called via a different move");