Skip to content
Snippets Groups Projects
Commit 1c3fe925 authored by Paolo Brasolin's avatar Paolo Brasolin
Browse files

refactor: #fe MainScene

parent 37a49965
No related branches found
No related tags found
No related merge requests found
......@@ -56,7 +56,7 @@ export default class FightScene extends MainScene {
);
}
async afterCreate() {
async beforeGameStart() {
(this.scene.get("background") as BackgroundScene).atmosphere.play();
this.planMusicChanges();
this.planWaveAnnouncements();
......@@ -222,7 +222,7 @@ export default class FightScene extends MainScene {
(expLength + (length - expLength) * difficulty);
if (currentCount < maxCount && currentChars < maxChars) {
await this.spawnFoe(length, duration);
await this.spawnFoe(10, 1);
}
this.spawner = this.time.delayedCall(100, this.spawnFoes.bind(this));
......@@ -233,7 +233,7 @@ export default class FightScene extends MainScene {
await new Foe(this, timeout).initialize(length);
}
async afterEndGame() {
async afterGameEnd() {
this.beGame = (
await backend.updateGame(this.beGame.id, {
ended_at: new Date().toISOString(),
......
import "phaser";
import Player from "./player";
import levenshtein from "damerau-levenshtein";
import Foe from "./foe";
import Typewriter from "./typewriter";
import HUD from "./hud";
import Player from "./player";
import Typewriter from "./typewriter";
export interface InputStatus {
began_at: string;
......@@ -30,40 +28,140 @@ interface UIDimensions {
}
export default class MainScene extends Phaser.Scene {
foes: Array<Foe>;
player: Player;
cluesGroup: Phaser.Physics.Arcade.Group;
typewriter: Typewriter;
acceptedWords: number;
score: number;
health: number;
hud: HUD;
gameTime: Phaser.Time.TimerEvent;
uiDimensions: UIDimensions;
music!: Phaser.Sound.BaseSound;
player!: Player;
constructor(config: string | Phaser.Types.Scenes.SettingsConfig) {
super(config);
this.foes = [];
async create(data: { music: Phaser.Sound.BaseSound }) {
this.music = data.music;
this.createAnimations();
this.resetGameState();
this.createGameTime();
this.resetGameTime();
this.createUiDimensions();
this.createHUD();
this.resetHUD();
this.bindPauseResumeEvents();
this.bindPauseUserControls();
this.createPhysics();
this.initCluesGroup();
this.player = new Player(this);
// this.scale.displaySize.setAspectRatio(
// this.cameras.main.width / this.cameras.main.height,
// );
// this.scale.refresh();
this.createAndBindTypewriter();
await this.beforeGameStart();
this.setGameTimePaused(false);
}
init() {
this.score = 0;
this.health = 100;
this.acceptedWords = 0;
update() {
this.updateClock();
}
this.uiDimensions = this.initUiDimensions();
this.hud = new HUD(this, {
statsPadding: this.uiDimensions.statsPadding,
statsFontSize: this.uiDimensions.statsFontSize,
inputPadding: this.uiDimensions.inputPadding,
inputFontSize: this.uiDimensions.inputFontSize,
inputPosition: this.uiDimensions.inputPosition,
async beforeGameStart() {
console.error("beforeGameStart not implemented");
}
async endGame() {
this.setGameTimePaused(true);
this.foes.forEach((foe) => foe.destroy());
this.sound.play("sfx_game_over");
this.typewriter.setActive(false);
this.typewriter.resetInputStatus();
await this.afterGameEnd();
}
async afterGameEnd() {
console.error("afterGameEnd not implemented");
}
//=[ Sprite animations ]======================================================
createAnimations() {
const defaults: Phaser.Types.Animations.Animation = {
frameRate: 10,
repeat: -1,
};
this.anims.create({
key: "player_idle",
frames: this.anims.generateFrameNumbers("Oetzi", {
start: 1,
end: 5,
}),
...defaults,
});
this.hud.setHealth(this.health);
this.hud.setScore(this.score);
this.hud.setClock(0);
this.anims.create({
key: "player_run",
frames: this.anims.generateFrameNumbers("Oetzi", {
start: 6,
end: 13,
}),
...defaults,
});
this.anims.create({ key: "SpearStill", frames: "SpearStill", ...defaults });
this.anims.create({
key: "SpearWobble",
frames: "SpearWobble",
...defaults,
frameRate: 48,
});
this.anims.create({ key: "BearRun", frames: "BearRun", ...defaults });
this.anims.create({ key: "BearWalk", frames: "BearWalk", ...defaults });
this.anims.create({ key: "BoarRun", frames: "BoarRun", ...defaults });
this.anims.create({ key: "BoarWalk", frames: "BoarWalk", ...defaults });
this.anims.create({ key: "DeerRun", frames: "DeerRun", ...defaults });
this.anims.create({ key: "DeerWalk", frames: "DeerWalk", ...defaults });
this.anims.create({ key: "FoxRun", frames: "FoxRun", ...defaults });
this.anims.create({ key: "FoxWalk", frames: "FoxWalk", ...defaults });
this.anims.create({ key: "HorseRun", frames: "HorseRun", ...defaults });
this.anims.create({ key: "HorseWalk", frames: "HorseWalk", ...defaults });
this.anims.create({ key: "RabbitRun", frames: "RabbitRun", ...defaults });
this.anims.create({ key: "RabbitWalk", frames: "RabbitWalk", ...defaults });
this.anims.create({ key: "WolfRun", frames: "WolfRun", ...defaults });
this.anims.create({ key: "WolfWalk", frames: "WolfWalk", ...defaults });
}
//=[ Game time ]==============================================================
gameTime!: Phaser.Time.TimerEvent;
getGameTime() {
// NOTE: we round because we don't need sub-ms precision.
return Math.round(this.gameTime.getElapsed());
}
createGameTime() {
this.gameTime = this.time.addEvent({});
}
resetGameTime() {
this.gameTime.reset({
delay: Number.MAX_SAFE_INTEGER,
paused: true,
});
}
setGameTimePaused(paused: boolean) {
this.gameTime.paused = paused;
}
//=[ Pause/resume ]===========================================================
bindPauseResumeEvents() {
this.events.on("pause", this.onPause.bind(this));
this.events.on("resume", this.onResume.bind(this));
}
......@@ -75,6 +173,10 @@ export default class MainScene extends Phaser.Scene {
this.scene.launch("pause");
}
concealClues() {
this.foes.forEach((foe) => foe.clue.conceal());
}
onResume() {
this.uncoverClues();
this.typewriter.setActive(true);
......@@ -82,9 +184,55 @@ export default class MainScene extends Phaser.Scene {
this.scene.stop("pause");
}
initUiDimensions(): UIDimensions {
const ch = this.cameras.main.height;
const cw = this.cameras.main.width;
uncoverClues() {
this.foes.forEach((foe) => foe.clue.uncover());
}
bindPauseUserControls() {
if (this.game.device.os.desktop) {
const escBinding = this.input.keyboard.addKey(
Phaser.Input.Keyboard.KeyCodes.ESC,
);
escBinding.onDown = () => this.scene.pause();
} else {
const onPointerUp = (pointer: Phaser.Input.Pointer) => {
const tapped =
pointer.downY <
this.cameras.main.height - this.uiDimensions.kbdHeight;
if (tapped) this.scene.pause();
};
this.input.on("pointerup", onPointerUp);
}
}
//=[ HUD and UI dimensions ]==================================================
uiDimensions!: UIDimensions;
hud!: HUD;
createUiDimensions() {
this.uiDimensions = this.computeUiDimensions(this.cameras.main);
}
createHUD() {
this.hud = new HUD(this, {
statsPadding: this.uiDimensions.statsPadding,
statsFontSize: this.uiDimensions.statsFontSize,
inputPadding: this.uiDimensions.inputPadding,
inputFontSize: this.uiDimensions.inputFontSize,
inputPosition: this.uiDimensions.inputPosition,
});
}
resetHUD() {
this.hud.setHealth(this.health);
this.hud.setScore(this.score);
this.hud.setClock(0);
}
computeUiDimensions(camera: Phaser.Cameras.Scene2D.Camera): UIDimensions {
const ch = camera.height;
const cw = camera.width;
const vh = ch * 0.01;
const vw = cw * 0.01;
......@@ -119,20 +267,11 @@ export default class MainScene extends Phaser.Scene {
};
}
async create(data: { music: Phaser.Sound.BaseSound }) {
this.music = data.music;
this.bindPauseShortcut();
this.gameTime = this.time.addEvent({
delay: Number.MAX_SAFE_INTEGER,
paused: true,
});
//=[ Physics ]================================================================
this.initCluesGroup();
this.createAnimations();
cluesGroup!: Phaser.Physics.Arcade.Group;
createPhysics() {
this.physics.world.setBounds(
0,
0,
......@@ -159,70 +298,31 @@ export default class MainScene extends Phaser.Scene {
body.gameObject.emit("hitWorldBounds", { up, down, left, right });
},
);
this.player = new Player(this);
// this.scale.displaySize.setAspectRatio(
// this.cameras.main.width / this.cameras.main.height,
// );
// this.scale.refresh();
this.createAndBindTypewriter();
await this.afterCreate();
}
async afterCreate() {
this.gameTime.paused = false;
initCluesGroup() {
this.cluesGroup = this.physics.add.group({
collideWorldBounds: true,
customBoundsRectangle: this.uiDimensions.cluesBounds,
bounceY: 0.2,
dragY: 180,
});
this.physics.add.collider(this.cluesGroup, this.cluesGroup);
}
createAnimations() {
const defaults: Phaser.Types.Animations.Animation = {
frameRate: 10,
repeat: -1,
};
//=[ Game state ]=============================================================
this.anims.create({
key: "player_idle",
frames: this.anims.generateFrameNumbers("Oetzi", {
start: 1,
end: 5,
}),
...defaults,
});
this.anims.create({
key: "player_run",
frames: this.anims.generateFrameNumbers("Oetzi", {
start: 6,
end: 13,
}),
...defaults,
});
this.anims.create({ key: "SpearStill", frames: "SpearStill", ...defaults });
this.anims.create({
key: "SpearWobble",
frames: "SpearWobble",
...defaults,
frameRate: 48,
});
foes!: Array<Foe>;
score!: number;
health!: number;
acceptedWords!: number;
this.anims.create({ key: "BearRun", frames: "BearRun", ...defaults });
this.anims.create({ key: "BearWalk", frames: "BearWalk", ...defaults });
this.anims.create({ key: "BoarRun", frames: "BoarRun", ...defaults });
this.anims.create({ key: "BoarWalk", frames: "BoarWalk", ...defaults });
this.anims.create({ key: "DeerRun", frames: "DeerRun", ...defaults });
this.anims.create({ key: "DeerWalk", frames: "DeerWalk", ...defaults });
this.anims.create({ key: "FoxRun", frames: "FoxRun", ...defaults });
this.anims.create({ key: "FoxWalk", frames: "FoxWalk", ...defaults });
this.anims.create({ key: "HorseRun", frames: "HorseRun", ...defaults });
this.anims.create({ key: "HorseWalk", frames: "HorseWalk", ...defaults });
this.anims.create({ key: "RabbitRun", frames: "RabbitRun", ...defaults });
this.anims.create({ key: "RabbitWalk", frames: "RabbitWalk", ...defaults });
this.anims.create({ key: "WolfRun", frames: "WolfRun", ...defaults });
this.anims.create({ key: "WolfWalk", frames: "WolfWalk", ...defaults });
resetGameState() {
this.foes = [];
this.score = 0;
this.health = 100;
this.acceptedWords = 0;
}
updateScore(delta: number) {
this.score += delta;
this.hud.setScore(this.score);
......@@ -235,42 +335,18 @@ export default class MainScene extends Phaser.Scene {
this.hud.setHealth(this.health);
this.hud.changeFlash(this.hud.health, delta > 0 ? 0x00ff00 : 0xff0000);
if (this.health <= 25) this.hud.startLowHealthPulse();
this.checkAlive();
if (this.health <= 0) this.endGame();
}
update(time: number, delta: number): void {
// TODO: something poissonian
// console.log(time, delta);
updateClock() {
this.hud.setClock(this.getGameTime());
}
checkAlive() {
if (this.health > 0) return;
this.endGame();
}
async endGame() {
this.foes.forEach((foe) => foe.destroy());
this.sound.play("sfx_game_over");
this.typewriter.setActive(false);
this.typewriter.resetInputStatus();
await this.afterEndGame();
}
async afterEndGame() {
console.error("afterEndGame not implemented");
popFoe(foe: Foe) {
this.foes.splice(this.foes.indexOf(foe), 1);
}
initCluesGroup() {
this.cluesGroup = this.physics.add.group({
collideWorldBounds: true,
customBoundsRectangle: this.uiDimensions.cluesBounds,
bounceY: 0.2,
dragY: 180,
});
this.physics.add.collider(this.cluesGroup, this.cluesGroup);
}
//=[ Matching logic ]=========================================================
findMatchingFoe(transcription: string) {
let result: { similarity: number; match: Foe | null } = {
......@@ -292,9 +368,9 @@ export default class MainScene extends Phaser.Scene {
return result;
}
popFoe(foe) {
this.foes.splice(this.foes.indexOf(foe), 1);
}
//=[ Typewriter and submission ]==============================================
typewriter!: Typewriter;
createAndBindTypewriter() {
this.typewriter ??= new Typewriter(this.game.device.os.desktop);
......@@ -322,38 +398,7 @@ export default class MainScene extends Phaser.Scene {
};
}
submitTranscription(inputStatus: InputStatus) {
console.debug(inputStatus);
}
concealClues() {
this.foes.forEach((foe) => foe.clue.conceal());
}
uncoverClues() {
this.foes.forEach((foe) => foe.clue.uncover());
}
getGameTime() {
// NOTE: we don't need sub-ms precision.
// NOTE: pretty please, don't access the timer directly.
return Math.round(this.gameTime.getElapsed());
}
bindPauseShortcut() {
if (this.game.device.os.desktop) {
const escBinding = this.input.keyboard.addKey(
Phaser.Input.Keyboard.KeyCodes.ESC,
);
escBinding.onDown = () => this.scene.pause();
} else {
const onPointerUp = (pointer: Phaser.Input.Pointer) => {
const tapped =
pointer.downY <
this.cameras.main.height - this.uiDimensions.kbdHeight;
if (tapped) this.scene.pause();
};
this.input.on("pointerup", onPointerUp);
}
submitTranscription(_inputStatus: InputStatus) {
console.error("submitTranscription not implemented");
}
}
import "phaser";
import FightScene from "./fight_scene";
class Player extends Phaser.Physics.Arcade.Sprite {
body: Phaser.Physics.Arcade.Body;
constructor(scene: FightScene) {
constructor(scene: Phaser.Scene) {
super(scene, 0, 0, "Oetzi");
scene.add.existing(this);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment