Refactor modifiers

This commit is contained in:
Flashfyre 2023-04-09 19:15:21 -04:00
parent 4ec91695f7
commit 15105231ba
14 changed files with 1919 additions and 1551 deletions

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

(image error) Size: 27 KiB

After

(image error) Size: 28 KiB

Binary file not shown.

After

(image error) Size: 347 B

Binary file not shown.

After

(image error) Size: 245 B

View File

@ -101,24 +101,77 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
}
}
updateInfo(pokemon: Pokemon, callback?: Function) {
if (!this.scene) {
if (callback)
callback();
return;
}
updateInfo(pokemon: Pokemon): Promise<void> {
return new Promise(resolve => {
if (!this.scene) {
resolve();
return;
}
const updatePokemonHp = () => {
const duration = Utils.clampInt(Math.abs((this.lastHp) - pokemon.hp) * 5, 250, 5000);
const updatePokemonHp = () => {
const duration = Utils.clampInt(Math.abs((this.lastHp) - pokemon.hp) * 5, 250, 5000);
this.scene.tweens.add({
targets: this.hpBar,
ease: 'Sine.easeOut',
scaleX: pokemon.getHpRatio(),
duration: duration,
onUpdate: () => {
if (this.player && this.lastHp !== pokemon.hp) {
const tweenHp = Math.ceil(this.hpBar.scaleX * pokemon.getMaxHp());
this.setHpNumbers(tweenHp, pokemon.getMaxHp())
this.lastHp = tweenHp;
}
const hpFrame = this.hpBar.scaleX > 0.5 ? 'high' : this.hpBar.scaleX > 0.25 ? 'medium' : 'low';
if (hpFrame !== this.lastHpFrame) {
this.hpBar.setFrame(hpFrame);
this.lastHpFrame = hpFrame;
}
},
onComplete: () => {
resolve();
}
});
if (!this.player)
this.lastHp = pokemon.hp;
this.lastMaxHp = pokemon.getMaxHp();
};
if (this.player && this.lastExp !== pokemon.exp) {
const originalResolve = resolve;
resolve = () => this.updatePokemonExp(pokemon).then(() => originalResolve());
}
if (this.lastHp !== pokemon.hp || this.lastMaxHp !== pokemon.getMaxHp()) {
updatePokemonHp();
return;
} else if (!this.player && this.lastLevel !== pokemon.level) {
this.setLevel(pokemon.level);
this.lastLevel = pokemon.level;
}
resolve();
});
}
updatePokemonExp(battler: Pokemon): Promise<void> {
return new Promise(resolve => {
const levelUp = this.lastLevel < battler.level;
const relLevelExp = getLevelRelExp(this.lastLevel + 1, battler.species.growthRate);
const levelExp = levelUp ? relLevelExp : battler.levelExp;
let ratio = levelExp / relLevelExp;
let duration = this.visible ? ((levelExp - this.lastLevelExp) / relLevelExp) * 1650 : 0;
if (duration)
this.scene.sound.play('exp');
this.scene.tweens.add({
targets: this.hpBar,
ease: 'Sine.easeOut',
scaleX: pokemon.getHpRatio(),
targets: this.expBar,
ease: 'Sine.easeIn',
scaleX: ratio,
duration: duration,
onUpdate: () => {
if (this.player && this.lastHp !== pokemon.hp) {
const tweenHp = Math.ceil(this.hpBar.scaleX * pokemon.getMaxHp());
this.setHpNumbers(tweenHp, pokemon.getMaxHp())
if (this.player && this.lastHp !== battler.hp) {
const tweenHp = Math.ceil(this.hpBar.scaleX * battler.getMaxHp());
this.setHpNumbers(tweenHp, battler.getMaxHp());
this.lastHp = tweenHp;
}
@ -129,81 +182,25 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
}
},
onComplete: () => {
if (callback) {
callback();
callback = null;
if (duration)
this.scene.sound.stopByKey('exp');
if (ratio === 1) {
this.lastLevelExp = 0;
this.lastLevel++;
this.scene.sound.play('level_up');
this.setLevel(this.lastLevel);
this.scene.time.delayedCall(500, () => {
this.expBar.setScale(0, 1);
this.updateInfo(battler).then(() => resolve());
});
return;
} else {
this.lastExp = battler.exp;
this.lastLevelExp = battler.levelExp;
}
resolve();
}
});
if (!this.player)
this.lastHp = pokemon.hp;
this.lastMaxHp = pokemon.getMaxHp();
};
if (this.player && this.lastExp !== pokemon.exp) {
const originalCallback = callback;
callback = () => this.updatePokemonExp(pokemon, originalCallback);
}
if (this.lastHp !== pokemon.hp || this.lastMaxHp !== pokemon.getMaxHp())
updatePokemonHp();
else if (!this.player && this.lastLevel !== pokemon.level) {
this.setLevel(pokemon.level);
this.lastLevel = pokemon.level;
if (callback)
callback();
} else if (callback)
callback();
}
updatePokemonExp(battler: Pokemon, callback?: Function) {
const levelUp = this.lastLevel < battler.level;
const relLevelExp = getLevelRelExp(this.lastLevel + 1, battler.species.growthRate);
const levelExp = levelUp ? relLevelExp : battler.levelExp;
let ratio = levelExp / relLevelExp;
let duration = this.visible ? ((levelExp - this.lastLevelExp) / relLevelExp) * 1650 : 0;
if (duration)
this.scene.sound.play('exp');
this.scene.tweens.add({
targets: this.expBar,
ease: 'Sine.easeIn',
scaleX: ratio,
duration: duration,
onUpdate: () => {
if (this.player && this.lastHp !== battler.hp) {
const tweenHp = Math.ceil(this.hpBar.scaleX * battler.getMaxHp());
this.setHpNumbers(tweenHp, battler.getMaxHp());
this.lastHp = tweenHp;
}
const hpFrame = this.hpBar.scaleX > 0.5 ? 'high' : this.hpBar.scaleX > 0.25 ? 'medium' : 'low';
if (hpFrame !== this.lastHpFrame) {
this.hpBar.setFrame(hpFrame);
this.lastHpFrame = hpFrame;
}
},
onComplete: () => {
if (duration)
this.scene.sound.stopByKey('exp');
if (ratio === 1) {
this.lastLevelExp = 0;
this.lastLevel++;
this.scene.sound.play('level_up');
this.setLevel(this.lastLevel);
this.scene.time.delayedCall(500, () => {
this.expBar.setScale(0, 1);
this.updateInfo(battler, callback);
});
return;
} else {
this.lastExp = battler.exp;
this.lastLevelExp = battler.levelExp;
}
if (callback) {
callback();
callback = null;
}
}
});
}

View File

@ -29,6 +29,18 @@ export class BattlePhase {
}
}
export class SelectStarterPhase extends BattlePhase {
constructor(scene: BattleScene) {
super(scene);
}
start() {
super.start();
this.scene.ui.setMode(Mode.STARTER_SELECT);
}
}
export class EncounterPhase extends BattlePhase {
constructor(scene: BattleScene) {
super(scene);
@ -722,7 +734,7 @@ export class ExpPhase extends PartyMemberPokemonPhase {
newLevel = pokemon.level;
if (newLevel > lastLevel)
this.scene.unshiftPhase(new LevelUpPhase(this.scene, this.partyMemberIndex, newLevel));
pokemon.updateInfo(() => this.end());
pokemon.updateInfo().then(() => this.end());
}, null, true);
}
@ -1016,7 +1028,7 @@ export class SelectModifierPhase extends BattlePhase {
this.scene.applyModifiers(ExtraModifierModifier, modifierCount);
const types: Array<ModifierType> = getModifierTypesForWave(this.scene.currentBattle.waveIndex - 1, modifierCount.value, party);
this.scene.ui.setMode(Mode.MODIFIER_SELECT, types, (cursor: integer) => {
const modifierSelectCallback = (cursor: integer) => {
if (cursor < 0) {
this.scene.ui.setMode(Mode.MESSAGE);
super.end();
@ -1033,14 +1045,15 @@ export class SelectModifierPhase extends BattlePhase {
this.scene.ui.clearText();
this.scene.ui.setMode(Mode.MESSAGE);
} else
this.scene.ui.setMode(Mode.MODIFIER_SELECT);
this.scene.ui.setMode(Mode.MODIFIER_SELECT, types, modifierSelectCallback);
}, pokemonModifierType.selectFilter);
} else {
this.scene.addModifier(types[cursor].newModifier()).then(() => super.end());
this.scene.ui.clearText();
this.scene.ui.setMode(Mode.MESSAGE);
}
});
};
this.scene.ui.setMode(Mode.MODIFIER_SELECT, types, modifierSelectCallback);
}
}

View File

@ -1,16 +1,17 @@
import Phaser from 'phaser';
import { Biome, BiomeArena } from './biome';
import UI from './ui/ui';
import { BattlePhase, EncounterPhase, SummonPhase, CommandPhase, NextEncounterPhase, SwitchBiomePhase, NewBiomeEncounterPhase, LearnMovePhase } from './battle-phase';
import { BattlePhase, EncounterPhase, SummonPhase, CommandPhase, NextEncounterPhase, SwitchBiomePhase, NewBiomeEncounterPhase } from './battle-phase';
import { PlayerPokemon, EnemyPokemon } from './pokemon';
import PokemonSpecies, { allSpecies, getPokemonSpecies } from './pokemon-species';
import * as Utils from './utils';
import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PokemonModifier} from './modifier';
import { Modifier, ModifierBar, ConsumablePokemonModifier, ConsumableModifier, PartyShareModifier, PokemonHpRestoreModifier, HealingBoosterModifier, PersistentModifier, PokemonBaseStatBoosterModifierType, PokemonBaseStatModifier } from './modifier';
import { PokeballType } from './pokeball';
import { Species } from './species';
import { initAutoPlay } from './auto-play';
import { Battle } from './battle';
import { populateAnims } from './battle-anims';
import { Stat } from './pokemon-stat';
const enableAuto = true;
@ -36,7 +37,7 @@ export default class BattleScene extends Phaser.Scene {
public pokeballCounts = Object.fromEntries(Utils.getEnumValues(PokeballType).filter(p => p <= PokeballType.MASTER_BALL).map(t => [ t, 0 ]));
private party: PlayerPokemon[];
private modifierBar: ModifierBar;
private modifiers: Modifier[];
private modifiers: PersistentModifier[];
public uiContainer: Phaser.GameObjects.Container;
public ui: UI;
@ -301,6 +302,10 @@ export default class BattleScene extends Phaser.Scene {
this.plusKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.PLUS);
this.minusKey = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.MINUS);
for (let a = 0; a < 3; a++) {
this.addModifier(new PokemonBaseStatModifier(new PokemonBaseStatBoosterModifierType('HP-UP', Stat.HP), this.getParty()[0].id, Stat.HP));
}
Promise.all(loadPokemonAssets).then(() => {
if (enableAuto)
initAutoPlay.apply(this);
@ -341,6 +346,7 @@ export default class BattleScene extends Phaser.Scene {
this.unshiftPhase(new NewBiomeEncounterPhase(this));
}
} else {
//this.pushPhase(new SelectStarterPhase(this));
this.pushPhase(new EncounterPhase(this));
this.pushPhase(new SummonPhase(this));
}
@ -443,43 +449,71 @@ export default class BattleScene extends Phaser.Scene {
this.phaseQueue.push(new CommandPhase(this));
}
addModifier(modifier: Modifier): Promise<void> {
addModifier(modifier: Modifier, virtual?: boolean): Promise<void> {
return new Promise(resolve => {
if (modifier.add(this.modifierBar, this.modifiers))
if (modifier instanceof PersistentModifier) {
if ((modifier as PersistentModifier).add(this.modifiers, !!virtual) && !virtual)
this.sound.play('restore');
if (!virtual)
this.updateModifiers().then(() => resolve());
} else if (modifier instanceof ConsumableModifier) {
this.sound.play('restore');
if (modifier instanceof ConsumableModifier) {
const args = [ this ];
if (modifier.shouldApply(args))
modifier.apply(args);
resolve();
return;
}
if (modifier instanceof ConsumablePokemonModifier) {
for (let p in this.party) {
const pokemon = this.party[p];
let pokemonToUpdate = 0;
if (modifier instanceof PokemonModifier) {
for (let p in this.party) {
const pokemon = this.party[p];
if (modifier instanceof ConsumablePokemonModifier) {
const args = [ pokemon ];
const args: any[] = [ pokemon ];
if (modifier instanceof PokemonHpRestoreModifier) {
const hpRestoreMultiplier = new Utils.IntegerHolder(1);
this.applyModifiers(HealingBoosterModifier, hpRestoreMultiplier);
args.push(hpRestoreMultiplier.value);
}
if (modifier.shouldApply(args))
modifier.apply(args);
}
pokemonToUpdate++;
pokemon.calculateStats();
pokemon.updateInfo(() => {
if (!(--pokemonToUpdate))
resolve();
});
Promise.allSettled(this.party.map(p => p.updateInfo())).then(() => resolve());
} else {
const args = [ this ];
if (modifier.shouldApply(args))
modifier.apply(args);
resolve();
}
}
});
}
if (!pokemonToUpdate)
updatePartyForModifiers(): Promise<void> {
return new Promise(resolve => {
Promise.allSettled(this.party.map(p => {
p.calculateStats();
return p.updateInfo();
})).then(() => resolve());
});
}
updateModifiers(): Promise<void> {
return new Promise(resolve => {
for (let modifier of this.modifiers) {
if (modifier instanceof PersistentModifier)
(modifier as PersistentModifier).virtualStackCount = 0;
}
this.applyModifiers(PartyShareModifier, this, this.modifiers);
const modifiers = this.modifiers.slice(0);
for (let modifier of modifiers) {
if (!modifier.getStackCount())
this.modifiers.splice(this.modifiers.indexOf(modifier), 1);
}
this.updatePartyForModifiers().then(() => {
this.modifierBar.updateModifiers(this.modifiers);
resolve();
});
});
}

View File

@ -18,19 +18,13 @@ export class ModifierBar extends Phaser.GameObjects.Container {
this.setScale(0.5);
}
addModifier(modifier: Modifier) {
const icon = modifier.getIcon(this.scene as BattleScene);
this.add(icon);
this.setModifierIconPosition(icon);
}
updateModifiers(modifiers: PersistentModifier[]) {
this.removeAll(true);
updateModifier(modifier: Modifier, modifiers: Modifier[]) {
const index = modifiers.indexOf(modifier);
if (index > -1) {
this.getAt(index).destroy();
const newIcon = modifier.getIcon(this.scene as BattleScene);
this.addAt(newIcon, index);
this.setModifierIconPosition(newIcon);
for (let modifier of modifiers) {
const icon = modifier.getIcon(this.scene as BattleScene);
this.add(icon);
this.setModifierIconPosition(icon);
}
}
@ -44,19 +38,13 @@ export class ModifierBar extends Phaser.GameObjects.Container {
export abstract class Modifier {
public type: ModifierType;
public stackCount: integer;
constructor(type: ModifierType) {
this.type = type;
this.stackCount = 1;
}
add(modifierBar: ModifierBar, modifiers: Modifier[]): boolean {
modifiers.push(this);
modifierBar.addModifier(this);
return true;
match(_modifier: Modifier): boolean {
return false;
}
shouldApply(_args: any[]): boolean {
@ -64,10 +52,47 @@ export abstract class Modifier {
}
abstract apply(args: any[]): boolean;
}
incrementStack(): void {
if (this.stackCount < this.getMaxStackCount())
this.stackCount++;
export abstract class PersistentModifier extends Modifier {
public stackCount: integer;
public virtualStackCount: integer;
constructor(type: ModifierType) {
super(type);
this.stackCount = 1;
this.virtualStackCount = 0;
}
add(modifiers: PersistentModifier[], virtual: boolean): boolean {
for (let modifier of modifiers) {
if (this.match(modifier)) {
modifier.incrementStack(virtual);
return true;
}
}
if (virtual) {
this.virtualStackCount += this.stackCount;
this.stackCount = 0;
}
modifiers.push(this);
return true;
}
abstract clone(): PersistentModifier;
incrementStack(virtual: boolean): void {
if (this.getStackCount() < this.getMaxStackCount()) {
if (!virtual)
this.stackCount++;
else
this.virtualStackCount++;
}
}
getStackCount(): integer {
return this.stackCount + this.virtualStackCount;
}
getMaxStackCount(): integer {
@ -86,16 +111,34 @@ export abstract class Modifier {
if (stackText)
container.add(stackText);
const virtualStackText = this.getIconStackText(scene, true);
if (virtualStackText)
container.add(virtualStackText);
return container;
}
getIconStackText(scene: BattleScene): Phaser.GameObjects.Text {
if (this.stackCount <= 1)
getIconStackText(scene: BattleScene, virtual?: boolean): Phaser.GameObjects.Text {
if (this.getMaxStackCount() === 1 || (virtual && !this.virtualStackCount))
return null;
const text = addTextObject(scene, 16, 12, this.stackCount.toString(), TextStyle.PARTY, { fontSize: '66px', color: this.stackCount < this.getMaxStackCount() ? '#f8f8f8' : '#e64a18' });
const isStackMax = this.getStackCount() >= this.getMaxStackCount();
const maxColor = '#f89890';
const maxStrokeColor = '#984038';
if (virtual) {
const virtualText = addTextObject(scene, 1 * 11 + 16, 12, `+${this.virtualStackCount.toString()}`, TextStyle.PARTY, { fontSize: '66px', color: !isStackMax ? '#40c8f8' : maxColor });
virtualText.setShadow(0, 0, null);
virtualText.setStroke(!isStackMax ? '#006090' : maxStrokeColor, 16)
virtualText.setOrigin(1, 0);
return virtualText;
}
const text = addTextObject(scene, 8, 12, this.stackCount.toString(), TextStyle.PARTY, { fontSize: '66px', color: !isStackMax ? '#f8f8f8' : maxColor });
text.setShadow(0, 0, null);
text.setStroke('#424242', 16)
text.setOrigin(1, 0);
text.setOrigin(0, 0);
return text;
}
@ -106,7 +149,7 @@ export abstract class ConsumableModifier extends Modifier {
super(type);
}
add(_modifierBar: ModifierBar, _modifiers: Modifier[]): boolean {
add(_modifiers: Modifier[]): boolean {
return true;
}
@ -134,7 +177,7 @@ class AddPokeballModifier extends ConsumableModifier {
}
}
export abstract class PokemonModifier extends Modifier {
export abstract class PokemonHeldItemModifier extends PersistentModifier {
public pokemonId: integer;
constructor(type: ModifierType, pokemonId: integer) {
@ -144,7 +187,6 @@ export abstract class PokemonModifier extends Modifier {
}
shouldApply(args: any[]): boolean {
console.log(args[0]);
return super.shouldApply(args) && args.length && args[0] instanceof Pokemon && (this.pokemonId === -1 || (args[0] as Pokemon).id === this.pokemonId);
}
@ -152,8 +194,8 @@ export abstract class PokemonModifier extends Modifier {
const container = scene.add.container(0, 0);
const pokemon = this.getPokemon(scene);
const pokemonIcon = scene.add.sprite(0, 8, pokemon.getIconAtlasKey());
pokemonIcon.play(pokemon.getIconKey()).stop();
const pokemonIcon = scene.add.sprite(0, 8, pokemon.species.getIconAtlasKey());
pokemonIcon.play(pokemon.species.getIconKey()).stop();
pokemonIcon.setOrigin(0, 0.5);
container.add(pokemonIcon);
@ -166,7 +208,7 @@ export abstract class PokemonModifier extends Modifier {
}
}
export class PokemonBaseStatModifier extends PokemonModifier {
export class PokemonBaseStatModifier extends PokemonHeldItemModifier {
protected stat: Stat;
constructor(type: PokemonBaseStatBoosterModifierType, pokemonId: integer, stat: Stat) {
@ -174,19 +216,16 @@ export class PokemonBaseStatModifier extends PokemonModifier {
this.stat = stat;
}
add(modifierBar: ModifierBar, modifiers: Modifier[]): boolean {
for (let modifier of modifiers) {
if (modifier instanceof PokemonBaseStatModifier) {
const pokemonStatModifier = modifier as PokemonBaseStatModifier;
if (pokemonStatModifier.pokemonId === this.pokemonId && pokemonStatModifier.stat === this.stat) {
pokemonStatModifier.incrementStack();
modifierBar.updateModifier(pokemonStatModifier, modifiers);
return true;
}
}
match(modifier: Modifier): boolean {
if (modifier instanceof PokemonBaseStatModifier) {
const pokemonStatModifier = modifier as PokemonBaseStatModifier;
return pokemonStatModifier.pokemonId === this.pokemonId && pokemonStatModifier.stat === this.stat;
}
return false;
}
return super.add(modifierBar, modifiers);
clone(): PersistentModifier {
return new PokemonBaseStatModifier(this.type as PokemonBaseStatBoosterModifierType, this.pokemonId, this.stat);
}
shouldApply(args: any[]): boolean {
@ -194,7 +233,7 @@ export class PokemonBaseStatModifier extends PokemonModifier {
}
apply(args: any[]): boolean {
args[1][this.stat] = Math.min(Math.floor(args[1][this.stat] * (1 + this.stackCount * 0.2)), 999999);
args[1][this.stat] = Math.min(Math.floor(args[1][this.stat] * (1 + this.getStackCount() * 0.2)), 999999);
return true;
}
@ -202,7 +241,7 @@ export class PokemonBaseStatModifier extends PokemonModifier {
getIcon(scene: BattleScene): Phaser.GameObjects.Container {
const container = super.getIcon(scene);
const item = scene.add.sprite(16, 16, 'items');
const item = scene.add.sprite(16, this.virtualStackCount ? 8 : 16, 'items');
item.setScale(0.5);
item.setOrigin(0, 0.5);
item.setTexture('items', this.type.iconImage);
@ -212,35 +251,57 @@ export class PokemonBaseStatModifier extends PokemonModifier {
if (stackText)
container.add(stackText);
const virtualStackText = this.getIconStackText(scene, true);
if (virtualStackText)
container.add(virtualStackText);
return container;
}
}
export abstract class ConsumablePokemonModifier extends PokemonModifier {
export abstract class ConsumablePokemonModifier extends ConsumableModifier {
public pokemonId: integer;
constructor(type: ModifierType, pokemonId: integer) {
super(type, pokemonId);
super(type);
this.pokemonId = pokemonId;
}
add(_modifierBar: ModifierBar, _modifiers: Modifier[]): boolean {
return true;
shouldApply(args: any[]): boolean {
return args.length && args[0] instanceof Pokemon && (this.pokemonId === -1 || (args[0] as Pokemon).id === this.pokemonId);
}
getPokemon(scene: BattleScene) {
return scene.getParty().find(p => p.id === this.pokemonId);
}
}
export class PokemonHpRestoreModifier extends ConsumablePokemonModifier {
private restorePercent: integer;
private restorePoints: integer;
private percent: boolean;
private fainted: boolean;
constructor(type: ModifierType, pokemonId: integer, restorePercent: integer, fainted?: boolean) {
constructor(type: ModifierType, pokemonId: integer, restorePoints: integer, percent: boolean, fainted?: boolean) {
super(type, pokemonId);
this.restorePercent = restorePercent;
this.restorePoints = restorePoints;
this.percent = percent;
this.fainted = !!fainted;
}
shouldApply(args: any[]): boolean {
return super.shouldApply(args) && (this.fainted || (args.length > 1 && typeof(args[1]) === 'number'));
}
apply(args: any[]): boolean {
const pokemon = args[0] as Pokemon;
if (!pokemon.hp === this.fainted)
pokemon.hp = Math.min(pokemon.hp + Math.max((this.restorePercent * 0.01) * pokemon.getMaxHp(), this.restorePercent), pokemon.getMaxHp());
if (!pokemon.hp === this.fainted) {
let restorePoints = this.restorePoints;
if (!this.fainted)
restorePoints = Math.floor(restorePoints * (args[1] as number));
pokemon.hp = Math.min(pokemon.hp + (this.percent ? (restorePoints * 0.01) * pokemon.getMaxHp() : restorePoints), pokemon.getMaxHp());
}
return true;
}
@ -302,7 +363,79 @@ export class TmModifier extends ConsumablePokemonModifier {
}
}
export class ExpBoosterModifier extends Modifier {
export class PartyShareModifier extends PersistentModifier {
constructor(type: ModifierType) {
super(type);
}
match(modifier: Modifier) {
return modifier instanceof PartyShareModifier;
}
clone(): PartyShareModifier {
return new PartyShareModifier(this.type);
}
shouldApply(args: any[]): boolean {
return super.shouldApply(args) && args.length === 2 && args[0] instanceof BattleScene && args[1] instanceof Array<Modifier>;
}
apply(args: any[]): boolean {
const scene = args[0] as BattleScene;
const modifiers = args[1] as Modifier[];
const party = scene.getParty();
for (let modifier of modifiers) {
if (modifier instanceof PokemonHeldItemModifier) {
const heldItemModifier = modifier as PokemonHeldItemModifier;
const extraStacks = Math.floor(modifier.stackCount / Math.max(party.length - (this.getStackCount() - 1), 1));
for (let s = 0; s < extraStacks; s++) {
for (let p of party) {
if (p.id === heldItemModifier.pokemonId)
continue;
const newHeldItemModifier = heldItemModifier.clone() as PokemonHeldItemModifier;
newHeldItemModifier.pokemonId = p.id;
scene.addModifier(newHeldItemModifier, true);
}
}
}
}
return true;
}
getMaxStackCount(): number {
return 6;
}
}
export class HealingBoosterModifier extends PersistentModifier {
private multiplier: number;
constructor(type: ModifierType, multiplier: number) {
super(type);
this.multiplier = multiplier;
}
match(modifier: Modifier): boolean {
return modifier instanceof HealingBoosterModifier;
}
clone(): HealingBoosterModifier {
return new HealingBoosterModifier(this.type, this.multiplier);
}
apply(args: any[]): boolean {
const healingMultiplier = args[0] as Utils.IntegerHolder;
for (let s = 0; s < this.getStackCount(); s++)
healingMultiplier.value *= this.multiplier;
healingMultiplier.value = Math.floor(healingMultiplier.value);
return true;
}
}
export class ExpBoosterModifier extends PersistentModifier {
private boostMultiplier: integer;
constructor(type: ModifierType, boostPercent: integer) {
@ -311,29 +444,26 @@ export class ExpBoosterModifier extends Modifier {
this.boostMultiplier = boostPercent * 0.01;
}
add(modifierBar: ModifierBar, modifiers: Modifier[]): boolean {
for (let modifier of modifiers) {
if (modifier instanceof ExpBoosterModifier) {
const expModifier = modifier as ExpBoosterModifier;
if (expModifier.boostMultiplier === this.boostMultiplier) {
expModifier.incrementStack();
modifierBar.updateModifier(expModifier, modifiers);
return true;
}
}
match(modifier: Modifier): boolean {
if (modifier instanceof ExpBoosterModifier) {
const expModifier = modifier as ExpBoosterModifier;
return expModifier.boostMultiplier === this.boostMultiplier;
}
return false;
}
return super.add(modifierBar, modifiers);
clone(): ExpBoosterModifier {
return new ExpBoosterModifier(this.type, this.boostMultiplier * 100);
}
apply(args: any[]): boolean {
(args[0] as Utils.NumberHolder).value = Math.floor((args[0] as Utils.NumberHolder).value * (1 + (this.stackCount * (this.boostMultiplier))));
(args[0] as Utils.NumberHolder).value = Math.floor((args[0] as Utils.NumberHolder).value * (1 + (this.getStackCount() * this.boostMultiplier)));
return true;
}
}
export class ExpShareModifier extends Modifier {
export class ExpShareModifier extends PersistentModifier {
constructor(type: ModifierType) {
super(type);
}
@ -342,31 +472,30 @@ export class ExpShareModifier extends Modifier {
return true;
}
clone(): ExpShareModifier {
return new ExpShareModifier(this.type);
}
getMaxStackCount(): integer {
return 5;
}
}
export class ShinyRateBoosterModifier extends Modifier {
export class ShinyRateBoosterModifier extends PersistentModifier {
constructor(type: ModifierType) {
super(type);
}
add(modifierBar: ModifierBar, modifiers: Modifier[]): boolean {
for (let modifier of modifiers) {
if (modifier instanceof ShinyRateBoosterModifier) {
const shinyRateModifier = modifier as ShinyRateBoosterModifier;
shinyRateModifier.incrementStack();
modifierBar.updateModifier(shinyRateModifier, modifiers);
return true;
}
}
match(modifier: Modifier): boolean {
return modifier instanceof ShinyRateBoosterModifier;
}
return super.add(modifierBar, modifiers);
clone(): ShinyRateBoosterModifier {
return new ShinyRateBoosterModifier(this.type);
}
apply(args: any[]): boolean {
(args[0] as Utils.IntegerHolder).value = Math.pow((args[0] as Utils.IntegerHolder).value * 0.5, this.stackCount + 1);
(args[0] as Utils.IntegerHolder).value = Math.pow((args[0] as Utils.IntegerHolder).value * 0.5, this.getStackCount() + 1);
return true;
}
@ -376,13 +505,17 @@ export class ShinyRateBoosterModifier extends Modifier {
}
}
export class ExtraModifierModifier extends Modifier {
export class ExtraModifierModifier extends PersistentModifier {
constructor(type: ModifierType) {
super(type);
}
clone(): ExtraModifierModifier {
return new ExtraModifierModifier(this.type);
}
apply(args: any[]): boolean {
(args[0] as Utils.IntegerHolder).value += this.stackCount;
(args[0] as Utils.IntegerHolder).value += this.getStackCount();
return true;
}
@ -436,24 +569,26 @@ export abstract class PokemonModifierType extends ModifierType {
}
export class PokemonHpRestoreModifierType extends PokemonModifierType {
protected restorePercent: integer;
protected restorePoints: integer;
protected percent: boolean;
constructor(name: string, restorePercent: integer, newModifierFunc?: Function, selectFilter?: Function, iconImage?: string) {
super(name, `Restore ${restorePercent} HP or ${restorePercent}% HP for one POKéMON, whichever is higher`,
newModifierFunc || ((_type, args) => new PokemonHpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePercent, false)),
constructor(name: string, restorePoints: integer, percent?: boolean, newModifierFunc?: Function, selectFilter?: Function, iconImage?: string) {
super(name, `Restore ${restorePoints}${percent ? '%' : ''} HP for one POKéMON`,
newModifierFunc || ((_type, args) => new PokemonHpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePoints, this.percent, false)),
selectFilter || ((pokemon: PlayerPokemon) => {
if (!pokemon.hp || pokemon.hp >= pokemon.getMaxHp())
return PartyUiHandler.NoEffectMessage;
return null;
}), iconImage);
this.restorePercent = restorePercent;
this.restorePoints = restorePoints;
this.percent = !!percent;
}
}
export class PokemonReviveModifierType extends PokemonHpRestoreModifierType {
constructor(name: string, restorePercent: integer, iconImage?: string) {
super(name, restorePercent, (_type, args) => new PokemonHpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePercent, true),
super(name, restorePercent, true, (_type, args) => new PokemonHpRestoreModifier(this, (args[0] as PlayerPokemon).id, this.restorePoints, true, true),
((pokemon: PlayerPokemon) => {
if (pokemon.hp)
return PartyUiHandler.NoEffectMessage;
@ -583,7 +718,7 @@ const modifierPool = {
const faintedPartyMemberCount = party.filter(p => !p.hp).length;
return faintedPartyMemberCount;
}),
new WeightedModifierType(new PokemonHpRestoreModifierType('HYPER POTION', 80), (party: PlayerPokemon[]) => {
new WeightedModifierType(new PokemonHpRestoreModifierType('HYPER POTION', 200), (party: PlayerPokemon[]) => {
const thresholdPartyMemberCount = party.filter(p => p.getHpRatio() <= 0.6).length;
return thresholdPartyMemberCount;
}),
@ -610,6 +745,9 @@ const modifierPool = {
new WeightedModifierType(new AllPokemonFullReviveModifierType('SACRED ASH'), (party: PlayerPokemon[]) => {
return party.filter(p => !p.hp).length >= Math.ceil(party.length / 2) ? 1 : 0;
}),
new ModifierType('OVAL CHARM', 'For every X (no. of party members) items in a POKéMON\'s held item stack, give one to each other party member',
(type, _args) => new PartyShareModifier(type), 'oval_charm'),
new ModifierType('HEALING CHARM', 'Doubles the effectiveness of HP restoring items (excludes revives)', (type, _args) => new HealingBoosterModifier(type, 2), 'healing_charm'),
new ExpBoosterModifierType('LUCKY EGG', 25),
new ModifierType('EXP. SHARE', 'All POKéMON in your party gain an additional 10% of a battle\'s EXP. Points', (type, _args) => new ExpShareModifier(type), 'exp_share')
].map(m => { m.setTier(ModifierTier.ULTRA); return m; }),

View File

@ -1,3 +1,4 @@
import BattleScene from './battle-scene';
import { GrowthRate } from './exp';
import { pokemonEvolutions } from './pokemon-evolutions';
import { Species } from './species';
@ -160,7 +161,6 @@ export default class PokemonSpecies {
const subPrevolutionLevels = getPokemonSpecies(speciesId).getPrevolutionLevels();
for (let spl of subPrevolutionLevels)
prevolutionLevels.push(spl);
console.log(Species[speciesId])
}
}
}
@ -168,6 +168,72 @@ export default class PokemonSpecies {
return prevolutionLevels;
}
getIconAtlasKey(): string {
return `pokemon_icons_${this.generation}`;
}
getIconId(): string {
let ret = `${Utils.padInt(this.speciesId, 3)}`;
switch (this.speciesId) {
case Species.UNOWN:
ret += 'a';
break;
case Species.BURMY:
case Species.WORMADAM:
ret += 'plant';
break;
case Species.SHELLOS:
case Species.GASTRODON:
ret += 'east';
break;
case Species.GIRATINA:
ret += 'altered';
break;
case Species.SHAYMIN:
ret += 'land';
break;
case Species.BASCULIN:
ret += 'redstriped';
break;
case Species.DEERLING:
case Species.SAWSBUCK:
ret += 'spring';
break;
case Species.FRILLISH:
case Species.JELLICENT:
ret += 'm';
break;
case Species.TORNADUS:
case Species.THUNDURUS:
case Species.LANDORUS:
ret += 'incarnate';
break;
case Species.KELDEO:
ret += 'ordinary';
break;
case Species.MELOETTA:
ret += 'aria';
break;
}
return ret;
}
getIconKey(): string {
return `pkmn_icon__${this.getIconId()}`;
}
generateIconAnim(scene: BattleScene): void {
const frameNames = scene.anims.generateFrameNames(this.getIconAtlasKey(), { prefix: `${this.getIconId()}_`, zeroPad: 2, suffix: '.png', start: 1, end: 34 });
scene.anims.create({
key: this.getIconKey(),
frames: frameNames,
frameRate: 128,
repeat: -1
});
}
}
class PokemonForm extends PokemonSpecies {
@ -718,7 +784,7 @@ export const allSpecies = [
]
],
[ Species.ARCEUS, "Arceus", 4, 0, 0, 1, "Alpha Pokémon", Type.NORMAL, -1, 3.2, 320, "Multitype", null, null, 720, 120, 120, 120, 120, 120, 120, 3, 0, 324, GrowthRate.SLOW, "Undiscovered", null, null, 120, 0 ],
[ Species.VICTINI, "Victini", 5, 0, 0, 1, "Victory Pokémon", Type.PSYCHIC, Type.FIRE, 0.4, 4, "Victory Star", null, null, 600, 100, 100, 100, 100, 100, 100, 3, 100, 270, GrowthRate.SLOW, "Undiscovered", null, null, 120, 0 ],
[ Species.VICTINI, "Victini", 4, 0, 0, 1, "Victory Pokémon", Type.PSYCHIC, Type.FIRE, 0.4, 4, "Victory Star", null, null, 600, 100, 100, 100, 100, 100, 100, 3, 100, 270, GrowthRate.SLOW, "Undiscovered", null, null, 120, 0 ],
[ Species.SNIVY, "Snivy", 5, 0, 0, 0, "Grass Snake Pokémon", Type.GRASS, -1, 0.6, 8.1, "Overgrow", null, "Contrary", 308, 45, 45, 55, 45, 55, 63, 45, 70, 62, GrowthRate.MEDIUM_SLOW, "Field", "Grass", 87.5, 20, 0 ],
[ Species.SERVINE, "Servine", 5, 0, 0, 0, "Grass Snake Pokémon", Type.GRASS, -1, 0.8, 16, "Overgrow", null, "Contrary", 413, 60, 60, 75, 60, 75, 83, 45, 70, 145, GrowthRate.MEDIUM_SLOW, "Field", "Grass", 87.5, 20, 0 ],
[ Species.SERPERIOR, "Serperior", 5, 0, 0, 0, "Regal Pokémon", Type.GRASS, -1, 3.3, 63, "Overgrow", null, "Contrary", 528, 75, 75, 95, 75, 95, 113, 45, 70, 238, GrowthRate.MEDIUM_SLOW, "Field", "Grass", 87.5, 20, 0 ],

View File

@ -220,12 +220,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
return `pkmn__${this.getBattleSpriteId()}`;
}
getIconAtlasKey(): string {
return `pokemon_icons_${this.species.generation}`;
}
getIconId(): string {
return `${Utils.padInt(this.species.speciesId, 3)}`;
// TODO: Add form special cases
return this.species.getIconId();
}
getIconKey(): string {
@ -355,8 +352,8 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
}
updateInfo(callback?: Function) {
this.battleInfo.updateInfo(this, callback);
updateInfo(): Promise<void> {
return this.battleInfo.updateInfo(this);
}
addExp(exp: integer) {
@ -434,7 +431,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
callback: () => {
this.getSprite().setVisible(flashTimer.repeatCount % 2 === 0);
if (!flashTimer.repeatCount) {
this.battleInfo.updateInfo(this, () => {
this.battleInfo.updateInfo(this).then(() => {
if (callback)
callback();
});
@ -442,7 +439,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
});
} else {
this.battleInfo.updateInfo(this, () => {
this.battleInfo.updateInfo(this).then(() => {
if (callback)
callback();
});
@ -579,7 +576,7 @@ export class PlayerPokemon extends Pokemon {
constructor(scene: BattleScene, species: PokemonSpecies, level: integer, dataSource?: Pokemon) {
super(scene, 106, 148, species, level, dataSource);
this.generateIconAnim();
this.species.generateIconAnim(scene);
this.generateCompatibleTms();
}
@ -587,16 +584,6 @@ export class PlayerPokemon extends Pokemon {
return true;
}
generateIconAnim(): void {
const frameNames = this.scene.anims.generateFrameNames(this.getIconAtlasKey(), { prefix: `${this.getIconId()}_`, zeroPad: 2, suffix: '.png', start: 1, end: 34 });
this.scene.anims.create({
key: this.getIconKey(),
frames: frameNames,
frameRate: 128,
repeat: -1
});
}
generateCompatibleTms(): void {
this.compatibleTms = [];

View File

@ -33,7 +33,15 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
}
show(args: any[]) {
if (this.active || args.length !== 2 || !(args[0] instanceof Array) || !args[0].length || !(args[1] instanceof Function))
if (this.active) {
if (args.length === 2) {
this.awaitingActionInput = true;
this.onActionInput = args[1];
}
return;
}
if (args.length !== 2 || !(args[0] instanceof Array) || !args[0].length || !(args[1] instanceof Function))
return;
super.show(args);
@ -92,6 +100,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
success = true;
if (this.onActionInput) {
const originalOnActionInput = this.onActionInput;
this.awaitingActionInput = false;
this.onActionInput = null;
originalOnActionInput(this.cursor);
}
@ -99,6 +108,7 @@ export default class ModifierSelectUiHandler extends AwaitableUiHandler {
success = true;
if (this.onActionInput) {
const originalOnActionInput = this.onActionInput;
this.awaitingActionInput = false;
this.onActionInput = null;
originalOnActionInput(-1);
}

View File

@ -158,6 +158,8 @@ export default class PartyUiHandler extends MessageUiHandler {
selectCallback(this.cursor);
} else if (this.cursor)
(this.scene.getCurrentPhase() as CommandPhase).handleCommand(Command.POKEMON, this.cursor);
if (this.partyUiMode !== PartyUiMode.MODIFIER)
ui.playSelect();
return;
} else {
this.clearOptions();
@ -396,9 +398,9 @@ class PartySlot extends Phaser.GameObjects.Container {
this.add(slotPb);
const pokemonIcon = this.scene.add.sprite(slotPb.x, slotPb.y, this.pokemon.getIconAtlasKey());
const pokemonIcon = this.scene.add.sprite(slotPb.x, slotPb.y, this.pokemon.species.getIconAtlasKey());
console.log(pokemonIcon)
pokemonIcon.play(this.pokemon.getIconKey());
pokemonIcon.play(this.pokemon.species.getIconKey());
this.slotPokemonIcon = pokemonIcon;
this.add(pokemonIcon);

View File

@ -0,0 +1,75 @@
import BattleScene from "../battle-scene";
import { allSpecies } from "../pokemon-species";
import { Mode } from "./ui";
import UiHandler from "./uiHandler";
export default class StarterSelectUiHandler extends UiHandler {
private starterSelectContainer: Phaser.GameObjects.Container;
constructor(scene: BattleScene) {
super(scene, Mode.STARTER_SELECT);
}
setup() {
const ui = this.getUi();
this.starterSelectContainer = this.scene.add.container(0, -this.scene.game.canvas.height / 6);
this.starterSelectContainer.setVisible(false);
ui.add(this.starterSelectContainer);
let s = 0;
for (let species of allSpecies) {
if (species.getSpeciesForLevel(1) !== species.speciesId || species.generation >= 6)
continue;
species.generateIconAnim(this.scene);
const x = (s % 24) * 13;
const y = Math.floor(s / 24) * 13;
const icon = this.scene.add.sprite(x, y, species.getIconAtlasKey());
icon.setScale(0.5);
icon.setOrigin(0, 0);
icon.play(species.getIconKey()).stop();
this.starterSelectContainer.add(icon);
s++;
}
}
show(args: any[]) {
super.show(args);
this.starterSelectContainer.setVisible(true);
this.setCursor(0);
}
processInput(keyCode: integer) {
const ui = this.getUi();
const keyCodes = Phaser.Input.Keyboard.KeyCodes;
let success = false;
if (keyCode === keyCodes.Z) {
} else if (keyCode === keyCodes.X) {
} else {
}
if (success)
ui.playSelect();
}
setCursor(cursor: integer): boolean {
let changed: boolean = this.cursor !== cursor;
if (changed) {
const forward = this.cursor < cursor;
this.cursor = cursor;
}
return changed;
}
clear() {
super.clear();
this.cursor = -1;
this.starterSelectContainer.setVisible(false);
}
}

View File

@ -9,6 +9,7 @@ import ConfirmUiHandler from './confirm-ui-handler';
import ModifierSelectUiHandler from './modifier-select-ui-handler';
import BallUiHandler from './ball-ui-handler';
import SummaryUiHandler from './summary-ui-handler';
import StarterSelectUiHandler from './starter-select-ui-handler';
export enum Mode {
MESSAGE = 0,
@ -18,12 +19,14 @@ export enum Mode {
CONFIRM,
MODIFIER_SELECT,
PARTY,
SUMMARY
SUMMARY,
STARTER_SELECT,
};
const transitionModes = [
Mode.PARTY,
Mode.SUMMARY
Mode.SUMMARY,
Mode.STARTER_SELECT,
];
export default class UI extends Phaser.GameObjects.Container {
@ -45,7 +48,8 @@ export default class UI extends Phaser.GameObjects.Container {
new ConfirmUiHandler(scene),
new ModifierSelectUiHandler(scene),
new PartyUiHandler(scene),
new SummaryUiHandler(scene)
new SummaryUiHandler(scene),
new StarterSelectUiHandler(scene)
];
}