Adds move Upper Hand (#4085)

* Remove non-existent tilesets from label comments and alphabetize

* Fixed braces style

* gbagfx bit depth upconversion fix

* jsonproc: filter out every non-alphanumeric character

* fix(linking): link gflib/malloc.c at top of EWRAM in ld_script_modern.ld

* Adds move Upper Hand

* Requested changes

- Tabs / spaces where proper
- HitFromAtkString -> HitFromAccCheck
- Actually compiles now lol
- Moved assumes into relevant tests
- Cleaned up the check for TryUpperHand

* Fixed || positioning

* Update upper_hand.c

* Revert "Merge remote-tracking branch 'upstream/master' into upper-hand"

This reverts commit b21275dfe9, reversing
changes made to 89b1ad1ea1.

* AI logic and conflicts solved

* Test fix

* Fix Sheer Force test

* Requested changes

* Requested changes

* Update battle_script_commands.c

---------

Co-authored-by: GriffinR <griffin.g.richards@gmail.com>
Co-authored-by: Eduardo Quezada <eduardo602002@gmail.com>
Co-authored-by: Sierraffinity <sierra@domoreaweso.me>
Co-authored-by: sbird <sbird@no.tld>
This commit is contained in:
ZnogyroP 2024-02-01 06:23:58 -05:00 committed by GitHub
parent 6d3fa525d5
commit 71b49a114f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 191 additions and 4 deletions

View file

@ -1583,6 +1583,11 @@
callnative BS_SetPhotonGeyserCategory
.endm
.macro tryupperhand failInstr:req
callnative BS_TryUpperHand
.4byte \failInstr
.endm
@ various command changed to more readable macros
.macro cancelmultiturnmoves battler:req
various \battler, VARIOUS_CANCEL_MULTI_TURN_MOVES

View file

@ -17159,6 +17159,33 @@ Move_RAGING_BULL::
restorebg
waitbgfadein
end
@ Credits to Z-nogyroP. Simple anim that combines Force Palm + Fake Out
Move_UPPER_HAND::
loadspritegfx ANIM_TAG_SHADOW_BALL
loadspritegfx ANIM_TAG_HANDS_AND_FEET
loadspritegfx ANIM_TAG_IMPACT
monbg ANIM_DEF_PARTNER
splitbgprio ANIM_TARGET
setalpha 12, 8
playsewithpan SE_M_DOUBLE_TEAM, SOUND_PAN_TARGET
createsprite gKarateChopSpriteTemplate, 2, 8, -16, 0, 0, 0, 10, 1, 3, 0
waitforvisualfinish
playsewithpan SE_M_COMET_PUNCH, SOUND_PAN_TARGET
createsprite gForcePalmSpriteTemplate 3, 4, 0, 0, 1, 2
createvisualtask AnimTask_ShakeMon, 5, ANIM_TARGET, 4, 0, 6, 1
waitforvisualfinish
playsewithpan SE_M_FLATTER, 0
createvisualtask AnimTask_FakeOut, 5
waitforvisualfinish
playsewithpan SE_M_SKETCH, SOUND_PAN_TARGET
createvisualtask AnimTask_ShakeMon2, 2, ANIM_TARGET, 4, 0, 5, 1
createvisualtask AnimTask_StretchTargetUp, 3
waitforvisualfinish
createsprite gSimplePaletteBlendSpriteTemplate, ANIM_ATTACKER, 2, F_PAL_BG, 3, 16, 0, RGB_WHITE
clearmonbg ANIM_DEF_PARTNER
blendoff
end
Move_TERA_BLAST::
Move_AXE_KICK::
@ -17215,7 +17242,6 @@ Move_DRAGON_CHEER::
Move_TEMPER_FLARE::
Move_SUPERCELL_SLAM::
Move_PSYCHIC_NOISE::
Move_UPPER_HAND::
Move_MALIGNANT_CHAIN::
end @to do

View file

@ -20,6 +20,11 @@
.section script_data, "aw", %progbits
BattleScript_EffectUpperHand::
attackcanceler
tryupperhand BattleScript_FailedFromAtkString
goto BattleScript_HitFromAccCheck
BattleScript_EffectShedTail::
attackcanceler
attackstring

View file

@ -822,5 +822,6 @@ extern const u8 BattleScript_EffectBrickBreak[];
extern const u8 BattleScript_EffectDoodle[];
extern const u8 BattleScript_EffectFilletAway[];
extern const u8 BattleScript_EffectShedTail[];
extern const u8 BattleScript_EffectUpperHand[];
#endif // GUARD_BATTLE_SCRIPTS_H

View file

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

View file

@ -2648,6 +2648,10 @@ static s32 AI_CheckBadMove(u32 battlerAtk, u32 battlerDef, u32 move, s32 score)
if (IsDynamaxed(battlerDef))
ADJUST_SCORE(-10);
break;
case EFFECT_UPPER_HAND:
if (predictedMove == MOVE_NONE || IS_MOVE_STATUS(predictedMove) || AI_WhoStrikesFirst(battlerAtk, battlerDef, move) == AI_IS_SLOWER || GetMovePriority(battlerDef, move) < 1 || GetMovePriority(battlerDef, move) > 3) // Opponent going first or not using priority move
ADJUST_SCORE(-10);
break;
case EFFECT_PLACEHOLDER:
return 0; // cannot even select
} // move effect checks

View file

@ -1456,7 +1456,8 @@ static void Cmd_attackcanceler(void)
else if (IsBattlerProtected(gBattlerTarget, gCurrentMove)
&& (gCurrentMove != MOVE_CURSE || IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST))
&& ((!gMovesInfo[gCurrentMove].twoTurnMove || (gBattleMons[gBattlerAttacker].status2 & STATUS2_MULTIPLETURNS)))
&& gMovesInfo[gCurrentMove].effect != EFFECT_SUCKER_PUNCH)
&& gMovesInfo[gCurrentMove].effect != EFFECT_SUCKER_PUNCH
&& gMovesInfo[gCurrentMove].effect != EFFECT_UPPER_HAND)
{
if (IsMoveMakingContact(gCurrentMove, gBattlerAttacker))
gProtectStructs[gBattlerAttacker].touchedProtectLike = TRUE;
@ -5409,7 +5410,7 @@ static void Cmd_moveend(void)
gBattlescriptCurrInstr = BattleScript_BanefulBunkerEffect;
effect = 1;
}
else if (gProtectStructs[gBattlerTarget].obstructed && gCurrentMove != MOVE_SUCKER_PUNCH)
else if (gProtectStructs[gBattlerTarget].obstructed && gMovesInfo[gCurrentMove].effect != EFFECT_SUCKER_PUNCH && gMovesInfo[gCurrentMove].effect != EFFECT_UPPER_HAND)
{
gProtectStructs[gBattlerAttacker].touchedProtectLike = FALSE;
i = gBattlerAttacker;
@ -16524,6 +16525,19 @@ void BS_TryDefog(void)
}
}
void BS_TryUpperHand(void)
{
NATIVE_ARGS(const u8 *failInstr);
if (GetBattlerTurnOrderNum(gBattlerAttacker) > GetBattlerTurnOrderNum(gBattlerTarget)
|| gChosenMoveByBattler[gBattlerTarget] == MOVE_NONE
|| IS_MOVE_STATUS(gChosenMoveByBattler[gBattlerTarget])
|| GetChosenMovePriority(gBattlerTarget) < 1 || GetChosenMovePriority(gBattlerTarget) > 3) // Fails if priority is less than 1 or greater than 3, if target already moved, or if using a status
gBattlescriptCurrInstr = cmd->failInstr;
else
gBattlescriptCurrInstr = cmd->nextInstr;
}
void BS_TryTriggerStatusForm(void)
{
NATIVE_ARGS();

View file

@ -2223,4 +2223,10 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] =
.battleScript = BattleScript_EffectShedTail,
.battleTvScore = 0, // TODO: Assign points
},
[EFFECT_UPPER_HAND] =
{
.battleScript = BattleScript_EffectUpperHand,
.battleTvScore = 0, // TODO: Assign points
},
};

View file

@ -19952,11 +19952,11 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
[MOVE_UPPER_HAND] =
{
.effect = EFFECT_UPPER_HAND,
.name = COMPOUND_STRING("Upper Hand"),
.description = COMPOUND_STRING(
"Makes the target flinch if\n"
"readying a priority move."),
.effect = EFFECT_PLACEHOLDER, //EFFECT_UPPER_HAND
.power = 65,
.type = TYPE_FIGHTING,
.accuracy = 100,
@ -19965,6 +19965,11 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] =
.priority = 3,
.category = DAMAGE_CATEGORY_PHYSICAL,
.makesContact = TRUE,
.sheerForceBoost = TRUE,
.additionalEffects = ADDITIONAL_EFFECTS({
.moveEffect = MOVE_EFFECT_FLINCH,
.chance = 100,
}),
},
[MOVE_MALIGNANT_CHAIN] =

View file

@ -24,6 +24,8 @@ SINGLE_BATTLE_TEST("Sheer Force boosts power, but removes secondary effects of m
} WHEN {
if (move == MOVE_ALLURING_VOICE || move == MOVE_BURNING_JEALOUSY) // Alluring Voice requires the target to boost stats to have an effect
TURN { MOVE(opponent, MOVE_AGILITY); MOVE(player, move); }
else if (move == MOVE_UPPER_HAND) // Upper Hand requires the target to be using a damaging priority move
TURN { MOVE(opponent, MOVE_QUICK_ATTACK); MOVE(player, move); }
else
TURN { MOVE(player, move); }
if (gMovesInfo[move].effect == EFFECT_TWO_TURNS_ATTACK || gMovesInfo[move].effect == EFFECT_SEMI_INVULNERABLE

View file

@ -0,0 +1,118 @@
#include "global.h"
#include "test/battle.h"
ASSUMPTIONS
{
ASSUME(gMovesInfo[MOVE_UPPER_HAND].effect == EFFECT_UPPER_HAND);
ASSUME(gMovesInfo[MOVE_UPPER_HAND].priority == 3);
ASSUME(MoveHasMoveEffect(MOVE_UPPER_HAND, MOVE_EFFECT_FLINCH) == TRUE);
}
SINGLE_BATTLE_TEST("Upper Hand succeeds if the target is using a priority attacking move and causes it to flinch")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_EXTREME_SPEED].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(gMovesInfo[MOVE_EXTREME_SPEED].priority == 2);
PLAYER(SPECIES_MIENSHAO);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_EXTREME_SPEED); MOVE(player, MOVE_UPPER_HAND); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_UPPER_HAND, player);
HP_BAR(opponent);
MESSAGE("Foe Wobbuffet flinched!");
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_EXTREME_SPEED, opponent);
}
}
SINGLE_BATTLE_TEST("Upper Hand fails if the target is using a status move")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_BABY_DOLL_EYES].category == DAMAGE_CATEGORY_STATUS);
ASSUME(gMovesInfo[MOVE_BABY_DOLL_EYES].priority == 1);
PLAYER(SPECIES_MIENSHAO);
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_BABY_DOLL_EYES); MOVE(player, MOVE_UPPER_HAND); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_UPPER_HAND, player);
MESSAGE("Mienshao used Upper Hand!");
MESSAGE("But it failed!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_BABY_DOLL_EYES, opponent);
ANIMATION(ANIM_TYPE_GENERAL, B_ANIM_STATS_CHANGE, player);
MESSAGE("Mienshao's Attack fell!");
}
}
SINGLE_BATTLE_TEST("Upper Hand fails if the target is not using a priority move")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_DRAINING_KISS].category == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_DRAINING_KISS].priority == 0);
PLAYER(SPECIES_MIENSHAO);
OPPONENT(SPECIES_COMFEY) { Ability(ABILITY_FLOWER_VEIL); }
} WHEN {
TURN { MOVE(opponent, MOVE_DRAINING_KISS); MOVE(player, MOVE_UPPER_HAND); }
} SCENE {
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_UPPER_HAND, player);
MESSAGE("Mienshao used Upper Hand!");
MESSAGE("But it failed!");
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAINING_KISS, opponent);
HP_BAR(player);
HP_BAR(opponent);
}
}
SINGLE_BATTLE_TEST("Upper Hand succeeds if the target's move is boosted in priority by an Ability")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_DRAINING_KISS].category == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_DRAINING_KISS].priority == 0);
PLAYER(SPECIES_MIENSHAO) { Speed(10); }
OPPONENT(SPECIES_COMFEY) { Speed(5); Ability(ABILITY_TRIAGE); }
} WHEN {
TURN { MOVE(opponent, MOVE_DRAINING_KISS); MOVE(player, MOVE_UPPER_HAND); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_UPPER_HAND, player);
HP_BAR(opponent);
MESSAGE("Foe Comfey flinched!");
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAINING_KISS, opponent);
}
}
SINGLE_BATTLE_TEST("Upper Hand fails if the target moves first")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_DRAINING_KISS].category == DAMAGE_CATEGORY_SPECIAL);
ASSUME(gMovesInfo[MOVE_DRAINING_KISS].priority == 0);
PLAYER(SPECIES_MIENSHAO) { Speed(5); }
OPPONENT(SPECIES_COMFEY) { Speed(10); Ability(ABILITY_TRIAGE); }
} WHEN {
TURN { MOVE(opponent, MOVE_DRAINING_KISS); MOVE(player, MOVE_UPPER_HAND); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_DRAINING_KISS, opponent);
HP_BAR(player);
HP_BAR(opponent);
NOT ANIMATION(ANIM_TYPE_MOVE, MOVE_UPPER_HAND, player);
MESSAGE("Mienshao used Upper Hand!");
MESSAGE("But it failed!");
}
}
SINGLE_BATTLE_TEST("Upper Hand is boosted by Sheer Force")
{
GIVEN {
ASSUME(gMovesInfo[MOVE_EXTREME_SPEED].category == DAMAGE_CATEGORY_PHYSICAL);
ASSUME(gMovesInfo[MOVE_EXTREME_SPEED].priority == 2);
ASSUME(gMovesInfo[MOVE_UPPER_HAND].sheerForceBoost == TRUE);
PLAYER(SPECIES_HARIYAMA) { Ability(ABILITY_SHEER_FORCE); }
OPPONENT(SPECIES_WOBBUFFET);
} WHEN {
TURN { MOVE(opponent, MOVE_EXTREME_SPEED); MOVE(player, MOVE_UPPER_HAND); }
} SCENE {
ANIMATION(ANIM_TYPE_MOVE, MOVE_UPPER_HAND, player);
HP_BAR(opponent);
ANIMATION(ANIM_TYPE_MOVE, MOVE_EXTREME_SPEED, opponent);
HP_BAR(player);
}
}