Add game settings

pull/2/head
Flashfyre 2023-10-26 16:33:59 -04:00
parent 23a21af173
commit d3f55ad4ae
10 changed files with 388 additions and 59 deletions

View File

@ -99,7 +99,7 @@ export class SelectStarterPhase extends BattlePhase {
start() { start() {
super.start(); super.start();
this.scene.playSound('menu', { loop: true }); this.scene.playBgm('menu');
this.scene.ui.setMode(Mode.STARTER_SELECT, (starters: Starter[]) => { this.scene.ui.setMode(Mode.STARTER_SELECT, (starters: Starter[]) => {
const party = this.scene.getParty(); const party = this.scene.getParty();
@ -2209,7 +2209,7 @@ export class UnlockPhase extends BattlePhase {
this.scene.time.delayedCall(2000, () => { this.scene.time.delayedCall(2000, () => {
this.scene.gameData.unlocks[this.unlockable] = true; this.scene.gameData.unlocks[this.unlockable] = true;
this.scene.gameData.saveSystem(); this.scene.gameData.saveSystem();
this.scene.playSound('level_up_fanfare'); this.scene.playSoundWithoutBgm('level_up_fanfare');
this.scene.ui.setMode(Mode.MESSAGE); this.scene.ui.setMode(Mode.MESSAGE);
this.scene.arenaBg.setVisible(false); this.scene.arenaBg.setVisible(false);
this.scene.ui.fadeIn(250).then(() => { this.scene.ui.fadeIn(250).then(() => {
@ -2857,8 +2857,7 @@ export class PartyHealPhase extends BattlePhase {
move.ppUsed = 0; move.ppUsed = 0;
pokemon.updateInfo(true); pokemon.updateInfo(true);
} }
const healSong = this.scene.sound.add('heal'); const healSong = this.scene.playSoundWithoutBgm('heal');
healSong.play({ volume: this.scene.gameVolume });
this.scene.time.delayedCall(healSong.totalDuration * 1000, () => { this.scene.time.delayedCall(healSong.totalDuration * 1000, () => {
healSong.destroy(); healSong.destroy();
if (this.resumeBgm && bgmPlaying) if (this.resumeBgm && bgmPlaying)

View File

@ -1,6 +1,6 @@
import Phaser from 'phaser'; import Phaser from 'phaser';
import { Biome } from './data/biome'; import { Biome } from './data/biome';
import UI from './ui/ui'; import UI, { Mode } from './ui/ui';
import { EncounterPhase, SummonPhase, NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, CheckLoadPhase, TurnInitPhase, ReturnPhase, ToggleDoublePositionPhase, CheckSwitchPhase, LevelCapPhase, TestMessagePhase, ShowTrainerPhase } from './battle-phases'; import { EncounterPhase, SummonPhase, NextEncounterPhase, NewBiomeEncounterPhase, SelectBiomePhase, MessagePhase, CheckLoadPhase, TurnInitPhase, ReturnPhase, ToggleDoublePositionPhase, CheckSwitchPhase, LevelCapPhase, TestMessagePhase, ShowTrainerPhase } from './battle-phases';
import Pokemon, { PlayerPokemon, EnemyPokemon } from './pokemon'; import Pokemon, { PlayerPokemon, EnemyPokemon } from './pokemon';
import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies, initSpecies } from './data/pokemon-species'; import PokemonSpecies, { PokemonSpeciesFilter, allSpecies, getPokemonSpecies, initSpecies } from './data/pokemon-species';
@ -29,6 +29,9 @@ import TrainerData from './system/trainer-data';
import SoundFade from 'phaser3-rex-plugins/plugins/soundfade'; import SoundFade from 'phaser3-rex-plugins/plugins/soundfade';
import { pokemonPrevolutions } from './data/pokemon-evolutions'; import { pokemonPrevolutions } from './data/pokemon-evolutions';
import PokeballTray from './ui/pokeball-tray'; import PokeballTray from './ui/pokeball-tray';
import { Setting, settingOptions } from './system/settings';
import SettingsUiHandler from './ui/settings-ui-handler';
import MessageUiHandler from './ui/message-ui-handler';
const enableAuto = true; const enableAuto = true;
const quickStart = false; const quickStart = false;
@ -44,6 +47,7 @@ export enum Button {
RIGHT, RIGHT,
ACTION, ACTION,
CANCEL, CANCEL,
MENU,
CYCLE_SHINY, CYCLE_SHINY,
CYCLE_FORM, CYCLE_FORM,
CYCLE_GENDER, CYCLE_GENDER,
@ -58,9 +62,13 @@ export interface PokeballCounts {
[pb: string]: integer; [pb: string]: integer;
} }
export type AnySound = Phaser.Sound.WebAudioSound | Phaser.Sound.HTML5AudioSound | Phaser.Sound.NoAudioSound;
export default class BattleScene extends Phaser.Scene { export default class BattleScene extends Phaser.Scene {
public auto: boolean; public auto: boolean;
public gameVolume: number = 0.5; public masterVolume: number = 0.5;
public bgmVolume: number = 1;
public seVolume: number = 1;
public gameSpeed: integer = 1; public gameSpeed: integer = 1;
public quickStart: boolean = quickStart; public quickStart: boolean = quickStart;
public finalWave: integer = 200; public finalWave: integer = 200;
@ -105,8 +113,9 @@ export default class BattleScene extends Phaser.Scene {
public spritePipeline: SpritePipeline; public spritePipeline: SpritePipeline;
private bgm: Phaser.Sound.BaseSound; private bgm: AnySound;
private bgmResumeTimer: Phaser.Time.TimerEvent; private bgmResumeTimer: Phaser.Time.TimerEvent;
private bgmCache: Set<string> = new Set();
private buttonKeys: Phaser.Input.Keyboard.Key[][]; private buttonKeys: Phaser.Input.Keyboard.Key[][];
@ -484,7 +493,8 @@ export default class BattleScene extends Phaser.Scene {
[Button.LEFT]: [keyCodes.LEFT, keyCodes.A], [Button.LEFT]: [keyCodes.LEFT, keyCodes.A],
[Button.RIGHT]: [keyCodes.RIGHT, keyCodes.D], [Button.RIGHT]: [keyCodes.RIGHT, keyCodes.D],
[Button.ACTION]: [keyCodes.ENTER, keyCodes.SPACE, keyCodes.Z], [Button.ACTION]: [keyCodes.ENTER, keyCodes.SPACE, keyCodes.Z],
[Button.CANCEL]: [keyCodes.BACKSPACE, keyCodes.ESC, keyCodes.X], [Button.CANCEL]: [keyCodes.BACKSPACE, keyCodes.X],
[Button.MENU]: [keyCodes.ESC, keyCodes.M],
[Button.CYCLE_SHINY]: [keyCodes.R], [Button.CYCLE_SHINY]: [keyCodes.R],
[Button.CYCLE_FORM]: [keyCodes.F], [Button.CYCLE_FORM]: [keyCodes.F],
[Button.CYCLE_GENDER]: [keyCodes.G], [Button.CYCLE_GENDER]: [keyCodes.G],
@ -813,6 +823,32 @@ export default class BattleScene extends Phaser.Scene {
this.ui.processInput(Button.ACTION); this.ui.processInput(Button.ACTION);
else if (this.isButtonPressed(Button.CANCEL)) else if (this.isButtonPressed(Button.CANCEL))
this.ui.processInput(Button.CANCEL); this.ui.processInput(Button.CANCEL);
else if (this.isButtonPressed(Button.MENU)) {
switch (this.ui.getMode()) {
case Mode.MESSAGE:
if (!(this.ui.getHandler() as MessageUiHandler).pendingPrompt)
return;
case Mode.COMMAND:
case Mode.FIGHT:
case Mode.BALL:
case Mode.TARGET_SELECT:
case Mode.PARTY:
case Mode.SUMMARY:
case Mode.BIOME_SELECT:
case Mode.STARTER_SELECT:
case Mode.CONFIRM:
case Mode.GAME_MODE_SELECT:
this.ui.setModeWithoutClear(Mode.SETTINGS);
this.playSound('menu_open');
break;
case Mode.SETTINGS:
this.ui.revertMode();
this.playSound('select');
break;
default:
return;
}
}
else if (this.ui?.getHandler() instanceof StarterSelectUiHandler) { else if (this.ui?.getHandler() instanceof StarterSelectUiHandler) {
if (this.isButtonPressed(Button.CYCLE_SHINY)) if (this.isButtonPressed(Button.CYCLE_SHINY))
this.ui.processInput(Button.CYCLE_SHINY); this.ui.processInput(Button.CYCLE_SHINY);
@ -826,25 +862,24 @@ export default class BattleScene extends Phaser.Scene {
return; return;
} }
else if (this.isButtonPressed(Button.SPEED_UP)) { else if (this.isButtonPressed(Button.SPEED_UP)) {
if (!this.auto) { if (this.gameSpeed < 5) {
if (this.gameSpeed < 2.5) this.gameData.saveSetting(Setting.Game_Speed, settingOptions[Setting.Game_Speed].indexOf(`${this.gameSpeed}x`) + 1);
this.gameSpeed += 0.25; if (this.ui.getMode() === Mode.SETTINGS)
} else if (this.gameSpeed < 20) (this.ui.getHandler() as SettingsUiHandler).show([]);
this.gameSpeed++; }
} else if (this.isButtonPressed(Button.SLOW_DOWN)) { } else if (this.isButtonPressed(Button.SLOW_DOWN)) {
if (this.gameSpeed > 1) { if (this.gameSpeed > 1) {
if (!this.auto) this.gameData.saveSetting(Setting.Game_Speed, Math.max(settingOptions[Setting.Game_Speed].indexOf(`${this.gameSpeed}x`) - 1, 0));
this.gameSpeed -= 0.25; if (this.ui.getMode() === Mode.SETTINGS)
else (this.ui.getHandler() as SettingsUiHandler).show([]);
this.gameSpeed--;
} }
} else if (enableAuto) { } else if (enableAuto) {
if (this.isButtonPressed(Button.AUTO)) { if (this.isButtonPressed(Button.AUTO)) {
this.auto = !this.auto; this.auto = !this.auto;
if (this.auto) if (this.auto)
this.gameSpeed = Math.floor(this.gameSpeed); this.gameSpeed = Math.floor(this.gameSpeed);
else if (this.gameSpeed > 2.5) else if (this.gameSpeed > 5)
this.gameSpeed = 2.5; this.gameSpeed = 5;
} else } else
return; return;
} else } else
@ -867,13 +902,14 @@ export default class BattleScene extends Phaser.Scene {
if (this.bgm && bgmName === this.bgm.key) { if (this.bgm && bgmName === this.bgm.key) {
if (!this.bgm.isPlaying) { if (!this.bgm.isPlaying) {
this.bgm.play({ this.bgm.play({
volume: this.gameVolume volume: this.masterVolume * this.bgmVolume
}); });
} }
return; return;
} }
if (fadeOut && !this.bgm) if (fadeOut && !this.bgm)
fadeOut = false; fadeOut = false;
this.bgmCache.add(bgmName);
this.loadBgm(bgmName); this.loadBgm(bgmName);
let loopPoint = 0; let loopPoint = 0;
loopPoint = bgmName === this.arena.bgm loopPoint = bgmName === this.arena.bgm
@ -883,7 +919,7 @@ export default class BattleScene extends Phaser.Scene {
const playNewBgm = () => { const playNewBgm = () => {
if (bgmName === null && this.bgm && !this.bgm.pendingRemove) { if (bgmName === null && this.bgm && !this.bgm.pendingRemove) {
this.bgm.play({ this.bgm.play({
volume: this.gameVolume volume: this.masterVolume * this.bgmVolume
}); });
return; return;
} }
@ -891,7 +927,7 @@ export default class BattleScene extends Phaser.Scene {
this.bgm.stop(); this.bgm.stop();
this.bgm = this.sound.add(bgmName, { loop: true }); this.bgm = this.sound.add(bgmName, { loop: true });
this.bgm.play({ this.bgm.play({
volume: this.gameVolume volume: this.masterVolume * this.bgmVolume
}); });
if (loopPoint) if (loopPoint)
this.bgm.on('looped', () => this.bgm.play({ seek: loopPoint })); this.bgm.on('looped', () => this.bgm.play({ seek: loopPoint }));
@ -912,14 +948,27 @@ export default class BattleScene extends Phaser.Scene {
this.load.start(); this.load.start();
} }
pauseBgm(): void { pauseBgm(): boolean {
if (this.bgm && this.bgm.isPlaying) if (this.bgm && this.bgm.isPlaying) {
this.bgm.pause(); this.bgm.pause();
return true;
}
return false;
} }
resumeBgm(): void { resumeBgm(): boolean {
if (this.bgm && this.bgm.isPaused) if (this.bgm && this.bgm.isPaused) {
this.bgm.resume(); this.bgm.resume();
return true;
}
return false;
}
updateSoundVolume(): void {
if (this.sound) {
for (let sound of this.sound.getAllPlaying())
(sound as AnySound).setVolume(this.masterVolume * (this.bgmCache.has(sound.key) ? this.bgmVolume : this.seVolume));
}
} }
fadeOutBgm(duration?: integer, destroy?: boolean): boolean { fadeOutBgm(duration?: integer, destroy?: boolean): boolean {
@ -938,27 +987,32 @@ export default class BattleScene extends Phaser.Scene {
return false; return false;
} }
playSound(soundName: string, config?: object) { playSound(soundName: string, config?: object): AnySound {
if (config) { if (config) {
if (config.hasOwnProperty('volume')) if (config.hasOwnProperty('volume'))
config['volume'] *= this.gameVolume; config['volume'] *= this.masterVolume * this.seVolume;
else else
config['volume'] = this.gameVolume; config['volume'] = this.masterVolume * this.seVolume;
} else } else
config = { volume: this.gameVolume }; config = { volume: this.masterVolume * this.seVolume };
this.sound.play(soundName, config); this.sound.play(soundName, config);
return this.sound.get(soundName) as AnySound;
} }
playSoundWithoutBgm(soundName: string, pauseDuration?: integer): void { playSoundWithoutBgm(soundName: string, pauseDuration?: integer): AnySound {
this.pauseBgm(); this.bgmCache.add(soundName);
const resumeBgm = this.pauseBgm();
this.playSound(soundName); this.playSound(soundName);
const sound = this.sound.get(soundName); const sound = this.sound.get(soundName) as AnySound;
if (this.bgmResumeTimer) if (this.bgmResumeTimer)
this.bgmResumeTimer.destroy(); this.bgmResumeTimer.destroy();
this.bgmResumeTimer = this.time.delayedCall((pauseDuration || Utils.fixedInt(sound.totalDuration * 1000)), () => { if (resumeBgm) {
this.resumeBgm(); this.bgmResumeTimer = this.time.delayedCall((pauseDuration || Utils.fixedInt(sound.totalDuration * 1000)), () => {
this.bgmResumeTimer = null; this.resumeBgm();
}); this.bgmResumeTimer = null;
});
}
return sound;
} }
getBgmLoopPoint(bgmName: string): number { getBgmLoopPoint(bgmName: string): number {

View File

@ -101,8 +101,8 @@ export class EvolutionPhase extends BattlePhase {
this.scene.unshiftPhase(new EndEvolutionPhase(this.scene)); this.scene.unshiftPhase(new EndEvolutionPhase(this.scene));
this.scene.time.delayedCall(1000, () => { this.scene.time.delayedCall(1000, () => {
const evolutionBgm = this.scene.sound.add('evolution'); const evolutionBgm = this.scene.playSoundWithoutBgm('evolution');
evolutionBgm.play({ volume: this.scene.gameVolume }); evolutionBgm.play({ volume: this.scene.masterVolume });
this.scene.tweens.add({ this.scene.tweens.add({
targets: this.evolutionBgOverlay, targets: this.evolutionBgOverlay,
alpha: 1, alpha: 1,
@ -167,7 +167,7 @@ export class EvolutionPhase extends BattlePhase {
this.scene.time.delayedCall(250, () => { this.scene.time.delayedCall(250, () => {
pokemon.cry(); pokemon.cry();
this.scene.time.delayedCall(1250, () => { this.scene.time.delayedCall(1250, () => {
this.scene.playSound('evolution_fanfare'); this.scene.playSoundWithoutBgm('evolution_fanfare');
this.scene.ui.showText(`Congratulations! Your ${preName}\nevolved into ${pokemon.name}!`, null, () => this.end(), null, true, 3000); this.scene.ui.showText(`Congratulations! Your ${preName}\nevolved into ${pokemon.name}!`, null, () => this.end(), null, true, 3000);
this.scene.time.delayedCall(Utils.fixedInt(4250), () => this.scene.playBgm()); this.scene.time.delayedCall(Utils.fixedInt(4250), () => this.scene.playBgm());
}); });

View File

@ -1,5 +1,5 @@
import Phaser from 'phaser'; import Phaser from 'phaser';
import BattleScene from './battle-scene'; import BattleScene, { AnySound } from './battle-scene';
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from './ui/battle-info'; import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from './ui/battle-info';
import Move, { StatChangeAttr, HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariablePowerAttr, Moves, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, AttackMove, AddBattlerTagAttr } from "./data/move"; import Move, { StatChangeAttr, HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariablePowerAttr, Moves, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, AttackMove, AddBattlerTagAttr } from "./data/move";
import { pokemonLevelMoves } from './data/pokemon-level-moves'; import { pokemonLevelMoves } from './data/pokemon-level-moves';
@ -885,7 +885,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
const key = this.species.speciesId.toString(); const key = this.species.speciesId.toString();
let i = 0; let i = 0;
let rate = 0.85; let rate = 0.85;
this.scene.playSound(key, { rate: rate }); const crySound = this.scene.playSound(key, { rate: rate }) as AnySound;
const sprite = this.getSprite(); const sprite = this.getSprite();
const tintSprite = this.getTintSprite(); const tintSprite = this.getTintSprite();
const delay = Math.max(this.scene.sound.get(key).totalDuration * 50, 25); const delay = Math.max(this.scene.sound.get(key).totalDuration * 50, 25);
@ -894,7 +894,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
sprite.anims.pause(); sprite.anims.pause();
tintSprite.anims.pause(); tintSprite.anims.pause();
let faintCryTimer = this.scene.time.addEvent({ let faintCryTimer = this.scene.time.addEvent({
delay: delay, delay: Utils.fixedInt(delay),
repeat: -1, repeat: -1,
callback: () => { callback: () => {
++i; ++i;
@ -907,14 +907,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
frameProgress -= frameThreshold; frameProgress -= frameThreshold;
} }
const crySound = this.scene.sound.get(key);
if (crySound && !crySound.pendingRemove) { if (crySound && !crySound.pendingRemove) {
rate *= 0.99; rate *= 0.99;
crySound.play({ crySound.setRate(rate);
rate: rate,
seek: (i * delay * 0.001) * rate,
volume: this.scene.gameVolume
});
} }
else { else {
faintCryTimer.destroy(); faintCryTimer.destroy();
@ -925,10 +920,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
} }
}); });
// Failsafe // Failsafe
this.scene.time.delayedCall(3000, () => { this.scene.time.delayedCall(Utils.fixedInt(3000), () => {
if (!faintCryTimer || !this.scene) if (!faintCryTimer || !this.scene)
return; return;
const crySound = this.scene.sound.get(key);
if (crySound?.isPlaying) if (crySound?.isPlaying)
crySound.stop(); crySound.stop();
faintCryTimer.destroy(); faintCryTimer.destroy();

View File

@ -14,6 +14,7 @@ import { GameMode } from "../game-mode";
import { BattleType } from "../battle"; import { BattleType } from "../battle";
import TrainerData from "./trainer-data"; import TrainerData from "./trainer-data";
import { trainerConfigs } from "../data/trainer-type"; import { trainerConfigs } from "../data/trainer-type";
import { Setting, setSetting, settingDefaults } from "./settings";
interface SystemSaveData { interface SystemSaveData {
trainerId: integer; trainerId: integer;
@ -81,8 +82,9 @@ export class GameData {
constructor(scene: BattleScene) { constructor(scene: BattleScene) {
this.scene = scene; this.scene = scene;
this.trainerId = Utils.randInt(65536); this.loadSettings();
this.secretId = Utils.randInt(65536); this.trainerId = Utils.randSeedInt(65536);
this.secretId = Utils.randSeedInt(65536);
this.unlocks = { this.unlocks = {
[Unlockables.ENDLESS_MODE]: false, [Unlockables.ENDLESS_MODE]: false,
[Unlockables.MINI_BLACK_HOLE]: false [Unlockables.MINI_BLACK_HOLE]: false
@ -109,7 +111,7 @@ export class GameData {
} }
private loadSystem(): boolean { private loadSystem(): boolean {
if (!localStorage.getItem('data')) if (!localStorage.hasOwnProperty('data'))
return false; return false;
const data = JSON.parse(atob(localStorage.getItem('data'))) as SystemSaveData; const data = JSON.parse(atob(localStorage.getItem('data'))) as SystemSaveData;
@ -133,6 +135,33 @@ export class GameData {
return true; return true;
} }
public saveSetting(setting: Setting, valueIndex: integer): boolean {
let settings: object = {};
if (localStorage.hasOwnProperty('settings'))
settings = JSON.parse(localStorage.getItem('settings'));
setSetting(this.scene, setting as Setting, valueIndex);
Object.keys(settingDefaults).forEach(s => {
if (s === setting)
settings[s] = valueIndex;
});
localStorage.setItem('settings', JSON.stringify(settings));
return true;
}
private loadSettings(): boolean {
if (!localStorage.hasOwnProperty('settings'))
return false;
const settings = JSON.parse(localStorage.getItem('settings'));
for (let setting of Object.keys(settings))
setSetting(this.scene, setting as Setting, settings[setting]);
}
saveSession(scene: BattleScene): boolean { saveSession(scene: BattleScene): boolean {
const sessionData = { const sessionData = {
seed: scene.seed, seed: scene.seed,

52
src/system/settings.ts Normal file
View File

@ -0,0 +1,52 @@
import BattleScene from "../battle-scene";
export enum Setting {
Game_Speed = "GAME_SPEED",
Master_Volume = "MASTER_VOLUME",
BGM_Volume = "BGM_VOLUME",
SE_Volume = "SE_VOLUME"
}
export interface SettingOptions {
[key: string]: string[]
}
export interface SettingDefaults {
[key: string]: integer
}
export const settingOptions: SettingOptions = {
[Setting.Game_Speed]: [ '1x', '1.25x', '1.5x', '2x', '2.5x', '3x', '4x', '5x' ],
[Setting.Master_Volume]: new Array(11).fill(null).map((_, i) => i ? (i * 10).toString() : 'Mute'),
[Setting.BGM_Volume]: new Array(11).fill(null).map((_, i) => i ? (i * 10).toString() : 'Mute'),
[Setting.SE_Volume]: new Array(11).fill(null).map((_, i) => i ? (i * 10).toString() : 'Mute')
};
export const settingDefaults: SettingDefaults = {
[Setting.Game_Speed]: 0,
[Setting.Master_Volume]: 5,
[Setting.BGM_Volume]: 10,
[Setting.SE_Volume]: 10
};
export function setSetting(scene: BattleScene, setting: Setting, value: integer): boolean {
switch (setting) {
case Setting.Game_Speed:
scene.gameSpeed = parseFloat(settingOptions[setting][value].replace('x', ''));
break;
case Setting.Master_Volume:
scene.masterVolume = value ? parseInt(settingOptions[setting][value]) * 0.01 : 0;
scene.updateSoundVolume();
break;
case Setting.BGM_Volume:
scene.bgmVolume = value ? parseInt(settingOptions[setting][value]) * 0.01 : 0;
scene.updateSoundVolume();
break;
case Setting.SE_Volume:
scene.seVolume = value ? parseInt(settingOptions[setting][value]) * 0.01 : 0;
scene.updateSoundVolume();
break;
}
return true;
}

View File

@ -6,7 +6,7 @@ import * as Utils from "../utils";
export default abstract class MessageUiHandler extends AwaitableUiHandler { export default abstract class MessageUiHandler extends AwaitableUiHandler {
protected textTimer: Phaser.Time.TimerEvent; protected textTimer: Phaser.Time.TimerEvent;
protected textCallbackTimer: Phaser.Time.TimerEvent; protected textCallbackTimer: Phaser.Time.TimerEvent;
protected pendingPrompt: boolean; public pendingPrompt: boolean;
public message: Phaser.GameObjects.Text; public message: Phaser.GameObjects.Text;
public prompt: Phaser.GameObjects.Sprite; public prompt: Phaser.GameObjects.Sprite;

View File

@ -0,0 +1,174 @@
import BattleScene, { Button } from "../battle-scene";
import { Setting, settingDefaults, settingOptions } from "../system/settings";
import { TextStyle, addTextObject, getTextColor } from "./text";
import { Mode } from "./ui";
import UiHandler from "./uiHandler";
export default class SettingsUiHandler extends UiHandler {
private settingsContainer: Phaser.GameObjects.Container;
private optionsContainer: Phaser.GameObjects.Container;
private optionsBg: Phaser.GameObjects.NineSlice;
private optionCursors: integer[];
private optionValueLabels: Phaser.GameObjects.Text[][];
private cursorObj: Phaser.GameObjects.NineSlice;
constructor(scene: BattleScene, mode?: Mode) {
super(scene, mode);
}
setup() {
const ui = this.getUi();
this.settingsContainer = this.scene.add.container(1, -(this.scene.game.canvas.height / 6) + 1);
const headerBg = this.scene.add.nineslice(0, 0, 'window', null, (this.scene.game.canvas.width / 6) - 2, 24, 6, 6, 6, 6);
headerBg.setOrigin(0, 0);
const headerText = addTextObject(this.scene, 0, 0, 'Options', TextStyle.SETTINGS_LABEL);
headerText.setOrigin(0, 0);
headerText.setPositionRelative(headerBg, 8, 4);
this.optionsBg = this.scene.add.nineslice(0, headerBg.height, 'window', null, (this.scene.game.canvas.width / 6) - 2, (this.scene.game.canvas.height / 6) - headerBg.height - 2, 6, 6, 6, 6);
this.optionsBg.setOrigin(0, 0);
this.optionsContainer = this.scene.add.container(0, 0);
this.optionValueLabels = [];
Object.keys(Setting).forEach((setting, s) => {
const settingLabel = addTextObject(this.scene, 8, 28 + s * 16, setting.replace(/\_/g, ' '), TextStyle.SETTINGS_LABEL);
settingLabel.setOrigin(0, 0);
this.optionsContainer.add(settingLabel);
this.optionValueLabels.push(settingOptions[Setting[setting]].map((option, o) => {
const valueLabel = addTextObject(this.scene, 0, 0, option, settingDefaults[Setting[setting]] === o ? TextStyle.SETTINGS_SELECTED : TextStyle.WINDOW);
valueLabel.setOrigin(0, 0);
this.optionsContainer.add(valueLabel);
return valueLabel;
}));
const totalWidth = this.optionValueLabels[s].map(o => o.width).reduce((total, width) => total += width, 0);
const totalSpace = 220 - totalWidth / 6;
const optionSpacing = Math.floor(totalSpace / (this.optionValueLabels[s].length - 1));
let xOffset = 0;
for (let value of this.optionValueLabels[s]) {
value.setPositionRelative(settingLabel, 82 + xOffset, 0);
xOffset += value.width / 6 + optionSpacing;
}
});
this.optionCursors = Object.values(settingDefaults);
this.settingsContainer.add(headerBg);
this.settingsContainer.add(headerText);
this.settingsContainer.add(this.optionsBg);
this.settingsContainer.add(this.optionsContainer);
ui.add(this.settingsContainer);
this.setCursor(0);
this.settingsContainer.setVisible(false);
}
show(args: any[]) {
super.show(args);
const settings: object = localStorage.hasOwnProperty('settings') ? JSON.parse(localStorage.getItem('settings')) : {};
Object.keys(settingDefaults).forEach((setting, s) => {
this.setOptionCursor(s, settings.hasOwnProperty(setting) ? settings[setting] : settingDefaults[setting]);
});
this.settingsContainer.setVisible(true);
this.setCursor(0);
this.getUi().moveTo(this.settingsContainer, this.getUi().length - 1);
}
processInput(button: Button) {
const ui = this.getUi();
let success = false;
if (button === Button.CANCEL) {
success = true;
this.scene.ui.revertMode();
} else {
switch (button) {
case Button.UP:
if (this.cursor)
success = this.setCursor(this.cursor - 1);
break;
case Button.DOWN:
if (this.cursor < this.optionValueLabels.length - 1)
success = this.setCursor(this.cursor + 1);
break;
case Button.LEFT:
if (this.optionCursors[this.cursor])
success = this.setOptionCursor(this.cursor, this.optionCursors[this.cursor] - 1, true);
break;
case Button.RIGHT:
if (this.optionCursors[this.cursor] < this.optionValueLabels[this.cursor].length - 1)
success = this.setOptionCursor(this.cursor, this.optionCursors[this.cursor] + 1, true);
break;
}
}
if (success)
ui.playSelect();
}
setCursor(cursor: integer): boolean {
const ret = super.setCursor(cursor);
if (!this.cursorObj) {
this.cursorObj = this.scene.add.nineslice(0, 0, 'starter_select_cursor_highlight', null, (this.scene.game.canvas.width / 6) - 10, 16, 1, 1, 1, 1);
this.cursorObj.setOrigin(0, 0);
this.optionsContainer.add(this.cursorObj);
}
this.cursorObj.setPositionRelative(this.optionsBg, 4, 4 + this.cursor * 16);
return ret;
}
setOptionCursor(settingIndex: integer, cursor: integer, save?: boolean): boolean {
const lastValueLabel = this.optionValueLabels[settingIndex][this.optionCursors[settingIndex]];
lastValueLabel.setColor(getTextColor(TextStyle.WINDOW));
lastValueLabel.setShadowColor(getTextColor(TextStyle.WINDOW, true));
this.optionCursors[settingIndex] = cursor;
const newValueLabel = this.optionValueLabels[settingIndex][cursor];
newValueLabel.setColor(getTextColor(TextStyle.SETTINGS_SELECTED));
newValueLabel.setShadowColor(getTextColor(TextStyle.SETTINGS_SELECTED, true));
if (save)
this.scene.gameData.saveSetting(Setting[Object.keys(Setting)[settingIndex]], cursor);
return true;
}
clear() {
super.clear();
this.settingsContainer.setVisible(false);
this.eraseCursor();
}
eraseCursor() {
if (this.cursorObj)
this.cursorObj.destroy();
this.cursorObj = null;
}
}

View File

@ -7,7 +7,9 @@ export enum TextStyle {
SUMMARY, SUMMARY,
SUMMARY_RED, SUMMARY_RED,
SUMMARY_GOLD, SUMMARY_GOLD,
MONEY MONEY,
SETTINGS_LABEL,
SETTINGS_SELECTED
}; };
export function addTextObject(scene: Phaser.Scene, x: number, y: number, content: string, style: TextStyle, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle) { export function addTextObject(scene: Phaser.Scene, x: number, y: number, content: string, style: TextStyle, extraStyleOptions?: Phaser.Types.GameObjects.Text.TextStyle) {
@ -29,6 +31,8 @@ export function addTextObject(scene: Phaser.Scene, x: number, y: number, content
case TextStyle.SUMMARY_GOLD: case TextStyle.SUMMARY_GOLD:
case TextStyle.WINDOW: case TextStyle.WINDOW:
case TextStyle.MESSAGE: case TextStyle.MESSAGE:
case TextStyle.SETTINGS_LABEL:
case TextStyle.SETTINGS_SELECTED:
styleOptions.fontSize = '96px'; styleOptions.fontSize = '96px';
break; break;
case TextStyle.BATTLE_INFO: case TextStyle.BATTLE_INFO:
@ -80,5 +84,9 @@ export function getTextColor(textStyle: TextStyle, shadow?: boolean) {
case TextStyle.SUMMARY_GOLD: case TextStyle.SUMMARY_GOLD:
case TextStyle.MONEY: case TextStyle.MONEY:
return !shadow ? '#e8e8a8' : '#a0a060' return !shadow ? '#e8e8a8' : '#a0a060'
case TextStyle.SETTINGS_LABEL:
return !shadow ? '#f8b050' : '#c07800';
case TextStyle.SETTINGS_SELECTED:
return !shadow ? '#f88880' : '#f83018';
} }
} }

View File

@ -14,6 +14,7 @@ import EvolutionSceneHandler from './evolution-scene-handler';
import BiomeSelectUiHandler from './biome-select-ui-handler'; import BiomeSelectUiHandler from './biome-select-ui-handler';
import TargetSelectUiHandler from './target-select-ui-handler'; import TargetSelectUiHandler from './target-select-ui-handler';
import GameModeSelectUiHandler from './game-mode-select-ui-handler'; import GameModeSelectUiHandler from './game-mode-select-ui-handler';
import SettingsUiHandler from './settings-ui-handler';
export enum Mode { export enum Mode {
MESSAGE, MESSAGE,
@ -28,7 +29,8 @@ export enum Mode {
STARTER_SELECT, STARTER_SELECT,
EVOLUTION_SCENE, EVOLUTION_SCENE,
CONFIRM, CONFIRM,
GAME_MODE_SELECT GAME_MODE_SELECT,
SETTINGS
}; };
const transitionModes = [ const transitionModes = [
@ -40,11 +42,13 @@ const transitionModes = [
const noTransitionModes = [ const noTransitionModes = [
Mode.CONFIRM, Mode.CONFIRM,
Mode.GAME_MODE_SELECT Mode.GAME_MODE_SELECT,
Mode.SETTINGS
]; ];
export default class UI extends Phaser.GameObjects.Container { export default class UI extends Phaser.GameObjects.Container {
private mode: Mode; private mode: Mode;
private lastMode: Mode;
private handlers: UiHandler[]; private handlers: UiHandler[];
private overlay: Phaser.GameObjects.Rectangle; private overlay: Phaser.GameObjects.Rectangle;
@ -67,7 +71,8 @@ export default class UI extends Phaser.GameObjects.Container {
new StarterSelectUiHandler(scene), new StarterSelectUiHandler(scene),
new EvolutionSceneHandler(scene), new EvolutionSceneHandler(scene),
new ConfirmUiHandler(scene), new ConfirmUiHandler(scene),
new GameModeSelectUiHandler(scene) new GameModeSelectUiHandler(scene),
new SettingsUiHandler(scene)
]; ];
} }
@ -181,6 +186,7 @@ export default class UI extends Phaser.GameObjects.Container {
if (this.mode !== mode) { if (this.mode !== mode) {
if (clear) if (clear)
this.getHandler().clear(); this.getHandler().clear();
this.lastMode = this.mode && !clear ? this.mode : undefined;
this.mode = mode; this.mode = mode;
this.getHandler().show(args); this.getHandler().show(args);
} }
@ -199,6 +205,10 @@ export default class UI extends Phaser.GameObjects.Container {
}); });
} }
getMode(): Mode {
return this.mode;
}
setMode(mode: Mode, ...args: any[]): Promise<void> { setMode(mode: Mode, ...args: any[]): Promise<void> {
return this.setModeInternal(mode, true, false, args); return this.setModeInternal(mode, true, false, args);
} }
@ -210,4 +220,13 @@ export default class UI extends Phaser.GameObjects.Container {
setModeWithoutClear(mode: Mode, ...args: any[]): Promise<void> { setModeWithoutClear(mode: Mode, ...args: any[]): Promise<void> {
return this.setModeInternal(mode, false, false, args); return this.setModeInternal(mode, false, false, args);
} }
revertMode(): void {
if (!this.lastMode)
return;
this.getHandler().clear();
this.mode = this.lastMode;
this.lastMode = undefined;
}
} }