Add type change abilities

pull/58/head
musicman928 2024-04-07 22:27:07 -07:00
parent a0e2427974
commit b7368afa9e
2 changed files with 71 additions and 26 deletions

View File

@ -583,6 +583,37 @@ export class VariableMovePowerAbAttr extends PreAttackAbAttr {
} }
} }
export class VariableMoveTypeAbAttr extends AbAttr {
apply(pokemon: Pokemon, cancelled: Utils.BooleanHolder, args: any[]): boolean {
//const power = args[0] as Utils.IntegerHolder;
return false;
}
}
export class MoveTypeChangePowerMultiplierAbAttr extends VariableMoveTypeAbAttr {
private matchType: Type;
private newType: Type;
private powerMultiplier: number;
constructor(matchType: Type, newType: Type, powerMultiplier: number){
super(true);
this.matchType = matchType;
this.newType = newType;
this.powerMultiplier = powerMultiplier;
}
apply(pokemon: Pokemon, cancelled: Utils.BooleanHolder, args: any[]): boolean {
const type = (args[0] as Utils.IntegerHolder);
if (type.value == this.matchType) {
type.value = this.newType;
(args[1] as Utils.NumberHolder).value *= this.powerMultiplier;
return true;
}
return false;
}
}
export class MovePowerBoostAbAttr extends VariableMovePowerAbAttr { export class MovePowerBoostAbAttr extends VariableMovePowerAbAttr {
private condition: PokemonAttackCondition; private condition: PokemonAttackCondition;
private powerMultiplier: number; private powerMultiplier: number;
@ -2517,7 +2548,8 @@ export function initAbilities() {
new Ability(Abilities.COMPETITIVE, "Competitive (N)", "Boosts the Sp. Atk stat sharply when a stat is lowered.", 6), new Ability(Abilities.COMPETITIVE, "Competitive (N)", "Boosts the Sp. Atk stat sharply when a stat is lowered.", 6),
new Ability(Abilities.STRONG_JAW, "Strong Jaw", "The Pokémon's strong jaw boosts the power of its biting moves.", 6) new Ability(Abilities.STRONG_JAW, "Strong Jaw", "The Pokémon's strong jaw boosts the power of its biting moves.", 6)
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.BITING_MOVE), 1.5), .attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.BITING_MOVE), 1.5),
new Ability(Abilities.REFRIGERATE, "Refrigerate (N)", "Normal-type moves become Ice-type moves. The power of those moves is boosted a little.", 6), new Ability(Abilities.REFRIGERATE, "Refrigerate", "Normal-type moves become Ice-type moves. The power of those moves is boosted a little.", 6)
.attr(MoveTypeChangePowerMultiplierAbAttr, Type.NORMAL, Type.ICE, 1.2),
new Ability(Abilities.SWEET_VEIL, "Sweet Veil (N)", "Prevents itself and ally Pokémon from falling asleep.", 6) new Ability(Abilities.SWEET_VEIL, "Sweet Veil (N)", "Prevents itself and ally Pokémon from falling asleep.", 6)
.ignorable(), .ignorable(),
new Ability(Abilities.STANCE_CHANGE, "Stance Change", "The Pokémon changes its form to Blade Forme when it uses an attack move and changes to Shield Forme when it uses King's Shield.", 6) new Ability(Abilities.STANCE_CHANGE, "Stance Change", "The Pokémon changes its form to Blade Forme when it uses an attack move and changes to Shield Forme when it uses King's Shield.", 6)
@ -2530,10 +2562,12 @@ export function initAbilities() {
new Ability(Abilities.SYMBIOSIS, "Symbiosis (N)", "The Pokémon passes its item to an ally that has used up an item.", 6), new Ability(Abilities.SYMBIOSIS, "Symbiosis (N)", "The Pokémon passes its item to an ally that has used up an item.", 6),
new Ability(Abilities.TOUGH_CLAWS, "Tough Claws", "Powers up moves that make direct contact.", 6) new Ability(Abilities.TOUGH_CLAWS, "Tough Claws", "Powers up moves that make direct contact.", 6)
.attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), 1.3), .attr(MovePowerBoostAbAttr, (user, target, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), 1.3),
new Ability(Abilities.PIXILATE, "Pixilate (N)", "Normal-type moves become Fairy-type moves. The power of those moves is boosted a little.", 6), new Ability(Abilities.PIXILATE, "Pixilate", "Normal-type moves become Fairy-type moves. The power of those moves is boosted a little.", 6)
.attr(MoveTypeChangePowerMultiplierAbAttr, Type.NORMAL, Type.FAIRY, 1.2),
new Ability(Abilities.GOOEY, "Gooey", "Contact with the Pokémon lowers the attacker's Speed stat.", 6) new Ability(Abilities.GOOEY, "Gooey", "Contact with the Pokémon lowers the attacker's Speed stat.", 6)
.attr(PostDefendStatChangeAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), BattleStat.SPD, -1, false), .attr(PostDefendStatChangeAbAttr, (target, user, move) => move.hasFlag(MoveFlags.MAKES_CONTACT), BattleStat.SPD, -1, false),
new Ability(Abilities.AERILATE, "Aerilate (N)", "Normal-type moves become Flying-type moves. The power of those moves is boosted a little.", 6), new Ability(Abilities.AERILATE, "Aerilate", "Normal-type moves become Flying-type moves. The power of those moves is boosted a little.", 6)
.attr(MoveTypeChangePowerMultiplierAbAttr, Type.NORMAL, Type.FLYING, 1.2),
new Ability(Abilities.PARENTAL_BOND, "Parental Bond (N)", "Parent and child each attacks.", 6), new Ability(Abilities.PARENTAL_BOND, "Parental Bond (N)", "Parent and child each attacks.", 6),
new Ability(Abilities.DARK_AURA, "Dark Aura", "Powers up each Pokémon's Dark-type moves.", 6) new Ability(Abilities.DARK_AURA, "Dark Aura", "Powers up each Pokémon's Dark-type moves.", 6)
.attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => getPokemonMessage(pokemon, ' is radiating a Dark Aura!')) .attr(PostSummonMessageAbAttr, (pokemon: Pokemon) => getPokemonMessage(pokemon, ' is radiating a Dark Aura!'))
@ -2574,7 +2608,8 @@ export function initAbilities() {
.attr(IgnoreContactAbAttr), .attr(IgnoreContactAbAttr),
new Ability(Abilities.LIQUID_VOICE, "Liquid Voice (N)", "All sound-based moves become Water-type moves.", 7), new Ability(Abilities.LIQUID_VOICE, "Liquid Voice (N)", "All sound-based moves become Water-type moves.", 7),
new Ability(Abilities.TRIAGE, "Triage (N)", "Gives priority to a healing move.", 7), new Ability(Abilities.TRIAGE, "Triage (N)", "Gives priority to a healing move.", 7),
new Ability(Abilities.GALVANIZE, "Galvanize (N)", "Normal-type moves become Electric-type moves. The power of those moves is boosted a little.", 7), new Ability(Abilities.GALVANIZE, "Galvanize", "Normal-type moves become Electric-type moves. The power of those moves is boosted a little.", 7)
.attr(MoveTypeChangePowerMultiplierAbAttr, Type.NORMAL, Type.ELECTRIC, 1.2),
new Ability(Abilities.SURGE_SURFER, "Surge Surfer", "Doubles the Pokémon's Speed stat on Electric Terrain.", 7) new Ability(Abilities.SURGE_SURFER, "Surge Surfer", "Doubles the Pokémon's Speed stat on Electric Terrain.", 7)
.conditionalAttr(getTerrainCondition(TerrainType.ELECTRIC), BattleStatMultiplierAbAttr, BattleStat.SPD, 2), .conditionalAttr(getTerrainCondition(TerrainType.ELECTRIC), BattleStatMultiplierAbAttr, BattleStat.SPD, 2),
new Ability(Abilities.SCHOOLING, "Schooling", "When it has a lot of HP, the Pokémon forms a powerful school. It stops schooling when its HP is low.", 7) new Ability(Abilities.SCHOOLING, "Schooling", "When it has a lot of HP, the Pokémon forms a powerful school. It stops schooling when its HP is low.", 7)

View File

@ -2,7 +2,7 @@ import Phaser from 'phaser';
import BattleScene, { ABILITY_OVERRIDE, AnySound, MOVE_OVERRIDE, OPP_ABILITY_OVERRIDE, OPP_MOVE_OVERRIDE } from '../battle-scene'; import BattleScene, { ABILITY_OVERRIDE, AnySound, MOVE_OVERRIDE, OPP_ABILITY_OVERRIDE, OPP_MOVE_OVERRIDE } from '../battle-scene';
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from '../ui/battle-info'; import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from '../ui/battle-info';
import { Moves } from "../data/enums/moves"; import { Moves } from "../data/enums/moves";
import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, VariablePowerAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, MultiHitAttr, StatusMoveTypeImmunityAttr, MoveTarget, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatChangesAttr, SacrificialAttr } from "../data/move"; import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, VariablePowerAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, MultiHitAttr, StatusMoveTypeImmunityAttr, MoveTarget, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatChangesAttr, SacrificialAttr, VariableMoveTypeAbAttr } from "../data/move";
import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm } from '../data/pokemon-species'; import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm } from '../data/pokemon-species';
import * as Utils from '../utils'; import * as Utils from '../utils';
import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from '../data/type'; import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from '../data/type';
@ -25,7 +25,7 @@ import { TempBattleStat } from '../data/temp-battle-stat';
import { ArenaTagSide, WeakenMoveScreenTag, WeakenMoveTypeTag } from '../data/arena-tag'; import { ArenaTagSide, WeakenMoveScreenTag, WeakenMoveTypeTag } from '../data/arena-tag';
import { ArenaTagType } from "../data/enums/arena-tag-type"; import { ArenaTagType } from "../data/enums/arena-tag-type";
import { Biome } from "../data/enums/biome"; import { Biome } from "../data/enums/biome";
import { Abilities, Ability, BattleStatMultiplierAbAttr, BlockCritAbAttr, BypassBurnDamageReductionAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs } from '../data/ability'; import { Abilities, Ability, BattleStatMultiplierAbAttr, BlockCritAbAttr, BypassBurnDamageReductionAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs } from '../data/ability';
import PokemonData from '../system/pokemon-data'; import PokemonData from '../system/pokemon-data';
import { BattlerIndex } from '../battle'; import { BattlerIndex } from '../battle';
import { BattleSpec } from "../enums/battle-spec"; import { BattleSpec } from "../enums/battle-spec";
@ -41,6 +41,7 @@ import { SpeciesFormChange, SpeciesFormChangeActiveTrigger, SpeciesFormChangeMov
import { TerrainType } from '../data/terrain'; import { TerrainType } from '../data/terrain';
import { TrainerSlot } from '../data/trainer-config'; import { TrainerSlot } from '../data/trainer-config';
export enum FieldPosition { export enum FieldPosition {
CENTER, CENTER,
LEFT, LEFT,
@ -1085,20 +1086,23 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const moveCategory = move.category; const moveCategory = move.category;
let damage = new Utils.NumberHolder(0); let damage = new Utils.NumberHolder(0);
const variableType = new Utils.IntegerHolder(move.type);
const typeChangeMovePowerMultiplier = new Utils.NumberHolder(1);
// 2nd argument is for MoveTypeChangePowerMultiplierAbAttr
applyAbAttrs(VariableMoveTypeAbAttr, source, null, variableType, typeChangeMovePowerMultiplier);
const type = variableType.value as Type;
const cancelled = new Utils.BooleanHolder(false); const cancelled = new Utils.BooleanHolder(false);
const typeless = !!move.getAttrs(TypelessAttr).length; const typeless = !!move.getAttrs(TypelessAttr).length;
const types = this.getTypes(true, true); const types = this.getTypes(true, true);
const typeMultiplier = new Utils.NumberHolder(!typeless && (moveCategory !== MoveCategory.STATUS || move.getAttrs(StatusMoveTypeImmunityAttr).find(attr => (attr as StatusMoveTypeImmunityAttr).immuneType === move.type)) const typeMultiplier = new Utils.NumberHolder(!typeless && (moveCategory !== MoveCategory.STATUS || move.getAttrs(StatusMoveTypeImmunityAttr).find(attr => (attr as StatusMoveTypeImmunityAttr).immuneType === type))
? getTypeDamageMultiplier(move.type, types[0]) * (types.length > 1 ? getTypeDamageMultiplier(move.type, types[1]) : 1) ? getTypeDamageMultiplier(type, types[0]) * (types.length > 1 ? getTypeDamageMultiplier(type, types[1]) : 1)
: 1); : 1);
applyMoveAttrs(VariableMoveTypeMultiplierAttr, source, this, move, typeMultiplier); applyMoveAttrs(VariableMoveTypeMultiplierAttr, source, this, move, typeMultiplier);
if (typeless) if (typeless)
typeMultiplier.value = 1; typeMultiplier.value = 1;
if (this.getTypes(true, true).find(t => move.isTypeImmune(t))) if (this.getTypes(true, true).find(t => move.isTypeImmune(t)))
typeMultiplier.value = 0; typeMultiplier.value = 0;
// Handle strong winds lowering effectiveness of types super effective against pure flying
if (this.scene.arena.weather?.weatherType === WeatherType.STRONG_WINDS && !this.scene.arena.weather.isEffectSuppressed(this.scene) && typeMultiplier.value >= 2 && this.isOfType(Type.FLYING) && getTypeDamageMultiplier(move.type, Type.FLYING) === 2)
typeMultiplier.value /= 2;
switch (moveCategory) { switch (moveCategory) {
case MoveCategory.PHYSICAL: case MoveCategory.PHYSICAL:
@ -1106,13 +1110,15 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const isPhysical = moveCategory === MoveCategory.PHYSICAL; const isPhysical = moveCategory === MoveCategory.PHYSICAL;
const power = new Utils.NumberHolder(move.power); const power = new Utils.NumberHolder(move.power);
const sourceTeraType = source.getTeraType(); const sourceTeraType = source.getTeraType();
if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === move.type && power.value < 60 && move.priority <= 0 && !move.getAttrs(MultiHitAttr).length && !this.scene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id)) if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === type && power.value < 60 && move.priority <= 0 && !move.getAttrs(MultiHitAttr).length && !this.scene.findModifier(m => m instanceof PokemonMultiHitModifier && m.pokemonId === source.id))
power.value = 60; power.value = 60;
applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, this, battlerMove, power); applyPreAttackAbAttrs(VariableMovePowerAbAttr, source, this, battlerMove, power);
this.scene.getField(true).map(p => applyPreAttackAbAttrs(FieldVariableMovePowerAbAttr, this, source, battlerMove, power)); this.scene.getField(true).map(p => applyPreAttackAbAttrs(FieldVariableMovePowerAbAttr, this, source, battlerMove, power));
applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, this, source, battlerMove, cancelled, power); applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, this, source, battlerMove, cancelled, power);
power.value *= typeChangeMovePowerMultiplier.value;
if (!typeless) if (!typeless)
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, battlerMove, cancelled, typeMultiplier); applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, battlerMove, cancelled, typeMultiplier);
if (!cancelled.value) if (!cancelled.value)
@ -1121,16 +1127,16 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (cancelled.value) if (cancelled.value)
result = HitResult.NO_EFFECT; result = HitResult.NO_EFFECT;
else { else {
if (source.findTag(t => t instanceof TypeBoostTag && (t as TypeBoostTag).boostedType === move.type)) if (source.findTag(t => t instanceof TypeBoostTag && (t as TypeBoostTag).boostedType === type))
power.value *= 1.5; power.value *= 1.5;
const arenaAttackTypeMultiplier = this.scene.arena.getAttackTypeMultiplier(move.type, this.isGrounded()); const arenaAttackTypeMultiplier = this.scene.arena.getAttackTypeMultiplier(type, this.isGrounded());
if (this.scene.arena.terrain?.terrainType === TerrainType.GRASSY && this.isGrounded() && move.type === Type.GROUND && move.moveTarget === MoveTarget.ALL_NEAR_OTHERS) if (this.scene.arena.terrain?.terrainType === TerrainType.GRASSY && this.isGrounded() && type === Type.GROUND && move.moveTarget === MoveTarget.ALL_NEAR_OTHERS)
power.value /= 2; power.value /= 2;
applyMoveAttrs(VariablePowerAttr, source, this, move, power); applyMoveAttrs(VariablePowerAttr, source, this, move, power);
this.scene.applyModifiers(PokemonMultiHitModifier, source.isPlayer(), source, new Utils.IntegerHolder(0), power); this.scene.applyModifiers(PokemonMultiHitModifier, source.isPlayer(), source, new Utils.IntegerHolder(0), power);
if (!typeless) { if (!typeless) {
this.scene.arena.applyTags(WeakenMoveTypeTag, move.type, power); this.scene.arena.applyTags(WeakenMoveTypeTag, type, power);
this.scene.applyModifiers(AttackTypeBoosterModifier, source.isPlayer(), source, move.type, power); this.scene.applyModifiers(AttackTypeBoosterModifier, source.isPlayer(), source, type, power);
} }
if (source.getTag(HelpingHandTag)) if (source.getTag(HelpingHandTag))
power.value *= 1.5; power.value *= 1.5;
@ -1163,11 +1169,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
const isTypeImmune = (typeMultiplier.value * arenaAttackTypeMultiplier) === 0; const isTypeImmune = (typeMultiplier.value * arenaAttackTypeMultiplier) === 0;
const sourceTypes = source.getTypes(); const sourceTypes = source.getTypes();
const matchesSourceType = sourceTypes[0] === move.type || (sourceTypes.length > 1 && sourceTypes[1] === move.type); const matchesSourceType = sourceTypes[0] === type || (sourceTypes.length > 1 && sourceTypes[1] === type);
let stabMultiplier = new Utils.NumberHolder(1); let stabMultiplier = new Utils.NumberHolder(1);
if (sourceTeraType === Type.UNKNOWN && matchesSourceType) if (sourceTeraType === Type.UNKNOWN && matchesSourceType)
stabMultiplier.value += 0.5; stabMultiplier.value += 0.5;
else if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === move.type) else if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === type)
stabMultiplier.value += 0.5; stabMultiplier.value += 0.5;
applyAbAttrs(StabBoostAbAttr, source, null, stabMultiplier); applyAbAttrs(StabBoostAbAttr, source, null, stabMultiplier);
@ -1192,7 +1198,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}); });
} }
if (this.scene.arena.terrain?.terrainType === TerrainType.MISTY && this.isGrounded() && move.type === Type.DRAGON) if (this.scene.arena.terrain?.terrainType === TerrainType.MISTY && this.isGrounded() && type === Type.DRAGON)
damage.value = Math.floor(damage.value / 2); damage.value = Math.floor(damage.value / 2);
applyMoveAttrs(ModifiedDamageAttr, source, this, move, damage); applyMoveAttrs(ModifiedDamageAttr, source, this, move, damage);
@ -2433,8 +2439,12 @@ export class EnemyPokemon extends Pokemon {
for (let m in movePool) { for (let m in movePool) {
const pokemonMove = movePool[m]; const pokemonMove = movePool[m];
const move = pokemonMove.getMove(); const move = pokemonMove.getMove();
let moveScore = moveScores[m];
const variableType = new Utils.IntegerHolder(move.type);
applyAbAttrs(VariableMoveTypeAbAttr, this, null, variableType);
const moveType = variableType.value as Type;
let moveScore = moveScores[m];
let targetScores: integer[] = []; let targetScores: integer[] = [];
for (let mt of moveTargets[move.id]) { for (let mt of moveTargets[move.id]) {
@ -2444,11 +2454,11 @@ export class EnemyPokemon extends Pokemon {
const effectiveness = target.getAttackMoveEffectiveness(this, pokemonMove); const effectiveness = target.getAttackMoveEffectiveness(this, pokemonMove);
if (target.isPlayer() !== this.isPlayer()) { if (target.isPlayer() !== this.isPlayer()) {
targetScore *= effectiveness; targetScore *= effectiveness;
if (this.isOfType(move.type)) if (this.isOfType(moveType))
targetScore *= 1.5; targetScore *= 1.5;
} else { } else {
targetScore /= effectiveness; targetScore /= effectiveness;
if (this.isOfType(move.type)) if (this.isOfType(moveType))
targetScore /= 1.5; targetScore /= 1.5;
} }
} }