Adds Move Shed Tail (#4016)

Co-authored-by: Bassoonian <iasperbassoonian@gmail.com>
This commit is contained in:
Alex 2024-01-18 00:14:18 +01:00 committed by GitHub
parent b20be66175
commit 9efdd9e0cd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 171 additions and 10 deletions

View file

@ -370,6 +370,27 @@ gBattleScriptsForMoveEffects::
.4byte BattleScript_EffectHit @ EFFECT_FICKLE_BEAM
.4byte BattleScript_EffectHit @ EFFECT_BLIZZARD
.4byte BattleScript_EffectHit @ EFFECT_RAIN_ALWAYS_HIT
.4byte BattleScript_EffectShedTail @ EFFECT_SHED_TAIL
BattleScript_EffectShedTail:
attackcanceler
attackstring
ppreduce
waitstate
jumpifstatus2 BS_ATTACKER, STATUS2_SUBSTITUTE, BattleScript_AlreadyHasSubstitute
jumpifbattletype BATTLE_TYPE_ARENA, BattleScript_ButItFailed
jumpifcantswitch SWITCH_IGNORE_ESCAPE_PREVENTION | BS_ATTACKER, BattleScript_ButItFailed
setsubstitute
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_SUBSTITUTE_FAILED, BattleScript_SubstituteString
attackanimation
waitanimation
healthbarupdate BS_ATTACKER
datahpupdate BS_ATTACKER
printstring STRINGID_SHEDITSTAIL
waitmessage B_WAIT_TIME_LONG
moveendto MOVEEND_ATTACKER_VISIBLE
moveendfrom MOVEEND_TARGET_VISIBLE
goto BattleScript_MoveSwitchOpenPartyScreen
BattleScript_EffectPsychicNoise::
printstring STRINGID_PKMNPREVENTEDFROMHEALING
@ -505,6 +526,7 @@ BattleScript_MoveSwitch:
jumpifcantswitch SWITCH_IGNORE_ESCAPE_PREVENTION | BS_ATTACKER, BattleScript_MoveSwitchEnd
printstring STRINGID_PKMNWENTBACK
waitmessage B_WAIT_TIME_SHORT
BattleScript_MoveSwitchOpenPartyScreen:
openpartyscreen BS_ATTACKER, BattleScript_MoveSwitchEnd
switchoutabilities BS_ATTACKER
waitstate
@ -4179,15 +4201,13 @@ BattleScript_EffectSubstitute::
waitstate
jumpifstatus2 BS_ATTACKER, STATUS2_SUBSTITUTE, BattleScript_AlreadyHasSubstitute
setsubstitute
jumpifbyte CMP_NOT_EQUAL, cMULTISTRING_CHOOSER, B_MSG_SUBSTITUTE_FAILED, BattleScript_SubstituteAnim
pause B_WAIT_TIME_SHORT
goto BattleScript_SubstituteString
BattleScript_SubstituteAnim::
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_SUBSTITUTE_FAILED, BattleScript_SubstituteString
attackanimation
waitanimation
healthbarupdate BS_ATTACKER
datahpupdate BS_ATTACKER
BattleScript_SubstituteString::
pause B_WAIT_TIME_SHORT
printfromtable gSubstituteUsedStringIds
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd

View file

@ -350,6 +350,7 @@ enum {
EFFECT_FICKLE_BEAM,
EFFECT_BLIZZARD,
EFFECT_RAIN_ALWAYS_HIT, // Unlike EFFECT_THUNDER, it doesn't get its accuracy reduced under sun.
EFFECT_SHED_TAIL,
NUM_BATTLE_MOVE_EFFECTS,
};

View file

@ -700,8 +700,9 @@
#define STRINGID_ELECTROSHOCKCHARGING 698
#define STRINGID_ITEMWASUSEDUP 699
#define STRINGID_ATTACKERLOSTITSTYPE 700
#define STRINGID_SHEDITSTAIL 701
#define BATTLESTRINGS_COUNT 701
#define BATTLESTRINGS_COUNT 702
// This is the string id that gBattleStringsTable starts with.
// String ids before this (e.g. STRINGID_INTROMSG) are not in the table,

View file

@ -25,6 +25,8 @@
#include "constants/songs.h"
#include "constants/rgb.h"
#include "constants/battle_palace.h"
#include "constants/battle_move_effects.h"
extern const u8 gBattlePalaceNatureToMoveTarget[];
extern const struct CompressedSpriteSheet gSpriteSheet_EnemyShadow;
@ -1000,7 +1002,7 @@ void LoadBattleMonGfxAndAnimate(u8 battler, bool8 loadMonSprite, u8 spriteId)
void TrySetBehindSubstituteSpriteBit(u8 battler, u16 move)
{
if (move == MOVE_SUBSTITUTE)
if (gBattleMoves[move].effect == EFFECT_SUBSTITUTE || gBattleMoves[move].effect == EFFECT_SHED_TAIL)
gBattleSpritesDataPtr->battlerData[battler].behindSubstitute = 1;
}

View file

@ -3156,6 +3156,11 @@ void SwitchInClearSetData(u32 battler)
gDisableStructs[battler].battlerPreventingEscape = disableStructCopy.battlerPreventingEscape;
gDisableStructs[battler].embargoTimer = disableStructCopy.embargoTimer;
}
else if (gBattleMoves[gCurrentMove].effect == EFFECT_SHED_TAIL)
{
gBattleMons[battler].status2 |= STATUS2_SUBSTITUTE;
gDisableStructs[battler].substituteHP = disableStructCopy.substituteHP;
}
gMoveResultFlags = 0;
gDisableStructs[battler].isFirstTurn = 2;

View file

@ -837,9 +837,11 @@ static const u8 sText_HospitalityRestoration[] = _("The {B_ATK_PARTNER_NAME} dra
static const u8 sText_ElectroShockCharging[] = _("{B_ATK_NAME_WITH_PREFIX} absorbed\nelectricity!");
static const u8 sText_ItemWasUsedUp[] = _("The {B_LAST_ITEM}\nwas used up...");
static const u8 sText_AttackerLostItsType[] = _("{B_ATK_NAME_WITH_PREFIX} lost\nits {B_BUFF1} type!");
static const u8 sText_ShedItsTail[] = _("{B_ATK_NAME_WITH_PREFIX} shed its tail\nto create a decoy!");
const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
{
[STRINGID_SHEDITSTAIL - BATTLESTRINGS_TABLE_START] = sText_ShedItsTail,
[STRINGID_ELECTROSHOCKCHARGING - BATTLESTRINGS_TABLE_START] = sText_ElectroShockCharging,
[STRINGID_HOSPITALITYRESTORATION - BATTLESTRINGS_TABLE_START] = sText_HospitalityRestoration,
[STRINGID_THESWAMPDISAPPEARED - BATTLESTRINGS_TABLE_START] = sText_TheSwampDisappeared,

View file

@ -12391,8 +12391,10 @@ static void Cmd_setsubstitute(void)
{
CMD_ARGS();
u32 hp = GetNonDynamaxMaxHP(gBattlerAttacker) / 4;
if (GetNonDynamaxMaxHP(gBattlerAttacker) / 4 == 0)
u32 factor = gBattleMoves[gCurrentMove].effect == EFFECT_SHED_TAIL ? 2 : 4;
u32 hp = GetNonDynamaxMaxHP(gBattlerAttacker) / factor;
if (GetNonDynamaxMaxHP(gBattlerAttacker) / factor == 0)
hp = 1;
if (gBattleMons[gBattlerAttacker].hp <= hp)
@ -12402,7 +12404,7 @@ static void Cmd_setsubstitute(void)
}
else
{
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / 4; // one bit value will only work for Pokémon which max hp can go to 1020(which is more than possible in games)
gBattleMoveDamage = GetNonDynamaxMaxHP(gBattlerAttacker) / factor; // one bit value will only work for Pokémon which max hp can go to 1020(which is more than possible in games)
if (gBattleMoveDamage == 0)
gBattleMoveDamage = 1;

View file

@ -12897,7 +12897,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_DYNAMAX] =
[MOVE_SHED_TAIL] =
{
.effect = EFFECT_PLACEHOLDER, // EFFECT_SHED_TAIL
.effect = EFFECT_SHED_TAIL,
.power = 0,
.type = TYPE_NORMAL,
.accuracy = 0,

View file

@ -0,0 +1,72 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_SHED_TAIL].effect == EFFECT_SHED_TAIL);
}
SINGLE_BATTLE_TEST("Shed Tail creates a Substitute at the cost of 1/2 users maximum HP and switches the user out")
{
s16 maxHP = 0;
s16 costHP = 0;
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SHED_TAIL); SEND_OUT(player, 1); }
} SCENE {
maxHP = GetMonData(&gPlayerParty[0], MON_DATA_HP);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHED_TAIL, player);
HP_BAR(player, captureDamage: &costHP);
MESSAGE("Wobbuffet shed its tail to create a decoy!");
MESSAGE("Go! Wynaut!");
}THEN {
EXPECT_EQ(maxHP / 2, costHP);
}
}
SINGLE_BATTLE_TEST("Shed Tail fails if the user doesn't have enough HP")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SHED_TAIL); }
} SCENE {
MESSAGE("It was too weak to make a SUBSTITUTE!");
}
}
SINGLE_BATTLE_TEST("Shed Tail's HP cost can trigger a berry before the user switches out")
{
GIVEN {
ASSUME(gItems[ITEM_SITRUS_BERRY].battleUsage == EFFECT_ITEM_RESTORE_HP);
PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_SITRUS_BERRY); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SHED_TAIL); SEND_OUT(player, 1); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SHED_TAIL, player);
MESSAGE("Wobbuffet's Sitrus Berry restored health!");
MESSAGE("Go! Wynaut!");
}
}
SINGLE_BATTLE_TEST("Shed Tail fails if there are no usable pokemon left")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET)
PLAYER(SPECIES_WYNAUT) { HP(0); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SHED_TAIL); }
} SCENE {
MESSAGE("Wobbuffet used Shed Tail!");
MESSAGE("But it failed!");
}
}

View file

@ -0,0 +1,56 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_SUBSTITUTE].effect == EFFECT_SUBSTITUTE);
}
SINGLE_BATTLE_TEST("Substitute creates a Substitute at the cost of 1/4 users maximum HP")
{
s16 maxHP = 0;
s16 costHP = 0;
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SUBSTITUTE); }
} SCENE {
maxHP = GetMonData(&gPlayerParty[0], MON_DATA_HP);
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, player);
HP_BAR(player, captureDamage: &costHP);
MESSAGE("Wobbuffet made a SUBSTITUTE!");
}THEN {
EXPECT_EQ(maxHP / 4, costHP);
}
}
SINGLE_BATTLE_TEST("Substitute fails if the user doesn't have enough HP")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SUBSTITUTE); }
} SCENE {
MESSAGE("It was too weak to make a SUBSTITUTE!");
}
}
SINGLE_BATTLE_TEST("Substitute's HP cost can trigger a berry")
{
GIVEN {
ASSUME(gItems[ITEM_SITRUS_BERRY].battleUsage == EFFECT_ITEM_RESTORE_HP);
PLAYER(SPECIES_WOBBUFFET) { HP(300); Item(ITEM_SITRUS_BERRY); }
PLAYER(SPECIES_WYNAUT);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SUBSTITUTE); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SUBSTITUTE, player);
MESSAGE("Wobbuffet's Sitrus Berry restored health!");
}
}