Adds Move Shed Tail (#4016)
Co-authored-by: Bassoonian <iasperbassoonian@gmail.com>
This commit is contained in:
parent
b20be66175
commit
9efdd9e0cd
10 changed files with 171 additions and 10 deletions
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
72
test/battle/move_effect/shed_tail.c
Normal file
72
test/battle/move_effect/shed_tail.c
Normal 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!");
|
||||
}
|
||||
}
|
56
test/battle/move_effect/substitute.c
Normal file
56
test/battle/move_effect/substitute.c
Normal 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!");
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue