pull/205/merge
NxKarim 2024-05-15 16:02:12 +02:00 committed by GitHub
commit bcdd943d8d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 122 additions and 30 deletions

View File

@ -309,19 +309,69 @@ export class ReceivedTypeDamageMultiplierAbAttr extends ReceivedMoveDamageMultip
} }
} }
export class PreDefendMovePowerToOneAbAttr extends ReceivedMoveDamageMultiplierAbAttr { /*
constructor(condition: PokemonDefendCondition) { Attribute used for the Disguise ability
super(condition, 1); 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 { applyPreDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
if (this.condition(pokemon, attacker, move.getMove())) { //If disguise has not been triggered during this battle, and the move will hit the user (User is not inmune and or foe's move failed).
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[0] as Utils.NumberHolder).value = 1;
(args[1] as Utils.NumberHolder).value = HitResult.EFFECTIVE;
return true; return true;
} }
return false; return false;
} }
getTriggerMessage(pokemon: Pokemon, abilityName: string, ...args: any[]): string {
return `Its disguise served it as a decoy!`;
}
}
/*
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 { export class TypeImmunityAbAttr extends PreDefendAbAttr {
@ -449,20 +499,35 @@ export class PostDefendAbAttr extends AbAttr {
} }
} }
export class PostDefendDisguiseAbAttr extends PostDefendAbAttr { /*
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 { applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (pokemon.formIndex == 0 && pokemon.battleData.hitCount != 0 && (move.getMove().category == MoveCategory.SPECIAL || move.getMove().category == MoveCategory.PHYSICAL)) { //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)) {
const recoilDamage = Math.ceil((pokemon.getMaxHp() / 8) - attacker.turnData.damageDealt); //damageDealt is taken into account as it is and not just 1 to prevent issues.
if (!recoilDamage) const damageDealt = attacker.turnData.damageDealt;
return false; let hpLost = Math.round(pokemon.getMaxHp() / 8 - damageDealt);
pokemon.damageAndUpdate(recoilDamage, HitResult.OTHER); if (!hpLost )
pokemon.turnData.damageTaken += recoilDamage; return false;
pokemon.damageAndUpdate(hpLost , HitResult.OTHER);
pokemon.turnData.damageTaken += hpLost;
pokemon.battleData.abilitiesApplied.push(Abilities.DISGUISE);
pokemon.scene.queueMessage(getPokemonMessage(pokemon, '\'s disguise was busted!')); pokemon.scene.queueMessage(getPokemonMessage(pokemon, '\'s disguise was busted!'));
return true; return true;
} }
return false; return false;
} }
} }
@ -1601,7 +1666,7 @@ export class PostSummonFormChangeAbAttr extends PostSummonAbAttr {
const formIndex = this.formFunc(pokemon); const formIndex = this.formFunc(pokemon);
if (formIndex !== pokemon.formIndex) if (formIndex !== pokemon.formIndex)
return pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false); return pokemon.scene.triggerPokemonFormChange(pokemon, SpeciesFormChangeManualTrigger, false);
return false; return false;
} }
} }
@ -2647,6 +2712,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 { export class IgnoreTypeImmunityAbAttr extends AbAttr {
private defenderType: Type; private defenderType: Type;
private allowedMoveTypes: Type[]; private allowedMoveTypes: Type[];
@ -2721,7 +2796,7 @@ function applyAbAttrsInternal<TAttr extends AbAttr>(attrType: { new(...args: any
return applyNextAbAttr(); return applyNextAbAttr();
pokemon.scene.setPhaseQueueSplice(); pokemon.scene.setPhaseQueueSplice();
const onApplySuccess = () => { 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); pokemon.battleData.abilitiesApplied.push(ability.id);
} }
if (attr.showAbility && !quiet) { if (attr.showAbility && !quiet) {
@ -3485,17 +3560,22 @@ export function initAbilities() {
.attr(UnsuppressableAbilityAbAttr) .attr(UnsuppressableAbilityAbAttr)
.attr(NoFusionAbilityAbAttr), .attr(NoFusionAbilityAbAttr),
new Ability(Abilities.DISGUISE, 7) new Ability(Abilities.DISGUISE, 7)
.attr(PreDefendMovePowerToOneAbAttr, (target, user, move) => target.formIndex == 0 && target.getAttackTypeEffectiveness(move.type, user) > 0) .attr(DisguiseConfusionInteractionAbAttr)
.attr(PostSummonFormChangeAbAttr, p => p.battleData.hitCount === 0 ? 0 : 1) .attr(PreDefendDisguiseNullifyDamageAbAttr)
.attr(PostBattleInitFormChangeAbAttr, p => p.battleData.hitCount === 0 ? 0 : 1) .attr(PostDefendDisguiseRecoilAbAttr)
.attr(PostDefendFormChangeAbAttr, p => p.battleData.hitCount === 0 ? 0 : 1) //Disguise has the disguised and busted states. Acording to the state, one can change into the other in certain moments of battle:
.attr(PreDefendFormChangeAbAttr, p => p.battleData.hitCount === 0 ? 0 : 1) //Before being hit (both ways), after switch in (busted to disguised), after battle starts (busted to disguised), after being hit (disguised to busted).
.attr(PostDefendDisguiseAbAttr) .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)
//Being a signature ability with form change that triggers once per battle:
.attr(UncopiableAbilityAbAttr) .attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr) .attr(UnswappableAbilityAbAttr)
.attr(UnsuppressableAbilityAbAttr) .attr(UnsuppressableAbilityAbAttr)
.attr(NoTransformAbilityAbAttr) .attr(NoTransformAbilityAbAttr)
.attr(NoFusionAbilityAbAttr) .attr(NoFusionAbilityAbAttr)
.attr(SpecialOncePerBattleAbilityAbAttr)
.ignorable() .ignorable()
.partial(), .partial(),
new Ability(Abilities.BATTLE_BOND, 7) new Ability(Abilities.BATTLE_BOND, 7)

View File

@ -8,7 +8,7 @@ import * as Utils from "../utils";
import { Moves } from "./enums/moves"; import { Moves } from "./enums/moves";
import { ChargeAttr, MoveFlags, allMoves } from "./move"; import { ChargeAttr, MoveFlags, allMoves } from "./move";
import { Type } from "./type"; import { Type } from "./type";
import { BlockNonDirectDamageAbAttr, FlinchEffectAbAttr, ReverseDrainAbAttr, applyAbAttrs } from "./ability"; import { BlockNonDirectDamageAbAttr, FlinchEffectAbAttr, ReverseDrainAbAttr, applyAbAttrs, DisguiseConfusionInteractionAbAttr } from "./ability";
import { Abilities } from "./enums/abilities"; import { Abilities } from "./enums/abilities";
import { BattlerTagType } from "./enums/battler-tag-type"; import { BattlerTagType } from "./enums/battler-tag-type";
import { TerrainType } from "./terrain"; import { TerrainType } from "./terrain";
@ -237,8 +237,15 @@ export class ConfusedTag extends BattlerTag {
const def = pokemon.getBattleStat(Stat.DEF); const def = pokemon.getBattleStat(Stat.DEF);
const damage = Math.ceil(((((2 * pokemon.level / 5 + 2) * 40 * atk / def) / 50) + 2) * (pokemon.randSeedInt(15, 85) / 100)); 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!'); pokemon.scene.queueMessage('It hurt itself in its\nconfusion!');
pokemon.damageAndUpdate(damage);
pokemon.battleData.hitCount++; 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++;
}
(pokemon.scene.getCurrentPhase() as MovePhase).cancel(); (pokemon.scene.getCurrentPhase() as MovePhase).cancel();
} }
} }

View File

@ -27,7 +27,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 { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr } from '../data/ability'; import { Ability, AbAttr, BattleStatMultiplierAbAttr, BlockCritAbAttr, BonusCritAbAttr, BypassBurnDamageReductionAbAttr, FieldPriorityMoveImmunityAbAttr, FieldVariableMovePowerAbAttr, IgnoreOpponentStatChangesAbAttr, MoveImmunityAbAttr, MoveTypeChangeAttr, NonSuperEffectiveImmunityAbAttr, PreApplyBattlerTagAbAttr, PreDefendFullHpEndureAbAttr, ReceivedMoveDamageMultiplierAbAttr, ReduceStatusEffectDurationAbAttr, StabBoostAbAttr, StatusEffectImmunityAbAttr, TypeImmunityAbAttr, VariableMovePowerAbAttr, VariableMoveTypeAbAttr, WeightMultiplierAbAttr, allAbilities, applyAbAttrs, applyBattleStatMultiplierAbAttrs, applyPostDefendAbAttrs, applyPreApplyBattlerTagAbAttrs, applyPreAttackAbAttrs, applyPreDefendAbAttrs, applyPreSetStatusAbAttrs, UnsuppressableAbilityAbAttr, SuppressFieldAbilitiesAbAttr, NoFusionAbilityAbAttr, MultCritAbAttr, IgnoreTypeImmunityAbAttr, DamageBoostAbAttr, IgnoreTypeStatusEffectImmunityAbAttr, PreDefendDisguiseNullifyDamageAbAttr } from '../data/ability';
import { Abilities } from "#app/data/enums/abilities"; import { Abilities } from "#app/data/enums/abilities";
import PokemonData from '../system/pokemon-data'; import PokemonData from '../system/pokemon-data';
import Battle, { BattlerIndex } from '../battle'; import Battle, { BattlerIndex } from '../battle';
@ -1607,7 +1607,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (power.value === 0) { if (power.value === 0) {
damage.value = 0; 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(PreDefendDisguiseNullifyDamageAbAttr, this, source, battlerMove, cancelled, damage , preResult);
result = (preResult as Utils.NumberHolder).value;
console.log('damage', damage.value, move.name, power.value, sourceAtk, targetDef); console.log('damage', damage.value, move.name, power.value, sourceAtk, targetDef);
if (damage.value) { if (damage.value) {

View File

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