Implemented explosive moves and Damp (#290)

* Implemented explosives

* Add Aftermath and Magic Guard interactions

* Adjust AI score for Mind Blown/Steel Beam
This commit is contained in:
AJ Fontaine 2024-04-25 21:42:41 -04:00 committed by GitHub
parent 06f6797ece
commit 9f3bef0142
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 59 additions and 7 deletions

View File

@ -869,6 +869,13 @@ export class MoveTypeChangePowerMultiplierAbAttr extends VariableMoveTypeAbAttr
}
}
export class FieldPreventExplosiveMovesAbAttr extends AbAttr {
apply(pokemon: Pokemon, passive: boolean, cancelled: Utils.BooleanHolder, args: any[]): boolean | Promise<boolean> {
cancelled.value = true;
return true;
}
}
export class MoveTypeChangeAttr extends PreAttackAbAttr {
private newType: Type;
private powerMultiplier: number;
@ -2057,6 +2064,11 @@ export class PostFaintContactDamageAbAttr extends PostFaintAbAttr {
applyPostFaint(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: PokemonMove, hitResult: HitResult, args: any[]): boolean {
if (move.getMove().checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon)) {
const cancelled = new Utils.BooleanHolder(false);
pokemon.scene.getField(true).map(p=>applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled))
if (cancelled) {
return false;
}
attacker.damageAndUpdate(Math.ceil(attacker.getMaxHp() * (1 / this.damageRatio)), HitResult.OTHER);
return true;
}
@ -2468,8 +2480,8 @@ export function initAbilities() {
.attr(BlockOneHitKOAbAttr)
.ignorable(),
new Ability(Abilities.DAMP, 3)
.ignorable()
.unimplemented(),
.attr(FieldPreventExplosiveMovesAbAttr)
.ignorable(),
new Ability(Abilities.LIMBER, 3)
.attr(StatusEffectImmunityAbAttr, StatusEffect.PARALYSIS)
.ignorable(),

View File

@ -12,7 +12,7 @@ import * as Utils from "../utils";
import { WeatherType } from "./weather";
import { ArenaTagSide, ArenaTrapTag } from "./arena-tag";
import { ArenaTagType } from "./enums/arena-tag-type";
import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, NoTransformAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr, applyPostDefendAbAttrs, PostDefendContactApplyStatusEffectAbAttr, MoveAbilityBypassAbAttr, ReverseDrainAbAttr } from "./ability";
import { UnswappableAbilityAbAttr, UncopiableAbilityAbAttr, UnsuppressableAbilityAbAttr, NoTransformAbilityAbAttr, BlockRecoilDamageAttr, BlockOneHitKOAbAttr, IgnoreContactAbAttr, MaxMultiHitAbAttr, applyAbAttrs, BlockNonDirectDamageAbAttr, applyPreSwitchOutAbAttrs, PreSwitchOutAbAttr, applyPostDefendAbAttrs, PostDefendContactApplyStatusEffectAbAttr, MoveAbilityBypassAbAttr, ReverseDrainAbAttr, FieldPreventExplosiveMovesAbAttr } from "./ability";
import { Abilities } from "./enums/abilities";
import { allAbilities } from './ability';
import { PokemonHeldItemModifier } from "../modifier/modifier";
@ -725,6 +725,40 @@ export class SacrificialAttr extends MoveEffectAttr {
}
}
export class HalfSacrificialAttr extends MoveEffectAttr {
constructor() {
super(true, MoveEffectTrigger.PRE_APPLY);
}
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
if (!super.apply(user, target, move, args))
return false;
const cancelled = new Utils.BooleanHolder(false);
applyAbAttrs(BlockNonDirectDamageAbAttr, user, cancelled);
if (!cancelled){
user.damageAndUpdate(Math.ceil(user.getMaxHp()/2), HitResult.OTHER, false, true, true);
}
return true;
}
getUserBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer {
if (user.isBoss())
return -10;
return Math.ceil(((1 - user.getHpRatio()/2) * 10 - 10) * (target.getAttackTypeEffectiveness(move.type) - 0.5));
}
}
export class ExplosiveAttr extends MoveEffectAttr {
getCondition(): MoveCondition | MoveConditionFunc {
return (user, target, move) =>{
const cancelled = new Utils.BooleanHolder(false);
user.scene.getField(true).map(p=>applyAbAttrs(FieldPreventExplosiveMovesAbAttr, p, cancelled));
return !cancelled.value;
}
}
}
export enum MultiHitType {
_2,
_2_TO_5,
@ -4000,6 +4034,7 @@ export function initMoves() {
.attr(CopyMoveAttr)
.ignoresVirtual(),
new AttackMove(Moves.SELF_DESTRUCT, Type.NORMAL, MoveCategory.PHYSICAL, 200, 100, 5, -1, 0, 1)
.attr(ExplosiveAttr)
.attr(SacrificialAttr)
.makesContact(false)
.target(MoveTarget.ALL_NEAR_OTHERS),
@ -4090,6 +4125,7 @@ export function initMoves() {
new AttackMove(Moves.CRABHAMMER, Type.WATER, MoveCategory.PHYSICAL, 100, 90, 10, -1, 0, 1)
.attr(HighCritAttr),
new AttackMove(Moves.EXPLOSION, Type.NORMAL, MoveCategory.PHYSICAL, 250, 100, 5, -1, 0, 1)
.attr(ExplosiveAttr)
.attr(SacrificialAttr)
.makesContact(false)
.target(MoveTarget.ALL_NEAR_OTHERS),
@ -5622,7 +5658,8 @@ export function initMoves() {
.partial(),
/* End Unused */
new AttackMove(Moves.MIND_BLOWN, Type.FIRE, MoveCategory.SPECIAL, 150, 100, 5, -1, 0, 7)
.attr(RecoilAttr, true, 0.5)
.attr(ExplosiveAttr)
.attr(HalfSacrificialAttr)
.target(MoveTarget.ALL_NEAR_OTHERS),
new AttackMove(Moves.PLASMA_FISTS, Type.ELECTRIC, MoveCategory.PHYSICAL, 100, 100, 15, -1, 0, 7)
.punchingMove()
@ -5837,7 +5874,7 @@ export function initMoves() {
new AttackMove(Moves.ETERNABEAM, Type.DRAGON, MoveCategory.SPECIAL, 160, 90, 5, -1, 0, 8)
.attr(RechargeAttr),
new AttackMove(Moves.STEEL_BEAM, Type.STEEL, MoveCategory.SPECIAL, 140, 95, 5, -1, 0, 8)
.attr(RecoilAttr, true, 0.5),
.attr(HalfSacrificialAttr),
new AttackMove(Moves.EXPANDING_FORCE, Type.PSYCHIC, MoveCategory.SPECIAL, 80, 100, 10, -1, 0, 8)
.partial(),
new AttackMove(Moves.STEEL_ROLLER, Type.STEEL, MoveCategory.PHYSICAL, 130, 100, 5, -1, 0, 8)
@ -5858,8 +5895,10 @@ export function initMoves() {
.attr(StatusEffectAttr, StatusEffect.POISON)
.partial(),
new AttackMove(Moves.MISTY_EXPLOSION, Type.FAIRY, MoveCategory.SPECIAL, 100, 100, 5, -1, 0, 8)
.attr(ExplosiveAttr)
.attr(SacrificialAttr)
.target(MoveTarget.ALL_NEAR_OTHERS)
.partial(),
.attr(MovePowerMultiplierAttr, (user, target, move) => user.scene.arena.getTerrainType() === TerrainType.MISTY && user.isGrounded() ? 1.5 : 1),
new AttackMove(Moves.GRASSY_GLIDE, Type.GRASS, MoveCategory.PHYSICAL, 55, 100, 20, -1, 0, 8)
.partial(),
new AttackMove(Moves.RISING_VOLTAGE, Type.ELECTRIC, MoveCategory.SPECIAL, 70, 100, 20, -1, 0, 8)

View File

@ -4,7 +4,7 @@ import { Variant, VariantSet, variantColorCache } from '#app/data/variant';
import { variantData } from '#app/data/variant';
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from '../ui/battle-info';
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 } 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, ExplosiveAttr } from "../data/move";
import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm, starterPassiveAbilities } from '../data/pokemon-species';
import * as Utils from '../utils';
import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from '../data/type';
@ -1229,6 +1229,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (types.find(t => move.isTypeImmune(t)))
typeMultiplier.value = 0;
applyMoveAttrs(ExplosiveAttr, source, null, battlerMove.getMove());
switch (moveCategory) {
case MoveCategory.PHYSICAL:
case MoveCategory.SPECIAL: