From 64b28124fb33f8c340b4ac312d99486b0a2bc902 Mon Sep 17 00:00:00 2001 From: kittenchilly Date: Sat, 27 Apr 2024 12:35:29 -0500 Subject: [PATCH] Add Tera Starstorm move + make Tera Blast displayed type reflect current type due to tera state (#4447) * Add Tera Starstorm move + make Tera Blast/Tera Starstorm displayed type reflect tera type * Ooops * Curse tests --- include/constants/battle_move_effects.h | 1 + src/battle_controller_player.c | 37 ++++++++++++------------- src/battle_gfx_sfx_util.c | 12 +------- src/battle_main.c | 7 ++++- src/battle_script_commands.c | 3 +- src/battle_util.c | 24 ++++++++-------- src/data/battle_move_effects.h | 6 ++++ src/data/moves_info.h | 6 ++-- src/data/pokemon/form_species_tables.h | 2 +- test/battle/move_effect/curse.c | 36 ++++++++++++++++++++++++ 10 files changed, 86 insertions(+), 48 deletions(-) create mode 100644 test/battle/move_effect/curse.c diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index b01685fbb72e..abb1a9ab0f62 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -351,6 +351,7 @@ enum { EFFECT_LAST_RESPECTS, EFFECT_TIDY_UP, EFFECT_TERA_BLAST, + EFFECT_TERA_STARSTORM, NUM_BATTLE_MOVE_EFFECTS, }; diff --git a/src/battle_controller_player.c b/src/battle_controller_player.c index b86da34d2421..be6a63e00bd9 100644 --- a/src/battle_controller_player.c +++ b/src/battle_controller_player.c @@ -693,17 +693,8 @@ static void HandleInputChooseMove(u32 battler) if (JOY_NEW(A_BUTTON)) { PlaySE(SE_SELECT); - if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_CURSE) - { - if (moveInfo->monType1 != TYPE_GHOST && moveInfo->monType2 != TYPE_GHOST && moveInfo->monType3 != TYPE_GHOST) - moveTarget = MOVE_TARGET_USER; - else - moveTarget = MOVE_TARGET_SELECTED; - } - else - { - moveTarget = GetBattlerMoveTargetType(battler, moveInfo->moves[gMoveSelectionCursor[battler]]); - } + + moveTarget = GetBattlerMoveTargetType(battler, moveInfo->moves[gMoveSelectionCursor[battler]]); if (gBattleStruct->zmove.viewing) { @@ -931,6 +922,7 @@ static void HandleInputChooseMove(u32 battler) { gBattleStruct->tera.playerSelect ^= 1; ChangeTeraTriggerSprite(gBattleStruct->tera.triggerSpriteId, gBattleStruct->tera.playerSelect); + MoveSelectionDisplayMoveType(battler); // For Tera Blast / Tera Starstorm PlaySE(SE_SELECT); } } @@ -1748,25 +1740,32 @@ static void MoveSelectionDisplayMoveType(u32 battler) u8 *txtPtr, *end; u8 type; u32 speciesId; - struct Pokemon *mon; struct ChooseMoveStruct *moveInfo = (struct ChooseMoveStruct *)(&gBattleResources->bufferA[battler][4]); txtPtr = StringCopy(gDisplayedStringBattle, gText_MoveInterfaceType); - if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_IVY_CUDGEL) + type = gMovesInfo[moveInfo->moves[gMoveSelectionCursor[battler]]].type; + + if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_TERA_BLAST) + { + if (gBattleStruct->tera.playerSelect || IsTerastallized(battler)) + type = GetBattlerTeraType(battler); + } + else if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_IVY_CUDGEL) { - mon = &GetSideParty(GetBattlerSide(battler))[gBattlerPartyIndexes[battler]]; - speciesId = GetMonData(mon, MON_DATA_SPECIES); + speciesId = gBattleMons[battler].species; if (speciesId == SPECIES_OGERPON_WELLSPRING_MASK || speciesId == SPECIES_OGERPON_WELLSPRING_MASK_TERA || speciesId == SPECIES_OGERPON_HEARTHFLAME_MASK || speciesId == SPECIES_OGERPON_HEARTHFLAME_MASK_TERA || speciesId == SPECIES_OGERPON_CORNERSTONE_MASK || speciesId == SPECIES_OGERPON_CORNERSTONE_MASK_TERA) type = gBattleMons[battler].type2; - else - type = gMovesInfo[MOVE_IVY_CUDGEL].type; } - else - type = gMovesInfo[moveInfo->moves[gMoveSelectionCursor[battler]]].type; + else if (moveInfo->moves[gMoveSelectionCursor[battler]] == MOVE_TERA_STARSTORM) + { + if (gBattleMons[battler].species == SPECIES_TERAPAGOS_STELLAR + || (gBattleStruct->tera.playerSelect && gBattleMons[battler].species == SPECIES_TERAPAGOS_TERASTAL)) + type = TYPE_STELLAR; + } end = StringCopy(txtPtr, gTypesInfo[type].name); PrependFontIdToFit(txtPtr, end, FONT_NORMAL, WindowWidthPx(B_WIN_MOVE_TYPE) - 25); diff --git a/src/battle_gfx_sfx_util.c b/src/battle_gfx_sfx_util.c index 91861b3b4aaa..8cc5afb424c0 100644 --- a/src/battle_gfx_sfx_util.c +++ b/src/battle_gfx_sfx_util.c @@ -263,17 +263,7 @@ u16 ChooseMoveAndTargetInBattlePalace(u32 battler) } } - if (moveInfo->moves[chosenMoveId] == MOVE_CURSE) - { - if (moveInfo->monType1 != TYPE_GHOST && moveInfo->monType2 != TYPE_GHOST && moveInfo->monType3 != TYPE_GHOST) - moveTarget = MOVE_TARGET_USER; - else - moveTarget = MOVE_TARGET_SELECTED; - } - else - { - moveTarget = GetBattlerMoveTargetType(battler, moveInfo->moves[chosenMoveId]); - } + moveTarget = GetBattlerMoveTargetType(battler, moveInfo->moves[chosenMoveId]); if (moveTarget & MOVE_TARGET_USER) chosenMoveId |= (battler << 8); diff --git a/src/battle_main.c b/src/battle_main.c index 30154ef17268..d7bbb764bf83 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -5341,7 +5341,7 @@ static void PopulateArrayWithBattlers(u8 *battlers) static bool32 TryDoGimmicksBeforeMoves(void) { if (!(gHitMarker & HITMARKER_RUN) - && (gBattleStruct->mega.toEvolve || gBattleStruct->burst.toBurst + && (gBattleStruct->mega.toEvolve || gBattleStruct->burst.toBurst || gBattleStruct->dynamax.toDynamax || gBattleStruct->tera.toTera)) { u32 i, battler; @@ -6072,6 +6072,10 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk) { gBattleStruct->dynamicMoveType = GetBattlerTeraType(battlerAtk) | F_DYNAMIC_TYPE_SET; } + else if (gMovesInfo[move].effect == EFFECT_TERA_STARSTORM && gBattleMons[battlerAtk].species == SPECIES_TERAPAGOS_STELLAR) + { + gBattleStruct->dynamicMoveType = TYPE_STELLAR | F_DYNAMIC_TYPE_SET; + } attackerAbility = GetBattlerAbility(battlerAtk); @@ -6081,6 +6085,7 @@ void SetTypeBeforeUsingMove(u32 move, u32 battlerAtk) && gMovesInfo[move].effect != EFFECT_CHANGE_TYPE_ON_ITEM && gMovesInfo[move].effect != EFFECT_NATURAL_GIFT && !(gMovesInfo[move].effect == EFFECT_TERA_BLAST && IsTerastallized(battlerAtk)) + && !(gMovesInfo[move].effect == EFFECT_TERA_STARSTORM && gBattleMons[battlerAtk].species == SPECIES_TERAPAGOS_STELLAR) && ((attackerAbility == ABILITY_PIXILATE && (ateType = TYPE_FAIRY)) || (attackerAbility == ABILITY_REFRIGERATE && (ateType = TYPE_ICE)) || (attackerAbility == ABILITY_AERILATE && (ateType = TYPE_FLYING)) diff --git a/src/battle_script_commands.c b/src/battle_script_commands.c index ce8a1635050e..bfbb2b3ca09c 100644 --- a/src/battle_script_commands.c +++ b/src/battle_script_commands.c @@ -16735,7 +16735,8 @@ void BS_AllySwitchFailChance(void) void BS_SetPhotonGeyserCategory(void) { NATIVE_ARGS(); - if (!(gMovesInfo[gCurrentMove].effect == EFFECT_TERA_BLAST && !IsTerastallized(gBattlerAttacker))) + if (!(gMovesInfo[gCurrentMove].effect == EFFECT_TERA_BLAST && !IsTerastallized(gBattlerAttacker)) + && !(gMovesInfo[gCurrentMove].effect == EFFECT_TERA_STARSTORM && gBattleMons[gBattlerAttacker].species != SPECIES_TERAPAGOS_STELLAR)) gBattleStruct->swapDamageCategory = (GetCategoryBasedOnStats(gBattlerAttacker) == DAMAGE_CATEGORY_PHYSICAL); gBattlescriptCurrInstr = cmd->nextInstr; } diff --git a/src/battle_util.c b/src/battle_util.c index 6e82195b3472..6d6ce38a7cf8 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -8043,10 +8043,6 @@ u32 GetMoveTarget(u16 move, u8 setTarget) else moveTarget = GetBattlerMoveTargetType(gBattlerAttacker, move); - // Special cases - if (move == MOVE_CURSE && !IS_BATTLER_OF_TYPE(gBattlerAttacker, TYPE_GHOST)) - moveTarget = MOVE_TARGET_USER; - switch (moveTarget) { case MOVE_TARGET_SELECTED: @@ -10104,6 +10100,8 @@ static inline void MulByTypeEffectiveness(uq4_12_t *modifier, u32 move, u32 move mod = UQ_4_12(1.0); if (moveType == TYPE_FIRE && gDisableStructs[battlerDef].tarShot) mod = UQ_4_12(2.0); + if (moveType == TYPE_STELLAR && IsTerastallized(battlerDef)) + mod = UQ_4_12(2.0); // B_WEATHER_STRONG_WINDS weakens Super Effective moves against Flying-type Pokémon if (gBattleWeather & B_WEATHER_STRONG_WINDS && WEATHER_HAS_EFFECT) @@ -10220,16 +10218,12 @@ uq4_12_t CalcTypeEffectivenessMultiplier(u32 move, u32 moveType, u32 battlerAtk, { uq4_12_t modifier = UQ_4_12(1.0); - if (move != MOVE_STRUGGLE && moveType != TYPE_MYSTERY && moveType != TYPE_STELLAR) + if (move != MOVE_STRUGGLE && moveType != TYPE_MYSTERY) { modifier = CalcTypeEffectivenessMultiplierInternal(move, moveType, battlerAtk, battlerDef, recordAbilities, modifier, defAbility); if (gMovesInfo[move].effect == EFFECT_TWO_TYPED_MOVE) modifier = CalcTypeEffectivenessMultiplierInternal(move, gMovesInfo[move].argument, battlerAtk, battlerDef, recordAbilities, modifier, defAbility); } - else if (moveType == TYPE_STELLAR) - { - modifier = IsTerastallized(battlerDef) ? UQ_4_12(2.0) : UQ_4_12(1.0); - } if (recordAbilities) UpdateMoveResultFlags(modifier); @@ -11173,11 +11167,17 @@ bool32 IsBattlerWeatherAffected(u32 battler, u32 weatherFlags) // Possible return values are defined in battle.h following MOVE_TARGET_SELECTED u32 GetBattlerMoveTargetType(u32 battler, u32 move) { - if (gMovesInfo[move].effect == EFFECT_EXPANDING_FORCE + if (move == MOVE_CURSE + && !IS_BATTLER_OF_TYPE(battler, TYPE_GHOST)) + return MOVE_TARGET_USER; + else if (gMovesInfo[move].effect == EFFECT_EXPANDING_FORCE && IsBattlerTerrainAffected(battler, STATUS_FIELD_PSYCHIC_TERRAIN)) return MOVE_TARGET_BOTH; - else - return gMovesInfo[move].target; + else if (gMovesInfo[move].effect == EFFECT_TERA_STARSTORM + && gBattleMons[battler].species == SPECIES_TERAPAGOS_STELLAR) + return MOVE_TARGET_BOTH; + + return gMovesInfo[move].target; } bool32 CanTargetBattler(u32 battlerAtk, u32 battlerDef, u16 move) diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index de8d3f9ec8a0..29acfc4630cf 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -2236,4 +2236,10 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .battleScript = BattleScript_EffectPhotonGeyser, .battleTvScore = 0, // TODO: Assign points }, + + [EFFECT_TERA_STARSTORM] = + { + .battleScript = BattleScript_EffectPhotonGeyser, + .battleTvScore = 0, // TODO: Assign points + }, }; diff --git a/src/data/moves_info.h b/src/data/moves_info.h index ed7b80ce95ca..0899e55a869f 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -19563,12 +19563,12 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "Damages all opponents if user is\n" "Stellar form Terapagos."), - .effect = EFFECT_PLACEHOLDER, //EFFECT_TERA_STARSTORM + .effect = EFFECT_TERA_STARSTORM, .power = 120, - .type = TYPE_NORMAL, // Stellar type if used by Terapagos-Stellar + .type = TYPE_NORMAL, .accuracy = 100, .pp = 5, - .target = MOVE_TARGET_SELECTED, // MOVE_TARGET_BOTH if used by Terapagos-Stellar + .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_SPECIAL, .assistBanned = TRUE, diff --git a/src/data/pokemon/form_species_tables.h b/src/data/pokemon/form_species_tables.h index 9ae2c3ebdb89..af35503fe336 100644 --- a/src/data/pokemon/form_species_tables.h +++ b/src/data/pokemon/form_species_tables.h @@ -2169,7 +2169,7 @@ static const u16 sOgerponFormSpeciesIdTable[] = { static const u16 sTerapagosFormSpeciesIdTable[] = { SPECIES_TERAPAGOS_NORMAL, SPECIES_TERAPAGOS_TERASTAL, -#if P_TERA_FORMS +#if P_TERA_FORMS SPECIES_TERAPAGOS_STELLAR, #endif FORM_SPECIES_END, diff --git a/test/battle/move_effect/curse.c b/test/battle/move_effect/curse.c new file mode 100644 index 000000000000..5fe17d356101 --- /dev/null +++ b/test/battle/move_effect/curse.c @@ -0,0 +1,36 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_CURSE].effect == EFFECT_CURSE); +} + +SINGLE_BATTLE_TEST("Curse lowers Speed, raises Attack, and raises Defense when used by non-Ghost-types") +{ + GIVEN { + PLAYER(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CURSE); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_CURSE, player); + MESSAGE("Wobbuffet's Speed fell!"); + MESSAGE("Wobbuffet's Attack rose!"); + MESSAGE("Wobbuffet's Defense rose!"); + } +} + +SINGLE_BATTLE_TEST("Curse cuts the user's HP in half when used by Ghost-types") +{ + GIVEN { + PLAYER(SPECIES_MISDREAVUS); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_CURSE); } + } SCENE { + s32 maxHP = GetMonData(&PLAYER_PARTY[0], MON_DATA_MAX_HP); + ANIMATION(ANIM_TYPE_MOVE, MOVE_CURSE, player); + HP_BAR(player, hp: maxHP / 2); + } +}