Add move priority, flinching, and fixed damage

pull/1/head
Flashfyre 2023-04-15 17:40:18 -04:00
parent 656b6951b6
commit 0e9710d45c
4 changed files with 700 additions and 605 deletions

View File

@ -1,7 +1,7 @@
import BattleScene from "./battle-scene"; import BattleScene from "./battle-scene";
import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult } from "./pokemon"; import { default as Pokemon, PlayerPokemon, EnemyPokemon, PokemonMove, MoveResult, DamageResult } from "./pokemon";
import * as Utils from './utils'; import * as Utils from './utils';
import { allMoves, applyMoveAttrs, ChargeAttr, HitsTagAttr, MissEffectAttr, MoveCategory, MoveEffectAttr, MoveHitEffectAttr, Moves, MultiHitAttr, OverrideMoveEffectAttr } from "./move"; import { allMoves, applyMoveAttrs, ChargeAttr, ConditionalFailMoveAttr, HitsTagAttr, MissEffectAttr, MoveCategory, MoveEffectAttr, MoveHitEffectAttr, Moves, MultiHitAttr, OverrideMoveEffectAttr } from "./move";
import { Mode } from './ui/ui'; import { Mode } from './ui/ui';
import { Command } from "./ui/command-ui-handler"; import { Command } from "./ui/command-ui-handler";
import { Stat } from "./pokemon-stat"; import { Stat } from "./pokemon-stat";
@ -440,12 +440,31 @@ export class CommandPhase extends BattlePhase {
const playerSpeed = playerPokemon.getBattleStat(Stat.SPD); const playerSpeed = playerPokemon.getBattleStat(Stat.SPD);
const enemySpeed = enemyPokemon.getBattleStat(Stat.SPD); const enemySpeed = enemyPokemon.getBattleStat(Stat.SPD);
const isDelayed = () => playerSpeed < enemySpeed || (playerSpeed === enemySpeed && Utils.randInt(2) === 1); let isDelayed = (command: Command, playerMove: PokemonMove, enemyMove: PokemonMove) => {
switch (command) {
case Command.FIGHT:
const playerMovePriority = playerMove.getMove().priority;
const enemyMovePriority = enemyMove.getMove().priority;
if (playerMovePriority !== enemyMovePriority)
return playerMovePriority < enemyMovePriority;
break;
case Command.BALL:
case Command.POKEMON:
return false;
case Command.RUN:
return true;
}
return playerSpeed < enemySpeed || (playerSpeed === enemySpeed && Utils.randInt(2) === 1);
};
let playerMove: PokemonMove;
switch (command) { switch (command) {
case Command.FIGHT: case Command.FIGHT:
if (playerPokemon.trySelectMove(cursor)) { if (playerPokemon.trySelectMove(cursor)) {
const playerPhase = new PlayerMovePhase(this.scene, playerPokemon, playerPokemon.moveset[cursor]); playerMove = playerPokemon.moveset[cursor];
const playerPhase = new PlayerMovePhase(this.scene, playerPokemon, playerMove);
this.scene.pushPhase(playerPhase); this.scene.pushPhase(playerPhase);
success = true; success = true;
} }
@ -469,7 +488,7 @@ export class CommandPhase extends BattlePhase {
if (success) { if (success) {
const enemyMove = enemyPokemon.getNextMove(); const enemyMove = enemyPokemon.getNextMove();
const enemyPhase = new EnemyMovePhase(this.scene, enemyPokemon, enemyMove); const enemyPhase = new EnemyMovePhase(this.scene, enemyPokemon, enemyMove);
if (isDelayed()) if (isDelayed(command, playerMove, enemyMove))
this.scene.unshiftPhase(enemyPhase); this.scene.unshiftPhase(enemyPhase);
else else
this.scene.pushPhase(enemyPhase); this.scene.pushPhase(enemyPhase);
@ -479,7 +498,7 @@ export class CommandPhase extends BattlePhase {
statusEffectPhases.push(new PostTurnStatusEffectPhase(this.scene, true)); statusEffectPhases.push(new PostTurnStatusEffectPhase(this.scene, true));
if (enemyPokemon.status && enemyPokemon.status.isPostTurn()) { if (enemyPokemon.status && enemyPokemon.status.isPostTurn()) {
const enemyStatusEffectPhase = new PostTurnStatusEffectPhase(this.scene, false); const enemyStatusEffectPhase = new PostTurnStatusEffectPhase(this.scene, false);
if (isDelayed()) if (isDelayed(command, playerMove, enemyMove))
statusEffectPhases.unshift(enemyStatusEffectPhase); statusEffectPhases.unshift(enemyStatusEffectPhase);
else else
statusEffectPhases.push(enemyStatusEffectPhase); statusEffectPhases.push(enemyStatusEffectPhase);
@ -506,8 +525,14 @@ export class TurnEndPhase extends BattlePhase {
} }
start() { start() {
this.scene.getPlayerPokemon().lapseTags(BattleTagLapseType.TURN_END); const playerPokemon = this.scene.getPlayerPokemon();
this.scene.getEnemyPokemon().lapseTags(BattleTagLapseType.TURN_END); const enemyPokemon = this.scene.getEnemyPokemon();
playerPokemon.lapseTags(BattleTagLapseType.TURN_END);
enemyPokemon.lapseTags(BattleTagLapseType.TURN_END);
playerPokemon.battleSummonData.turnCount++;
enemyPokemon.battleSummonData.turnCount++;
this.end(); this.end();
} }
@ -591,9 +616,17 @@ export abstract class MovePhase extends BattlePhase {
return; return;
} }
this.scene.unshiftPhase(new MessagePhase(this.scene, getPokemonMessage(this.pokemon, ` used\n${this.move.getName()}!`), 500)); this.scene.unshiftPhase(new MessagePhase(this.scene, getPokemonMessage(this.pokemon, ` used\n${this.move.getName()}!`), 500));
this.scene.unshiftPhase(this.getEffectPhase());
if (this.pokemon.summonData.moveQueue.length && !this.pokemon.summonData.moveQueue.shift().ignorePP) if (this.pokemon.summonData.moveQueue.length && !this.pokemon.summonData.moveQueue.shift().ignorePP)
this.move.ppUsed++; this.move.ppUsed++;
const failed = new Utils.BooleanHolder(false);
applyMoveAttrs(ConditionalFailMoveAttr, this.scene, this.pokemon,
this.pokemon.isPlayer() ? this.scene.getEnemyPokemon() : this.scene.getPlayerPokemon(), this.move.getMove(), failed);
if (failed.value)
this.scene.unshiftPhase(new MessagePhase(this.scene, 'But it failed!'));
else
this.scene.unshiftPhase(this.getEffectPhase());
this.end(); this.end();
}; };

View File

@ -7,6 +7,7 @@ import * as Utils from "./utils";
export enum BattleTagType { export enum BattleTagType {
NONE, NONE,
FLINCHED,
CONFUSED, CONFUSED,
FRENZY, FRENZY,
FLYING, FLYING,
@ -39,7 +40,22 @@ export class BattleTag {
onOverlap(pokemon: Pokemon): void { } onOverlap(pokemon: Pokemon): void { }
lapse(pokemon: Pokemon): boolean { lapse(pokemon: Pokemon): boolean {
return !!--this.turnCount; return --this.turnCount > 0;
}
}
export class FlinchedTag extends BattleTag {
constructor() {
super(BattleTagType.FLINCHED, BattleTagLapseType.MOVE, 1);
}
lapse(pokemon: Pokemon): boolean {
super.lapse(pokemon);
(pokemon.scene.getCurrentPhase() as MovePhase).cancel();
pokemon.scene.unshiftPhase(new MessagePhase(pokemon.scene, getPokemonMessage(pokemon, ' flinched!')));
return true;
} }
} }
@ -118,6 +134,9 @@ export class HideSpriteTag extends BattleTag {
export function getBattleTag(tagType: BattleTagType, turnCount: integer): BattleTag { export function getBattleTag(tagType: BattleTagType, turnCount: integer): BattleTag {
switch (tagType) { switch (tagType) {
case BattleTagType.FLINCHED:
return new FlinchedTag();
break;
case BattleTagType.CONFUSED: case BattleTagType.CONFUSED:
return new ConfusedTag(tagType, turnCount); return new ConfusedTag(tagType, turnCount);
case BattleTagType.FLYING: case BattleTagType.FLYING:

File diff suppressed because it is too large Load Diff

View File

@ -1,7 +1,7 @@
import Phaser from 'phaser'; import Phaser from 'phaser';
import BattleScene from './battle-scene'; import BattleScene from './battle-scene';
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from './battle-info'; import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from './battle-info';
import { default as Move, allMoves, MoveCategory, Moves, StatChangeAttr, HighCritAttr, HitsTagAttr, applyMoveAttrs } from './move'; import { default as Move, allMoves, MoveCategory, Moves, StatChangeAttr, HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr } from './move';
import { pokemonLevelMoves } from './pokemon-level-moves'; import { pokemonLevelMoves } from './pokemon-level-moves';
import { default as PokemonSpecies, getPokemonSpecies } from './pokemon-species'; import { default as PokemonSpecies, getPokemonSpecies } from './pokemon-species';
import * as Utils from './utils'; import * as Utils from './utils';
@ -412,7 +412,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
apply(source: Pokemon, battlerMove: PokemonMove): MoveResult { apply(source: Pokemon, battlerMove: PokemonMove): MoveResult {
let result: MoveResult = MoveResult.STATUS; let result: MoveResult;
let success = false; let success = false;
const move = battlerMove.getMove(); const move = battlerMove.getMove();
const moveCategory = move.category; const moveCategory = move.category;
@ -422,6 +422,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
case MoveCategory.SPECIAL: case MoveCategory.SPECIAL:
const isPhysical = moveCategory === MoveCategory.PHYSICAL; const isPhysical = moveCategory === MoveCategory.PHYSICAL;
const power = new Utils.NumberHolder(move.power); const power = new Utils.NumberHolder(move.power);
const typeMultiplier = getTypeDamageMultiplier(move.type, this.species.type1) * (this.species.type2 > -1 ? getTypeDamageMultiplier(move.type, this.species.type2) : 1);
this.scene.applyModifiers(AttackTypeBoosterModifier, source, power); this.scene.applyModifiers(AttackTypeBoosterModifier, source, power);
const critChance = new Utils.IntegerHolder(16); const critChance = new Utils.IntegerHolder(16);
applyMoveAttrs(HighCritAttr, this.scene, source, this, move, critChance); applyMoveAttrs(HighCritAttr, this.scene, source, this, move, critChance);
@ -429,7 +430,6 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const sourceAtk = source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK); const sourceAtk = source.getBattleStat(isPhysical ? Stat.ATK : Stat.SPATK);
const targetDef = this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF); const targetDef = this.getBattleStat(isPhysical ? Stat.DEF : Stat.SPDEF);
const stabMultiplier = source.species.type1 === move.type || (source.species.type2 > -1 && source.species.type2 === move.type) ? 1.5 : 1; const stabMultiplier = source.species.type1 === move.type || (source.species.type2 > -1 && source.species.type2 === move.type) ? 1.5 : 1;
const typeMultiplier = getTypeDamageMultiplier(move.type, this.species.type1) * (this.species.type2 > -1 ? getTypeDamageMultiplier(move.type, this.species.type2) : 1);
const criticalMultiplier = isCritical ? 2 : 1; const criticalMultiplier = isCritical ? 2 : 1;
damage = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk / targetDef) / 50) + 2) * stabMultiplier * typeMultiplier * ((Utils.randInt(15) + 85) / 100)) * criticalMultiplier; damage = Math.ceil(((((2 * source.level / 5 + 2) * power.value * sourceAtk / targetDef) / 50) + 2) * stabMultiplier * typeMultiplier * ((Utils.randInt(15) + 85) / 100)) * criticalMultiplier;
if (isPhysical && source.status && source.status.effect === StatusEffect.BURN) if (isPhysical && source.status && source.status.effect === StatusEffect.BURN)
@ -438,8 +438,17 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
if (this.getTag(hta.tagType)) if (this.getTag(hta.tagType))
damage *= 2; damage *= 2;
}); });
const fixedDamage = new Utils.IntegerHolder(0);
applyMoveAttrs(FixedDamageAttr, this.scene, source, this, move, fixedDamage);
if (damage && fixedDamage.value) {
damage = fixedDamage.value;
result = MoveResult.EFFECTIVE;
}
console.log('damage', damage, move.name, move.power, sourceAtk, targetDef); console.log('damage', damage, move.name, move.power, sourceAtk, targetDef);
if (!result) {
if (typeMultiplier >= 2) if (typeMultiplier >= 2)
result = MoveResult.SUPER_EFFECTIVE; result = MoveResult.SUPER_EFFECTIVE;
else if (typeMultiplier >= 1) else if (typeMultiplier >= 1)
@ -448,8 +457,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
result = MoveResult.NOT_VERY_EFFECTIVE; result = MoveResult.NOT_VERY_EFFECTIVE;
else else
result = MoveResult.NO_EFFECT; result = MoveResult.NO_EFFECT;
}
if (damage) { if (damage) {
this.hp = Math.max(this.hp - damage, 0); this.hp = Math.max(this.hp - damage, 0);
@ -493,7 +501,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return false; return false;
} }
const newTag = getBattleTag(tagType, turnCount || 1); const newTag = getBattleTag(tagType, turnCount || 0);
this.summonData.tags.push(newTag); this.summonData.tags.push(newTag);
newTag.onAdd(this); newTag.onAdd(this);
} }
@ -529,7 +537,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
lapseTags(lapseType: BattleTagLapseType): void { lapseTags(lapseType: BattleTagLapseType): void {
const tags = this.summonData.tags; const tags = this.summonData.tags;
tags.filter(t => lapseType === BattleTagLapseType.FAINT || ((t.lapseType === lapseType) && !(t.lapse(this)))).forEach(t => { tags.filter(t => lapseType === BattleTagLapseType.FAINT || ((t.lapseType === lapseType) && !(t.lapse(this))) || (lapseType === BattleTagLapseType.TURN_END && t.turnCount < 1)).forEach(t => {
t.onRemove(this); t.onRemove(this);
tags.splice(tags.indexOf(t), 1); tags.splice(tags.indexOf(t), 1);
}); });
@ -910,7 +918,7 @@ export class PokemonSummonData {
} }
export class PokemonBattleSummonData { export class PokemonBattleSummonData {
public infatuated: boolean; public turnCount: integer = 1;
} }
export class PokemonTurnData { export class PokemonTurnData {
@ -928,7 +936,7 @@ export enum AiType {
}; };
export enum MoveResult { export enum MoveResult {
EFFECTIVE, EFFECTIVE = 1,
SUPER_EFFECTIVE, SUPER_EFFECTIVE,
NOT_VERY_EFFECTIVE, NOT_VERY_EFFECTIVE,
NO_EFFECT, NO_EFFECT,