Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds Move Shed Tail #4016

Merged
merged 2 commits into from
Jan 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions data/battle_scripts_1.s
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions include/constants/battle_move_effects.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

Expand Down
3 changes: 2 additions & 1 deletion include/constants/battle_string_ids.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 3 additions & 1 deletion src/battle_gfx_sfx_util.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down
5 changes: 5 additions & 0 deletions src/battle_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions src/battle_message.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
8 changes: 5 additions & 3 deletions src/battle_script_commands.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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;

Expand Down
2 changes: 1 addition & 1 deletion src/data/battle_moves.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
72 changes: 72 additions & 0 deletions test/battle/move_effect/shed_tail.c
Original file line number Diff line number Diff line change
@@ -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!");
}
}
56 changes: 56 additions & 0 deletions test/battle/move_effect/substitute.c
Original file line number Diff line number Diff line change
@@ -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!");
}
}
Loading