Make type boost items like silk scarf affect the move after its type change

This commit is contained in:
Sirz Benjie 2025-04-15 16:54:44 -05:00
parent 9f09378de4
commit d3b1dab17f
No known key found for this signature in database
GPG Key ID: 4A524B4D196C759E
7 changed files with 72 additions and 20 deletions

View File

@ -9,3 +9,6 @@ export const SESSION_ID_COOKIE_NAME: string = "pokerogue_sessionId";
/** Max value for an integer attribute in {@linkcode SystemSaveData} */
export const MAX_INT_ATTR_VALUE = 0x80000000;
/** The raw percentage power boost for type boost items*/
export const TYPE_BOOST_ITEM_BOOST_PERCENT = 20;

View File

@ -69,6 +69,7 @@ import type { AbAttrCondition, PokemonDefendCondition, PokemonStatStageChangeCon
import type { BattlerIndex } from "#app/battle";
import type Move from "#app/data/moves/move";
import type { ArenaTrapTag, SuppressAbilitiesTag } from "#app/data/arena-tag";
import { noAbilityTypeOverrideMoves } from "../moves/invalid-moves";
export class BlockRecoilDamageAttr extends AbAttr {
constructor() {
@ -1240,12 +1241,40 @@ export class MoveTypeChangeAbAttr extends PreAttackAbAttr {
super(false);
}
override canApplyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon | null, move: Move, args: any[]): boolean {
return (this.condition && this.condition(pokemon, defender, move)) ?? false;
/**
* Determine if the move type change attribute can be applied
*
* Can be applied if:
* - The ability's condition is met, e.g. pixilate only boosts normal moves,
* - The move is not forbidden from having its type changed by an ability, e.g. {@linkcode Moves.MULTI_ATTACK}
* - The user is not terastallized and using tera blast
* - The user is not a terastallized terapagos with tera stellar using tera starstorm
* @param pokemon - The pokemon that has the move type changing ability and is using the attacking move
* @param _passive - Unused
* @param _simulated - Unused
* @param _defender - The pokemon being attacked (unused)
* @param move - The move being used
* @param _args - args[0] holds the type that the move is changed to, args[1] holds the multiplier
* @returns whether the move type change attribute can be applied
*/
override canApplyPreAttack(pokemon: Pokemon, _passive: boolean, _simulated: boolean, _defender: Pokemon | null, move: Move, _args: [NumberHolder?, NumberHolder?, ...any]): boolean {
return (this.condition && this.condition(pokemon, _defender, move) &&
!noAbilityTypeOverrideMoves.has(move.id) &&
(!pokemon.isTerastallized ||
(move.id !== Moves.TERA_BLAST &&
(move.id !== Moves.TERA_STARSTORM || pokemon.getTeraType() !== PokemonType.STELLAR || !pokemon.hasSpecies(Species.TERAPAGOS)))))
?? false;
}
// TODO: Decouple this into two attributes (type change / power boost)
override applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: any[]): void {
/**
* @param pokemon - The pokemon that has the move type changing ability and is using the attacking move
* @param passive - Unused
* @param simulated - Unused
* @param defender - The pokemon being attacked (unused)
* @param move - The move being used
* @param args - args[0] holds the type that the move is changed to, args[1] holds the multiplier
*/
override applyPreAttack(pokemon: Pokemon, passive: boolean, simulated: boolean, defender: Pokemon, move: Move, args: [NumberHolder?, NumberHolder?, ...any]): void {
if (args[0] && args[0] instanceof NumberHolder) {
args[0].value = this.newType;
}
@ -6610,9 +6639,7 @@ export function initAbilities() {
.conditionalAttr(pokemon => pokemon.status ? pokemon.status.effect === StatusEffect.PARALYSIS : false, StatMultiplierAbAttr, Stat.SPD, 2)
.conditionalAttr(pokemon => !!pokemon.status || pokemon.hasAbility(Abilities.COMATOSE), StatMultiplierAbAttr, Stat.SPD, 1.5),
new Ability(Abilities.NORMALIZE, 4)
.attr(MoveTypeChangeAbAttr, PokemonType.NORMAL, 1.2, (user, target, move) => {
return ![ Moves.MULTI_ATTACK, Moves.REVELATION_DANCE, Moves.TERRAIN_PULSE, Moves.HIDDEN_POWER, Moves.WEATHER_BALL, Moves.NATURAL_GIFT, Moves.JUDGMENT, Moves.TECHNO_BLAST ].includes(move.id);
}),
.attr(MoveTypeChangeAbAttr, PokemonType.NORMAL, 1.2),
new Ability(Abilities.SNIPER, 4)
.attr(MultCritAbAttr, 1.5),
new Ability(Abilities.MAGIC_GUARD, 4)

View File

@ -240,3 +240,18 @@ export const invalidMirrorMoveMoves: ReadonlySet<Moves> = new Set([
Moves.WATER_SPORT,
Moves.WIDE_GUARD,
]);
/** Set of moves that can never have their type overridden by an ability like Pixilate or Normalize
*
* Excludes tera blast and tera starstorm, as these are only conditionally forbidden
*/
export const noAbilityTypeOverrideMoves: ReadonlySet<Moves> = new Set([
Moves.WEATHER_BALL,
Moves.JUDGMENT,
Moves.REVELATION_DANCE,
Moves.MULTI_ATTACK,
Moves.TERRAIN_PULSE,
Moves.NATURAL_GIFT,
Moves.TECHNO_BLAST,
Moves.HIDDEN_POWER,
]);

View File

@ -797,8 +797,14 @@ export default class Move implements Localizable {
const power = new NumberHolder(this.power);
const typeChangeMovePowerMultiplier = new NumberHolder(1);
const typeChangeHolder = new NumberHolder(this.type);
applyPreAttackAbAttrs(MoveTypeChangeAbAttr, source, target, this, true, null, typeChangeMovePowerMultiplier);
// apply move type changing ability attributes
applyPreAttackAbAttrs(MoveTypeChangeAbAttr, source, target, this, true, typeChangeHolder, typeChangeMovePowerMultiplier);
if (typeChangeHolder.value !== this.type) {
console.log("==========================================");
console.log("Move type change to " + PokemonType[typeChangeHolder.value]);
}
const sourceTeraType = source.getTeraType();
if (source.isTerastallized && sourceTeraType === this.type && power.value < 60 && this.priority <= 0 && !this.hasAttr(MultiHitAttr) && !globalScene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) {
@ -828,7 +834,7 @@ export default class Move implements Localizable {
power.value *= typeChangeMovePowerMultiplier.value;
const typeBoost = source.findTag(t => t instanceof TypeBoostTag && t.boostedType === this.type) as TypeBoostTag;
const typeBoost = source.findTag(t => t instanceof TypeBoostTag && t.boostedType === typeChangeHolder.value) as TypeBoostTag;
if (typeBoost) {
power.value *= typeBoost.boostValue;
}
@ -836,8 +842,10 @@ export default class Move implements Localizable {
applyMoveAttrs(VariablePowerAttr, source, target, this, power);
if (!this.hasAttr(TypelessAttr)) {
globalScene.arena.applyTags(WeakenMoveTypeTag, simulated, this.type, power);
globalScene.applyModifiers(AttackTypeBoosterModifier, source.isPlayer(), source, this.type, power);
globalScene.arena.applyTags(WeakenMoveTypeTag, simulated, typeChangeHolder.value, power);
console.log("Before applying attack type boosters, power is " + power.value);
globalScene.applyModifiers(AttackTypeBoosterModifier, source.isPlayer(), source, typeChangeHolder.value, power);
console.log("After applying attack type boosters, power is " + power.value);
}
if (source.getTag(HelpingHandTag)) {

View File

@ -2547,22 +2547,19 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
*/
public getMoveType(move: Move, simulated = true): PokemonType {
const moveTypeHolder = new NumberHolder(move.type);
// If the user is terastallized and the move is tera blast, then the move type is the tera type
// Abilities that change the move type go first
applyMoveAttrs(VariableMoveTypeAttr, this, null, move, moveTypeHolder);
applyPreAttackAbAttrs(
MoveTypeChangeAbAttr,
this,
null,
move,
simulated,
moveTypeHolder,
moveTypeHolder
);
// And then moves that change the move type go
applyMoveAttrs(VariableMoveTypeAttr, this, null, move, moveTypeHolder);
// If the user is terastallized and the move is tera blast, or tera starstorm that is stellar type,
// then bypass the check for ion deluge
// then bypass the check for ion deluge and electrify
if (this.isTerastallized && (move.id === Moves.TERA_BLAST || move.id === Moves.TERA_STARSTORM && moveTypeHolder.value === PokemonType.STELLAR)) {
return moveTypeHolder.value as PokemonType;
}

View File

@ -128,6 +128,7 @@ import { getStatKey, Stat, TEMP_BATTLE_STATS } from "#enums/stat";
import { StatusEffect } from "#enums/status-effect";
import i18next from "i18next";
import { timedEventManager } from "#app/global-event-manager";
import { TYPE_BOOST_ITEM_BOOST_PERCENT } from "#app/constants";
const outputModifierData = false;
const useMaxWeightForOutput = false;
@ -1329,7 +1330,7 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator {
constructor() {
super((party: Pokemon[], pregenArgs?: any[]) => {
if (pregenArgs && pregenArgs.length === 1 && pregenArgs[0] in PokemonType) {
return new AttackTypeBoosterModifierType(pregenArgs[0] as PokemonType, 20);
return new AttackTypeBoosterModifierType(pregenArgs[0] as PokemonType, TYPE_BOOST_ITEM_BOOST_PERCENT);
}
const attackMoveTypes = party.flatMap(p =>
@ -1377,7 +1378,7 @@ class AttackTypeBoosterModifierTypeGenerator extends ModifierTypeGenerator {
weight += typeWeight;
}
return new AttackTypeBoosterModifierType(type!, 20);
return new AttackTypeBoosterModifierType(type!, TYPE_BOOST_ITEM_BOOST_PERCENT);
});
}
}

View File

@ -1479,7 +1479,8 @@ export class AttackTypeBoosterModifier extends PokemonHeldItemModifier {
return (
super.shouldApply(pokemon, moveType, movePower) &&
typeof moveType === "number" &&
movePower instanceof NumberHolder
movePower instanceof NumberHolder &&
this.moveType === moveType
);
}