[Bug] Liquid Ooze hurts move user instead of one with ability (#1301)

* Fixing Liquid Ooze to turn move user and not one with the ability as well as converted strength sap to use same logic instead of making it a separate class

* Replaced "undefined" with "null"

* Updated based on feedback + fix file permissions

* Fixing file permission on battler-tags

* Adding localization for drain message

* Apparently this file is 755 unlike the others

* Removed ability for custom message in exchange for getting to pass Pokemon name

* Once again changing moves from 644 to 755 like it is in repo right now :)

* putting ability back to 755 (why are file permissions all over the place)
This commit is contained in:
td76099 2024-06-01 15:53:32 -04:00 committed by GitHub
parent b7ecebbc6e
commit 06ba63dd7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 96 additions and 40 deletions

View File

@ -9,7 +9,7 @@ import { BattlerTag } from "./battler-tags";
import { BattlerTagType } from "./enums/battler-tag-type";
import { StatusEffect, getNonVolatileStatusEffects, getStatusEffectDescriptor, getStatusEffectHealText } from "./status-effect";
import { Gender } from "./gender";
import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, StatusMoveTypeImmunityAttr, FlinchAttr, OneHitKOAttr, HitHealAttr, StrengthSapHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, IncrementMovePriorityAttr } from "./move";
import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, StatusMoveTypeImmunityAttr, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, IncrementMovePriorityAttr } from "./move";
import { ArenaTagSide, ArenaTrapTag } from "./arena-tag";
import { ArenaTagType } from "./enums/arena-tag-type";
import { Stat } from "./pokemon-stat";
@ -575,10 +575,26 @@ export class MoveImmunityStatChangeAbAttr extends MoveImmunityAbAttr {
return ret;
}
}
/**
* Class for abilities that make drain moves deal damage to user instead of healing them.
* @extends PostDefendAbAttr
* @see {@linkcode applyPostDefend}
*/
export class ReverseDrainAbAttr extends PostDefendAbAttr {
/**
* Determines if a damage and draining move was used to check if this ability should stop the healing.
* Examples include: Absorb, Draining Kiss, Bitter Blade, etc.
* Also displays a message to show this ability was activated.
* @param pokemon {@linkcode Pokemon} with this ability
* @param passive N/A
* @param attacker {@linkcode Pokemon} that is attacking this Pokemon
* @param move {@linkcode PokemonMove} that is being used
* @param hitResult N/A
* @args N/A
* @returns true if healing should be reversed on a healing move, false otherwise.
*/
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (move.getMove().hasAttr(HitHealAttr) || move.getMove().hasAttr(StrengthSapHealAttr) ) {
if (move.getMove().hasAttr(HitHealAttr)) {
pokemon.scene.queueMessage(getPokemonMessage(attacker, " sucked up the liquid ooze!"));
return true;
}

View File

@ -391,7 +391,7 @@ export class SeedTag extends BattlerTag {
pokemon.scene.unshiftPhase(new CommonAnimPhase(pokemon.scene, source.getBattlerIndex(), pokemon.getBattlerIndex(), CommonAnim.LEECH_SEED));
const damage = pokemon.damageAndUpdate(Math.max(Math.floor(pokemon.getMaxHp() / 8), 1));
const reverseDrain = pokemon.hasAbilityWithAttr(ReverseDrainAbAttr);
const reverseDrain = pokemon.hasAbilityWithAttr(ReverseDrainAbAttr, false);
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, source.getBattlerIndex(),
!reverseDrain ? damage : damage * -1,
!reverseDrain ? getPokemonMessage(pokemon, "'s health is\nsapped by Leech Seed!") : getPokemonMessage(source, "'s Leech Seed\nsucked up the liquid ooze!"),
@ -1479,4 +1479,3 @@ export function loadBattlerTag(source: BattlerTag | any): BattlerTag {
tag.loadTag(source);
return tag;
}

View File

@ -1201,48 +1201,72 @@ export class BoostHealAttr extends HealAttr {
}
}
/**
* Heals user as a side effect of a move that hits a target.
* Healing is based on {@linkcode healRatio} * the amount of damage dealt or a stat of the target.
* @extends MoveEffectAttr
* @see {@linkcode apply}
* @see {@linkcode getUserBenefitScore}
*/
export class HitHealAttr extends MoveEffectAttr {
private healRatio: number;
private message: string;
private healStat: Stat;
constructor(healRatio?: number) {
constructor(healRatio?: number, healStat?: Stat) {
super(true, MoveEffectTrigger.HIT);
this.healRatio = healRatio || 0.5;
this.healStat = healStat || null;
}
/**
* Heals the user the determined amount and possibly displays a message about regaining health.
* If the target has the {@linkcode ReverseDrainAbAttr}, all healing is instead converted
* to damage to the user.
* @param user {@linkcode Pokemon} using this move
* @param target {@linkcode Pokemon} target of this move
* @param move {@linkcode Move} being used
* @param args N/A
* @returns true if the function succeeds
*/
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const healAmount = Math.max(Math.floor(user.turnData.damageDealt * this.healRatio), 1);
const reverseDrain = user.hasAbilityWithAttr(ReverseDrainAbAttr);
user.scene.unshiftPhase(new PokemonHealPhase(user.scene, user.getBattlerIndex(),
!reverseDrain ? healAmount : healAmount * -1,
!reverseDrain ? getPokemonMessage(target, " had its\nenergy drained!") : undefined,
false, true));
let healAmount = 0;
let message = "";
const reverseDrain = target.hasAbilityWithAttr(ReverseDrainAbAttr, false);
if (this.healStat) {
// Strength Sap formula
healAmount = target.getBattleStat(this.healStat);
message = i18next.t("battle:drainMessage", {pokemonName: target.name});
} else {
// Default healing formula used by draining moves like Absorb, Draining Kiss, Bitter Blade, etc.
healAmount = Math.max(Math.floor(user.turnData.damageDealt * this.healRatio), 1);
message = i18next.t("battle:regainHealth", {pokemonName: user.name});
}
if (reverseDrain) {
user.turnData.damageTaken += healAmount;
healAmount = healAmount * -1;
message = null;
}
user.scene.unshiftPhase(new PokemonHealPhase(user.scene, user.getBattlerIndex(), healAmount, message, false, true));
return true;
}
/**
* Used by the Enemy AI to rank an attack based on a given user
* @param user {@linkcode Pokemon} using this move
* @param target {@linkcode Pokemon} target of this move
* @param move {@linkcode Move} being used
* @returns an integer. Higher means enemy is more likely to use that move.
*/
getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
return Math.floor(Math.max((1 - user.getHpRatio()) - 0.33, 0) * ((move.power / 5) / 4));
if (this.healStat) {
const healAmount = target.getBattleStat(this.healStat);
return Math.floor(Math.max(0, (Math.min(1, (healAmount+user.hp)/user.getMaxHp() - 0.33))) / user.getHpRatio());
}
return Math.floor(Math.max((1 - user.getHpRatio()) - 0.33, 0) * (move.power / 4));
}
}
export class StrengthSapHealAttr extends MoveEffectAttr {
constructor() {
super(true, MoveEffectTrigger.HIT);
}
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const healAmount = target.stats[Stat.ATK] * (Math.max(2, 2 + target.summonData.battleStats[BattleStat.ATK]) / Math.max(2, 2 - target.summonData.battleStats[BattleStat.ATK]));
const reverseDrain = user.hasAbilityWithAttr(ReverseDrainAbAttr);
user.scene.unshiftPhase(new PokemonHealPhase(user.scene, user.getBattlerIndex(),
!reverseDrain ? healAmount : healAmount * -1,
!reverseDrain ? getPokemonMessage(user, " regained\nhealth!") : undefined,
false, true));
return true;
}
}
/**
* Attribute used for moves that change priority in a turn given a condition,
* e.g. Grassy Glide
@ -6918,7 +6942,7 @@ export function initMoves() {
.triageMove(),
new AttackMove(Moves.HIGH_HORSEPOWER, Type.GROUND, MoveCategory.PHYSICAL, 95, 95, 10, -1, 0, 7),
new StatusMove(Moves.STRENGTH_SAP, Type.GRASS, 100, 10, 100, 0, 7)
.attr(StrengthSapHealAttr)
.attr(HitHealAttr, null, Stat.ATK)
.attr(StatChangeAttr, BattleStat.ATK, -1)
.condition((user, target, move) => target.summonData.battleStats[BattleStat.ATK] > -6)
.triageMove(),

View File

@ -55,5 +55,7 @@ export const battle: SimpleTranslationEntries = {
"skipItemQuestion": "Bist du sicher, dass du kein Item nehmen willst?",
"notDisabled": "{{pokemonName}}'s {{moveName}} ist\nnicht mehr deaktiviert!",
"eggHatching": "Oh?",
"ivScannerUseQuestion": "IV-Scanner auf {{pokemonName}} benutzen?"
"ivScannerUseQuestion": "IV-Scanner auf {{pokemonName}} benutzen?",
"drainMessage": "{{pokemonName}} wurde Energie abgesaugt",
"regainHealth": "KP von {{pokemonName}} wurden wieder aufgefrischt!"
} as const;

View File

@ -55,5 +55,7 @@ export const battle: SimpleTranslationEntries = {
"notDisabled": "{{pokemonName}}'s {{moveName}} is disabled\nno more!",
"skipItemQuestion": "Are you sure you want to skip taking an item?",
"eggHatching": "Oh?",
"ivScannerUseQuestion": "Use IV Scanner on {{pokemonName}}?"
"ivScannerUseQuestion": "Use IV Scanner on {{pokemonName}}?",
"drainMessage": "{{pokemonName}} had its\nenergy drained!",
"regainHealth": "{{pokemonName}} regained\nhealth!"
} as const;

View File

@ -55,5 +55,7 @@ export const battle: SimpleTranslationEntries = {
"notDisabled": "¡El movimiento {{moveName}} de {{pokemonName}}\nya no está anulado!",
"skipItemQuestion": "¿Estás seguro de que no quieres coger un objeto?",
"eggHatching": "¿Y esto?",
"ivScannerUseQuestion": "¿Quieres usar el Escáner de IVs en {{pokemonName}}?"
"ivScannerUseQuestion": "¿Quieres usar el Escáner de IVs en {{pokemonName}}?",
"drainMessage": "{{pokemonName}} had its\nenergy drained!",
"regainHealth": "{{pokemonName}} regained\nhealth!"
} as const;

View File

@ -55,5 +55,7 @@ export const battle: SimpleTranslationEntries = {
"notDisabled": "La capacité {{moveName}}\nde {{pokemonName}} nest plus sous entrave !",
"skipItemQuestion": "Êtes-vous sûr·e de ne pas vouloir prendre dobjet ?",
"eggHatching": "Oh ?",
"ivScannerUseQuestion": "Utiliser le Scanner dIV sur {{pokemonName}} ?"
"ivScannerUseQuestion": "Utiliser le Scanner dIV sur {{pokemonName}} ?",
"drainMessage": "{{pokemonName}} had its\nenergy drained!",
"regainHealth": "{{pokemonName}} regained\nhealth!"
} as const;

View File

@ -55,5 +55,7 @@ export const battle: SimpleTranslationEntries = {
"notDisabled": "{{pokemonName}}'s {{moveName}} non è più\ndisabilitata!",
"skipItemQuestion": "Sei sicuro di non voler prendere nessun oggetto?",
"eggHatching": "Oh!",
"ivScannerUseQuestion": "Vuoi usare lo scanner di IV su {{pokemonName}}?"
"ivScannerUseQuestion": "Vuoi usare lo scanner di IV su {{pokemonName}}?",
"drainMessage": "{{pokemonName}} had its\nenergy drained!",
"regainHealth": "{{pokemonName}} regained\nhealth!"
} as const;

View File

@ -55,5 +55,7 @@ export const battle: SimpleTranslationEntries = {
"notDisabled": "O movimento {{moveName}}\nnão está mais desabilitado!",
"skipItemQuestion": "Tem certeza de que não quer escolher um item?",
"eggHatching": "Opa?",
"ivScannerUseQuestion": "Quer usar o Scanner de IVs em {{pokemonName}}?"
"ivScannerUseQuestion": "Quer usar o Scanner de IVs em {{pokemonName}}?",
"drainMessage": "{{pokemonName}} had its\nenergy drained!",
"regainHealth": "{{pokemonName}} regained\nhealth!"
} as const;

View File

@ -55,5 +55,7 @@ export const battle: SimpleTranslationEntries = {
"notDisabled": "{{moveName}} 不再被禁用!",
"skipItemQuestion": "你确定要跳过拾取道具吗?",
"eggHatching": "咦?",
"ivScannerUseQuestion": "对 {{pokemonName}} 使用个体值扫描仪?"
"ivScannerUseQuestion": "对 {{pokemonName}} 使用个体值扫描仪?",
"drainMessage": "{{pokemonName}} had its\nenergy drained!",
"regainHealth": "{{pokemonName}} regained\nhealth!"
} as const;

View File

@ -52,5 +52,7 @@ export const battle: SimpleTranslationEntries = {
"notDisabled": "{{moveName}} 不再被禁用!",
"skipItemQuestion": "你要跳過拾取道具嗎?",
"eggHatching": "咦?",
"ivScannerUseQuestion": "對 {{pokemonName}} 使用個體值掃描?"
"ivScannerUseQuestion": "對 {{pokemonName}} 使用個體值掃描?",
"drainMessage": "{{pokemonName}} had its\nenergy drained!",
"regainHealth": "{{pokemonName}} regained\nhealth!"
} as const;

View File

@ -4483,9 +4483,10 @@ export class PokemonHealPhase extends CommonAnimPhase {
const fullHp = pokemon.getHpRatio() >= 1;
const hasMessage = !!this.message;
const healOrDamage = (!fullHp || this.hpHealed < 0);
let lastStatusEffect = StatusEffect.NONE;
if (!fullHp || this.hpHealed < 0) {
if (healOrDamage) {
const hpRestoreMultiplier = new Utils.IntegerHolder(1);
if (!this.revive) {
this.scene.applyModifiers(HealingBoosterModifier, this.player, hpRestoreMultiplier);
@ -4530,7 +4531,7 @@ export class PokemonHealPhase extends CommonAnimPhase {
this.scene.queueMessage(getPokemonMessage(pokemon, getStatusEffectHealText(lastStatusEffect)));
}
if (fullHp && !lastStatusEffect) {
if (!healOrDamage && !lastStatusEffect) {
super.end();
}
}