Hydro Steam (#231)

* Hydro Steam, Spirit Shackle, U-turn fix

Match formatting to project style

Co-authored-by: Samuel H <flashfireex@gmail.com>

* Fix Hydro Steam power up in Sun, even harsh sun if type changes

* Fix issue with Hydro Steam outside sun

* Add comments to IgnoreWeatherTypeDebuffAttr for documentation

* Move U-turn fix to another PR

---------

Co-authored-by: Samuel H <flashfireex@gmail.com>
pull/676/head
AJ Fontaine 2024-05-09 21:21:57 -04:00 committed by GitHub
parent 2bc1cfb0fc
commit 7219556e77
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 34 additions and 5 deletions

View File

@ -888,6 +888,33 @@ export class SacrificialFullRestoreAttr extends SacrificialAttr {
} }
} }
/**
* Attribute used for moves which ignore type-based debuffs from weather, namely Hydro Steam.
* Called during damage calculation after getting said debuff from getAttackTypeMultiplier in the Pokemon class.
*/
export class IgnoreWeatherTypeDebuffAttr extends MoveAttr {
public weather: WeatherType;
constructor(weather: WeatherType){
super();
this.weather = weather;
}
/**
* Changes the type-based weather modifier if this move's power would be reduced by it
* @param user Pokemon that used the move
* @param target N/A
* @param move Move with this attribute
* @param args Utils.NumberHolder for arenaAttackTypeMultiplier
* @returns true if the function succeeds
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const weatherModifier=args[0] as Utils.NumberHolder;
//If the type-based attack power modifier due to weather (e.g. Water moves in Sun) is below 1, set it to 1
if (user.scene.arena.weather?.weatherType === this.weather)
weatherModifier.value = Math.max(weatherModifier.value, 1);
return true;
}
}
export abstract class WeatherHealAttr extends HealAttr { export abstract class WeatherHealAttr extends HealAttr {
constructor() { constructor() {
super(0.5); super(0.5);
@ -6666,7 +6693,8 @@ export function initMoves() {
.attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.ELECTRIC && user.isGrounded() ? 1.5 : 1) .attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.ELECTRIC && user.isGrounded() ? 1.5 : 1)
.slicingMove(), .slicingMove(),
new AttackMove(Moves.HYDRO_STEAM, Type.WATER, MoveCategory.SPECIAL, 80, 100, 15, -1, 0, 9) new AttackMove(Moves.HYDRO_STEAM, Type.WATER, MoveCategory.SPECIAL, 80, 100, 15, -1, 0, 9)
.partial(), .attr(IgnoreWeatherTypeDebuffAttr, WeatherType.SUNNY)
.attr(MovePowerMultiplierAttr, (user, target, move) => [WeatherType.SUNNY, WeatherType.HARSH_SUN].includes(user.scene.arena.weather?.weatherType) && !user.scene.arena.weather?.isEffectSuppressed(user.scene) ? 1.5 : 1),
new AttackMove(Moves.RUINATION, Type.DARK, MoveCategory.SPECIAL, -1, 90, 10, -1, 0, 9) new AttackMove(Moves.RUINATION, Type.DARK, MoveCategory.SPECIAL, -1, 90, 10, -1, 0, 9)
.attr(TargetHalfHpDamageAttr), .attr(TargetHalfHpDamageAttr),
new AttackMove(Moves.COLLISION_COURSE, Type.FIGHTING, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9) new AttackMove(Moves.COLLISION_COURSE, Type.FIGHTING, MoveCategory.PHYSICAL, 100, 100, 5, -1, 0, 9)

View File

@ -4,7 +4,7 @@ import { Variant, VariantSet, variantColorCache } from '#app/data/variant';
import { variantData } from '#app/data/variant'; import { variantData } from '#app/data/variant';
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, VariableMoveTypeAttr, VariableMoveCategoryAttr, CounterDamageAttr } 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, VariableMoveTypeAttr, VariableMoveCategoryAttr, CounterDamageAttr, IgnoreWeatherTypeDebuffAttr } from "../data/move";
import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities } from '../data/pokemon-species'; import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities } 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';
@ -1338,7 +1338,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
source.removeTag(typeBoost.tagType); source.removeTag(typeBoost.tagType);
} }
} }
const arenaAttackTypeMultiplier = this.scene.arena.getAttackTypeMultiplier(type, source.isGrounded()); const arenaAttackTypeMultiplier = new Utils.NumberHolder(this.scene.arena.getAttackTypeMultiplier(type, source.isGrounded()));
applyMoveAttrs(IgnoreWeatherTypeDebuffAttr, source, this, move, arenaAttackTypeMultiplier);
if (this.scene.arena.getTerrainType() === TerrainType.GRASSY && this.isGrounded() && type === Type.GROUND && move.moveTarget === MoveTarget.ALL_NEAR_OTHERS) if (this.scene.arena.getTerrainType() === 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);
@ -1383,7 +1384,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (!isCritical) { if (!isCritical) {
this.scene.arena.applyTagsForSide(WeakenMoveScreenTag, this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, move.category, this.scene.currentBattle.double, screenMultiplier); this.scene.arena.applyTagsForSide(WeakenMoveScreenTag, this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY, move.category, this.scene.currentBattle.double, screenMultiplier);
} }
const isTypeImmune = (typeMultiplier.value * arenaAttackTypeMultiplier) === 0; const isTypeImmune = (typeMultiplier.value * arenaAttackTypeMultiplier.value) === 0;
const sourceTypes = source.getTypes(); const sourceTypes = source.getTypes();
const matchesSourceType = sourceTypes[0] === type || (sourceTypes.length > 1 && sourceTypes[1] === type); const matchesSourceType = sourceTypes[0] === type || (sourceTypes.length > 1 && sourceTypes[1] === type);
let stabMultiplier = new Utils.NumberHolder(1); let stabMultiplier = new Utils.NumberHolder(1);
@ -1401,7 +1402,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
applyMoveAttrs(VariableDefAttr, source, this, move, targetDef); applyMoveAttrs(VariableDefAttr, source, this, move, targetDef);
if (!isTypeImmune) { if (!isTypeImmune) {
damage.value = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk.value / targetDef.value) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * arenaAttackTypeMultiplier * screenMultiplier.value * ((this.scene.randBattleSeedInt(15) + 85) / 100) * criticalMultiplier.value); damage.value = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk.value / targetDef.value) / 50) + 2) * stabMultiplier.value * typeMultiplier.value * arenaAttackTypeMultiplier.value * screenMultiplier.value * ((this.scene.randBattleSeedInt(15) + 85) / 100) * criticalMultiplier.value);
if (isPhysical && source.status && source.status.effect === StatusEffect.BURN) { if (isPhysical && source.status && source.status.effect === StatusEffect.BURN) {
const burnDamageReductionCancelled = new Utils.BooleanHolder(false); const burnDamageReductionCancelled = new Utils.BooleanHolder(false);
applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled); applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled);