Adds moves Matcha Gotcha, Syrup Bomb and Ivy Cudgel (#3402)

* initial commit

* done

* clean up
This commit is contained in:
Alex 2023-10-11 11:09:50 +02:00 committed by GitHub
parent a795148068
commit 0b9126ff53
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 299 additions and 9 deletions

View file

@ -438,6 +438,48 @@ gBattleScriptsForMoveEffects::
.4byte BattleScript_EffectHit @ EFFECT_GIGATON_HAMMER
.4byte BattleScript_EffectSaltCure @ EFFECT_SALT_CURE
.4byte BattleScript_EffectMatchaGotcha @ EFFECT_MATCHA_GOTCHA
.4byte BattleScript_EffectSyrupBomb @ EFFECT_SYRUP_BOMB
.4byte BattleScript_EffectHit @ EFFECT_IVY_CUDGEL
BattleScript_EffectSyrupBomb::
setmoveeffect MOVE_EFFECT_SYRUP_BOMB
call BattleScript_EffectHit_Ret
seteffectwithchance
tryfaintmon BS_TARGET
printstring STRINGID_TARGETCOVEREDINSTICKYCANDYSYRUP
waitmessage B_WAIT_TIME_LONG
goto BattleScript_MoveEnd
BattleScript_SyrupBombEndTurn::
setbyte sSTAT_ANIM_PLAYED, FALSE
copybyte sBATTLER, gBattlerTarget
jumpifholdeffect BS_TARGET, HOLD_EFFECT_CLEAR_AMULET, BattleScript_SyrupBombItemNoStatLoss
jumpifability BS_TARGET, ABILITY_CLEAR_BODY, BattleScript_SyrupBombAbilityNoStatLoss
jumpifability BS_TARGET, ABILITY_FULL_METAL_BODY, BattleScript_SyrupBombAbilityNoStatLoss
jumpifability BS_TARGET, ABILITY_WHITE_SMOKE, BattleScript_SyrupBombAbilityNoStatLoss
jumpifstat BS_TARGET, CMP_GREATER_THAN, BIT_SPEED, MIN_STAT_STAGE, BattleScript_SyrupBombLowerSpeed
goto BattleScript_SyrupBombEnd2
BattleScript_SyrupBombLowerSpeed:
playstatchangeanimation BS_ATTACKER, BIT_SPEED, STAT_CHANGE_NEGATIVE
setbyte sSTAT_ANIM_PLAYED, TRUE
setstatchanger STAT_SPEED, 1, TRUE
statbuffchange STAT_CHANGE_ALLOW_PTR, BattleScript_SyrupBombEnd2
jumpifbyte CMP_EQUAL, cMULTISTRING_CHOOSER, B_MSG_STAT_WONT_DECREASE, BattleScript_SyrupBombEnd2
printfromtable gStatDownStringIds
waitmessage B_WAIT_TIME_LONG
BattleScript_SyrupBombItemNoStatLoss::
call BattleScript_ItemNoStatLoss
goto BattleScript_SyrupBombEnd2
BattleScript_SyrupBombAbilityNoStatLoss::
call BattleScript_AbilityNoStatLoss
BattleScript_SyrupBombEnd2::
end2
BattleScript_EffectMatchaGotcha::
setmoveeffect MOVE_EFFECT_BURN
goto BattleScript_EffectAbsorb
BattleScript_EffectSaltCure:
call BattleScript_EffectHit_Ret
tryfaintmon BS_TARGET
@ -3333,6 +3375,7 @@ BattleScript_EffectPoisonHit:
BattleScript_EffectAbsorb::
call BattleScript_EffectHit_Ret
seteffectwithchance
jumpifstatus3 BS_ATTACKER, STATUS3_HEAL_BLOCK, BattleScript_AbsorbHealBlock
setdrainedhp
manipulatedamage DMG_BIG_ROOT

View file

@ -103,6 +103,7 @@ struct DisableStruct
u8 toxicSpikesDone:1;
u8 stickyWebDone:1;
u8 stealthRockDone:1;
u8 syrupBombTimer;
};
struct ProtectStruct

View file

@ -479,6 +479,7 @@ extern const u8 BattleScript_UltraBurst[];
extern const u8 BattleScript_SelectingNotAllowedCurrentMove[];
extern const u8 BattleScript_SelectingNotAllowedCurrentMoveInPalace[];
extern const u8 BattleScript_SaltCureExtraDamage[];
extern const u8 BattleScript_SyrupBombEndTurn[];
// zmoves
extern const u8 BattleScript_ZMoveActivateDamaging[];

View file

@ -189,6 +189,7 @@
#define STATUS4_WATER_SPORT (1 << 3) // Only used if B_SPORT_TURNS < GEN_6
#define STATUS4_INFINITE_CONFUSION (1 << 4) // Used for Berserk Gene
#define STATUS4_SALT_CURE (1 << 5)
#define STATUS4_SYRUP_BOMB (1 << 6)
#define HITMARKER_WAKE_UP_CLEAR (1 << 4) // Cleared when waking up. Never set or checked.
#define HITMARKER_SKIP_DMG_TRACK (1 << 5)
@ -385,8 +386,9 @@
#define MOVE_EFFECT_STEALTH_ROCK 76
#define MOVE_EFFECT_SPIKES 77
#define MOVE_EFFECT_TRIPLE_ARROWS 78
#define MOVE_EFFECT_SYRUP_BOMB 79
#define NUM_MOVE_EFFECTS 79
#define NUM_MOVE_EFFECTS 80
#define MOVE_EFFECT_AFFECTS_USER 0x4000
#define MOVE_EFFECT_CERTAIN 0x8000

View file

@ -414,7 +414,10 @@
#define EFFECT_MORTAL_SPIN 408
#define EFFECT_GIGATON_HAMMER 409
#define EFFECT_SALT_CURE 410
#define EFFECT_MATCHA_GOTCHA 411
#define EFFECT_SYRUP_BOMB 412
#define EFFECT_IVY_CUDGEL 413
#define NUM_BATTLE_MOVE_EFFECTS 411
#define NUM_BATTLE_MOVE_EFFECTS 414
#endif // GUARD_CONSTANTS_BATTLE_MOVE_EFFECTS_H

View file

@ -671,8 +671,9 @@
#define STRINGID_TARGETISBEINGSALTCURED 669
#define STRINGID_TARGETISHURTBYSALTCURE 670
#define STRINGID_OPPORTUNISTCOPIED 671
#define STRINGID_TARGETCOVEREDINSTICKYCANDYSYRUP 672
#define BATTLESTRINGS_COUNT 672
#define BATTLESTRINGS_COUNT 673
// This is the string id that gBattleStringsTable starts with.
// String ids before this (e.g. STRINGID_INTROMSG) are not in the table,

View file

@ -160,9 +160,10 @@
#define HOLD_EFFECT_COVERT_CLOAK 179
#define HOLD_EFFECT_LOADED_DICE 180
#define HOLD_EFFECT_BOOSTER_ENERGY 181 // Not implemented.
#define HOLD_EFFECT_MASK 183
// Gen2 hold effect
#define HOLD_EFFECT_BERSERK_GENE 182
#define HOLD_EFFECT_BERSERK_GENE 184
#define HOLD_EFFECT_CHOICE(holdEffect)((holdEffect == HOLD_EFFECT_CHOICE_BAND || holdEffect == HOLD_EFFECT_CHOICE_SCARF || holdEffect == HOLD_EFFECT_CHOICE_SPECS))

View file

@ -5644,6 +5644,10 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk)
if (holdEffect == gBattleMoves[move].argument)
gBattleStruct->dynamicMoveType = ItemId_GetSecondaryId(gBattleMons[battlerAtk].item) | F_DYNAMIC_TYPE_2;
}
else if (gBattleMoves[move].effect == EFFECT_IVY_CUDGEL && holdEffect == HOLD_EFFECT_MASK)
{
gBattleStruct->dynamicMoveType = ItemId_GetSecondaryId(gBattleMons[battlerAtk].item) | F_DYNAMIC_TYPE_2;
}
else if (gBattleMoves[move].effect == EFFECT_REVELATION_DANCE)
{
if (gBattleMons[battlerAtk].type1 != TYPE_MYSTERY)

View file

@ -808,6 +808,7 @@ static const u8 sText_CurrentMoveCantSelect[] = _("{B_BUFF1} cannot be used!\p")
static const u8 sText_TargetIsBeingSaltCured[] = _("{B_DEF_NAME_WITH_PREFIX} is being salt cured!");
static const u8 sText_TargetIsHurtBySaltCure[] = _("{B_DEF_NAME_WITH_PREFIX} is hurt by {B_BUFF1}!");
static const u8 sText_OpportunistCopied[] = _("{B_SCR_ACTIVE_NAME_WITH_PREFIX} copied its\nopponent's stat changes!");
static const u8 sText_TargetCoveredInStickyCandySyrup[] = _("{B_DEF_NAME_WITH_PREFIX} got covered\nin sticky syrup!");
const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
{
@ -1471,6 +1472,7 @@ const u8 *const gBattleStringsTable[BATTLESTRINGS_COUNT] =
[STRINGID_ULTRABURSTREACTING - BATTLESTRINGS_TABLE_START] = sText_UltraBurstReacting,
[STRINGID_ULTRABURSTCOMPLETED - BATTLESTRINGS_TABLE_START] = sText_UltraBurstCompleted,
[STRINGID_TEAMGAINEDEXP - BATTLESTRINGS_TABLE_START] = sText_TeamGainedEXP,
[STRINGID_TARGETCOVEREDINSTICKYCANDYSYRUP - BATTLESTRINGS_TABLE_START] = sText_TargetCoveredInStickyCandySyrup,
};
const u16 gTrainerUsedItemStringIds[] =

View file

@ -3681,6 +3681,19 @@ void SetMoveEffect(bool32 primary, u32 certain)
gBattlescriptCurrInstr++;
}
}
break;
case MOVE_EFFECT_SYRUP_BOMB:
if (gStatuses4[gEffectBattler] & STATUS4_SYRUP_BOMB)
{
gBattlescriptCurrInstr++;
}
else
{
gStatuses4[gEffectBattler] |= STATUS4_SYRUP_BOMB;
gDisableStructs[gBattlerTarget].syrupBombTimer = 3;
gBattlescriptCurrInstr++;
}
break;
}
}

View file

@ -2541,6 +2541,7 @@ enum
ENDTURN_PLASMA_FISTS,
ENDTURN_CUD_CHEW,
ENDTURN_SALT_CURE,
ENDTURN_SYRUP_BOMB,
ENDTURN_BATTLER_COUNT
};
@ -3116,6 +3117,30 @@ u8 DoBattlerEndTurnEffects(void)
}
gBattleStruct->turnEffectsTracker++;
break;
case ENDTURN_SYRUP_BOMB:
if ((gStatuses4[battler] & STATUS4_SYRUP_BOMB) && (gBattleMons[battler].hp != 0))
{
u16 battlerAbility = GetBattlerAbility(battler);
u32 battlerHoldEffect = GetBattlerHoldEffect(battler, TRUE);
gDisableStructs[battler].syrupBombTimer--;
if (gDisableStructs[battler].syrupBombTimer == 0)
{
gStatuses4[battler] &= ~STATUS4_SYRUP_BOMB;
PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_SYRUP_BOMB);
gBattlescriptCurrInstr = BattleScript_WrapEnds;
}
else if (gDisableStructs[battler].syrupBombTimer != 0)
{
gBattlerTarget = battler;
PREPARE_MOVE_BUFFER(gBattleTextBuff1, MOVE_SYRUP_BOMB);
gBattlescriptCurrInstr = BattleScript_SyrupBombEndTurn;
}
BattleScriptExecute(gBattlescriptCurrInstr);
effect++;
}
gBattleStruct->turnEffectsTracker++;
break;
case ENDTURN_BATTLER_COUNT: // done
gBattleStruct->turnEffectsTracker = 0;
gBattleStruct->turnEffectsBattlerId++;
@ -11230,7 +11255,7 @@ bool32 IsGen6ExpShareEnabled(void)
u8 GetBattlerType(u32 battler, u8 typeIndex)
{
{
u16 types[3] = {0};
types[0] = gBattleMons[battler].type1;
types[1] = gBattleMons[battler].type2;

View file

@ -13829,12 +13829,12 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] =
[MOVE_MATCHA_GOTCHA] =
{
.effect = EFFECT_PLACEHOLDER, // EFFECT_MATCHA_GOTCHA
.effect = EFFECT_MATCHA_GOTCHA,
.power = 80,
.type = TYPE_GRASS,
.accuracy = 90,
.pp = 15,
.secondaryEffectChance = 20, //burn
.secondaryEffectChance = 20,
.target = MOVE_TARGET_BOTH,
.priority = 0,
.split = SPLIT_SPECIAL,
@ -13845,7 +13845,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] =
[MOVE_SYRUP_BOMB] =
{
.effect = EFFECT_PLACEHOLDER, // EFFECT_SYRUP_BOMB
.effect = EFFECT_SYRUP_BOMB,
.power = 60,
.type = TYPE_GRASS,
.accuracy = 85,
@ -13861,7 +13861,7 @@ const struct BattleMove gBattleMoves[MOVES_COUNT_Z] =
[MOVE_IVY_CUDGEL] =
{
.effect = EFFECT_PLACEHOLDER, // EFFECT_IVY_CUDGEL
.effect = EFFECT_IVY_CUDGEL,
.power = 100,
.type = TYPE_GRASS,
.accuracy = 100,

View file

@ -9700,30 +9700,36 @@ const struct Item gItems[] =
{
.name = _("CornrstneMask"),
.price = 0,
.holdEffect = HOLD_EFFECT_MASK,
.description = sCornerstoneMaskDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
.secondaryId = TYPE_ROCK,
},
[ITEM_WELLSPRING_MASK] =
{
.name = _("WellsprngMask"),
.price = 0,
.holdEffect = HOLD_EFFECT_MASK,
.description = sWellspringMaskDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
.secondaryId = TYPE_WATER,
},
[ITEM_HEARTHFLAME_MASK] =
{
.name = _("HrthflameMask"),
.price = 0,
.holdEffect = HOLD_EFFECT_MASK,
.description = sHearthflameMaskDesc,
.pocket = POCKET_ITEMS,
.type = ITEM_USE_BAG_MENU,
.fieldUseFunc = ItemUseOutOfBattle_CannotUse,
.secondaryId = TYPE_FIRE,
},
[ITEM_HEALTH_MOCHI] =

View file

@ -0,0 +1,24 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Ivy Gudgel changes the move type depending on the mask the user holds")
{
u16 species;
u16 item;
PARAMETRIZE { species = SPECIES_BLASTOISE; item = ITEM_NONE; }
PARAMETRIZE { species = SPECIES_CHARIZARD; item = ITEM_CORNERSTONE_MASK; }
PARAMETRIZE { species = SPECIES_CHARIZARD; item = ITEM_WELLSPRING_MASK; }
PARAMETRIZE { species = SPECIES_VENUSAUR; item = ITEM_HEARTHFLAME_MASK; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { Item(item); }
OPPONENT(species);
} WHEN {
TURN { MOVE(player, MOVE_IVY_CUDGEL); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_IVY_CUDGEL, player);
HP_BAR(opponent);
MESSAGE("It's super effective!");
}
}

View file

@ -0,0 +1,66 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gBattleMoves[MOVE_MATCHA_GOTCHA].effect == EFFECT_MATCHA_GOTCHA);
}
SINGLE_BATTLE_TEST("Matcha Gotcha inflicts burn 20% of the time")
{
PASSES_RANDOMLY(20, 100, RNG_SECONDARY_EFFECT);
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_MATCHA_GOTCHA); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_MATCHA_GOTCHA, player);
HP_BAR(opponent);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponent);
STATUS_ICON(opponent, burn: TRUE);
}
}
DOUBLE_BATTLE_TEST("Matcha Gatcha can burn both targets")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerLeft, MOVE_MATCHA_GOTCHA); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_MATCHA_GOTCHA, playerLeft);
HP_BAR(opponentLeft);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponentLeft);
STATUS_ICON(opponentLeft, burn: TRUE);
HP_BAR(opponentRight);
ANIMATION(ANIM_TYPE_STATUS, B_ANIM_STATUS_BRN, opponentRight);
STATUS_ICON(opponentRight, burn: TRUE);
}
}
DOUBLE_BATTLE_TEST("Matcha Gatcha recovers 50% of the damage dealt from both targets")
{
s16 damageLeft;
s16 damageRight;
s16 healedLeft;
s16 healedRight;
GIVEN {
PLAYER(SPECIES_WOBBUFFET) { HP(1); }
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(playerLeft, MOVE_MATCHA_GOTCHA); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_MATCHA_GOTCHA, playerLeft);
HP_BAR(opponentLeft, captureDamage: &damageLeft);
HP_BAR(playerLeft, captureDamage: &healedLeft);
HP_BAR(opponentRight, captureDamage: &damageRight);
HP_BAR(playerLeft, captureDamage: &healedRight);
}
}

View file

@ -0,0 +1,98 @@
#include "global.h"
#include "test/battle.h"
SINGLE_BATTLE_TEST("Syrup Bomb covers the foe in sticky syrup for 3 turns")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(player, MOVE_SYRUP_BOMB); }
TURN {}
TURN {}
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SYRUP_BOMB, player);
HP_BAR(opponent);
MESSAGE("Foe Wobbuffet got covered in sticky syrup!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Wobbuffet's Speed fell!");
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
MESSAGE("Foe Wobbuffet's Speed fell!");
MESSAGE("Foe Wobbuffet was freed from Syrup Bomb!");
}
}
SINGLE_BATTLE_TEST("Syrup Bomb is prevented by Bulletproof")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_CHESPIN) { Ability(ABILITY_BULLETPROOF); }
} WHEN {
TURN { MOVE(player, MOVE_SYRUP_BOMB); }
} SCENE {
ABILITY_POPUP(opponent, ABILITY_BULLETPROOF);
MESSAGE("Foe Chespin's Bulletproof blocks Syrup Bomb!");
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_SYRUP_BOMB, player);
NOT HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Clear Body, White Smoke and Full Metal Body prevent Sticky Syrup speed reduction")
{
u32 species;
u32 ability;
PARAMETRIZE { species = SPECIES_BELDUM; ability = ABILITY_CLEAR_BODY; }
PARAMETRIZE { species = SPECIES_TORKOAL; ability = ABILITY_WHITE_SMOKE; }
PARAMETRIZE { species = SPECIES_SOLGALEO; ability = ABILITY_FULL_METAL_BODY; }
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(species) { Ability(ability); }
} WHEN {
TURN { MOVE(player, MOVE_SYRUP_BOMB); }
TURN {}
TURN {}
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SYRUP_BOMB, player);
HP_BAR(opponent);
if (species == SPECIES_BELDUM)
{
MESSAGE("Foe Beldum got covered in sticky syrup!");
ABILITY_POPUP(opponent, ABILITY_CLEAR_BODY);
MESSAGE("Foe Beldum's Clear Body prevents stat loss!");
}
else if (species == SPECIES_TORKOAL)
{
MESSAGE("Foe Torkoal got covered in sticky syrup!");
ABILITY_POPUP(opponent, ABILITY_WHITE_SMOKE);
MESSAGE("Foe Torkoal's White Smoke prevents stat loss!");
}
else if (species == SPECIES_SOLGALEO)
{
MESSAGE("Foe Solgaleo got covered in sticky syrup!");
ABILITY_POPUP(opponent, ABILITY_FULL_METAL_BODY);
MESSAGE("Foe Solgaleo's Full Metal Body prevents stat loss!");
}
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
NOT MESSAGE("Foe Beldum's Speed fell!");
}
}
SINGLE_BATTLE_TEST("Clear Amulet prevents Sticky Syrup speed reduction")
{
GIVEN {
PLAYER(SPECIES_WOBBUFFET);
OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_CLEAR_AMULET); }
} WHEN {
TURN { MOVE(player, MOVE_SYRUP_BOMB); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_SYRUP_BOMB, player);
HP_BAR(opponent);
MESSAGE("Foe Wobbuffet got covered in sticky syrup!");
MESSAGE("Foe Wobbuffet's Speed was not lowered!");
NOT ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, opponent);
NOT MESSAGE("Foe Wobbuffet's Speed fell!");
}
}