mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-24 16:29:27 +02:00
Implement Fire/Grass Pledge combo
This commit is contained in:
parent
000b8d9b2b
commit
ad4f07f154
@ -19,6 +19,7 @@ import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
|
||||
import { ShowAbilityPhase } from "#app/phases/show-ability-phase";
|
||||
import { StatStageChangePhase } from "#app/phases/stat-stage-change-phase";
|
||||
import { CommonAnimPhase } from "#app/phases/common-anim-phase";
|
||||
|
||||
export enum ArenaTagSide {
|
||||
BOTH,
|
||||
@ -992,6 +993,40 @@ class ImprisonTag extends ArenaTrapTag {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Arena Tag implementing the "sea of fire" effect from the combination
|
||||
* of {@link https://bulbapedia.bulbagarden.net/wiki/Fire_Pledge_(move) | Fire Pledge}
|
||||
* and {@link https://bulbapedia.bulbagarden.net/wiki/Grass_Pledge_(move) | Grass Pledge}.
|
||||
* Damages all non-Fire-type Pokemon on the given side of the field at the end
|
||||
* of each turn for 4 turns.
|
||||
*/
|
||||
class FireGrassPledgeTag extends ArenaTag {
|
||||
constructor(sourceId: number, side: ArenaTagSide) {
|
||||
super(ArenaTagType.FIRE_GRASS_PLEDGE, 4, Moves.FIRE_PLEDGE, sourceId, side);
|
||||
}
|
||||
|
||||
override onAdd(arena: Arena): void {
|
||||
// "A sea of fire enveloped your/the opposing team!"
|
||||
arena.scene.queueMessage(i18next.t(`arenaTag:fireGrassPledgeOnAdd${this.side === ArenaTagSide.PLAYER ? "Player" : this.side === ArenaTagSide.ENEMY ? "Enemy" : ""}`));
|
||||
}
|
||||
|
||||
override lapse(arena: Arena): boolean {
|
||||
const field: Pokemon[] = (this.side === ArenaTagSide.PLAYER)
|
||||
? arena.scene.getPlayerField()
|
||||
: arena.scene.getEnemyField();
|
||||
|
||||
field.filter(pokemon => !pokemon.isOfType(Type.FIRE)).forEach(pokemon => {
|
||||
// "{pokemonNameWithAffix} was hurt by the sea of fire!"
|
||||
pokemon.scene.queueMessage(i18next.t("arenaTag:fireGrassPledgeLapse", { pokemonNameWithAffix: getPokemonNameWithAffix(pokemon) }));
|
||||
// TODO: Replace this with a proper animation
|
||||
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, pokemon.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.MAGMA_STORM));
|
||||
pokemon.damageAndUpdate(Utils.toDmgValue(pokemon.getMaxHp() / 8));
|
||||
});
|
||||
|
||||
return super.lapse(arena);
|
||||
}
|
||||
}
|
||||
|
||||
export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMove: Moves | undefined, sourceId: integer, targetIndex?: BattlerIndex, side: ArenaTagSide = ArenaTagSide.BOTH): ArenaTag | null {
|
||||
switch (tagType) {
|
||||
case ArenaTagType.MIST:
|
||||
@ -1041,6 +1076,8 @@ export function getArenaTag(tagType: ArenaTagType, turnCount: integer, sourceMov
|
||||
return new SafeguardTag(turnCount, sourceId, side);
|
||||
case ArenaTagType.IMPRISON:
|
||||
return new ImprisonTag(sourceId, side);
|
||||
case ArenaTagType.FIRE_GRASS_PLEDGE:
|
||||
return new FireGrassPledgeTag(sourceId, side);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
|
169
src/data/move.ts
169
src/data/move.ts
@ -2685,6 +2685,55 @@ export class DelayedAttackAttr extends OverrideMoveEffectAttr {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attribute that cancels the associated move's effects when set to be combined with the user's ally's
|
||||
* subsequent move this turn. Used for Grass Pledge, Water Pledge, and Fire Pledge.
|
||||
* @extends OverrideMoveEffectAttr
|
||||
*/
|
||||
export class AwaitCombinedPledgeAttr extends OverrideMoveEffectAttr {
|
||||
constructor() {
|
||||
super(true);
|
||||
}
|
||||
/**
|
||||
* If the user's ally is set to use a different move with this attribute,
|
||||
* defer this move's effects for a combined move on the ally's turn.
|
||||
* @param user the {@linkcode Pokemon} using this move
|
||||
* @param target n/a
|
||||
* @param move the {@linkcode Move} being used
|
||||
* @param args
|
||||
* - [0] a {@linkcode Utils.BooleanHolder} indicating whether the move's base
|
||||
* effects should be overridden this turn.
|
||||
* @returns `true` if base move effects were overridden; `false` otherwise
|
||||
*/
|
||||
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
if (user.turnData.combiningPledge) {
|
||||
// "The two moves have become one!\nIt's a combined move!"
|
||||
user.scene.queueMessage(i18next.t("moveTriggers:combiningPledge"));
|
||||
return false;
|
||||
}
|
||||
|
||||
const overridden = args[0] as Utils.BooleanHolder;
|
||||
|
||||
const allyMovePhase = user.scene.findPhase<MovePhase>(phase => phase instanceof MovePhase && phase.pokemon.isPlayer() === user.isPlayer());
|
||||
if (allyMovePhase) {
|
||||
const allyMove = allyMovePhase.move.getMove();
|
||||
if (allyMove !== move && allyMove.hasAttr(AwaitCombinedPledgeAttr)) {
|
||||
[user, user.getAlly()].forEach(p => p.turnData.combiningPledge = move.id);
|
||||
|
||||
// "{userPokemonName} is waiting for {allyPokemonName}'s move..."
|
||||
user.scene.queueMessage(i18next.t("moveTriggers:awaitingPledge", {
|
||||
userPokemonName: getPokemonNameWithAffix(user),
|
||||
allyPokemonName: getPokemonNameWithAffix(user.getAlly())
|
||||
}));
|
||||
|
||||
overridden.value = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class StatStageChangeAttr extends MoveEffectAttr {
|
||||
public stats: BattleStat[];
|
||||
public stages: integer;
|
||||
@ -3738,6 +3787,39 @@ export class LastMoveDoublePowerAttr extends VariablePowerAttr {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes a Pledge move's power to 150 when combined with another unique Pledge
|
||||
* move from an ally.
|
||||
*/
|
||||
export class CombinedPledgePowerAttr extends VariablePowerAttr {
|
||||
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
const power = args[0] as Utils.NumberHolder;
|
||||
const combinedPledgeMove = user.turnData.combiningPledge;
|
||||
|
||||
if (combinedPledgeMove && combinedPledgeMove !== move.id) {
|
||||
power.value *= 150/80;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies STAB to the given Pledge move if the move is part of a combined attack.
|
||||
*/
|
||||
export class CombinedPledgeStabBoostAttr extends MoveAttr {
|
||||
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
const stabMultiplier = args[0] as Utils.NumberHolder;
|
||||
const combinedPledgeMove = user.turnData.combiningPledge;
|
||||
|
||||
if (combinedPledgeMove && combinedPledgeMove !== move.id) {
|
||||
stabMultiplier.value = 1.5;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export class VariableAtkAttr extends MoveAttr {
|
||||
constructor() {
|
||||
super();
|
||||
@ -4303,6 +4385,47 @@ export class MatchUserTypeAttr extends VariableMoveTypeAttr {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the type of a Pledge move based on the Pledge move combined with it.
|
||||
* @extends VariableMoveTypeAttr
|
||||
*/
|
||||
export class CombinedPledgeTypeAttr extends VariableMoveTypeAttr {
|
||||
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
const moveType = args[0];
|
||||
if (!(moveType instanceof Utils.NumberHolder)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const combinedPledgeMove = user.turnData.combiningPledge;
|
||||
if (!combinedPledgeMove) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (move.id) {
|
||||
case Moves.FIRE_PLEDGE:
|
||||
if (combinedPledgeMove === Moves.WATER_PLEDGE) {
|
||||
moveType.value = Type.WATER;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case Moves.WATER_PLEDGE:
|
||||
if (combinedPledgeMove === Moves.GRASS_PLEDGE) {
|
||||
moveType.value = Type.GRASS;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
case Moves.GRASS_PLEDGE:
|
||||
if (combinedPledgeMove === Moves.FIRE_PLEDGE) {
|
||||
moveType.value = Type.FIRE;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class VariableMoveTypeMultiplierAttr extends MoveAttr {
|
||||
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
return false;
|
||||
@ -5139,6 +5262,32 @@ export class SwapArenaTagsAttr extends MoveEffectAttr {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attribute that adds a secondary effect to the field when two unique Pledge moves
|
||||
* are combined. The effect added varies based on the two Pledge moves combined.
|
||||
*/
|
||||
export class AddPledgeEffectAttr extends AddArenaTagAttr {
|
||||
private readonly requiredPledge: Moves;
|
||||
|
||||
constructor(tagType: ArenaTagType, requiredPledge: Moves, selfSideTarget: boolean = false) {
|
||||
super(tagType, 4, false, selfSideTarget);
|
||||
|
||||
this.requiredPledge = requiredPledge;
|
||||
}
|
||||
|
||||
override apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
|
||||
// TODO: add support for `HIT` effect triggering in AddArenaTagAttr to remove the need for this check
|
||||
if (user.getLastXMoves(1)[0].result !== MoveResult.SUCCESS) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (user.turnData.combiningPledge === this.requiredPledge) {
|
||||
return super.apply(user, target, move, args);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Attribute used for Revival Blessing.
|
||||
* @extends MoveEffectAttr
|
||||
@ -8275,11 +8424,25 @@ export function initMoves() {
|
||||
new AttackMove(Moves.INFERNO, Type.FIRE, MoveCategory.SPECIAL, 100, 50, 5, 100, 0, 5)
|
||||
.attr(StatusEffectAttr, StatusEffect.BURN),
|
||||
new AttackMove(Moves.WATER_PLEDGE, Type.WATER, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 5)
|
||||
.partial(),
|
||||
.attr(AwaitCombinedPledgeAttr)
|
||||
.attr(CombinedPledgeTypeAttr)
|
||||
.attr(CombinedPledgePowerAttr)
|
||||
.attr(CombinedPledgeStabBoostAttr)
|
||||
.attr(BypassRedirectAttr), // technically incorrect, should only bypass Storm Drain/Lightning Rod
|
||||
new AttackMove(Moves.FIRE_PLEDGE, Type.FIRE, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 5)
|
||||
.partial(),
|
||||
.attr(AwaitCombinedPledgeAttr)
|
||||
.attr(CombinedPledgeTypeAttr)
|
||||
.attr(CombinedPledgePowerAttr)
|
||||
.attr(CombinedPledgeStabBoostAttr)
|
||||
.attr(AddPledgeEffectAttr, ArenaTagType.FIRE_GRASS_PLEDGE, Moves.GRASS_PLEDGE, false)
|
||||
.attr(BypassRedirectAttr), // technically incorrect, should only bypass Storm Drain/Lightning Rod
|
||||
new AttackMove(Moves.GRASS_PLEDGE, Type.GRASS, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 5)
|
||||
.partial(),
|
||||
.attr(AwaitCombinedPledgeAttr)
|
||||
.attr(CombinedPledgeTypeAttr)
|
||||
.attr(CombinedPledgePowerAttr)
|
||||
.attr(CombinedPledgeStabBoostAttr)
|
||||
.attr(AddPledgeEffectAttr, ArenaTagType.FIRE_GRASS_PLEDGE, Moves.FIRE_PLEDGE, false)
|
||||
.attr(BypassRedirectAttr), // technically incorrect, should only bypass Storm Drain/Lightning Rod
|
||||
new AttackMove(Moves.VOLT_SWITCH, Type.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 20, -1, 0, 5)
|
||||
.attr(ForceSwitchOutAttr, true),
|
||||
new AttackMove(Moves.STRUGGLE_BUG, Type.BUG, MoveCategory.SPECIAL, 50, 100, 20, 100, 0, 5)
|
||||
|
@ -25,4 +25,5 @@ export enum ArenaTagType {
|
||||
SAFEGUARD = "SAFEGUARD",
|
||||
NO_CRIT = "NO_CRIT",
|
||||
IMPRISON = "IMPRISON",
|
||||
FIRE_GRASS_PLEDGE = "FIRE_GRASS_PLEDGE",
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import BattleScene, { AnySound } from "../battle-scene";
|
||||
import { Variant, VariantSet, variantColorCache } from "#app/data/variant";
|
||||
import { variantData } from "#app/data/variant";
|
||||
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from "../ui/battle-info";
|
||||
import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, VariableMoveTypeAttr, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatStagesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatStageChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, OneHitKOAccuracyAttr, RespectAttackTypeImmunityAttr, MoveTarget } from "../data/move";
|
||||
import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, VariableMoveTypeAttr, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatStagesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatStageChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, OneHitKOAccuracyAttr, RespectAttackTypeImmunityAttr, MoveTarget, CombinedPledgeStabBoostAttr } from "../data/move";
|
||||
import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species";
|
||||
import { Constructor, isNullOrUndefined, randSeedInt } from "#app/utils";
|
||||
import * as Utils from "../utils";
|
||||
@ -2540,6 +2540,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||
if (matchesSourceType) {
|
||||
stabMultiplier.value += 0.5;
|
||||
}
|
||||
applyMoveAttrs(CombinedPledgeStabBoostAttr, source, this, move, stabMultiplier);
|
||||
if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === moveType) {
|
||||
stabMultiplier.value += 0.5;
|
||||
}
|
||||
@ -5011,6 +5012,7 @@ export class PokemonTurnData {
|
||||
public order: number;
|
||||
public statStagesIncreased: boolean = false;
|
||||
public statStagesDecreased: boolean = false;
|
||||
public combiningPledge?: Moves;
|
||||
}
|
||||
|
||||
export enum AiType {
|
||||
|
@ -53,5 +53,8 @@
|
||||
"safeguardOnAddEnemy": "The opposing team cloaked itself in a mystical veil!",
|
||||
"safeguardOnRemove": "The field is no longer protected by Safeguard!",
|
||||
"safeguardOnRemovePlayer": "Your team is no longer protected by Safeguard!",
|
||||
"safeguardOnRemoveEnemy": "The opposing team is no longer protected by Safeguard!"
|
||||
"safeguardOnRemoveEnemy": "The opposing team is no longer protected by Safeguard!",
|
||||
"fireGrassPledgeOnAddPlayer": "A sea of fire enveloped your team!",
|
||||
"fireGrassPledgeOnAddEnemy": "A sea of fire enveloped the opposing team!",
|
||||
"fireGrassPledgeLapse": "{{pokemonNameWithAffix}} was hurt by the sea of fire!"
|
||||
}
|
@ -71,5 +71,7 @@
|
||||
"safeguard": "{{targetName}} is protected by Safeguard!",
|
||||
"substituteOnOverlap": "{{pokemonName}} already\nhas a substitute!",
|
||||
"substituteNotEnoughHp": "But it does not have enough HP\nleft to make a substitute!",
|
||||
"afterYou": "{{pokemonName}} took the kind offer!"
|
||||
"afterYou": "{{pokemonName}} took the kind offer!",
|
||||
"combiningPledge": "The two moves have become one!\nIt's a combined move!",
|
||||
"awaitingPledge": "{{userPokemonName}} is waiting for {{allyPokemonName}}'s move..."
|
||||
}
|
Loading…
Reference in New Issue
Block a user