From 405b1d389d882b14644622d492984f672cca0c2e Mon Sep 17 00:00:00 2001 From: Dean <69436131+emdeann@users.noreply.github.com> Date: Fri, 19 Sep 2025 21:08:02 -0700 Subject: [PATCH] [Bug] Apply Supreme Overlord only once on summon (#6575) * Implement supreme overlord as a battler tag * Make ability a conditionalattr * Add tag to map * Update test Co-authored-by: Bertie690 <136088738+Bertie690@users.noreply.github.com> * Add edgeCase marker * Extend from AbilityBattlerTag * Run biome --------- Co-authored-by: Bertie690 <136088738+Bertie690@users.noreply.github.com> --- src/@types/battler-tags.ts | 3 +- src/data/abilities/ability.ts | 4 +-- src/data/battler-tags.ts | 38 +++++++++++++++++++++++++ src/data/moves/move.ts | 3 ++ src/enums/battler-tag-type.ts | 1 + test/abilities/supreme-overlord.test.ts | 38 +++++++++++++++++++++++++ 6 files changed, 84 insertions(+), 3 deletions(-) diff --git a/src/@types/battler-tags.ts b/src/@types/battler-tags.ts index e47b4f8cfc1..ec72c811447 100644 --- a/src/@types/battler-tags.ts +++ b/src/@types/battler-tags.ts @@ -89,7 +89,8 @@ export type AbilityBattlerTagType = | BattlerTagType.QUARK_DRIVE | BattlerTagType.UNBURDEN | BattlerTagType.SLOW_START - | BattlerTagType.TRUANT; + | BattlerTagType.TRUANT + | BattlerTagType.SUPREME_OVERLORD; /** Subset of {@linkcode BattlerTagType}s that provide type boosts */ export type TypeBoostTagType = BattlerTagType.FIRE_BOOST | BattlerTagType.CHARGED; diff --git a/src/data/abilities/ability.ts b/src/data/abilities/ability.ts index af1f7bab7bc..ebe8b816e5e 100644 --- a/src/data/abilities/ability.ts +++ b/src/data/abilities/ability.ts @@ -7742,8 +7742,8 @@ export function initAbilities() { new Ability(AbilityId.SHARPNESS, 9) .attr(MovePowerBoostAbAttr, (_user, _target, move) => move.hasFlag(MoveFlags.SLICING_MOVE), 1.5), new Ability(AbilityId.SUPREME_OVERLORD, 9) - .attr(VariableMovePowerBoostAbAttr, (user, _target, _move) => 1 + 0.1 * Math.min(user.isPlayer() ? globalScene.arena.playerFaints : globalScene.currentBattle.enemyFaints, 5)) - .partial(), // Should only boost once, on summon + .conditionalAttr((p) => (p.isPlayer() ? globalScene.arena.playerFaints : globalScene.currentBattle.enemyFaints) > 0, PostSummonAddBattlerTagAbAttr, BattlerTagType.SUPREME_OVERLORD, 0, true) + .edgeCase(), // Tag is not tied to ability, so suppression/removal etc will not function until a structure to allow this is implemented new Ability(AbilityId.COSTAR, 9, -2) .attr(PostSummonCopyAllyStatsAbAttr), new Ability(AbilityId.TOXIC_DEBRIS, 9) diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index 80a30516903..b6c3cf2b5a6 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -3626,6 +3626,41 @@ export class MagicCoatTag extends BattlerTag { } } +/** + * Tag associated with {@linkcode AbilityId.SUPREME_OVERLORD} + */ +export class SupremeOverlordTag extends AbilityBattlerTag { + public override readonly tagType = BattlerTagType.SUPREME_OVERLORD; + /** The number of faints at the time the user was sent out */ + public readonly faintCount: number; + constructor() { + super(BattlerTagType.SUPREME_OVERLORD, AbilityId.SUPREME_OVERLORD, BattlerTagLapseType.FAINT, 0); + } + + public override onAdd(pokemon: Pokemon): boolean { + (this as Mutable).faintCount = Math.min( + pokemon.isPlayer() ? globalScene.arena.playerFaints : globalScene.currentBattle.enemyFaints, + 5, + ); + globalScene.phaseManager.queueMessage( + i18next.t("battlerTags:supremeOverlordOnAdd", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }), + ); + return true; + } + + /** + * @returns The damage multiplier for Supreme Overlord + */ + public getBoost(): number { + return 1 + 0.1 * this.faintCount; + } + + public override loadTag(source: BaseBattlerTag & Pick): void { + super.loadTag(source); + (this as Mutable).faintCount = source.faintCount; + } +} + /** * Retrieves a {@linkcode BattlerTag} based on the provided tag type, turn count, source move, and source ID. * @param sourceId - The ID of the pokemon adding the tag @@ -3826,6 +3861,8 @@ export function getBattlerTag( return new PsychoShiftTag(); case BattlerTagType.MAGIC_COAT: return new MagicCoatTag(); + case BattlerTagType.SUPREME_OVERLORD: + return new SupremeOverlordTag(); } } @@ -3960,4 +3997,5 @@ export type BattlerTagTypeMap = { [BattlerTagType.GRUDGE]: GrudgeTag; [BattlerTagType.PSYCHO_SHIFT]: PsychoShiftTag; [BattlerTagType.MAGIC_COAT]: MagicCoatTag; + [BattlerTagType.SUPREME_OVERLORD]: SupremeOverlordTag; }; diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 91d61c15b8b..0fdb0d01e43 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -18,6 +18,7 @@ import { ShellTrapTag, StockpilingTag, SubstituteTag, + SupremeOverlordTag, TrappedTag, TypeBoostTag, } from "#data/battler-tags"; @@ -879,6 +880,8 @@ export abstract class Move implements Localizable { power.value *= 1.5; } + power.value *= (source.getTag(BattlerTagType.SUPREME_OVERLORD) as SupremeOverlordTag | undefined)?.getBoost() ?? 1; + return power.value; } diff --git a/src/enums/battler-tag-type.ts b/src/enums/battler-tag-type.ts index 6d9d2dd4a92..7956e506886 100644 --- a/src/enums/battler-tag-type.ts +++ b/src/enums/battler-tag-type.ts @@ -94,4 +94,5 @@ export enum BattlerTagType { ENDURE_TOKEN = "ENDURE_TOKEN", POWDER = "POWDER", MAGIC_COAT = "MAGIC_COAT", + SUPREME_OVERLORD = "SUPREME_OVERLORD", } diff --git a/test/abilities/supreme-overlord.test.ts b/test/abilities/supreme-overlord.test.ts index a0f2d9050b3..d5470b70476 100644 --- a/test/abilities/supreme-overlord.test.ts +++ b/test/abilities/supreme-overlord.test.ts @@ -1,6 +1,7 @@ import { allMoves } from "#data/data-lists"; import { AbilityId } from "#enums/ability-id"; import { BattlerIndex } from "#enums/battler-index"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import type { Move } from "#moves/move"; @@ -166,4 +167,41 @@ describe("Abilities - Supreme Overlord", () => { expect(move.calculateBattlePower).toHaveLastReturnedWith(basePower); }); + + it("should not increase in power if ally faints while on the field", async () => { + game.override.battleStyle("double"); + await game.classicMode.startBattle([SpeciesId.BULBASAUR, SpeciesId.CHARMANDER, SpeciesId.SQUIRTLE]); + + game.move.select(MoveId.TACKLE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); + game.move.select(MoveId.LUNAR_DANCE, BattlerIndex.PLAYER_2); + await game.setTurnOrder([BattlerIndex.PLAYER_2, BattlerIndex.PLAYER, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); + await game.toEndOfTurn(); + + expect(game.field.getPlayerPokemon()).not.toHaveBattlerTag(BattlerTagType.SUPREME_OVERLORD); + expect(move.calculateBattlePower).toHaveLastReturnedWith(basePower); + }); + + it("should persist fainted count through reload", async () => { + // Avoid learning moves + game.override.startingLevel(1000); + + await game.classicMode.startBattle([SpeciesId.BULBASAUR, SpeciesId.CHARMANDER, SpeciesId.SQUIRTLE]); + + game.move.select(MoveId.LUNAR_DANCE); + game.doSelectPartyPokemon(1); + await game.toNextTurn(); + + game.move.select(MoveId.TACKLE); + await game.toEndOfTurn(); + expect(move.calculateBattlePower).toHaveLastReturnedWith(basePower * 1.1); + + await game.toNextWave(); + await game.reload.reloadSession(); + + expect(game.field.getPlayerPokemon()).toHaveBattlerTag({ tagType: BattlerTagType.SUPREME_OVERLORD, faintCount: 1 }); + + game.move.select(MoveId.TACKLE); + await game.toEndOfTurn(); + expect(move.calculateBattlePower).toHaveLastReturnedWith(basePower * 1.1); + }); });