Factor type immunity abilities into enemy AI
parent
be405f61dd
commit
fdff3a549c
|
@ -100,7 +100,7 @@ export default class Battle {
|
||||||
|
|
||||||
randSeedGaussForLevel(value: number): number {
|
randSeedGaussForLevel(value: number): number {
|
||||||
let rand = 0;
|
let rand = 0;
|
||||||
for (var i = value; i > 0; i--)
|
for (let i = value; i > 0; i--)
|
||||||
rand += Phaser.Math.RND.realInRange(0, 1);
|
rand += Phaser.Math.RND.realInRange(0, 1);
|
||||||
return rand / value;
|
return rand / value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,9 +213,13 @@ export class TypeImmunityHealAbAttr extends TypeImmunityAbAttr {
|
||||||
const ret = super.applyPreDefend(pokemon, attacker, move, cancelled, args);
|
const ret = super.applyPreDefend(pokemon, attacker, move, cancelled, args);
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
if (pokemon.getHpRatio() < 1)
|
if (pokemon.getHpRatio() < 1) {
|
||||||
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(),
|
const simulated = args.length > 1 && args[1];
|
||||||
Math.max(Math.floor(pokemon.getMaxHp() / 4), 1), getPokemonMessage(pokemon, `'s ${pokemon.getAbility().name}\nrestored its HP a little!`), true));
|
if (!simulated) {
|
||||||
|
pokemon.scene.unshiftPhase(new PokemonHealPhase(pokemon.scene, pokemon.getBattlerIndex(),
|
||||||
|
Math.max(Math.floor(pokemon.getMaxHp() / 4), 1), getPokemonMessage(pokemon, `'s ${pokemon.getAbility().name}\nrestored its HP a little!`), true));
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,7 +243,9 @@ class TypeImmunityStatChangeAbAttr extends TypeImmunityAbAttr {
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
cancelled.value = true;
|
cancelled.value = true;
|
||||||
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.levels));
|
const simulated = args.length > 1 && args[1];
|
||||||
|
if (!simulated)
|
||||||
|
pokemon.scene.unshiftPhase(new StatChangePhase(pokemon.scene, pokemon.getBattlerIndex(), true, [ this.stat ], this.levels));
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -262,7 +268,9 @@ class TypeImmunityAddBattlerTagAbAttr extends TypeImmunityAbAttr {
|
||||||
|
|
||||||
if (ret) {
|
if (ret) {
|
||||||
cancelled.value = true;
|
cancelled.value = true;
|
||||||
pokemon.addTag(this.tagType, this.turnCount, undefined, pokemon.id);
|
const simulated = args.length > 1 && args[1];
|
||||||
|
if (!simulated)
|
||||||
|
pokemon.addTag(this.tagType, this.turnCount, undefined, pokemon.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
|
@ -275,7 +283,7 @@ export class NonSuperEffectiveImmunityAbAttr extends TypeImmunityAbAttr {
|
||||||
}
|
}
|
||||||
|
|
||||||
applyPreDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
applyPreDefend(pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, args: any[]): boolean {
|
||||||
if (pokemon.getAttackMoveEffectiveness(move.getMove().type) < 2) {
|
if (pokemon.getAttackTypeEffectiveness(move.getMove().type) < 2) {
|
||||||
cancelled.value = true;
|
cancelled.value = true;
|
||||||
(args[0] as Utils.NumberHolder).value = 0;
|
(args[0] as Utils.NumberHolder).value = 0;
|
||||||
return true;
|
return true;
|
||||||
|
@ -1103,7 +1111,7 @@ export class SyncEncounterNatureAbAttr extends AbAttr {
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyAbAttrsInternal<TAttr extends AbAttr>(attrType: { new(...args: any[]): TAttr },
|
function applyAbAttrsInternal<TAttr extends AbAttr>(attrType: { new(...args: any[]): TAttr },
|
||||||
pokemon: Pokemon, applyFunc: AbAttrApplyFunc<TAttr>, isAsync?: boolean, showAbilityInstant?: boolean): Promise<void> {
|
pokemon: Pokemon, applyFunc: AbAttrApplyFunc<TAttr>, isAsync: boolean = false, showAbilityInstant: boolean = false, quiet: boolean = false): Promise<void> {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
if (!pokemon.canApplyAbility())
|
if (!pokemon.canApplyAbility())
|
||||||
return resolve();
|
return resolve();
|
||||||
|
@ -1126,18 +1134,20 @@ function applyAbAttrsInternal<TAttr extends AbAttr>(attrType: { new(...args: any
|
||||||
return applyNextAbAttr();
|
return applyNextAbAttr();
|
||||||
pokemon.scene.setPhaseQueueSplice();
|
pokemon.scene.setPhaseQueueSplice();
|
||||||
const onApplySuccess = () => {
|
const onApplySuccess = () => {
|
||||||
if (attr.showAbility) {
|
if (attr.showAbility && !quiet) {
|
||||||
if (showAbilityInstant)
|
if (showAbilityInstant)
|
||||||
pokemon.scene.abilityBar.showAbility(pokemon);
|
pokemon.scene.abilityBar.showAbility(pokemon);
|
||||||
else
|
else
|
||||||
queueShowAbility(pokemon);
|
queueShowAbility(pokemon);
|
||||||
}
|
}
|
||||||
const message = attr.getTriggerMessage(pokemon);
|
if (!quiet) {
|
||||||
if (message) {
|
const message = attr.getTriggerMessage(pokemon);
|
||||||
if (isAsync)
|
if (message) {
|
||||||
pokemon.scene.ui.showText(message, null, () => pokemon.scene.ui.showText(null, 0), null, true);
|
if (isAsync)
|
||||||
else
|
pokemon.scene.ui.showText(message, null, () => pokemon.scene.ui.showText(null, 0), null, true);
|
||||||
pokemon.scene.queueMessage(message);
|
else
|
||||||
|
pokemon.scene.queueMessage(message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const result = applyFunc(attr);
|
const result = applyFunc(attr);
|
||||||
|
@ -1163,7 +1173,8 @@ export function applyAbAttrs(attrType: { new(...args: any[]): AbAttr }, pokemon:
|
||||||
|
|
||||||
export function applyPreDefendAbAttrs(attrType: { new(...args: any[]): PreDefendAbAttr },
|
export function applyPreDefendAbAttrs(attrType: { new(...args: any[]): PreDefendAbAttr },
|
||||||
pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, ...args: any[]): Promise<void> {
|
pokemon: Pokemon, attacker: Pokemon, move: PokemonMove, cancelled: Utils.BooleanHolder, ...args: any[]): Promise<void> {
|
||||||
return applyAbAttrsInternal<PreDefendAbAttr>(attrType, pokemon, attr => attr.applyPreDefend(pokemon, attacker, move, cancelled, args));
|
const simulated = args.length > 1 && args[1];
|
||||||
|
return applyAbAttrsInternal<PreDefendAbAttr>(attrType, pokemon, attr => attr.applyPreDefend(pokemon, attacker, move, cancelled, args), false, false, simulated);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function applyPostDefendAbAttrs(attrType: { new(...args: any[]): PostDefendAbAttr },
|
export function applyPostDefendAbAttrs(attrType: { new(...args: any[]): PostDefendAbAttr },
|
||||||
|
@ -1928,7 +1939,7 @@ export function initAbilities() {
|
||||||
new Ability(Abilities.SHADOW_SHIELD, "Shadow Shield (N)", "Reduces the amount of damage the Pokémon takes while its HP is full.", 7),
|
new Ability(Abilities.SHADOW_SHIELD, "Shadow Shield (N)", "Reduces the amount of damage the Pokémon takes while its HP is full.", 7),
|
||||||
new Ability(Abilities.PRISM_ARMOR, "Prism Armor (N)", "Reduces the power of supereffective attacks taken.", 7),
|
new Ability(Abilities.PRISM_ARMOR, "Prism Armor (N)", "Reduces the power of supereffective attacks taken.", 7),
|
||||||
new Ability(Abilities.NEUROFORCE, "Neuroforce", "Powers up moves that are super effective.", 7)
|
new Ability(Abilities.NEUROFORCE, "Neuroforce", "Powers up moves that are super effective.", 7)
|
||||||
.attr(MovePowerBoostAbAttr, (user, target, move) => target.getAttackMoveEffectiveness(move.type) >= 2, 1.25),
|
.attr(MovePowerBoostAbAttr, (user, target, move) => target.getAttackTypeEffectiveness(move.type) >= 2, 1.25),
|
||||||
new Ability(Abilities.INTREPID_SWORD, "Intrepid Sword (N)", "Boosts the Pokémon's Attack stat when the Pokémon enters a battle.", 8),
|
new Ability(Abilities.INTREPID_SWORD, "Intrepid Sword (N)", "Boosts the Pokémon's Attack stat when the Pokémon enters a battle.", 8),
|
||||||
new Ability(Abilities.DAUNTLESS_SHIELD, "Dauntless Shield (N)", "Boosts the Pokémon's Defense stat when the Pokémon enters a battle.", 8),
|
new Ability(Abilities.DAUNTLESS_SHIELD, "Dauntless Shield (N)", "Boosts the Pokémon's Defense stat when the Pokémon enters a battle.", 8),
|
||||||
new Ability(Abilities.LIBERO, "Libero (N)", "Changes the Pokémon's type to the type of the move it's about to use.", 8),
|
new Ability(Abilities.LIBERO, "Libero (N)", "Changes the Pokémon's type to the type of the move it's about to use.", 8),
|
||||||
|
|
|
@ -240,7 +240,7 @@ class StealthRockTag extends ArenaTrapTag {
|
||||||
}
|
}
|
||||||
|
|
||||||
activateTrap(pokemon: Pokemon): boolean {
|
activateTrap(pokemon: Pokemon): boolean {
|
||||||
const effectiveness = pokemon.getAttackMoveEffectiveness(Type.ROCK);
|
const effectiveness = pokemon.getAttackTypeEffectiveness(Type.ROCK);
|
||||||
|
|
||||||
let damageHpRatio: number;
|
let damageHpRatio: number;
|
||||||
|
|
||||||
|
|
|
@ -282,7 +282,7 @@ export class AttackMove extends Move {
|
||||||
|
|
||||||
let attackScore = 0;
|
let attackScore = 0;
|
||||||
|
|
||||||
const effectiveness = target.getAttackMoveEffectiveness(this.type);
|
const effectiveness = target.getAttackTypeEffectiveness(this.type);
|
||||||
attackScore = Math.pow(effectiveness - 1, 2) * effectiveness < 1 ? -2 : 2;
|
attackScore = Math.pow(effectiveness - 1, 2) * effectiveness < 1 ? -2 : 2;
|
||||||
if (attackScore) {
|
if (attackScore) {
|
||||||
if (this.category === MoveCategory.PHYSICAL) {
|
if (this.category === MoveCategory.PHYSICAL) {
|
||||||
|
@ -770,7 +770,7 @@ export class StatusEffectAttr extends MoveEffectAttr {
|
||||||
}
|
}
|
||||||
|
|
||||||
getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number {
|
getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): number {
|
||||||
return !(this.selfTarget ? user : target).status && target.getAttackMoveEffectiveness(move.type) ? Math.floor(move.chance * -0.1) : 0;
|
return !(this.selfTarget ? user : target).status && target.getAttackTypeEffectiveness(move.type) ? Math.floor(move.chance * -0.1) : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Phaser from 'phaser';
|
||||||
import BattleScene, { AnySound } from './battle-scene';
|
import BattleScene, { AnySound } from './battle-scene';
|
||||||
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, VariablePowerAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, MultiHitAttr, StatusEffectAttr } from "./data/move";
|
import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariablePowerAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, MultiHitAttr, StatusEffectAttr, AttackMove } from "./data/move";
|
||||||
import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies } from './data/pokemon-species';
|
import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies } 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';
|
||||||
|
@ -707,7 +707,18 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
return this.getTeraType() !== Type.UNKNOWN;
|
return this.getTeraType() !== Type.UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAttackMoveEffectiveness(moveType: Type): TypeDamageMultiplier {
|
getAttackMoveEffectiveness(source: Pokemon, move: PokemonMove): TypeDamageMultiplier {
|
||||||
|
const typeless = !!move.getMove().getAttrs(TypelessAttr).length;
|
||||||
|
const typeMultiplier = new Utils.NumberHolder(this.getAttackTypeEffectiveness(move.getMove().type));
|
||||||
|
const cancelled = new Utils.BooleanHolder(false);
|
||||||
|
if (!typeless)
|
||||||
|
applyPreDefendAbAttrs(TypeImmunityAbAttr, this, source, move, cancelled, typeMultiplier, true);
|
||||||
|
if (!cancelled.value)
|
||||||
|
applyPreDefendAbAttrs(MoveImmunityAbAttr, this, source, move, cancelled, typeMultiplier, true);
|
||||||
|
return (!cancelled.value ? typeMultiplier.value : 0) as TypeDamageMultiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
getAttackTypeEffectiveness(moveType: Type): TypeDamageMultiplier {
|
||||||
if (moveType === Type.STELLAR)
|
if (moveType === Type.STELLAR)
|
||||||
return this.isTerastallized() ? 2 : 1;
|
return this.isTerastallized() ? 2 : 1;
|
||||||
const types = this.getTypes(true);
|
const types = this.getTypes(true);
|
||||||
|
@ -718,12 +729,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
|
||||||
const types = this.getTypes(true);
|
const types = this.getTypes(true);
|
||||||
const enemyTypes = pokemon.getTypes(true);
|
const enemyTypes = pokemon.getTypes(true);
|
||||||
const outspeed = (this.isActive(true) ? this.getBattleStat(Stat.SPD, pokemon) : this.getStat(Stat.SPD)) <= pokemon.getBattleStat(Stat.SPD, this);
|
const outspeed = (this.isActive(true) ? this.getBattleStat(Stat.SPD, pokemon) : this.getStat(Stat.SPD)) <= pokemon.getBattleStat(Stat.SPD, this);
|
||||||
let atkScore = pokemon.getAttackMoveEffectiveness(types[0]) * (outspeed ? 1.25 : 1);
|
let atkScore = pokemon.getAttackTypeEffectiveness(types[0]) * (outspeed ? 1.25 : 1);
|
||||||
let defScore = 1 / Math.max(this.getAttackMoveEffectiveness(enemyTypes[0]), 0.25);
|
let defScore = 1 / Math.max(this.getAttackTypeEffectiveness(enemyTypes[0]), 0.25);
|
||||||
if (types.length > 1)
|
if (types.length > 1)
|
||||||
atkScore *= pokemon.getAttackMoveEffectiveness(types[1]);
|
atkScore *= pokemon.getAttackTypeEffectiveness(types[1]);
|
||||||
if (enemyTypes.length > 1)
|
if (enemyTypes.length > 1)
|
||||||
defScore *= (1 / this.getAttackMoveEffectiveness(enemyTypes[1]));
|
defScore *= (1 / this.getAttackTypeEffectiveness(enemyTypes[1]));
|
||||||
let hpDiffRatio = this.getHpRatio() + (1 - pokemon.getHpRatio());
|
let hpDiffRatio = this.getHpRatio() + (1 - pokemon.getHpRatio());
|
||||||
if (outspeed)
|
if (outspeed)
|
||||||
hpDiffRatio = Math.min(hpDiffRatio * 1.5, 1);
|
hpDiffRatio = Math.min(hpDiffRatio * 1.5, 1);
|
||||||
|
@ -2204,7 +2215,7 @@ export class EnemyPokemon extends Pokemon {
|
||||||
const target = this.scene.getField()[mt];
|
const target = this.scene.getField()[mt];
|
||||||
let targetScore = move.getUserBenefitScore(this, target, move) + move.getTargetBenefitScore(this, target, move) * (mt < BattlerIndex.ENEMY === this.isPlayer() ? 1 : -1);
|
let targetScore = move.getUserBenefitScore(this, target, move) + move.getTargetBenefitScore(this, target, move) * (mt < BattlerIndex.ENEMY === this.isPlayer() ? 1 : -1);
|
||||||
if (mt !== this.getBattlerIndex())
|
if (mt !== this.getBattlerIndex())
|
||||||
targetScore *= target.getAttackMoveEffectiveness(move.type);
|
targetScore *= target.getAttackMoveEffectiveness(this, pokemonMove);
|
||||||
targetScores.push(targetScore);
|
targetScores.push(targetScore);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import { BattlerIndex } from "../battle";
|
import { BattlerIndex } from "../battle";
|
||||||
import BattleScene, { Button } from "../battle-scene";
|
import BattleScene, { Button } from "../battle-scene";
|
||||||
import { Moves, getMoveTargets } from "../data/move";
|
import { Moves } from "../data/enums/moves";
|
||||||
import { Mode } from "./ui";
|
import { Mode } from "./ui";
|
||||||
import UiHandler from "./ui-handler";
|
import UiHandler from "./ui-handler";
|
||||||
import * as Utils from "../utils";
|
import * as Utils from "../utils";
|
||||||
|
import { getMoveTargets } from "../data/move";
|
||||||
|
|
||||||
export type TargetSelectCallback = (cursor: integer) => void;
|
export type TargetSelectCallback = (cursor: integer) => void;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue