Disguise Changes 2

Added comments as requested.
Changed names and conditions.
Using now the new once per battle restriction on abilities.
New attribute "SpecialOncePerBattleAbilityAbAttr"
pull/205/head
NxKarim 2024-05-07 02:21:24 -06:00
parent 90b8f2ac68
commit ffadd87d22
4 changed files with 102 additions and 62 deletions

View File

@ -306,17 +306,24 @@ export class ReceivedTypeDamageMultiplierAbAttr extends ReceivedMoveDamageMultip
}
}
export class PreDefendReceivedMoveNullifierAbAttr extends PreDefendAbAttr {
protected condition: PokemonDefendCondition;
constructor(condition: PokemonDefendCondition) {
super();
this.condition = condition;
}
/*
Attribute used for the Disguise ability
Before getting hit and the ability has not been triggered for the battle nullifies damage from moves.
*/
export class PreDefendDisguiseNullifyDamageAbAttr extends PreDefendAbAttr {
/**
* Sets damage received to 1 and hit result as "Effective". Set ability as triggered.
* @param {Pokemon} pokemon Pokemon has the ability
* @param {Pokemon} passive N/A
* @param {Pokemon} attacker N/A.
* @param {PokemonMove} move N/A.
* @param {any[]} args 0: Damage dealt to user. 1: Hit result type.
* @returns {boolean} true if the function succeeds
*/
applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
if (this.condition(pokemon, attacker, move.getMove()) && (args[1] as Utils.NumberHolder).value != HitResult.NO_EFFECT && (args[1] as Utils.NumberHolder).value != HitResult.FAIL) {
if (pokemon.battleData && !pokemon.battleData.abilitiesApplied.includes(Abilities.DISGUISE) && (args[1] as Utils.NumberHolder).value != HitResult.NO_EFFECT && (args[1] as Utils.NumberHolder).value != HitResult.FAIL) {
//Damage dealt is set to 1 as 0 can cause some issues. This 1 damage is subtracted later.
console.log(pokemon.battleData);
(args[0] as Utils.NumberHolder).value = 1;
(args[1] as Utils.NumberHolder).value = HitResult.EFFECTIVE;
return true;
@ -330,6 +337,39 @@ export class PreDefendReceivedMoveNullifierAbAttr extends PreDefendAbAttr {
}
}
/*
Attribute used for the Disguise ability
When the user is hit by confusion, Disguise triggers.
*/
export class DisguiseConfusionInteractionAbAttr extends AbAttr {
/**
* Cancels confusion damage and triggers disguise.
* @param {Pokemon} pokemon Pokemon has the ability
* @param {Pokemon} passive N/A
* @param {Utils.BooleanHolder} damageCancelled If true confusion damage is cancelled.
* @param {any[]} args N/A
* @returns {boolean} true if the function succeeds
*/
apply(pokemon: Pokemon, passive: boolean, damageCancelled: Utils.BooleanHolder, args: any[]): boolean {
//This checks if ability has been triggered during battle, and if the current form is correct.
if((pokemon.battleData && pokemon.battleData.abilitiesApplied.includes(Abilities.DISGUISE)) || pokemon.formIndex === 1)
return false;
damageCancelled.value = true;
const hpLost = Math.round(pokemon.getMaxHp() / 8)
pokemon.damageAndUpdate(hpLost, HitResult.OTHER);
pokemon.turnData.damageTaken += hpLost;
pokemon.battleData.abilitiesApplied.push(Abilities.DISGUISE);
pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false);
pokemon.scene.queueMessage(`Its disguise served it as a decoy!`);
return true;
}
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string {
return `${pokemon.name}\'s disguise was busted!`;
}
}
export class TypeImmunityAbAttr extends PreDefendAbAttr {
private immuneType: Type;
private condition: AbAttrCondition;
@ -455,25 +495,32 @@ export class PostDefendAbAttr extends AbAttr {
}
}
export class PostDefendDisguiseAbAttr extends PostDefendAbAttr {
protected condition: PokemonDefendCondition;
constructor(condition: PokemonDefendCondition) {
super(true);
this.condition = condition;
}
/*
Attribute used for the Disguise ability
After getting hit and the ability has not been triggered for the battle inflicts recoil damage on the user.
*/
export class PostDefendDisguiseRecoilAbAttr extends PostDefendAbAttr {
/**
* Inflicts 1/8 user's of max HP as recoil when triggered, and sets ability as triggered for the battle.
* @param {Pokemon} pokemon Pokemon has the ability
* @param {Pokemon} passive N/A
* @param {Pokemon} attacker Pokémon that targeted the ability's user.
* @param {HitResult} hitResult The result type of the hit, should be "EFFECTIVE".
* @param {any[]} args N/A
* @returns {boolean} true if the function succeeds
*/
applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (this.condition(pokemon, attacker, move.getMove()) && (hitResult == HitResult.EFFECTIVE)) {
//If ability has not been triggered during the battle and the hit result is effective...
if (pokemon.battleData && !pokemon.battleData.abilitiesApplied.includes(Abilities.DISGUISE) && (hitResult == HitResult.EFFECTIVE)) {
//damageDealt is taken into account as it is and not just 1 to prevent issues.
const damageDealt = attacker.turnData.damageDealt;
let recoilDamage = Math.round(pokemon.getMaxHp() / 8 - damageDealt);
if (!recoilDamage)
let hpLost = Math.round(pokemon.getMaxHp() / 8 - damageDealt);
if (!hpLost )
return false;
pokemon.damageAndUpdate(recoilDamage, HitResult.OTHER);
pokemon.battleData.abilityTriggered = true;
pokemon.turnData.damageTaken += recoilDamage;
pokemon.damageAndUpdate(hpLost , HitResult.OTHER);
pokemon.turnData.damageTaken += hpLost;
pokemon.battleData.abilitiesApplied.push(Abilities.DISGUISE);
pokemon.scene.queueMessage(getPokemonMessage(pokemon, '\'s disguise was busted!'));
return true;
}
@ -1724,25 +1771,6 @@ export class BlockNonDirectDamageAbAttr extends AbAttr {
}
}
export class DisguiseConfusionDamageInteractionAbAttr extends AbAttr {
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
if(pokemon.battleData.abilityTriggered == true)
return false;
cancelled.value = true;
pokemon.damageAndUpdate(Math.round(pokemon.getMaxHp() / 8), HitResult.OTHER);
pokemon.battleData.abilityTriggered = true;
pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false);
pokemon.scene.queueMessage(`Its disguise served it as a decoy!`);
return true;
}
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string {
return `${pokemon.name}\'s disguise was busted!`;
}
}
export class BlockOneHitKOAbAttr extends AbAttr {
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean {
cancelled.value = true;
@ -2521,6 +2549,16 @@ export class NoFusionAbilityAbAttr extends AbAttr {
}
}
/**
* Attribute for to not be pushed to the Pokémon battleData.abilityApplied
* This is for abilities that have secondary attributes (like Form Change) that could malfunction.
* The ability has to be pushed to battleData.abilityApplied in another attribute.
*/
export class SpecialOncePerBattleAbilityAbAttr extends AbAttr {
constructor() {
super(false);
}
}
export class IgnoreTypeImmunityAbAttr extends AbAttr {
defenderType: Type;
allowedMoveTypes: Type[];
@ -2571,7 +2609,7 @@ function applyAbAttrsInternal<TAttr extends AbAttr>(attrType: { new(...args: any
return applyNextAbAttr();
pokemon.scene.setPhaseQueueSplice();
const onApplySuccess = () => {
if (pokemon.battleData && !pokemon.battleData.abilitiesApplied.includes(ability.id)) {
if (pokemon.battleData && !pokemon.battleData.abilitiesApplied.includes(ability.id) && !ability.hasAttr(SpecialOncePerBattleAbilityAbAttr)) {
pokemon.battleData.abilitiesApplied.push(ability.id);
}
if (attr.showAbility && !quiet) {
@ -3329,18 +3367,19 @@ export function initAbilities() {
.attr(UnsuppressableAbilityAbAttr)
.attr(NoFusionAbilityAbAttr),
new Ability(Abilities.DISGUISE, 7)
.attr(DisguiseConfusionDamageInteractionAbAttr)
.attr(PreDefendReceivedMoveNullifierAbAttr, (target, user, move) => target.battleData.abilityTriggered == false)
.attr(PostDefendDisguiseAbAttr, (target, user, move) => target.battleData.abilityTriggered == false)
.attr(PreDefendFormChangeAbAttr, p => p.battleData.abilityTriggered == false ? 0 : 1)
.attr(PostSummonFormChangeAbAttr, p => p.battleData.abilityTriggered == false ? 0 : 1)
.attr(PostBattleInitFormChangeAbAttr, p => p.battleData.abilityTriggered == false ? 0 : 1)
.attr(PostDefendFormChangeAbAttr, p => p.battleData.abilityTriggered == false ? 0 : 1)
.attr(DisguiseConfusionInteractionAbAttr)
.attr(PreDefendDisguiseNullifyDamageAbAttr)
.attr(PostDefendDisguiseRecoilAbAttr)
.attr(PreDefendFormChangeAbAttr, p => p.battleData && !p.battleData.abilitiesApplied.includes(Abilities.DISGUISE) ? 0 : 1)
.attr(PostSummonFormChangeAbAttr, p => p.battleData && !p.battleData.abilitiesApplied.includes(Abilities.DISGUISE) ? 0 : 1)
.attr(PostBattleInitFormChangeAbAttr, p => p.battleData && !p.battleData.abilitiesApplied.includes(Abilities.DISGUISE) ? 0 : 1)
.attr(PostDefendFormChangeAbAttr, p => p.battleData && !p.battleData.abilitiesApplied.includes(Abilities.DISGUISE) ? 0 : 1)
.attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr)
.attr(UnsuppressableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr)
.attr(NoFusionAbilityAbAttr)
.attr(SpecialOncePerBattleAbilityAbAttr)
.ignorable()
.partial(),
new Ability(Abilities.BATTLE_BOND, 7)

View File

@ -8,7 +8,7 @@ import * as Utils from "../utils";
import { Moves } from "./enums/moves";
import { ChargeAttr, MoveFlags, allMoves } from "./move";
import { Type } from "./type";
import { BlockNonDirectDamageAbAttr, FlinchEffectAbAttr, ReverseDrainAbAttr, applyAbAttrs, DisguiseConfusionDamageInteractionAbAttr } from "./ability";
import { BlockNonDirectDamageAbAttr, FlinchEffectAbAttr, ReverseDrainAbAttr, applyAbAttrs, DisguiseConfusionInteractionAbAttr } from "./ability";
import { Abilities } from "./enums/abilities";
import { BattlerTagType } from "./enums/battler-tag-type";
import { TerrainType } from "./terrain";
@ -227,10 +227,10 @@ export class ConfusedTag extends BattlerTag {
const damage = Math.ceil(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * (pokemon.randSeedInt(15, 85) / 100));
pokemon.scene.queueMessage('It hurt itself in its\nconfusion!');
const cancelled = new Utils.BooleanHolder(false);
applyAbAttrs(DisguiseConfusionDamageInteractionAbAttr, pokemon, cancelled);
if (!cancelled.value) {
const damageCancelled = new Utils.BooleanHolder(false);
applyAbAttrs(DisguiseConfusionInteractionAbAttr, pokemon, damageCancelled);
//Confusion damage will not be dealt if Pokémon has Disguise and has not triggered.
if (!damageCancelled.value) {
pokemon.damageAndUpdate(damage);
pokemon.battleData.hitCount++;
}

View File

@ -27,7 +27,7 @@ import { TempBattleStat } from '../data/temp-battle-stat';
import { ArenaTagSide, WeakenMoveScreenTag, WeakenMoveTypeTag } from '../data/arena-tag';
import { ArenaTagType } from "../data/enums/arena-tag-type";
import { Biome } from "../data/enums/biome";
import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, PreDefendReceivedMoveNullifierAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr } from '../data/ability';
import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, PreDefendDisguiseNullifyDamageAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr } from '../data/ability';
import { Abilities } from "#app/data/enums/abilities";
import PokemonData from '../system/pokemon-data';
import Battle, { BattlerIndex } from '../battle';
@ -1444,8 +1444,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
damage.value = 0;
}
//All incoming damage from moves will be set to 1 if the target is a Disguise user.
let preResult = new Utils.NumberHolder(result);
applyPreDefendAbAttrs(PreDefendReceivedMoveNullifierAbAttr, this, source, battlerMove, cancelled, damage , preResult);
applyPreDefendAbAttrs(PreDefendDisguiseNullifyDamageAbAttr, this, source, battlerMove, cancelled, damage , preResult);
result = (preResult as Utils.NumberHolder).value;
console.log('damage', damage.value, move.name, power.value, sourceAtk, targetDef);

View File

@ -1616,7 +1616,7 @@ export class TurnInitPhase extends FieldPhase {
this.scene.currentBattle.addParticipant(pokemon as PlayerPokemon);
pokemon.resetTurnData();
applyPostBattleInitAbAttrs(PostBattleInitAbAttr, pokemon);
this.scene.pushPhase(pokemon.isPlayer() ? new CommandPhase(this.scene, i) : new EnemyCommandPhase(this.scene, i - BattlerIndex.ENEMY));
}
});