Refactor modifiers

pull/1/head
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

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 B

Binary file not shown.

After

Width:  |  Height:  |  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++;
Promise.allSettled(this.party.map(p => p.updateInfo())).then(() => resolve());
} else {
const args = [ this ];
if (modifier.shouldApply(args))
modifier.apply(args);
pokemon.calculateStats();
pokemon.updateInfo(() => {
if (!(--pokemonToUpdate))
resolve();
});
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)
];
}