diff --git a/src/battle-phases.ts b/src/battle-phases.ts index 486ad7ba4..c62d6cf12 100644 --- a/src/battle-phases.ts +++ b/src/battle-phases.ts @@ -1982,11 +1982,25 @@ export class DamagePhase extends PokemonPhase { start() { super.start(); + if (this.damageResult === HitResult.ONE_HIT_KO) { + this.scene.toggleInvert(true); + this.scene.time.delayedCall(Utils.fixedInt(1000), () => { + this.scene.toggleInvert(false); + this.applyDamage(); + }); + return; + } + + this.applyDamage(); + } + + applyDamage() { switch (this.damageResult) { case HitResult.EFFECTIVE: this.scene.playSound('hit'); break; case HitResult.SUPER_EFFECTIVE: + case HitResult.ONE_HIT_KO: this.scene.playSound('hit_strong'); break; case HitResult.NOT_VERY_EFFECTIVE: diff --git a/src/battle-scene.ts b/src/battle-scene.ts index e3ce16b3a..cc1e62446 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -33,6 +33,7 @@ import { Setting, settingOptions } from './system/settings'; import SettingsUiHandler from './ui/settings-ui-handler'; import MessageUiHandler from './ui/message-ui-handler'; import { Species } from './data/species'; +import InvertPostFX from './pipelines/invert'; const enableAuto = true; const quickStart = false; @@ -1072,6 +1073,13 @@ export default class BattleScene extends Phaser.Scene { return 0; } + toggleInvert(invert: boolean): void { + if (invert) + this.cameras.main.setPostPipeline(InvertPostFX); + else + this.cameras.main.removePostPipeline('InvertPostFX'); + } + getCurrentPhase(): BattlePhase { return this.currentPhase; } diff --git a/src/data/move.ts b/src/data/move.ts index ec0a41586..0ec2af072 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -1313,16 +1313,13 @@ export class ClearWeatherAttr extends MoveEffectAttr { } } -export class OneHitKOAttr extends MoveEffectAttr { - constructor() { - super(false, MoveEffectTrigger.HIT); - } - +export class OneHitKOAttr extends MoveAttr { apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean { if (target.species.speciesId === Species.ETERNATUS && target.formIndex === 1) return false; - target.damage(target.hp, true); - user.scene.queueMessage('It\'s a one-hit KO!'); + + (args[0] as Utils.BooleanHolder).value = true; + return true; } } diff --git a/src/main.ts b/src/main.ts index f43b9e062..a9cbd3641 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,6 @@ import Phaser from 'phaser'; import BattleScene from './battle-scene'; +import InvertPostFX from './pipelines/invert'; const config: Phaser.Types.Core.GameConfig = { type: Phaser.WEBGL, @@ -10,6 +11,7 @@ const config: Phaser.Types.Core.GameConfig = { mode: Phaser.Scale.FIT }, pixelArt: true, + pipeline: [ InvertPostFX ] as unknown as Phaser.Types.Core.PipelineConfig, scene: [ BattleScene ] }; diff --git a/src/pipelines/invert.ts b/src/pipelines/invert.ts new file mode 100644 index 000000000..21a2a9439 --- /dev/null +++ b/src/pipelines/invert.ts @@ -0,0 +1,27 @@ +import { Game } from "phaser"; + +const fragShader = ` +precision mediump float; + +uniform sampler2D uMainSampler; + +varying vec2 outTexCoord; + +void main() +{ + gl_FragColor = 1.0 - texture2D(uMainSampler, outTexCoord); +} +`; + +export default class InvertPostFX extends Phaser.Renderer.WebGL.Pipelines.PostFXPipeline { + constructor (game: Game) { + super({ + game, + name: 'InvertPostFX', + fragShader, + uniforms: [ + 'uMainSampler' + ] + } as Phaser.Types.Renderer.WebGL.WebGLPipelineConfig); + } +} \ No newline at end of file diff --git a/src/pokemon.ts b/src/pokemon.ts index e315658a7..fc4559ba4 100644 --- a/src/pokemon.ts +++ b/src/pokemon.ts @@ -1,7 +1,7 @@ import Phaser from 'phaser'; import BattleScene, { AnySound } from './battle-scene'; import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from './ui/battle-info'; -import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariablePowerAttr, Moves, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, AttackMove, AddBattlerTagAttr } from "./data/move"; +import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariablePowerAttr, Moves, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, AttackMove, AddBattlerTagAttr, OneHitKOAttr } from "./data/move"; import { default as PokemonSpecies, PokemonSpeciesForm, getPokemonSpecies } from './data/pokemon-species'; import * as Utils from './utils'; import { Type, TypeDamageMultiplier, getTypeDamageMultiplier } from './data/type'; @@ -790,14 +790,22 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { console.log('damage', damage, move.name, move.power, sourceAtk, targetDef); if (!result) { - if (typeMultiplier.value >= 2) - result = HitResult.SUPER_EFFECTIVE; - else if (typeMultiplier.value >= 1) - result = HitResult.EFFECTIVE; - else if (typeMultiplier.value > 0) - result = HitResult.NOT_VERY_EFFECTIVE; - else + if (!typeMultiplier.value) result = HitResult.NO_EFFECT; + else { + const oneHitKo = new Utils.BooleanHolder(false); + applyMoveAttrs(OneHitKOAttr, source, this, move, oneHitKo); + if (oneHitKo.value) { + result = HitResult.ONE_HIT_KO; + isCritical = false; + damage.value = this.hp; + } else if (typeMultiplier.value >= 2) + result = HitResult.SUPER_EFFECTIVE; + else if (typeMultiplier.value >= 1) + result = HitResult.EFFECTIVE; + else + result = HitResult.NOT_VERY_EFFECTIVE; + } } if (!source.isPlayer()) @@ -828,6 +836,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { case HitResult.NO_EFFECT: this.scene.queueMessage(`It doesn\'t affect ${this.name}!`); break; + case HitResult.ONE_HIT_KO: + this.scene.queueMessage('It\'s a one-hit KO!'); + break; } } @@ -1692,13 +1703,14 @@ export enum HitResult { SUPER_EFFECTIVE, NOT_VERY_EFFECTIVE, NO_EFFECT, + ONE_HIT_KO, STATUS, FAIL, MISS, OTHER } -export type DamageResult = HitResult.EFFECTIVE | HitResult.SUPER_EFFECTIVE | HitResult.NOT_VERY_EFFECTIVE | HitResult.OTHER; +export type DamageResult = HitResult.EFFECTIVE | HitResult.SUPER_EFFECTIVE | HitResult.NOT_VERY_EFFECTIVE | HitResult.ONE_HIT_KO | HitResult.OTHER; export class PokemonMove { public moveId: Moves;