From 203ba1646d4e2f658cadcf71ab99c20b6d920ac3 Mon Sep 17 00:00:00 2001 From: Flashfyre Date: Sun, 31 Mar 2024 12:00:54 -0400 Subject: [PATCH] Make trainer switch AI aware of arena traps --- src/battle-scene.ts | 7 ++++--- src/data/arena-tag.ts | 25 ++++++++++++++++++++++++- src/data/battler-tags.ts | 2 +- src/field/arena.ts | 10 +++++++++- src/field/trainer.ts | 6 +++++- src/phases.ts | 2 +- src/pipelines/field-sprite.ts | 6 +++--- 7 files changed, 47 insertions(+), 11 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index e3e1abd66..6a52ae6f2 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -948,15 +948,16 @@ export default class BattleScene extends Phaser.Scene { if (init) { const biomeKey = getBiomeKey(biome); - this.arenaBg.setTexture(`${biomeKey}_bg`); - this.arenaBg.pipelineData['terrainColorRatio'] = this.arena.getBgTerrainColorRatioForBiome(); - this.arenaBgTransition.setTexture(`${biomeKey}_bg`); this.arenaPlayer.setBiome(biome); this.arenaPlayerTransition.setBiome(biome); this.arenaEnemy.setBiome(biome); this.arenaNextEnemy.setBiome(biome); + this.arenaBg.setTexture(`${biomeKey}_bg`); + this.arenaBgTransition.setTexture(`${biomeKey}_bg`); } + this.arenaBg.pipelineData = { terrainColorRatio: this.arena.getBgTerrainColorRatioForBiome() }; + return this.arena; } diff --git a/src/data/arena-tag.ts b/src/data/arena-tag.ts index 8b5fc4612..ac2b54cad 100644 --- a/src/data/arena-tag.ts +++ b/src/data/arena-tag.ts @@ -152,6 +152,10 @@ export class ArenaTrapTag extends ArenaTag { activateTrap(pokemon: Pokemon): boolean { return false; } + + getMatchupScoreMultiplier(pokemon: Pokemon): number { + return pokemon.isGrounded() ? 1 : Phaser.Math.Linear(0, 1 / Math.pow(2, this.layers), Math.min(pokemon.getHpRatio(), 0.5) * 2); + } } class SpikesTag extends ArenaTrapTag { @@ -216,6 +220,14 @@ class ToxicSpikesTag extends ArenaTrapTag { return false; } + + getMatchupScoreMultiplier(pokemon: Pokemon): number { + if (pokemon.isGrounded() || !pokemon.canSetStatus(StatusEffect.POISON, true)) + return 1; + if (pokemon.isOfType(Type.POISON)) + return 1.25; + return super.getMatchupScoreMultiplier(pokemon); + } } class DelayedAttackTag extends ArenaTag { @@ -251,7 +263,7 @@ class StealthRockTag extends ArenaTrapTag { arena.scene.queueMessage(`Pointed stones float in the air\naround ${source.getOpponentDescriptor()}!`); } - activateTrap(pokemon: Pokemon): boolean { + getDamageHpRatio(pokemon: Pokemon): number { const effectiveness = pokemon.getAttackTypeEffectiveness(Type.ROCK); let damageHpRatio: number; @@ -277,6 +289,12 @@ class StealthRockTag extends ArenaTrapTag { break; } + return damageHpRatio; + } + + activateTrap(pokemon: Pokemon): boolean { + const damageHpRatio = this.getDamageHpRatio(pokemon); + if (damageHpRatio) { const damage = Math.ceil(pokemon.getMaxHp() * damageHpRatio); pokemon.scene.queueMessage(`Pointed stones dug into\n${pokemon.name}!`); @@ -285,6 +303,11 @@ class StealthRockTag extends ArenaTrapTag { return false; } + + getMatchupScoreMultiplier(pokemon: Pokemon): number { + const damageHpRatio = this.getDamageHpRatio(pokemon); + return Phaser.Math.Linear(super.getMatchupScoreMultiplier(pokemon), 1, 1 - Math.pow(damageHpRatio, damageHpRatio)); + } } export class TrickRoomTag extends ArenaTag { diff --git a/src/data/battler-tags.ts b/src/data/battler-tags.ts index dd7371aeb..6fff284d8 100644 --- a/src/data/battler-tags.ts +++ b/src/data/battler-tags.ts @@ -264,7 +264,7 @@ export class SeedTag extends BattlerTag { private sourceIndex: integer; constructor(sourceId: integer) { - super(BattlerTagType.SEEDED, BattlerTagLapseType.AFTER_MOVE, 1, Moves.LEECH_SEED, sourceId); + super(BattlerTagType.SEEDED, BattlerTagLapseType.TURN_END, 1, Moves.LEECH_SEED, sourceId); } onAdd(pokemon: Pokemon): void { diff --git a/src/field/arena.ts b/src/field/arena.ts index 7f23a9a34..76c58dbe2 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -488,6 +488,14 @@ export class Arena { : this.tags.find(t => t instanceof tagType && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side)); } + findTags(tagPredicate: (t: ArenaTag) => boolean): ArenaTag[] { + return this.findTagsOnSide(tagPredicate, ArenaTagSide.BOTH); + } + + findTagsOnSide(tagPredicate: (t: ArenaTag) => boolean, side: ArenaTagSide): ArenaTag[] { + return this.tags.filter(t => tagPredicate && (side === ArenaTagSide.BOTH || t.side === ArenaTagSide.BOTH || t.side === side)); + } + lapseTags(): void { this.tags.filter(t => !(t.lapse(this))).forEach(t => { t.onRemove(this); @@ -662,7 +670,7 @@ export class ArenaBase extends Phaser.GameObjects.Container { const hasProps = getBiomeHasProps(biome); const biomeKey = getBiomeKey(biome); const baseKey = `${biomeKey}_${this.player ? 'a' : 'b'}`; - + this.base.setTexture(baseKey); if (this.base.texture.frameTotal > 1) { diff --git a/src/field/trainer.ts b/src/field/trainer.ts index d680f0416..d52085b65 100644 --- a/src/field/trainer.ts +++ b/src/field/trainer.ts @@ -8,6 +8,8 @@ import { EnemyPokemon } from "./pokemon"; import * as Utils from "../utils"; import { PersistentModifier } from "../modifier/modifier"; import { trainerNamePools } from "../data/trainer-names"; +import { ArenaTagType } from "#app/data/enums/arena-tag-type"; +import { ArenaTag, ArenaTagSide, ArenaTrapTag } from "#app/data/arena-tag"; export enum TrainerVariant { DEFAULT, @@ -272,7 +274,7 @@ export default class Trainer extends Phaser.GameObjects.Container { return ret; } - getPartyMemberMatchupScores(trainerSlot: TrainerSlot = TrainerSlot.NONE): [integer, integer][] { + getPartyMemberMatchupScores(trainerSlot: TrainerSlot = TrainerSlot.NONE, forSwitch: boolean = false): [integer, integer][] { if (trainerSlot && !this.isDouble()) trainerSlot = TrainerSlot.NONE; @@ -288,6 +290,8 @@ export default class Trainer extends Phaser.GameObjects.Container { score /= 2; } score /= playerField.length; + if (forSwitch && !p.isOnField()) + this.scene.arena.findTagsOnSide(t => t instanceof ArenaTrapTag, ArenaTagSide.ENEMY).map(t => score *= (t as ArenaTrapTag).getMatchupScoreMultiplier(p)); ret = [ party.indexOf(p), score ]; return ret; }); diff --git a/src/phases.ts b/src/phases.ts index dca73ea55..7c3f16c79 100644 --- a/src/phases.ts +++ b/src/phases.ts @@ -1715,7 +1715,7 @@ export class EnemyCommandPhase extends FieldPhase { return this.end(); } else if (!trapTag && !trapped.value) { - const partyMemberScores = trainer.getPartyMemberMatchupScores(enemyPokemon.trainerSlot); + const partyMemberScores = trainer.getPartyMemberMatchupScores(enemyPokemon.trainerSlot, true); if (partyMemberScores.length) { const matchupScores = opponents.map(opp => enemyPokemon.getMatchupScore(opp)); diff --git a/src/pipelines/field-sprite.ts b/src/pipelines/field-sprite.ts index 80e3037a6..a5b660810 100644 --- a/src/pipelines/field-sprite.ts +++ b/src/pipelines/field-sprite.ts @@ -163,8 +163,8 @@ void main() { color = vec4(blendHardLight(color.rgb, dayNightTint), color.a); } - if (terrainColorRatio > 0.0 && 1.0 - terrainColorRatio < outTexCoord.y) { - if (color.a > 0.0 && terrainColor.r > 0.0 && terrainColor.g > 0.0 && terrainColor.b > 0.0) { + if (terrainColorRatio > 0.0 && (1.0 - terrainColorRatio) < outTexCoord.y) { + if (color.a > 0.0 && (terrainColor.r > 0.0 || terrainColor.g > 0.0 || terrainColor.b > 0.0)) { color.rgb = mix(color.rgb, blendHue(color.rgb, terrainColor), 1.0); } } @@ -226,7 +226,7 @@ export default class FieldSpritePipeline extends Phaser.Renderer.WebGL.Pipelines onBind(gameObject: Phaser.GameObjects.GameObject): void { super.onBind(); - const sprite = (gameObject as Phaser.GameObjects.Sprite); + const sprite = gameObject as Phaser.GameObjects.Sprite | Phaser.GameObjects.NineSlice; const scene = sprite.scene as BattleScene; const data = sprite.pipelineData;