diff --git a/include/battle.h b/include/battle.h index 1fadf6ec57..6dfb2aeb8c 100644 --- a/include/battle.h +++ b/include/battle.h @@ -748,7 +748,6 @@ struct BattleStruct u8 storedHealingWish:4; // Each battler as a bit. u8 storedLunarDance:4; // Each battler as a bit. u8 bonusCritStages[MAX_BATTLERS_COUNT]; // G-Max Chi Strike boosts crit stages of allies. - uq4_12_t supremeOverlordModifier[MAX_BATTLERS_COUNT]; u8 itemPartyIndex[MAX_BATTLERS_COUNT]; u8 itemMoveIndex[MAX_BATTLERS_COUNT]; u8 trainerSlideFirstCriticalHitMsgState:2; @@ -769,6 +768,7 @@ struct BattleStruct u8 transformZeroToHero[NUM_BATTLE_SIDES]; u8 stickySyrupdBy[MAX_BATTLERS_COUNT]; u8 abilityActivated[NUM_BATTLE_SIDES]; + u8 supremeOverlordCounter[MAX_BATTLERS_COUNT]; }; // The palaceFlags member of struct BattleStruct contains 1 flag per move to indicate which moves the AI should consider, diff --git a/include/constants/battle_move_effects.h b/include/constants/battle_move_effects.h index 7390f3e05b..5db532c135 100644 --- a/include/constants/battle_move_effects.h +++ b/include/constants/battle_move_effects.h @@ -349,6 +349,7 @@ enum { EFFECT_SHED_TAIL, EFFECT_UPPER_HAND, EFFECT_DRAGON_CHEER, + EFFECT_LAST_RESPECTS, NUM_BATTLE_MOVE_EFFECTS, }; diff --git a/src/battle_main.c b/src/battle_main.c index 2625481a83..f73990fbfd 100644 --- a/src/battle_main.c +++ b/src/battle_main.c @@ -3234,6 +3234,7 @@ const u8* FaintClearSetData(u32 battler) { s32 i; const u8 *result = NULL; + u8 battlerSide = GetBattlerSide(battler); for (i = 0; i < NUM_BATTLE_STATS; i++) gBattleMons[battler].statStages[i] = DEFAULT_STAT_STAGE; @@ -3320,7 +3321,7 @@ const u8* FaintClearSetData(u32 battler) for (i = 0; i < gBattlersCount; i++) { - if (i != battler && GetBattlerSide(i) != GetBattlerSide(battler)) + if (i != battler && GetBattlerSide(i) != battlerSide) gBattleStruct->lastTakenMove[i] = MOVE_NONE; gBattleStruct->lastTakenMoveFrom[i][battler] = 0; diff --git a/src/battle_util.c b/src/battle_util.c index 821ce5948f..1411b0b452 100644 --- a/src/battle_util.c +++ b/src/battle_util.c @@ -60,7 +60,6 @@ static bool32 IsUnnerveAbilityOnOpposingSide(u32 battler); static u32 GetFlingPowerFromItemId(u32 itemId); static void SetRandomMultiHitCounter(); static u32 GetBattlerItemHoldEffectParam(u32 battler, u32 item); -static uq4_12_t GetSupremeOverlordModifier(u32 battler); static bool32 CanBeInfinitelyConfused(u32 battler); extern const u8 *const gBattlescriptsForRunningByItem[]; @@ -3946,25 +3945,20 @@ bool32 ChangeTypeBasedOnTerrain(u32 battler) return TRUE; } -// Supreme Overlord adds a damage boost for each fainted ally. -// The first ally adds a x1.2 boost, and subsequent allies add an extra x0.1 boost each. -static uq4_12_t GetSupremeOverlordModifier(u32 battler) +static inline u8 GetSideFaintCounter(u32 side) { - u32 i; - struct Pokemon *party = GetBattlerParty(battler); - uq4_12_t modifier = UQ_4_12(1.0); - bool32 appliedFirstBoost = FALSE; + return (side == B_SIDE_PLAYER) ? gBattleResults.playerFaintCounter : gBattleResults.opponentFaintCounter; +} - for (i = 0; i < PARTY_SIZE; i++) - { - if (GetMonData(&party[i], MON_DATA_SPECIES) != SPECIES_NONE - && !GetMonData(&party[i], MON_DATA_IS_EGG) - && GetMonData(&party[i], MON_DATA_HP) == 0) - modifier += (!appliedFirstBoost) ? UQ_4_12(0.2) : UQ_4_12(0.1); - appliedFirstBoost = TRUE; - } +static inline u8 GetBattlerSideFaintCounter(u32 battler) +{ + return GetSideFaintCounter(GetBattlerSide(battler)); +} - return modifier; +// Supreme Overlord adds a x0.1 damage boost for each fainted ally. +static inline uq4_12_t GetSupremeOverlordModifier(u32 battler) +{ + return UQ_4_12(1.0) + (UQ_4_12(0.1) * gBattleStruct->supremeOverlordCounter[battler]); } static inline bool32 HadMoreThanHalfHpNowHasLess(u32 battler) @@ -4599,12 +4593,15 @@ u32 AbilityBattleEffects(u32 caseID, u32 battler, u32 ability, u32 special, u32 } break; case ABILITY_SUPREME_OVERLORD: - if (!gSpecialStatuses[battler].switchInAbilityDone && CountUsablePartyMons(battler) < PARTY_SIZE) + if (!gSpecialStatuses[battler].switchInAbilityDone) { gSpecialStatuses[battler].switchInAbilityDone = TRUE; - gBattleStruct->supremeOverlordModifier[battler] = GetSupremeOverlordModifier(battler); - BattleScriptPushCursorAndCallback(BattleScript_SupremeOverlordActivates); - effect++; + gBattleStruct->supremeOverlordCounter[battler] = min(5, GetBattlerSideFaintCounter(battler)); + if (gBattleStruct->supremeOverlordCounter[battler] > 0) + { + BattleScriptPushCursorAndCallback(BattleScript_SupremeOverlordActivates); + effect++; + } } break; case ABILITY_COSTAR: @@ -8662,6 +8659,9 @@ static inline u32 CalcMoveBasePower(u32 move, u32 battlerAtk, u32 battlerDef, u3 if (RandomPercentage(RNG_FICKLE_BEAM, 30)) basePower *= 2; break; + case EFFECT_LAST_RESPECTS: + basePower += (basePower * min(100, GetBattlerSideFaintCounter(battlerAtk))); + break; } // Move-specific base power changes @@ -8888,7 +8888,7 @@ static inline u32 CalcMoveBasePowerAfterModifiers(u32 move, u32 battlerAtk, u32 modifier = uq4_12_multiply(modifier, UQ_4_12(1.5)); break; case ABILITY_SUPREME_OVERLORD: - modifier = uq4_12_multiply(modifier, gBattleStruct->supremeOverlordModifier[battlerAtk]); + modifier = uq4_12_multiply(modifier, GetSupremeOverlordModifier(battlerAtk)); break; } diff --git a/src/data/battle_move_effects.h b/src/data/battle_move_effects.h index 5a11be96cc..ef61bb8239 100644 --- a/src/data/battle_move_effects.h +++ b/src/data/battle_move_effects.h @@ -2214,4 +2214,10 @@ const struct BattleMoveEffect gBattleMoveEffects[NUM_BATTLE_MOVE_EFFECTS] = .battleTvScore = 1, .encourageEncore = TRUE, }, + + [EFFECT_LAST_RESPECTS] = + { + .battleScript = BattleScript_EffectHit, + .battleTvScore = 0, // TODO: Assign points + }, }; diff --git a/src/data/moves_info.h b/src/data/moves_info.h index 5fc35dba83..88873e4f28 100644 --- a/src/data/moves_info.h +++ b/src/data/moves_info.h @@ -18634,7 +18634,6 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_SPECIAL, - .metronomeBanned = TRUE, // Only since it isn't implemented yet .forcePressure = TRUE, }, @@ -18686,7 +18685,7 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .description = COMPOUND_STRING( "This move deals more damage\n" "for each defeated ally."), - .effect = EFFECT_PLACEHOLDER, // EFFECT_LAST_RESPECTS + .effect = EFFECT_LAST_RESPECTS, .power = 50, .type = TYPE_GHOST, .accuracy = 100, @@ -18694,7 +18693,6 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .target = MOVE_TARGET_SELECTED, .priority = 0, .category = DAMAGE_CATEGORY_PHYSICAL, - .metronomeBanned = TRUE, // Only since it isn't implemented yet }, [MOVE_LUMINA_CRASH] = @@ -18851,7 +18849,6 @@ const struct MoveInfo gMovesInfo[MOVES_COUNT_DYNAMAX] = .priority = 0, .category = DAMAGE_CATEGORY_PHYSICAL, .makesContact = TRUE, - .metronomeBanned = TRUE, // Only since it isn't implemented yet }, [MOVE_REVIVAL_BLESSING] = diff --git a/test/battle/ability/supreme_overlord.c b/test/battle/ability/supreme_overlord.c new file mode 100644 index 0000000000..a01a5d6306 --- /dev/null +++ b/test/battle/ability/supreme_overlord.c @@ -0,0 +1,87 @@ +#include "global.h" +#include "test/battle.h" + +DOUBLE_BATTLE_TEST("Supreme Overlord boosts Attack by an additive 10% per fainted mon on the side", s16 damage) +{ + bool32 switchMon = 0; + PARAMETRIZE { switchMon = FALSE; } + PARAMETRIZE { switchMon = TRUE; } + GIVEN { + PLAYER(SPECIES_KINGAMBIT) { Ability(ABILITY_SUPREME_OVERLORD); } + PLAYER(SPECIES_PAWNIARD); + PLAYER(SPECIES_PAWNIARD); + PLAYER(SPECIES_PAWNIARD); + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + if (switchMon) + TURN { SWITCH(playerLeft, 3); } + TURN { MOVE(playerRight, MOVE_MEMENTO, target: opponentRight); SEND_OUT(playerRight, 2); } + if (switchMon) + TURN { SWITCH(playerLeft, 0); } + TURN { MOVE(playerLeft, MOVE_TACKLE, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, playerLeft); + HP_BAR(opponentLeft, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.1), results[1].damage); + } +} + +DOUBLE_BATTLE_TEST("Supreme Overlord's boost caps at a 1.5x multipler", s16 damage) +{ + u32 faintCount = 0; + PARAMETRIZE { faintCount = 5; } + PARAMETRIZE { faintCount = 6; } + GIVEN { + PLAYER(SPECIES_PAWNIARD); + PLAYER(SPECIES_PAWNIARD); + PLAYER(SPECIES_PAWNIARD); + PLAYER(SPECIES_KINGAMBIT) { Ability(ABILITY_SUPREME_OVERLORD); } + OPPONENT(SPECIES_WOBBUFFET); + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(playerLeft, MOVE_MEMENTO, target: opponentRight); SEND_OUT(playerLeft, 2); } + TURN { MOVE(playerLeft, MOVE_MEMENTO, target: opponentRight); SEND_OUT(playerLeft, 0); USE_ITEM(playerRight, ITEM_REVIVE, 0); } + TURN { MOVE(playerLeft, MOVE_MEMENTO, target: opponentRight); SEND_OUT(playerLeft, 2); USE_ITEM(playerRight, ITEM_REVIVE, 2); } + TURN { MOVE(playerLeft, MOVE_MEMENTO, target: opponentRight); SEND_OUT(playerLeft, 0); USE_ITEM(playerRight, ITEM_REVIVE, 0); } + TURN { MOVE(playerLeft, MOVE_MEMENTO, target: opponentRight); SEND_OUT(playerLeft, 2); USE_ITEM(playerRight, ITEM_REVIVE, 2); } + if (faintCount == 6) + TURN { MOVE(playerLeft, MOVE_MEMENTO, target: opponentRight); SEND_OUT(playerLeft, 0); USE_ITEM(playerRight, ITEM_REVIVE, 0); } + TURN { SWITCH(playerRight, 3); } + TURN { MOVE(playerRight, MOVE_TACKLE, target: opponentLeft); } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, playerRight); + HP_BAR(opponentLeft, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + } +} + +SINGLE_BATTLE_TEST("Supreme Overlord does not boost attack if party members are already fainted at the start of the battle", s16 damage) +{ + u32 fainted = 0; + + PARAMETRIZE { fainted = FALSE; } + PARAMETRIZE { fainted = TRUE; } + GIVEN { + PLAYER(SPECIES_KINGAMBIT) { Ability(ABILITY_SUPREME_OVERLORD); } + PLAYER(SPECIES_PAWNIARD) { HP(fainted ? 0 : 1); } + PLAYER(SPECIES_PAWNIARD) { HP(fainted ? 0 : 1); } + PLAYER(SPECIES_PAWNIARD) { HP(fainted ? 0 : 1); } + PLAYER(SPECIES_PAWNIARD) { HP(fainted ? 0 : 1); } + PLAYER(SPECIES_PAWNIARD) { HP(fainted ? 0 : 1); } + OPPONENT(SPECIES_WOBBUFFET); + } WHEN { + TURN { MOVE(player, MOVE_TACKLE, target: opponent); } + } SCENE { + NONE_OF { + ABILITY_POPUP(player, ABILITY_SUPREME_OVERLORD); + MESSAGE("Kingambit gained strength from the fallen!"); + } + ANIMATION(ANIM_TYPE_MOVE, MOVE_TACKLE, player); + HP_BAR(opponent, captureDamage: &results[i].damage); + } FINALLY { + EXPECT_EQ(results[0].damage, results[1].damage); + } +} diff --git a/test/battle/move_effect/last_respects.c b/test/battle/move_effect/last_respects.c new file mode 100644 index 0000000000..9b1f01f5fc --- /dev/null +++ b/test/battle/move_effect/last_respects.c @@ -0,0 +1,67 @@ +#include "global.h" +#include "test/battle.h" + +ASSUMPTIONS +{ + ASSUME(gMovesInfo[MOVE_LAST_RESPECTS].effect == EFFECT_LAST_RESPECTS); +} + +SINGLE_BATTLE_TEST("Last Respects power is multiplied by the amount of fainted mon in the user's side - Player", s16 damage) +{ + u32 j = 0, faintCount = 0; + PARAMETRIZE { faintCount = 0; } + PARAMETRIZE { faintCount = 1; } + PARAMETRIZE { faintCount = 2; } + GIVEN { + PLAYER(SPECIES_GOLEM); // Not Wobbuffet to omit type effectiveness + PLAYER(SPECIES_GEODUDE); + OPPONENT(SPECIES_WOBBUFFET) { Item(ITEM_LEPPA_BERRY); Moves(MOVE_RECYCLE, MOVE_NONE, MOVE_NONE, MOVE_NONE); } + } WHEN { + for (j = 0; j < faintCount; j++) + { + TURN { MOVE(opponent, MOVE_RECYCLE); SWITCH(player, 1); } + TURN { MOVE(opponent, MOVE_RECYCLE); MOVE(player, MOVE_MEMENTO); SEND_OUT(player, 0); } + TURN { MOVE(opponent, MOVE_RECYCLE); USE_ITEM(player, ITEM_REVIVE, partyIndex: 1); } + } + TURN { + MOVE(opponent, MOVE_RECYCLE); + MOVE(player, MOVE_LAST_RESPECTS); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_LAST_RESPECTS, player); + HP_BAR(opponent, captureDamage: &results[j].damage); + } THEN { + if (faintCount > 0) + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.0 + faintCount), results[faintCount].damage); + } +} + +SINGLE_BATTLE_TEST("Last Respects power is multiplied by the amount of fainted mon in the user's side - Opponent", s16 damage) +{ + u32 j = 0, faintCount = 0; + PARAMETRIZE { faintCount = 0; } + PARAMETRIZE { faintCount = 1; } + PARAMETRIZE { faintCount = 2; } + GIVEN { + PLAYER(SPECIES_WOBBUFFET) { Item(ITEM_LEPPA_BERRY); Moves(MOVE_RECYCLE, MOVE_NONE, MOVE_NONE, MOVE_NONE); } + OPPONENT(SPECIES_GOLEM); // Not Wobbuffet to omit type effectiveness + OPPONENT(SPECIES_GEODUDE); + } WHEN { + for (j = 0; j < faintCount; j++) + { + TURN { MOVE(player, MOVE_RECYCLE); SWITCH(opponent, 1); } + TURN { MOVE(player, MOVE_RECYCLE); MOVE(opponent, MOVE_MEMENTO); SEND_OUT(opponent, 0); } + TURN { MOVE(player, MOVE_RECYCLE); USE_ITEM(opponent, ITEM_REVIVE, partyIndex: 1); } + } + TURN { + MOVE(player, MOVE_RECYCLE); + MOVE(opponent, MOVE_LAST_RESPECTS); + } + } SCENE { + ANIMATION(ANIM_TYPE_MOVE, MOVE_LAST_RESPECTS, opponent); + HP_BAR(player, captureDamage: &results[j].damage); + } THEN { + if (faintCount > 0) + EXPECT_MUL_EQ(results[0].damage, Q_4_12(1.0 + faintCount), results[faintCount].damage); + } +}