diff --git a/.eslintrc b/.eslintrc index 4761ff1..10a811f 100644 --- a/.eslintrc +++ b/.eslintrc @@ -33,6 +33,7 @@ "webpack/*", "pwa/*", "@types/*", + "typings/*", "tsconfig.json", ], "rules": { diff --git a/src/electron/main.js b/src/electron/main.js index 17b93e7..717c7cd 100644 --- a/src/electron/main.js +++ b/src/electron/main.js @@ -1,21 +1,21 @@ /* eslint-disable no-undef */ /* eslint @typescript-eslint/no-var-requires: "off" */ // eslint-disable-next-line @typescript-eslint/no-unused-vars -const { app, protocol, BrowserWindow, ipcMain } = require('electron') -const path = require('path') +const { app, protocol, BrowserWindow, ipcMain } = require('electron'); +const path = require('path'); -process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true' +process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'; // NOTE: Change these file paths if you change the file structure. -const packageJsonPath = '../../package.json' -const htmlEntryPath = '../../dist/index.html' -const electronPreloadPath = 'preload.js' +const packageJsonPath = '../../package.json'; +const htmlEntryPath = '../../dist/index.html'; +const electronPreloadPath = 'preload.js'; -const localHostURL = 'http://localhost:3000' +const localHostURL = 'http://localhost:3000'; -const packageJson = require(path.join(__dirname, packageJsonPath)) +const packageJson = require(path.join(__dirname, packageJsonPath)); -const capitalize = s => (s && s[0].toUpperCase() + s.slice(1)) || '' +const capitalize = s => (s && s[0].toUpperCase() + s.slice(1)) || ''; const windowProperties = { title: capitalize(packageJson.name), // Use the name property of package.json as there window title. @@ -27,8 +27,8 @@ const windowProperties = { maximize: true, center: true, stealFocus: true, // Required otherwise alt-tabbing causes huge desync of audio and visuals. - openDevTools: false -} + openDevTools: false, +}; // At one point this was needed for something but now it seems obsolete. // Leaving it in case commenting it broke something that will later need to be fixed. @@ -39,18 +39,21 @@ protocol.registerSchemesAsPrivileged([ standard: true, secure: true, stream: true, - bypassCSP: true - } - } -]) + bypassCSP: true, + }, + }, +]); // Let electron reload by itself. -if (process.env.ELECTRON_DEBUG === 'true' || process.env.ELECTRON_DEBUG === 'vscode') { - const electronReload = require('electron-reload') - electronReload(__dirname, {}) +if ( + process.env.ELECTRON_DEBUG === 'true' || + process.env.ELECTRON_DEBUG === 'vscode' +) { + const electronReload = require('electron-reload'); + electronReload(__dirname, {}); } -let mainWindow = undefined +let mainWindow = undefined; function createWindow() { // Create the browser window. @@ -69,50 +72,50 @@ function createWindow() { preload: path.join(__dirname, electronPreloadPath), contextIsolation: true, nodeIntegration: true, - webSecurity: false + webSecurity: false, // allowRunningInsecureContent: true, - } - }) + }, + }); if (windowProperties.maximize) { - mainWindow.maximize() + mainWindow.maximize(); } // Hot reload. if (process.env.ELECTRON_HOT === 'true') { - mainWindow.loadURL(localHostURL) + mainWindow.loadURL(localHostURL); } else { - mainWindow.loadFile(path.join(__dirname, htmlEntryPath)) + mainWindow.loadFile(path.join(__dirname, htmlEntryPath)); } if (process.env.ELECTRON_DEBUG === 'true' && windowProperties.openDevTools) { - mainWindow.webContents.openDevTools() + mainWindow.webContents.openDevTools(); } // Maintain aspect ratio when scaling. - mainWindow.setAspectRatio(windowProperties.aspectRatio) - app.focus({ steal: windowProperties.stealFocus }) + mainWindow.setAspectRatio(windowProperties.aspectRatio); + app.focus({ steal: windowProperties.stealFocus }); } // This method will be called when Electron has finished // initialization and is ready to create browser windows. // Some APIs can only be used after this event occurs. -app.on('ready', createWindow) +app.on('ready', createWindow); // Quit when all windows are closed. app.on('window-all-closed', () => { // On OS X it is common for applications and their menu bar // to stay active until the user quits explicitly with Cmd + Q. if (process.platform !== 'darwin') { - app.quit() + app.quit(); } -}) +}); app.on('activate', () => { // On OS X it is common to re-create a window in the app when the // dock icon is clicked and there are no other windows open. - if (BrowserWindow.getAllWindows().length === 0) createWindow() -}) + if (BrowserWindow.getAllWindows().length === 0) createWindow(); +}); ipcMain.handle('get-app-path', () => { - return app.getAppPath() -}) + return app.getAppPath(); +}); diff --git a/src/ts/core/common.ts b/src/ts/core/common.ts index 4ab2291..41082ae 100644 --- a/src/ts/core/common.ts +++ b/src/ts/core/common.ts @@ -1,8 +1,10 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +/* eslint-disable no-async-promise-executor */ import { Vector2 } from '../core/phaserTypes'; import { debugMode } from './config'; export function initVariables(): Promise { - return new Promise((resolve, reject) => { + return new Promise((resolve, _reject) => { resolve(); }); } @@ -16,8 +18,8 @@ export function isDebugMode(): boolean { } export function storeData( - filePath: string, - data: string | NodeJS.ArrayBufferView, + _filePath: string, + _data: string | NodeJS.ArrayBufferView, ): void { // fs.mkdirSync(path.dirname(filePath), { recursive: true }) // fs.writeFileSync(filePath, data) @@ -25,7 +27,7 @@ export function storeData( function makeRequest(method, url) { return new Promise(function (resolve, reject) { - let xhr = new XMLHttpRequest(); + const xhr = new XMLHttpRequest(); xhr.open(method, url); xhr.onload = function () { if (this.status >= 200 && this.status < 300) { @@ -76,7 +78,7 @@ export function listLevelDirectories( //return fs.readdirSync(parentDir, { withFileTypes: true }).filter(dirent => dirent.isDirectory()).map(dirent => dirent.name) } -export function fileExists(absoluteFilePath: string): boolean { +export function fileExists(_absoluteFilePath: string): boolean { return true; //return fs.existsSync(absoluteFilePath) } diff --git a/src/ts/core/game.ts b/src/ts/core/game.ts index 8850cda..b761656 100644 --- a/src/ts/core/game.ts +++ b/src/ts/core/game.ts @@ -1,11 +1,11 @@ -import Phaser from 'phaser' -import { LoadingScene, ElectronScene } from '../scenes/loadingScene' -import LevelScene from '../scenes/levelScene' -import MainMenu from '../scenes/mainMenuScene' -import LevelSelect from '../scenes/levelSelectScene' -import Options from '../scenes/optionsScene' -import Scoreboard from '../scenes/scoreboardScene' -import Calibration from '../scenes/calibrationScene' +import Phaser from 'phaser'; +import { LoadingScene, ElectronScene } from '../scenes/loadingScene'; +import LevelScene from '../scenes/levelScene'; +import MainMenu from '../scenes/mainMenuScene'; +import LevelSelect from '../scenes/levelSelectScene'; +import Options from '../scenes/optionsScene'; +import Scoreboard from '../scenes/scoreboardScene'; +import Calibration from '../scenes/calibrationScene'; /* To add a new scene: @@ -24,10 +24,19 @@ const config = { default: 'matter', matter: { debug: false, - gravity: { x: 0, y: 0 } - } + gravity: { x: 0, y: 0 }, + }, }, - scene: [ElectronScene, LoadingScene, MainMenu, Options, LevelSelect, LevelScene, Scoreboard, Calibration] -} + scene: [ + ElectronScene, + LoadingScene, + MainMenu, + Options, + LevelSelect, + LevelScene, + Scoreboard, + Calibration, + ], +}; -const _game = new Phaser.Game(config) +const _game = new Phaser.Game(config); diff --git a/src/ts/level/infoHud.ts b/src/ts/level/infoHud.ts index 7141773..8429bbe 100644 --- a/src/ts/level/infoHud.ts +++ b/src/ts/level/infoHud.ts @@ -1,10 +1,10 @@ -import { TextOptions } from '../core/interfaces' -import { absPath, assert } from '../core/common' -import Level from '../level/level' -import { config } from '../managers/storageManager' -import HandScene from '../scenes/handScene' -import { Button } from '../ui/button' -import { GameObject, PhaserText, Sprite } from '../core/phaserTypes' +import { TextOptions } from '../core/interfaces'; +import { absPath, assert } from '../core/common'; +import Level from '../level/level'; +import { config } from '../managers/storageManager'; +import HandScene from '../scenes/handScene'; +import { Button } from '../ui/button'; +import { GameObject, PhaserText, Sprite } from '../core/phaserTypes'; import { bpmInfoText, bpmTextOptions, @@ -23,32 +23,32 @@ import { trackInfoText, trackTextOptions, uiHoverColor, - undefinedText -} from '../core/config' -import setInteraction from '../util/interaction' + undefinedText, +} from '../core/config'; +import setInteraction from '../util/interaction'; export default class InfoHUD extends GameObject { - public readonly scene: HandScene - private readonly level: Level + public readonly scene: HandScene; + private readonly level: Level; - private trackText: PhaserText - private difficultyText: PhaserText - private bpmText: PhaserText - private layerText: PhaserText - private loopText: PhaserText - private infoBackground: Sprite - private skipButton: Button + private trackText: PhaserText; + private difficultyText: PhaserText; + private bpmText: PhaserText; + private layerText: PhaserText; + private loopText: PhaserText; + private infoBackground: Sprite; + private skipButton: Button; constructor(scene: HandScene, level: Level) { - super(scene, 'infoHUD') - this.scene = scene - this.level = level + super(scene, 'infoHUD'); + this.scene = scene; + this.level = level; } public setup() { - this.addTexts() - this.addSkipButton() - this.setVisible(false) + this.addTexts(); + this.addSkipButton(); + this.setVisible(false); } private addSkipButton() { @@ -58,111 +58,135 @@ export default class InfoHUD extends GameObject { skipButtonOptions.scale, skipButtonOptions.textureKey, skipButtonOptions.soundKey, - false - ) + false, + ); this.skipButton.addPinchCallbacks({ startPinch: () => { - setInteraction(this.skipButton, false) - this.level.transitionLayers() + setInteraction(this.skipButton, false); + this.level.transitionLayers(); }, startHover: () => { - this.skipButton.setTintFill(uiHoverColor) + this.skipButton.setTintFill(uiHoverColor); }, endHover: () => { - this.skipButton.clearTint() - } - }) - this.updateSkipButtonAvailability(false) + this.skipButton.clearTint(); + }, + }); + this.updateSkipButtonAvailability(false); } public preload() { - this.scene.load.image(skipButtonOptions.textureKey, absPath(skipButtonOptions.path)) - this.scene.load.image(infoBackgroundOptions.textureKey, absPath(infoBackgroundOptions.path)) + this.scene.load.image( + skipButtonOptions.textureKey, + absPath(skipButtonOptions.path), + ); + this.scene.load.image( + infoBackgroundOptions.textureKey, + absPath(infoBackgroundOptions.path), + ); } public setVisible(visible: boolean) { - if (this.trackText != undefined) this.trackText.setVisible(visible) - if (this.difficultyText != undefined) this.difficultyText.setVisible(visible) - if (this.bpmText != undefined) this.bpmText.setVisible(visible) - if (this.layerText != undefined) this.layerText.setVisible(visible) - if (this.loopText != undefined) this.loopText.setVisible(visible) - if (this.infoBackground != undefined) this.infoBackground.setVisible(visible) - - this.updateSkipButtonAvailability(visible) + if (this.trackText != undefined) this.trackText.setVisible(visible); + if (this.difficultyText != undefined) + this.difficultyText.setVisible(visible); + if (this.bpmText != undefined) this.bpmText.setVisible(visible); + if (this.layerText != undefined) this.layerText.setVisible(visible); + if (this.loopText != undefined) this.loopText.setVisible(visible); + if (this.infoBackground != undefined) + this.infoBackground.setVisible(visible); + + this.updateSkipButtonAvailability(visible); } private addText(textOptions: TextOptions, text: string): PhaserText { return this.scene.add .text(textOptions.position.x, textOptions.position.y, text, { font: textOptions.font, - color: textOptions.color + color: textOptions.color, }) .setScale(textOptions.scale) - .setDepth(textOptions.depth) + .setDepth(textOptions.depth); } private getLoopText(): string { - const loopCount: number = this.level.score.getLoopCount() - const loopName: string = loopInfoText + loopCount.toString() - return loopName + const loopCount: number = this.level.score.getLoopCount(); + const loopName: string = loopInfoText + loopCount.toString(); + return loopName; } private addTexts() { - this.loopText = this.addText(loopTextOptions, this.getLoopText()) - const trackName: string = trackInfoText + this.level.track.data.displayName - this.trackText = this.addText(trackTextOptions, trackName) + this.loopText = this.addText(loopTextOptions, this.getLoopText()); + const trackName: string = trackInfoText + this.level.track.data.displayName; + this.trackText = this.addText(trackTextOptions, trackName); const layerName: string = layerInfoText + (this.level.activeLayerIndex + 1).toString() + layerDividerSymbol + - this.level.playableLayers.length.toString() + this.level.playableLayers.length.toString(); - let difficultyText: string = undefinedText + let difficultyText: string = undefinedText; switch (this.level.bpmIndex) { case 0: { - difficultyText = difficultyTextEasy - break + difficultyText = difficultyTextEasy; + break; } case 1: { - difficultyText = difficultyTextMedium - break + difficultyText = difficultyTextMedium; + break; } case 2: { - difficultyText = difficultyTextHard - break + difficultyText = difficultyTextHard; + break; } default: { - break + break; } } assert( difficultyText != undefinedText, - 'Extend the switch statement to have a difficulty text for the given track bpmIndex' - ) + 'Extend the switch statement to have a difficulty text for the given track bpmIndex', + ); - this.difficultyText = this.addText(difficultyTextOptions, difficultyInfoText + difficultyText) + this.difficultyText = this.addText( + difficultyTextOptions, + difficultyInfoText + difficultyText, + ); - this.bpmText = this.addText(bpmTextOptions, bpmInfoText + this.level.track.getBPM()) + this.bpmText = this.addText( + bpmTextOptions, + bpmInfoText + this.level.track.getBPM(), + ); - this.layerText = this.addText(layerTextOptions, layerName) + this.layerText = this.addText(layerTextOptions, layerName); this.infoBackground = this.scene.add - .sprite(infoBackgroundOptions.position.x, infoBackgroundOptions.position.y, infoBackgroundOptions.textureKey) + .sprite( + infoBackgroundOptions.position.x, + infoBackgroundOptions.position.y, + infoBackgroundOptions.textureKey, + ) .setAlpha(infoBackgroundOptions.opacity) .setOrigin(0, 0) - .setDisplaySize(infoBackgroundOptions.size.x, infoBackgroundOptions.size.y) + .setDisplaySize( + infoBackgroundOptions.size.x, + infoBackgroundOptions.size.y, + ); } public updateLoopText() { - this.loopText.setText(this.getLoopText()) - this.updateSkipButtonAvailability(true) + this.loopText.setText(this.getLoopText()); + this.updateSkipButtonAvailability(true); } private updateSkipButtonAvailability(visible: boolean) { const available: boolean = - config.enableSkipButton && this.level.score.getLoopCount() >= config.skipLoopThreshold && visible - if (this.skipButton != undefined) setInteraction(this.skipButton, available) + config.enableSkipButton && + this.level.score.getLoopCount() >= config.skipLoopThreshold && + visible; + if (this.skipButton != undefined) + setInteraction(this.skipButton, available); } } diff --git a/src/ts/level/layer.ts b/src/ts/level/layer.ts index bc1c5fd..acfe2fd 100644 --- a/src/ts/level/layer.ts +++ b/src/ts/level/layer.ts @@ -1,13 +1,18 @@ -import { PlayableTrackLayerData, TrackLayerData, TrackNode, TrackPinchNode } from '../level/trackTypes' -import Level from '../level/level' -import Metronome from '../level/metronome' -import Progress from '../level/progress' -import HandScene from '../scenes/handScene' -import InfoHUD from '../level/infoHud' -import { config, autoSaveToCSV } from '../managers/storageManager' -import { assert, normalizedToWindow } from '../core/common' -import { TargetManager } from '../managers/targetManager' -import Target from '../objects/target' +import { + PlayableTrackLayerData, + TrackLayerData, + TrackNode, + TrackPinchNode, +} from '../level/trackTypes'; +import Level from '../level/level'; +import Metronome from '../level/metronome'; +import Progress from '../level/progress'; +import HandScene from '../scenes/handScene'; +import InfoHUD from '../level/infoHud'; +import { config, autoSaveToCSV } from '../managers/storageManager'; +import { assert, normalizedToWindow } from '../core/common'; +import { TargetManager } from '../managers/targetManager'; +import Target from '../objects/target'; import { fingerColors, indexFingerId, @@ -16,180 +21,203 @@ import { targetHitOnTimeProgressImpact, targetHitOnTimeStreakImpact, targetMissProgressImpact, - targetSkipProgressImpact -} from '../core/config' + targetSkipProgressImpact, +} from '../core/config'; export class Layer { - public readonly level: Level - public readonly data: TrackLayerData + public readonly level: Level; + public readonly data: TrackLayerData; constructor(level: Level, data: TrackLayerData) { - this.level = level - this.data = data + this.level = level; + this.data = data; } public getDuration(): number { return this.level.track.songTimePositionToTime({ bar: this.data.lengthInBars + 1, step: 1, - tick: 0 - }) + tick: 0, + }); } } export class PlayableLayer extends Layer { - public readonly data: PlayableTrackLayerData - public progress: Progress - public infoHud: InfoHUD + public readonly data: PlayableTrackLayerData; + public progress: Progress; + public infoHud: InfoHUD; - private targetManager: TargetManager + private targetManager: TargetManager; - private readonly scene: HandScene - private readonly metronome: Metronome + private readonly scene: HandScene; + private readonly metronome: Metronome; - private nextNodeIndex: number = 0 - private songTime_: number = 0 - private oldTimePosition: number = 0 - private startTime: number = 0 - private time: number = 0 - private ready: boolean = false + private nextNodeIndex: number = 0; + private songTime_: number = 0; + private oldTimePosition: number = 0; + private startTime: number = 0; + private time: number = 0; + private ready: boolean = false; constructor(scene: HandScene, level: Level, data: PlayableTrackLayerData) { - super(level, data) - this.scene = scene - this.data = data - - this.metronome = new Metronome(this.scene) - this.progress = new Progress(this.scene) - this.infoHud = new InfoHUD(this.scene, this.level) - this.targetManager = new TargetManager() + super(level, data); + this.scene = scene; + this.data = data; + + this.metronome = new Metronome(this.scene); + this.progress = new Progress(this.scene); + this.infoHud = new InfoHUD(this.scene, this.level); + this.targetManager = new TargetManager(); } get songTime() { - return this.songTime_ + return this.songTime_; } public preload() { - this.infoHud.preload() - this.metronome.preload() - this.progress.preload() + this.infoHud.preload(); + this.metronome.preload(); + this.progress.preload(); } public unload() { - this.metronome.unload() - this.infoHud.destroy() + this.metronome.unload(); + this.infoHud.destroy(); } public start(delay: number) { - this.targetManager.start() - this.level.score.delay = delay + this.targetManager.start(); + this.level.score.delay = delay; - this.ready = true - this.startTime = this.time + delay - this.nextNodeIndex = 0 - this.oldTimePosition = 0 + this.ready = true; + this.startTime = this.time + delay; + this.nextNodeIndex = 0; + this.oldTimePosition = 0; - const barCount = Math.max(this.data.previewTime.bar, 1) + const barCount = Math.max(this.data.previewTime.bar, 1); - const tints: number[] = [] + const tints: number[] = []; this.data.nodes.forEach((node: TrackNode, _index: number) => { - const finger = config.indexFingerOnly ? indexFingerId : (node as TrackPinchNode).finger - tints.push(fingerColors[finger]) - }) - - this.progress.setup(0, this.data.nodes.length, this.data.progressCount, tints) - this.metronome.setup() - this.infoHud.setup() - - this.progress.setVisible(true) - this.infoHud.setVisible(true) - this.metronome.play(this.level.track.data.timeSignatureNumerator, this.level.track.beatLength, barCount) + const finger = config.indexFingerOnly + ? indexFingerId + : (node as TrackPinchNode).finger; + tints.push(fingerColors[finger]); + }); + + this.progress.setup( + 0, + this.data.nodes.length, + this.data.progressCount, + tints, + ); + this.metronome.setup(); + this.infoHud.setup(); + + this.progress.setVisible(true); + this.infoHud.setVisible(true); + this.metronome.play( + this.level.track.data.timeSignatureNumerator, + this.level.track.beatLength, + barCount, + ); } - public createTarget(node: TrackPinchNode, layerIndex: number, nodeIndex: number) { - const finger: string = config.indexFingerOnly ? indexFingerId : node.finger + public createTarget( + node: TrackPinchNode, + layerIndex: number, + nodeIndex: number, + ) { + const finger: string = config.indexFingerOnly ? indexFingerId : node.finger; const target = new Target( this.scene, this.songTime_, normalizedToWindow(node.normalizedPosition) /* screenPosition */, this.getPreviewTime() /* lifetime */, - this.level.track.getSoundKey(this.data, node.soundKey) /* targetSoundKey */, + this.level.track.getSoundKey( + this.data, + node.soundKey, + ) /* targetSoundKey */, this, finger, - nodeIndex - ) + nodeIndex, + ); target.setOnTargetMiss(() => { // Target miss. - this.level.score.missedPinch(target) + this.level.score.missedPinch(target); - this.progress.changeBy(targetMissProgressImpact) - this.progress.redraw() + this.progress.changeBy(targetMissProgressImpact); + this.progress.redraw(); - this.level.streak.stop() + this.level.streak.stop(); - this.targetManager.destroyTarget(target) + this.targetManager.destroyTarget(target); // If the missed target is the last node, increment loop counter. - this.checkForTransition(layerIndex, nodeIndex) - }) + this.checkForTransition(layerIndex, nodeIndex); + }); target.setOnTargetHit(() => { - const previousTargets: Target[] = this.targetManager.getPreviousTargets(target) + const previousTargets: Target[] = + this.targetManager.getPreviousTargets(target); // Count each previous undestroyed target as a skip. for (const previousTarget of previousTargets) { - assert(previousTarget != target) + assert(previousTarget != target); // Only skip targets that appeared before the current target. - this.progress.changeBy(targetSkipProgressImpact) + this.progress.changeBy(targetSkipProgressImpact); - this.level.score.skippedPinch(previousTarget) + this.level.score.skippedPinch(previousTarget); - this.targetManager.destroyTarget(previousTarget) + this.targetManager.destroyTarget(previousTarget); } if (previousTargets.length > 0) { - this.level.streak.stop() + this.level.streak.stop(); } // Hit called after previous targets are skipped. // This ensures that a streak always start on the first hit target. - const hitTime = this.songTime - target.songTime - const onTimeMargin = target.onTimeDuration / 2 + const hitTime = this.songTime - target.songTime; + const onTimeMargin = target.onTimeDuration / 2; - const early: boolean = hitTime < -onTimeMargin - const late: boolean = hitTime > onTimeMargin + const early: boolean = hitTime < -onTimeMargin; + const late: boolean = hitTime > onTimeMargin; if (early) { - this.level.streak.stop() - this.progress.changeBy(targetHitEarlyProgressImpact) - this.level.score.earlyPinch(target, this.songTime) + this.level.streak.stop(); + this.progress.changeBy(targetHitEarlyProgressImpact); + this.level.score.earlyPinch(target, this.songTime); } else if (late) { - this.level.streak.stop() - this.progress.changeBy(targetHitLateProgressImpact) - this.level.score.latePinch(target, this.songTime) + this.level.streak.stop(); + this.progress.changeBy(targetHitLateProgressImpact); + this.level.score.latePinch(target, this.songTime); } else { // on time. - this.level.streak.changeBy(targetHitOnTimeStreakImpact) - this.progress.changeBy(targetHitOnTimeProgressImpact) - this.level.score.onTimePinch(target, this.songTime, this.level.streak.current) + this.level.streak.changeBy(targetHitOnTimeStreakImpact); + this.progress.changeBy(targetHitOnTimeProgressImpact); + this.level.score.onTimePinch( + target, + this.songTime, + this.level.streak.current, + ); } - this.progress.redraw() + this.progress.redraw(); - this.level.streak.check() + this.level.streak.check(); if (!config.disableSonification) target.sound.play({ - volume: config.sonificationLevel - }) + volume: config.sonificationLevel, + }); - this.targetManager.destroyTarget(target) + this.targetManager.destroyTarget(target); - this.checkForTransition(layerIndex, nodeIndex) - }) + this.checkForTransition(layerIndex, nodeIndex); + }); - this.targetManager.addTarget(target) + this.targetManager.addTarget(target); } private checkForTransition(layerIndex: number, nodeIndex: number) { @@ -203,19 +231,20 @@ export class PlayableLayer extends Layer { // Transition if auto skip is enabled, and the desired loop-count has been reached, // or if the player's progress is sufficient if ( - (config.enableAutoSkip && this.level.score.getLoopCount() >= config.autoSkipThreshold) || + (config.enableAutoSkip && + this.level.score.getLoopCount() >= config.autoSkipThreshold) || (this.progress.canProgress() && !config.disableLayerProgression) ) { // Enough progress, transition to next layer / end scene. - this.level.transitionLayers() + this.level.transitionLayers(); } else { // Not enough progress, increment loop counter. - this.level.score.incrementLoopCount() - this.progress.setToStarting() - this.infoHud.updateLoopText() + this.level.score.incrementLoopCount(); + this.progress.setToStarting(); + this.infoHud.updateLoopText(); if (config.autoSaveCSV) { - autoSaveToCSV(this.level.score.levelStats) + autoSaveToCSV(this.level.score.levelStats); } } } @@ -225,64 +254,70 @@ export class PlayableLayer extends Layer { * Handles spawning nodes, it will spawn those that would happen soon according to the previewtime set in the layer. */ private handleTargetCreation() { - if (!this.ready) return + if (!this.ready) return; - const layerTime = this.getDuration() + const layerTime = this.getDuration(); - let previewTime = this.songTime_ + this.getPreviewTime() + let previewTime = this.songTime_ + this.getPreviewTime(); if (this.songTime_ > 0) { - previewTime %= layerTime + previewTime %= layerTime; } if (this.data.nodes.length <= this.nextNodeIndex) { if (previewTime < this.oldTimePosition) { - this.nextNodeIndex = 0 + this.nextNodeIndex = 0; } else { - return + return; } } - const nextNode = this.data.nodes[this.nextNodeIndex] + const nextNode = this.data.nodes[this.nextNodeIndex]; // Calculate the time position of the next node in terms of the looped layer time - const nextNodeLoopTime = this.level.track.songTimePositionToTime(nextNode.timePosition) % layerTime + const nextNodeLoopTime = + this.level.track.songTimePositionToTime(nextNode.timePosition) % + layerTime; if (nextNodeLoopTime <= previewTime) { if ((nextNode as TrackPinchNode).finger !== undefined) { - this.createTarget(nextNode as TrackPinchNode, this.level.activeLayerIndex, this.nextNodeIndex) + this.createTarget( + nextNode as TrackPinchNode, + this.level.activeLayerIndex, + this.nextNodeIndex, + ); } - this.nextNodeIndex++ + this.nextNodeIndex++; } - this.oldTimePosition = previewTime + this.oldTimePosition = previewTime; } public preDelayStop() { - this.ready = false - this.metronome.stop() - this.targetManager.destroyTargets() + this.ready = false; + this.metronome.stop(); + this.targetManager.destroyTargets(); } public postDelayStop() { - this.infoHud.setVisible(false) - this.progress.setVisible(false) + this.infoHud.setVisible(false); + this.progress.setVisible(false); } public stop() { - this.preDelayStop() - this.postDelayStop() + this.preDelayStop(); + this.postDelayStop(); } public update(time: number) { - this.time = time - this.songTime_ = this.ready ? this.time - this.startTime : 0 - this.handleTargetCreation() + this.time = time; + this.songTime_ = this.ready ? this.time - this.startTime : 0; + this.handleTargetCreation(); } public getPreviewTime(): number { return this.level.track.songTimePositionToTime({ bar: this.data.previewTime.bar + 1, step: this.data.previewTime.step + 1, - tick: this.data.previewTime.tick - }) + tick: this.data.previewTime.tick, + }); } } diff --git a/src/ts/level/metronome.ts b/src/ts/level/metronome.ts index ab8af81..3a8ec14 100644 --- a/src/ts/level/metronome.ts +++ b/src/ts/level/metronome.ts @@ -1,70 +1,96 @@ -import HandScene from '../scenes/handScene' -import { absPath, assert } from '../core/common' -import { config } from '../managers/storageManager' -import { GameObject, PhaserText, Sprite, TimerEvent, Tween } from '../core/phaserTypes' +import HandScene from '../scenes/handScene'; +import { absPath, assert } from '../core/common'; +import { config } from '../managers/storageManager'; +import { + GameObject, + PhaserText, + Sprite, + TimerEvent, + Tween, +} from '../core/phaserTypes'; import { countdownTextOptions, countdownTextureOptions, metronomeMinimumBarCount, metronomeOptions, - undefinedText -} from '../core/config' + undefinedText, +} from '../core/config'; export default class Metronome extends GameObject { - private beat: number = 0 - private timer: TimerEvent + private beat: number = 0; + private timer: TimerEvent; - private countdownText: PhaserText - private countdownTexture: Sprite - private shrinkTween: Tween + private countdownText: PhaserText; + private countdownTexture: Sprite; + private shrinkTween: Tween; constructor(scene: HandScene) { - super(scene, 'metronome') - this.timer = new TimerEvent({}) + super(scene, 'metronome'); + this.timer = new TimerEvent({}); this.countdownText = this.scene.add - .text(countdownTextOptions.position.x, countdownTextOptions.position.y, undefinedText, { - font: countdownTextOptions.font, - color: countdownTextOptions.color - }) + .text( + countdownTextOptions.position.x, + countdownTextOptions.position.y, + undefinedText, + { + font: countdownTextOptions.font, + color: countdownTextOptions.color, + }, + ) .setOrigin(0.5, 0.5) .setScale(countdownTextOptions.scale) - .setDepth(countdownTextOptions.depth) - this.setVisible(false) + .setDepth(countdownTextOptions.depth); + this.setVisible(false); } public preload() { - this.scene.load.image(countdownTextureOptions.key, absPath(countdownTextureOptions.path)) - this.scene.load.audio(metronomeOptions.highKey, absPath(metronomeOptions.highPath)) - this.scene.load.audio(metronomeOptions.lowKey, absPath(metronomeOptions.lowPath)) + this.scene.load.image( + countdownTextureOptions.key, + absPath(countdownTextureOptions.path), + ); + this.scene.load.audio( + metronomeOptions.highKey, + absPath(metronomeOptions.highPath), + ); + this.scene.load.audio( + metronomeOptions.lowKey, + absPath(metronomeOptions.lowPath), + ); } public unload() { - this.scene.cache.audio.remove(metronomeOptions.highKey) - this.scene.cache.audio.remove(metronomeOptions.lowKey) + this.scene.cache.audio.remove(metronomeOptions.highKey); + this.scene.cache.audio.remove(metronomeOptions.lowKey); } public setup() { this.countdownTexture = this.scene.add - .sprite(countdownTextOptions.position.x, countdownTextOptions.position.y, countdownTextureOptions.key) + .sprite( + countdownTextOptions.position.x, + countdownTextOptions.position.y, + countdownTextureOptions.key, + ) .setScale(countdownTextureOptions.scale) .setVisible(false) .setOrigin(0.5, 0.5) .setAlpha(countdownTextureOptions.opacity) - .setDepth(countdownTextureOptions.depth) + .setDepth(countdownTextureOptions.depth); } public setVisible(visible: boolean) { - if (this.countdownText != undefined) this.countdownText.setVisible(visible) - if (this.countdownTexture != undefined) this.countdownTexture.setVisible(visible) + if (this.countdownText != undefined) this.countdownText.setVisible(visible); + if (this.countdownTexture != undefined) + this.countdownTexture.setVisible(visible); } public stop() { - this.setVisible(false) + this.setVisible(false); if (this.scene != undefined) { - this.scene.time.removeEvent(this.timer) - this.scene.sound.stopByKey(metronomeOptions.highKey) - this.scene.sound.stopByKey(metronomeOptions.lowKey) - if (this.shrinkTween != undefined) this.scene.tweens.remove(this.shrinkTween) + this.scene.time.removeEvent(this.timer); + this.scene.sound.stopByKey(metronomeOptions.highKey); + this.scene.sound.stopByKey(metronomeOptions.lowKey); + if (this.shrinkTween != undefined) + this.scene.tweens.remove(this.shrinkTween); } } @@ -73,60 +99,65 @@ export default class Metronome extends GameObject { * @param barCount amount of bars this should play. */ public play(beatsPerBar: number, beatDuration: number, barCount: number) { - this.beat = 0 - this.stop() + this.beat = 0; + this.stop(); this.timer = this.scene.time.addEvent({ callback: this.tick.bind(this, beatsPerBar, beatDuration, barCount), delay: beatDuration, loop: true, - startAt: beatDuration - }) + startAt: beatDuration, + }); } private tick(beatsPerBar: number, beatDuration: number, barCount: number) { - assert(barCount >= metronomeMinimumBarCount, 'Cannot display metronome for less than minimum bar count') + assert( + barCount >= metronomeMinimumBarCount, + 'Cannot display metronome for less than minimum bar count', + ); // Only display countdown text on the last bar of the metronome. - const totalBeats = beatsPerBar * barCount - const beatInBar = this.beat % beatsPerBar - const finalBeat = beatInBar == 0 - const soundKey = finalBeat ? metronomeOptions.highKey : metronomeOptions.lowKey + const totalBeats = beatsPerBar * barCount; + const beatInBar = this.beat % beatsPerBar; + const finalBeat = beatInBar == 0; + const soundKey = finalBeat + ? metronomeOptions.highKey + : metronomeOptions.lowKey; if (this.beat >= totalBeats) { - this.setVisible(false) - return + this.setVisible(false); + return; } else { // Check metronome has not exited early. if (this.countdownText.active) { this.scene.sound.play(soundKey, { - volume: config.backgroundMusicLevel - }) + volume: config.backgroundMusicLevel, + }); } } // Display countdown on the final bar. - assert(totalBeats - beatsPerBar >= 0) + assert(totalBeats - beatsPerBar >= 0); if (this.beat >= totalBeats - beatsPerBar) { // Check metronome has not exited early. if (this.countdownText.active && !config.disableVisualMetronome) { - this.display(beatInBar + metronomeMinimumBarCount, beatDuration) + this.display(beatInBar + metronomeMinimumBarCount, beatDuration); } } - this.beat++ + this.beat++; } private display(beat: number, beatDuration: number) { - this.countdownText.setText(beat.toString()) - this.countdownText.setScale(countdownTextOptions.scale) - this.countdownTexture.setScale(countdownTextureOptions.scale) - this.setVisible(true) + this.countdownText.setText(beat.toString()); + this.countdownText.setScale(countdownTextOptions.scale); + this.countdownTexture.setScale(countdownTextureOptions.scale); + this.setVisible(true); this.shrinkTween = this.scene.tweens.add({ targets: [this.countdownText, this.countdownTexture], ease: countdownTextOptions.ease, duration: beatDuration * 0.9, // 0.9 provides a short buffer for next countdown number to spawn scale: 0, - repeat: 0 - }) + repeat: 0, + }); } } diff --git a/src/ts/level/progress.ts b/src/ts/level/progress.ts index 555b749..a82844f 100644 --- a/src/ts/level/progress.ts +++ b/src/ts/level/progress.ts @@ -1,116 +1,139 @@ -import { absPath, assert } from '../core/common' -import { entireLayerProgressRequired, progressBarDepth, progressBarOptions, startingProgress } from '../core/config' -import { GameObject, Group, Scene, Sprite } from '../core/phaserTypes' +import { absPath, assert } from '../core/common'; +import { + entireLayerProgressRequired, + progressBarDepth, + progressBarOptions, + startingProgress, +} from '../core/config'; +import { GameObject, Group, Scene, Sprite } from '../core/phaserTypes'; export default class Progress extends Group { - private current_: number = startingProgress + private current_: number = startingProgress; // Number of nodes which must be pinched 'on time' before progressing to the next playable layer. // If >= nodes.length or == entireLayerProgressRequired, all layer nodes must be pinched (100% perfect completion). - private requiredProgress: number = entireLayerProgressRequired - private nodeCount: number = 1 // Number of total nodes per layer. Default: 1 (placeholder until progress bar is setup). + private requiredProgress: number = entireLayerProgressRequired; + private nodeCount: number = 1; // Number of total nodes per layer. Default: 1 (placeholder until progress bar is setup). - private tints: number[] = [] + private tints: number[] = []; constructor(scene: Scene) { - super(scene) - assert(this.current_ >= 0) - this.tints = [] - this.scene.add.existing(this) - this.setVisible(false) + super(scene); + assert(this.current_ >= 0); + this.tints = []; + this.scene.add.existing(this); + this.setVisible(false); } public preload() { - this.scene.load.image(progressBarOptions.key, absPath(progressBarOptions.path)) + this.scene.load.image( + progressBarOptions.key, + absPath(progressBarOptions.path), + ); } - public setup(initialProgress: number, nodeCount: number, requiredProgress: number, tints: number[]) { - assert(requiredProgress <= nodeCount) - assert(tints.length === nodeCount, 'Must pass tints equal in number to nodes') - assert(nodeCount > 0) - assert(requiredProgress > 0) - assert(initialProgress >= 0) - - this.current_ = initialProgress - this.nodeCount = nodeCount - this.tints = tints - this.requiredProgress = requiredProgress - - const progressSpriteWidth: number = progressBarOptions.size.x / this.nodeCount - - this.clear() + public setup( + initialProgress: number, + nodeCount: number, + requiredProgress: number, + tints: number[], + ) { + assert(requiredProgress <= nodeCount); + assert( + tints.length === nodeCount, + 'Must pass tints equal in number to nodes', + ); + assert(nodeCount > 0); + assert(requiredProgress > 0); + assert(initialProgress >= 0); + + this.current_ = initialProgress; + this.nodeCount = nodeCount; + this.tints = tints; + this.requiredProgress = requiredProgress; + + const progressSpriteWidth: number = + progressBarOptions.size.x / this.nodeCount; + + this.clear(); this.createMultiple({ quantity: this.nodeCount, key: progressBarOptions.key, visible: true, setXY: { - x: progressBarOptions.position.x - progressBarOptions.size.x / 2 + progressSpriteWidth / 2, + x: + progressBarOptions.position.x - + progressBarOptions.size.x / 2 + + progressSpriteWidth / 2, y: progressBarOptions.position.y + progressBarOptions.size.y / 2, - stepX: progressSpriteWidth - } - }) + stepX: progressSpriteWidth, + }, + }); - this.propertyValueSet('displayWidth', progressSpriteWidth) - this.propertyValueSet('displayHeight', progressBarOptions.size.y) + this.propertyValueSet('displayWidth', progressSpriteWidth); + this.propertyValueSet('displayHeight', progressBarOptions.size.y); - this.setDepth(progressBarDepth) - this.redraw() + this.setDepth(progressBarDepth); + this.redraw(); } // Resets only the player progress, not the entire progress bar. public setToStarting() { - this.current_ = startingProgress - assert(this.current_ >= 0) - this.redraw() + this.current_ = startingProgress; + assert(this.current_ >= 0); + this.redraw(); } public redraw() { if (this.current <= this.nodeCount) { this.getChildren().forEach((child: GameObject, _index: number) => { - ;(child as Sprite).clearTint() - }) + (child as Sprite).clearTint(); + }); const required: number = - this.requiredProgress === entireLayerProgressRequired ? this.nodeCount : this.requiredProgress - 1 - const achieved: number = this.current + this.requiredProgress === entireLayerProgressRequired + ? this.nodeCount + : this.requiredProgress - 1; + const achieved: number = this.current; this.getChildren().forEach((child: GameObject, index: number) => { - const s: Sprite = child as Sprite + const s: Sprite = child as Sprite; if (progressBarOptions.tintByNode) { - s.setTint(this.tints[index]) + s.setTint(this.tints[index]); } if (index < achieved) { if (index <= required) { - s.setTint(progressBarOptions.completedTint) + s.setTint(progressBarOptions.completedTint); } else { - s.setTint(progressBarOptions.extraTint) + s.setTint(progressBarOptions.extraTint); } } if (index === required) { if (achieved > required) { - s.setTint(progressBarOptions.completedRequiredTint) + s.setTint(progressBarOptions.completedRequiredTint); } else { - s.setTint(progressBarOptions.requiredTint) + s.setTint(progressBarOptions.requiredTint); } } - }, this) + }, this); } } public canProgress(): boolean { - const perfectCompletionRequired: boolean = this.requiredProgress === entireLayerProgressRequired + const perfectCompletionRequired: boolean = + this.requiredProgress === entireLayerProgressRequired; return ( (perfectCompletionRequired && this.current >= this.nodeCount) || (!perfectCompletionRequired && this.current >= this.requiredProgress) - ) + ); } public changeBy(value: number) { // Constrain progress between 0 and this.nodeCount. - this.current_ = Math.max(0, Math.min(this.current + value, this.nodeCount)) + this.current_ = Math.max(0, Math.min(this.current + value, this.nodeCount)); } public get current() { - return this.current_ + return this.current_; } } diff --git a/src/ts/level/score.ts b/src/ts/level/score.ts index fe00233..7bc4d50 100644 --- a/src/ts/level/score.ts +++ b/src/ts/level/score.ts @@ -1,75 +1,85 @@ -import { correctTargetId, earlyTargetId, lateTargetId, missedTargetId, skippedTargetId } from '../core/config' -import { LayerStats, LevelStats } from '../core/interfaces' -import Target from '../objects/target' -import { Track } from '../level/track' +import { + correctTargetId, + earlyTargetId, + lateTargetId, + missedTargetId, + skippedTargetId, +} from '../core/config'; +import { LayerStats, LevelStats } from '../core/interfaces'; +import Target from '../objects/target'; +import { Track } from '../level/track'; export class Score { - public layerStats: LayerStats - public levelStats: LevelStats - public delay: number + public layerStats: LayerStats; + public levelStats: LevelStats; + public delay: number; constructor(track: Track) { - this.levelStats = new LevelStats(track) - this.layerStats = new LayerStats() + this.levelStats = new LevelStats(track); + this.layerStats = new LayerStats(); } public getLoopCount(): number { - return this.layerStats.loop + return this.layerStats.loop; } public incrementLoopCount() { - this.layerStats.correct = 0 - this.layerStats.early = 0 - this.layerStats.late = 0 - this.layerStats.miss = 0 - this.layerStats.loop++ + this.layerStats.correct = 0; + this.layerStats.early = 0; + this.layerStats.late = 0; + this.layerStats.miss = 0; + this.layerStats.loop++; } public saveLayerScores() { - this.levelStats.layersStats.push(this.layerStats) + this.levelStats.layersStats.push(this.layerStats); } public resetLayerScores(newNodeCount: number, newRequiredProgress: number) { - this.layerStats = new LayerStats() - this.layerStats.total = newNodeCount - this.layerStats.required = newRequiredProgress + this.layerStats = new LayerStats(); + this.layerStats.total = newNodeCount; + this.layerStats.required = newRequiredProgress; } - private addHit(target: Target, playerSongTime: number, classification: string) { + private addHit( + target: Target, + playerSongTime: number, + classification: string, + ) { this.layerStats.hits.push({ loopNumber: this.getLoopCount(), noteID: target.nodeIndex + 1, correctTime: target.songTime + this.delay, playerTime: playerSongTime, - classification: classification - }) + classification: classification, + }); } public onTimePinch(target: Target, playerSongTime: number, streak: number) { - this.addHit(target, playerSongTime + this.delay, correctTargetId) - this.layerStats.correct++ - this.levelStats.maxStreak = Math.max(streak, this.levelStats.maxStreak) + this.addHit(target, playerSongTime + this.delay, correctTargetId); + this.layerStats.correct++; + this.levelStats.maxStreak = Math.max(streak, this.levelStats.maxStreak); } public earlyPinch(target: Target, playerSongTime: number) { - this.addHit(target, playerSongTime + this.delay, earlyTargetId) - this.layerStats.early++ + this.addHit(target, playerSongTime + this.delay, earlyTargetId); + this.layerStats.early++; } public latePinch(target: Target, playerSongTime: number) { - this.addHit(target, playerSongTime + this.delay, lateTargetId) - this.layerStats.late++ + this.addHit(target, playerSongTime + this.delay, lateTargetId); + this.layerStats.late++; } // Called when a player hits a target ahead of time, // causing all previous targets to be destroyed and "skipped" public skippedPinch(target: Target) { - this.addHit(target, -1000, skippedTargetId) - this.layerStats.miss++ + this.addHit(target, -1000, skippedTargetId); + this.layerStats.miss++; } public missedPinch(target: Target) { - this.addHit(target, -1000, missedTargetId) - this.layerStats.miss++ + this.addHit(target, -1000, missedTargetId); + this.layerStats.miss++; } } diff --git a/src/ts/level/streak.ts b/src/ts/level/streak.ts index a392f7d..4d64aa2 100644 --- a/src/ts/level/streak.ts +++ b/src/ts/level/streak.ts @@ -1,7 +1,13 @@ -import HandScene from '../scenes/handScene' -import { absPath, assert } from '../core/common' -import { config } from '../managers/storageManager' -import { ColorObject, GameObject, ParticleEmitter, PhaserText, Tween } from '../core/phaserTypes' +import HandScene from '../scenes/handScene'; +import { absPath, assert } from '../core/common'; +import { config } from '../managers/storageManager'; +import { + ColorObject, + GameObject, + ParticleEmitter, + PhaserText, + Tween, +} from '../core/phaserTypes'; import { streakFireOptions, streakInfoText, @@ -11,30 +17,35 @@ import { streakParticleOptions, streakStartRequirement, streakTextOptions, - undefinedText -} from '../core/config' + undefinedText, +} from '../core/config'; export class Streak extends GameObject { - private readonly hsv: ColorObject[] + private readonly hsv: ColorObject[]; - private current_: number = 0 + private current_: number = 0; - private onFire: boolean = false + private onFire: boolean = false; - private onFireTween: Tween | undefined - private streakText: PhaserText - private flame: ParticleEmitter + private onFireTween: Tween | undefined; + private streakText: PhaserText; + private flame: ParticleEmitter; constructor(scene: HandScene) { - super(scene, 'streak') + super(scene, 'streak'); - this.hsv = Phaser.Display.Color.HSVColorWheel() + this.hsv = Phaser.Display.Color.HSVColorWheel(); this.streakText = this.scene.add - .text(streakTextOptions.position.x, streakTextOptions.position.y, undefinedText, { - font: streakTextOptions.font, - color: streakTextOptions.color - }) + .text( + streakTextOptions.position.x, + streakTextOptions.position.y, + undefinedText, + { + font: streakTextOptions.font, + color: streakTextOptions.color, + }, + ) .setVisible(false) .setOrigin(0.5, 0.5) .setScale(streakTextOptions.scale) @@ -46,67 +57,71 @@ export class Streak extends GameObject { streakTextOptions.shadowColor, streakTextOptions.shadowBlurRadius, true, - true - ) + true, + ); } public preload() { - this.scene.load.image(streakParticleOptions.key, absPath(streakParticleOptions.path)) + this.scene.load.image( + streakParticleOptions.key, + absPath(streakParticleOptions.path), + ); } public unload() { - this.scene.textures.remove(streakParticleOptions.key) + this.scene.textures.remove(streakParticleOptions.key); } get current() { - return this.current_ + return this.current_; } public changeBy(value: number) { - this.current_ = Math.max(0, this.current + value) + this.current_ = Math.max(0, this.current + value); } public check() { if (this.current_ === streakStartRequirement) { - this.start() + this.start(); } else if (this.current_ > streakStartRequirement) { - this.continue() + this.continue(); } else { - this.stop() + this.stop(); } } private updateStreakText() { - this.streakText.setText(streakInfoText + this.current_.toString()) + this.streakText.setText(streakInfoText + this.current_.toString()); } private start() { // TODO: Possibly play sound. - this.updateStreakText() - this.streakText.setVisible(true) + this.updateStreakText(); + this.streakText.setVisible(true); } public stop() { // TODO: Potentially play sound. - this.current_ = 0 - this.onFire = false - if (this.onFireTween != undefined) this.scene.tweens.remove(this.onFireTween) - if (this.streakText != undefined) this.streakText.clearTint() - if (this.streakText != undefined) this.streakText.setVisible(false) + this.current_ = 0; + this.onFire = false; + if (this.onFireTween != undefined) + this.scene.tweens.remove(this.onFireTween); + if (this.streakText != undefined) this.streakText.clearTint(); + if (this.streakText != undefined) this.streakText.setVisible(false); if (this.flame != undefined) { - this.flame.destroy() + this.flame.destroy(); } } private continue() { - this.updateStreakText() - const wasOnFire: boolean = this.onFire - this.onFire = this.current_ >= streakOnFireRequirement + this.updateStreakText(); + const wasOnFire: boolean = this.onFire; + this.onFire = this.current_ >= streakOnFireRequirement; if (!wasOnFire && this.onFire) { assert( streakOnFireColorWheelExtent > 0 && streakOnFireColorWheelExtent <= 255, - 'Streak color wheel extent must be from 1 to 255' - ) + 'Streak color wheel extent must be from 1 to 255', + ); this.onFireTween = this.scene.tweens.addCounter({ from: 0, to: streakOnFireColorWheelExtent, @@ -114,19 +129,21 @@ export class Streak extends GameObject { yoyo: true, onUpdate: tween => { if (this != undefined && this.hsv != undefined) { - const value = Math.floor(tween.getValue()) - const top = this.hsv[value].color - const bottom = this.hsv[streakOnFireColorWheelExtent - value].color - if (this.streakText != undefined) this.streakText.setTint(top, top, bottom, bottom) + const value = Math.floor(tween.getValue()); + const top = this.hsv[value].color; + const bottom = this.hsv[streakOnFireColorWheelExtent - value].color; + if (this.streakText != undefined) + this.streakText.setTint(top, top, bottom, bottom); } }, onStop: () => { - if (this.onFireTween != undefined) this.scene.tweens.remove(this.onFireTween) - } - }) + if (this.onFireTween != undefined) + this.scene.tweens.remove(this.onFireTween); + }, + }); if (!config.fancyEffectsDisabled) { if (this.flame != undefined) { - this.flame.destroy() + this.flame.destroy(); } if (this.streakText != undefined) { // Create particles for flame @@ -134,10 +151,10 @@ export class Streak extends GameObject { this.streakText.x, this.streakText.y, streakParticleOptions.key, - streakFireOptions - ) - assert(streakTextOptions.depth != 0) - this.flame.setDepth(streakTextOptions.depth - 1) + streakFireOptions, + ); + assert(streakTextOptions.depth != 0); + this.flame.setDepth(streakTextOptions.depth - 1); } } } diff --git a/src/ts/managers/storageManager.ts b/src/ts/managers/storageManager.ts index 407c6aa..ec57953 100644 --- a/src/ts/managers/storageManager.ts +++ b/src/ts/managers/storageManager.ts @@ -1,8 +1,9 @@ +/* eslint-disable no-async-promise-executor */ import { ConfigData, LevelStats } from '../core/interfaces'; import { levelStatsToCSV } from '../util/csvFormat'; -import { absRootPath, absPath, loadJSONData, storeData } from '../core/common'; -import { configFilePath, csvFileExtension, csvFilePath } from '../core/config'; -const FileSaver = require('file-saver'); +import { absPath, loadJSONData, storeData } from '../core/common'; +import { configFilePath, csvFileExtension } from '../core/config'; +import FileSaver from 'file-saver'; export let config: ConfigData; @@ -30,20 +31,20 @@ export function loadData(filePath: string): Promise { } export function saveToCSV(data: LevelStats) { - var blob = new Blob([levelStatsToCSV(data)], { + const blob = new Blob([levelStatsToCSV(data)], { type: 'text/plain;charset=utf-8', }); FileSaver.saveAs(blob, data.id + csvFileExtension); //saveCSV(data.id, levelStatsToCSV(data)) } -export function autoSaveToCSV(data: LevelStats) { +export function autoSaveToCSV(_data: LevelStats) { // TODO: Figure this out in browser. //saveCSV(data.id, levelStatsToCSV(data)) } -function saveCSV(fileName: string, csv: string): void { - const path = absRootPath(csvFilePath) + fileName + csvFileExtension; +// export function saveCSV(fileName: string, csv: string): void { +// const path = absRootPath(csvFilePath) + fileName + csvFileExtension; - storeData(path, csv); -} +// storeData(path, csv); +// } diff --git a/src/ts/managers/targetManager.ts b/src/ts/managers/targetManager.ts index 0e8aefc..53e30e5 100644 --- a/src/ts/managers/targetManager.ts +++ b/src/ts/managers/targetManager.ts @@ -1,34 +1,34 @@ -import Target from '../objects/target' +import Target from '../objects/target'; export class TargetManager { - private targets: Target[] = [] + private targets: Target[] = []; constructor() {} public start() { - this.destroyTargets() + this.destroyTargets(); } public destroyTarget(target: Target) { - target.destroyTarget() - const index = this.targets.indexOf(target, 0) + target.destroyTarget(); + const index = this.targets.indexOf(target, 0); if (index > -1) { - this.targets.splice(index, 1) + this.targets.splice(index, 1); } } public destroyTargets() { - const targets: Target[] = [...this.targets] + const targets: Target[] = [...this.targets]; for (const target of targets) { - this.destroyTarget(target) + this.destroyTarget(target); } } public addTarget(target: Target) { - this.targets.push(target) + this.targets.push(target); } public getPreviousTargets(target: Target): Target[] { - return this.targets.filter((t: Target) => t.songTime < target.songTime) + return this.targets.filter((t: Target) => t.songTime < target.songTime); } } diff --git a/src/ts/objects/handTracker.ts b/src/ts/objects/handTracker.ts index 4b61c0d..c43bc3d 100644 --- a/src/ts/objects/handTracker.ts +++ b/src/ts/objects/handTracker.ts @@ -1,107 +1,154 @@ -import { HandLandmarker, HandLandmarkerResult, FilesetResolver } from '@mediapipe/tasks-vision' -import { absRootPath, absPath, normalizedToWindow, assert } from '../core/common' -import webcam from '../objects/webcam' -import { HandIndex, handLandmarkCount, HandLandmarkIndex } from '../objects/handLandmarks' -import { landmarkDetectionOptions, modelPath, wasmDirectory } from '../core/config' -import { Vector2 } from '../core/phaserTypes' +import { + HandLandmarker, + HandLandmarkerResult, + FilesetResolver, +} from '@mediapipe/tasks-vision'; +import { + absRootPath, + absPath, + normalizedToWindow, + assert, +} from '../core/common'; +import webcam from '../objects/webcam'; +import { + HandIndex, + handLandmarkCount, + HandLandmarkIndex, +} from '../objects/handLandmarks'; +import { + landmarkDetectionOptions, + modelPath, + wasmDirectory, +} from '../core/config'; +import { Vector2 } from '../core/phaserTypes'; export class HandTracker { - private results_: HandLandmarkerResult | undefined - private handLandmarker_: HandLandmarker | undefined - private lastVideoTime: number = -1 + private results_: HandLandmarkerResult | undefined; + private handLandmarker_: HandLandmarker | undefined; + private lastVideoTime: number = -1; constructor() {} - public async init(progressCallback: (text: string) => void): Promise { + public async init( + progressCallback: (text: string) => void, + ): Promise { // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve, reject) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any - let wasmFileset: any = undefined + let wasmFileset: any = undefined; await FilesetResolver.forVisionTasks( // File from: https://cdn.jsdelivr.net/npm/@mediapipe/tasks-vision@0.10.0/wasm - absRootPath(wasmDirectory) + absRootPath(wasmDirectory), ) .then(result => (wasmFileset = result)) .catch(err => { - reject(err) - return - }) + reject(err); + return; + }); - progressCallback('Loading hand landmarker...') + progressCallback('Loading hand landmarker...'); // TODO: Add check that modelPath exists: // if (!fileExists(modelPathRelativeToSrc)) reject("Model path not found"); - landmarkDetectionOptions.baseOptions!.modelAssetPath = absPath(modelPath) + landmarkDetectionOptions.baseOptions!.modelAssetPath = absPath(modelPath); - await HandLandmarker.createFromOptions(wasmFileset, landmarkDetectionOptions) + await HandLandmarker.createFromOptions( + wasmFileset, + landmarkDetectionOptions, + ) .then(result => (this.handLandmarker_ = result)) .catch(err => { if (err instanceof Event && (err as Event).type == 'error') - reject('Failed to fetch vision_wasm_internal.js in specified wasm directory') - if (err instanceof TypeError) reject('Failed to fetch hand_landmark.task file') - reject(err) - return - }) - - resolve(true) - }) + reject( + 'Failed to fetch vision_wasm_internal.js in specified wasm directory', + ); + if (err instanceof TypeError) + reject('Failed to fetch hand_landmark.task file'); + reject(err); + return; + }); + + resolve(true); + }); } public precache(progressCallback: (text: string) => void) { // MediaPipe seems to cache landmarks, so this initial update allows the game // loop update to start immediately with no delay before drawing landmarks. - progressCallback('Pre-caching landmarks...') - this.update() + progressCallback('Pre-caching landmarks...'); + this.update(); } public found(): boolean { - return this.handLandmarker_ != undefined + return this.handLandmarker_ != undefined; } public update() { - if (this.lastVideoTime == webcam.video.currentTime) return + if (this.lastVideoTime == webcam.video.currentTime) return; - this.lastVideoTime = webcam.video.currentTime + this.lastVideoTime = webcam.video.currentTime; // This updates MediaPipe hand landmarks from webcam video. - this.results_ = this.handLandmarker.detectForVideo(webcam.video, performance.now()) + this.results_ = this.handLandmarker.detectForVideo( + webcam.video, + performance.now(), + ); } public get results(): HandLandmarkerResult { - assert(this.results_ != undefined) - return this.results_! + assert(this.results_ != undefined); + return this.results_!; } private get handLandmarker(): HandLandmarker { - assert(this.handLandmarker_ != undefined) - return this.handLandmarker_! + assert(this.handLandmarker_ != undefined); + return this.handLandmarker_!; } public landmarksFound(handIndex: HandIndex = 0): boolean { - return this.results != undefined && handIndex < this.results.landmarks.length + return ( + this.results != undefined && handIndex < this.results.landmarks.length + ); } - public landmarkFound(landmarkIndex: HandLandmarkIndex, handIndex: HandIndex = 0): boolean { - if (!this.landmarksFound(handIndex)) return false + public landmarkFound( + landmarkIndex: HandLandmarkIndex, + handIndex: HandIndex = 0, + ): boolean { + if (!this.landmarksFound(handIndex)) return false; - const landmark = this.results.landmarks[handIndex][landmarkIndex] - return landmark.x >= 0 && landmark.x <= 1 && landmark.y >= 0 && landmark.y <= 1 + const landmark = this.results.landmarks[handIndex][landmarkIndex]; + return ( + landmark.x >= 0 && landmark.x <= 1 && landmark.y >= 0 && landmark.y <= 1 + ); } - public getLandmarkPosition(landmarkIndex: HandLandmarkIndex, handIndex: HandIndex = 0): Vector2 | undefined { - if (!this.landmarksFound(handIndex)) return undefined - - const landmark = this.results.landmarks[handIndex][landmarkIndex] - const position = normalizedToWindow(new Vector2(landmark.x, landmark.y)) - return new Vector2(window.innerWidth - position.x, position.y) /* mirror landmarks in the x-direction */ + public getLandmarkPosition( + landmarkIndex: HandLandmarkIndex, + handIndex: HandIndex = 0, + ): Vector2 | undefined { + if (!this.landmarksFound(handIndex)) return undefined; + + const landmark = this.results.landmarks[handIndex][landmarkIndex]; + const position = normalizedToWindow(new Vector2(landmark.x, landmark.y)); + return new Vector2( + window.innerWidth - position.x, + position.y, + ); /* mirror landmarks in the x-direction */ } - public forEachLandmarkPosition(landmarkPositionCallback: (position: Vector2) => void, handIndex: HandIndex = 0) { + public forEachLandmarkPosition( + landmarkPositionCallback: (position: Vector2) => void, + handIndex: HandIndex = 0, + ) { for (let i = 0; i < handLandmarkCount; ++i) { - const position: Vector2 | undefined = this.getLandmarkPosition(i, handIndex) - if (position !== undefined) landmarkPositionCallback(position) + const position: Vector2 | undefined = this.getLandmarkPosition( + i, + handIndex, + ); + if (position !== undefined) landmarkPositionCallback(position); } } } -const handTracker: HandTracker = new HandTracker() +const handTracker: HandTracker = new HandTracker(); -export default handTracker +export default handTracker; diff --git a/src/ts/objects/target.ts b/src/ts/objects/target.ts index b2c6b14..48425e3 100644 --- a/src/ts/objects/target.ts +++ b/src/ts/objects/target.ts @@ -1,10 +1,17 @@ -import { TargetTweenOptions } from '../core/interfaces' -import HandScene from '../scenes/handScene' -import { config } from '../managers/storageManager' -import { PlayableLayer } from '../level/layer' -import { absPath } from '../core/common' +import { TargetTweenOptions } from '../core/interfaces'; +import HandScene from '../scenes/handScene'; +import { config } from '../managers/storageManager'; +import { PlayableLayer } from '../level/layer'; +import { absPath } from '../core/common'; -import { AudioTrack, MatterSprite, PhaserText, Sprite, Tween, Vector2 } from '../core/phaserTypes' +import { + AudioTrack, + MatterSprite, + PhaserText, + Sprite, + Tween, + Vector2, +} from '../core/phaserTypes'; import { deadTargetOptions, earlyTargetOptions, @@ -22,47 +29,53 @@ import { targetTextureOptions, undefinedText, outerRingDepth, - targetDepth -} from '../core/config' -import setInteraction from '../util/interaction' + targetDepth, +} from '../core/config'; +import setInteraction from '../util/interaction'; export default class Target extends MatterSprite { - public readonly scene: HandScene - public readonly sound: AudioTrack - public readonly songTime: number - public readonly nodeIndex: number - - private readonly outerRing: Sprite - private readonly fingerId: string - private readonly lifetime: number - private readonly spriteScale: number - - private onTargetMiss?: () => void - - private rotateTween: Tween | undefined - private earlyTween: Tween | undefined - private onTimeTween: Tween | undefined - private lateTween: Tween | undefined - private deathTween: Tween | undefined - private glowTween: Tween | undefined - private glow: Phaser.FX.Glow | undefined - private targetText: PhaserText + public readonly scene: HandScene; + public readonly sound: AudioTrack; + public readonly songTime: number; + public readonly nodeIndex: number; + + private readonly outerRing: Sprite; + private readonly fingerId: string; + private readonly lifetime: number; + private readonly spriteScale: number; + + private onTargetMiss?: () => void; + + private rotateTween: Tween | undefined; + private earlyTween: Tween | undefined; + private onTimeTween: Tween | undefined; + private lateTween: Tween | undefined; + private deathTween: Tween | undefined; + private glowTween: Tween | undefined; + private glow: Phaser.FX.Glow | undefined; + private targetText: PhaserText; // Based on user options. - public readonly onTimeDuration: number - private readonly lateDuration: number + public readonly onTimeDuration: number; + private readonly lateDuration: number; // Based on layer preview time and onTimeDuration. - private readonly earlyDuration: number + private readonly earlyDuration: number; // deadDuration is constant and taken from config.ts public static preload(scene: HandScene) { - scene.load.image(targetTextureOptions.keyInner, absPath(targetTextureOptions.pathInner)) - scene.load.image(targetTextureOptions.keyOuter, absPath(targetTextureOptions.pathOuter)) + scene.load.image( + targetTextureOptions.keyInner, + absPath(targetTextureOptions.pathInner), + ); + scene.load.image( + targetTextureOptions.keyOuter, + absPath(targetTextureOptions.pathOuter), + ); } public static unload(scene: HandScene) { - scene.textures.remove(targetTextureOptions.keyInner) - scene.textures.remove(targetTextureOptions.keyOuter) + scene.textures.remove(targetTextureOptions.keyInner); + scene.textures.remove(targetTextureOptions.keyOuter); } constructor( @@ -73,42 +86,54 @@ export default class Target extends MatterSprite { soundKey: string, layer: PlayableLayer, fingerId: string, - nodeIndex: number + nodeIndex: number, ) { - super(scene.matter.world, position.x, position.y, layer.data.spriteKeyInner, undefined, { - render: { - visible: true - } - }) - this.scene = scene - this.lifetime = lifetime - this.fingerId = fingerId - this.nodeIndex = nodeIndex + super( + scene.matter.world, + position.x, + position.y, + layer.data.spriteKeyInner, + undefined, + { + render: { + visible: true, + }, + }, + ); + this.scene = scene; + this.lifetime = lifetime; + this.fingerId = fingerId; + this.nodeIndex = nodeIndex; - this.spriteScale = config.targetSize + this.spriteScale = config.targetSize; - this.scene.add.existing(this) + this.scene.add.existing(this); - this.songTime = this.lifetime + songTime + this.songTime = this.lifetime + songTime; - this.lateDuration = config.lateTimeDuration - this.onTimeDuration = config.onTimeDuration - this.earlyDuration = this.lifetime - this.onTimeDuration / 2 + this.lateDuration = config.lateTimeDuration; + this.onTimeDuration = config.onTimeDuration; + this.earlyDuration = this.lifetime - this.onTimeDuration / 2; this.sound = this.scene.sound.add(soundKey, { - volume: config.sonificationLevel - }) + volume: config.sonificationLevel, + }); - this.setScale(this.spriteScale) - this.setTint(fingerColors[this.fingerId]) - this.setDepth(targetDepth) - setInteraction(this, true) + this.setScale(this.spriteScale); + this.setTint(fingerColors[this.fingerId]); + this.setDepth(targetDepth); + setInteraction(this, true); this.setCircle(this.displayWidth / 2, { - label: layer.level.trackKey + '-' + layer.data.id + targetCollisionLabel + nodeIndex.toString() - }) + label: + layer.level.trackKey + + '-' + + layer.data.id + + targetCollisionLabel + + nodeIndex.toString(), + }); - this.setSensor(true) + this.setSensor(true); if (!config.fancyEffectsDisabled) { this.glow = this.postFX.addGlow( @@ -117,31 +142,35 @@ export default class Target extends MatterSprite { 0, false, targetGlowOptions.quality, - targetGlowOptions.distance - ) + targetGlowOptions.distance, + ); } // Outer target - this.outerRing = this.scene.add.sprite(position.x, position.y, layer.data.spriteKeyOuter) - this.outerRing.setScale(this.spriteScale * outerRingStartScale) - this.outerRing.setDepth(outerRingDepth) + this.outerRing = this.scene.add.sprite( + position.x, + position.y, + layer.data.spriteKeyOuter, + ); + this.outerRing.setScale(this.spriteScale * outerRingStartScale); + this.outerRing.setDepth(outerRingDepth); - this.targetText = new PhaserText(this.scene, 0, 0, undefinedText, {}) + this.targetText = new PhaserText(this.scene, 0, 0, undefinedText, {}); - this.addTargetText() + this.addTargetText(); - this.setupTweens() + this.setupTweens(); } public setOnTargetMiss(onTargetMiss: () => void) { - this.onTargetMiss = onTargetMiss + this.onTargetMiss = onTargetMiss; } public setOnTargetHit(onTargetHit: () => void) { this.scene.hand.addPinchCheck(this.fingerId, this, { - startPinch: onTargetHit - }) - this.once('pointerdown', onTargetHit) + startPinch: onTargetHit, + }); + this.once('pointerdown', onTargetHit); } private addTargetText() { @@ -152,16 +181,20 @@ export default class Target extends MatterSprite { (this.nodeIndex + 1).toString(), { font: targetTextOptions.font, - color: targetTextOptions.color - } - ) - this.targetText.setOrigin(0.5, 0.5) - this.targetText.setDepth(targetTextDepth) + color: targetTextOptions.color, + }, + ); + this.targetText.setOrigin(0.5, 0.5); + this.targetText.setDepth(targetTextDepth); } - private createTween(targets: (Sprite | PhaserText)[], options: TargetTweenOptions, duration: number) { + private createTween( + targets: (Sprite | PhaserText)[], + options: TargetTweenOptions, + duration: number, + ) { for (const target of targets) { - target.tint = options.color + target.tint = options.color; } const tween: Tween = this.scene.tweens.add({ targets: targets, @@ -169,9 +202,9 @@ export default class Target extends MatterSprite { displayHeight: this.displayHeight * options.scale, ease: options.ease, duration: duration, - repeat: 0 - }) - return tween + repeat: 0, + }); + return tween; } private setupTweens() { @@ -182,21 +215,25 @@ export default class Target extends MatterSprite { rotation: 2 * Math.PI, ease: targetRotationEase, duration: targetRotationDuration, - repeat: -1 - }) + repeat: -1, + }); } // Shrink target - this.earlyTween = this.createTween([this.outerRing], earlyTargetOptions, this.earlyDuration) + this.earlyTween = this.createTween( + [this.outerRing], + earlyTargetOptions, + this.earlyDuration, + ); this.earlyTween.on('complete', () => { this.onTimeTween = this.createTween( [this], { ...onTimeTargetOptions, - scale: 1 + scale: 1, }, - this.onTimeDuration - ) + this.onTimeDuration, + ); if (!config.fancyEffectsDisabled && this.glow != undefined) { this.glowTween = this.scene.tweens.add({ targets: this.glow, @@ -204,51 +241,53 @@ export default class Target extends MatterSprite { yoyo: true, // This makes glow start and end at the beginning and end of the 'on time duration' duration: this.onTimeDuration / 2, - ease: targetGlowOptions.ease - }) + ease: targetGlowOptions.ease, + }); } this.onTimeTween.on('complete', () => { if (this.glow != undefined) { - this.glow.destroy() + this.glow.destroy(); } this.lateTween = this.createTween( [this, this.outerRing], { ...lateTargetOptions, - scale: 1 + scale: 1, }, - this.lateDuration - ) + this.lateDuration, + ); this.lateTween.on('complete', () => { - setInteraction(this, false) + setInteraction(this, false); this.deathTween = this.createTween( [this, this.outerRing, this.targetText], deadTargetOptions, - targetDeathDuration - ) + targetDeathDuration, + ); if (this.onTargetMiss != undefined) { - this.deathTween.on('complete', this.onTargetMiss) + this.deathTween.on('complete', this.onTargetMiss); } - }) - }) - }) + }); + }); + }); } public destroyTarget() { - this.scene.hand.removePinchCheck(this.fingerId, this) - this.off('pointerdown') - - if (this.rotateTween != undefined) this.scene.tweens.remove(this.rotateTween) - if (this.earlyTween != undefined) this.scene.tweens.remove(this.earlyTween) - if (this.onTimeTween != undefined) this.scene.tweens.remove(this.onTimeTween) - if (this.lateTween != undefined) this.scene.tweens.remove(this.lateTween) - if (this.deathTween != undefined) this.scene.tweens.remove(this.deathTween) - if (this.glowTween != undefined) this.scene.tweens.remove(this.glowTween) - if (this.glow != undefined) this.glow.destroy() - if (this.targetText != undefined) this.targetText.destroy() - - this.outerRing.destroy() - - this.destroy() + this.scene.hand.removePinchCheck(this.fingerId, this); + this.off('pointerdown'); + + if (this.rotateTween != undefined) + this.scene.tweens.remove(this.rotateTween); + if (this.earlyTween != undefined) this.scene.tweens.remove(this.earlyTween); + if (this.onTimeTween != undefined) + this.scene.tweens.remove(this.onTimeTween); + if (this.lateTween != undefined) this.scene.tweens.remove(this.lateTween); + if (this.deathTween != undefined) this.scene.tweens.remove(this.deathTween); + if (this.glowTween != undefined) this.scene.tweens.remove(this.glowTween); + if (this.glow != undefined) this.glow.destroy(); + if (this.targetText != undefined) this.targetText.destroy(); + + this.outerRing.destroy(); + + this.destroy(); } } diff --git a/src/ts/objects/webcam.ts b/src/ts/objects/webcam.ts index e482f8c..95ee1f4 100644 --- a/src/ts/objects/webcam.ts +++ b/src/ts/objects/webcam.ts @@ -1,20 +1,20 @@ -import { WebcamOptions } from '../core/interfaces' -import { webcamSourceOptions } from '../core/config' -import { assert } from '../core/common' +import { WebcamOptions } from '../core/interfaces'; +import { webcamSourceOptions } from '../core/config'; +import { assert } from '../core/common'; class Webcam { // Useful to hold onto the outer wrapper for methods like setDisplaySize(). - private video_: HTMLVideoElement | undefined + private video_: HTMLVideoElement | undefined; public async init( options: WebcamOptions, - progressCallback: (text: string) => void + progressCallback: (text: string) => void, ): Promise { // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve, reject) => { - progressCallback('Loading webcam...') + progressCallback('Loading webcam...'); - this.setupVideoElement(options) + this.setupVideoElement(options); // From: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia // To select a webmca use mediaDevices.enumerateDevices() and add set @@ -24,74 +24,74 @@ class Webcam { .then(mediaStream => { // https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/srcObject#supporting_fallback_to_the_src_property if (!('srcObject' in this.video)) { - reject('srcObject does not exist on older browsers') - return + reject('srcObject does not exist on older browsers'); + return; } - this.video.srcObject = mediaStream + this.video.srcObject = mediaStream; this.video.addEventListener('loadeddata', () => { - resolve(true) - }) + resolve(true); + }); }) .catch(err => { - reject(err) - }) - }) + reject(err); + }); + }); } public found(): boolean { - return this.video.srcObject != undefined + return this.video.srcObject != undefined; } public setVisibility(visible: boolean) { // For some reason excluding this line causes visibility change to fail. - this.video.style.visibility = '' - this.video.style.visibility = visible ? 'shown' : 'hidden' + this.video.style.visibility = ''; + this.video.style.visibility = visible ? 'shown' : 'hidden'; } private setWidth(width: number) { - this.video.width = width + this.video.width = width; } private setHeight(height: number) { - this.video.height = height + this.video.height = height; } private setupVideoElement(options: WebcamOptions) { - this.video = document.body.appendChild(document.createElement('video')) + this.video = document.body.appendChild(document.createElement('video')); // 'Mandatory' - this.video.autoplay = true - this.video.muted = true - this.video.playsInline = true - this.video.style.position = 'absolute' - this.video.style.top = '0' - this.video.style.left = '0' - this.video.style.bottom = '0' - this.video.style.zIndex = '-1' - this.video.style.pointerEvents = 'none' - this.video.style.objectFit = options.objectFit // DISCUSS: Slight but tolerable stretch of video? - this.setWidth(options.width) - this.setHeight(options.height) + this.video.autoplay = true; + this.video.muted = true; + this.video.playsInline = true; + this.video.style.position = 'absolute'; + this.video.style.top = '0'; + this.video.style.left = '0'; + this.video.style.bottom = '0'; + this.video.style.zIndex = '-1'; + this.video.style.pointerEvents = 'none'; + this.video.style.objectFit = options.objectFit; // DISCUSS: Slight but tolerable stretch of video? + this.setWidth(options.width); + this.setHeight(options.height); if (options.flip) { this.video.style.cssText += '-moz-transform: scale(-1, 1); \ -webkit-transform: scale(-1, 1); -o-transform: scale(-1, 1); \ - transform: scale(-1, 1); filter: FlipH;' + transform: scale(-1, 1); filter: FlipH;'; } - this.setVisibility(false) // Only show webcam after landmarks are pre-cached. + this.setVisibility(false); // Only show webcam after landmarks are pre-cached. } public get video(): HTMLVideoElement { - assert(this.video_ != undefined) - return this.video_! + assert(this.video_ != undefined); + return this.video_!; } public set video(video: HTMLVideoElement) { - this.video_ = video + this.video_ = video; } } -const webcam: Webcam = new Webcam() +const webcam: Webcam = new Webcam(); -export default webcam +export default webcam; diff --git a/src/ts/ui/button.ts b/src/ts/ui/button.ts index 4f9c76f..1fa19fc 100644 --- a/src/ts/ui/button.ts +++ b/src/ts/ui/button.ts @@ -1,13 +1,13 @@ -import { buttonDepth, buttonPinchFinger } from '../core/config' -import { PinchCallbacks } from '../core/interfaces' -import { AudioTrack, MatterSprite, Vector2 } from '../core/phaserTypes' -import { config } from '../managers/storageManager' -import HandScene from '../scenes/handScene' -import setInteraction from '../util/interaction' +import { buttonDepth, buttonPinchFinger } from '../core/config'; +import { PinchCallbacks } from '../core/interfaces'; +import { AudioTrack, MatterSprite, Vector2 } from '../core/phaserTypes'; +import { config } from '../managers/storageManager'; +import HandScene from '../scenes/handScene'; +import setInteraction from '../util/interaction'; export class Button extends MatterSprite { - public readonly scene: HandScene - private sound: AudioTrack + public readonly scene: HandScene; + private sound: AudioTrack; constructor( scene: HandScene, @@ -15,63 +15,63 @@ export class Button extends MatterSprite { scale: Vector2, spriteKey: string, soundKey: string, - interactable: boolean + interactable: boolean, ) { super(scene.matter.world, position.x, position.y, spriteKey, undefined, { render: { - visible: true + visible: true, }, - label: 'button-' + spriteKey - }) - this.scene = scene + label: 'button-' + spriteKey, + }); + this.scene = scene; - this.scene.add.existing(this) + this.scene.add.existing(this); this.sound = this.scene.sound.add(soundKey, { - volume: config.sonificationLevel - }) + volume: config.sonificationLevel, + }); - this.setSensor(true) - this.setOrigin(0.5, 0.5) - this.setScale(scale.x, scale.y) - this.setDepth(buttonDepth) - setInteraction(this, interactable) + this.setSensor(true); + this.setOrigin(0.5, 0.5); + this.setScale(scale.x, scale.y); + this.setDepth(buttonDepth); + setInteraction(this, interactable); this.on('destroy', () => { - this.scene.hand.removePinchCheck(buttonPinchFinger, this) - }) + this.scene.hand.removePinchCheck(buttonPinchFinger, this); + }); } public removePinchCallbacks() { - this.scene.hand.removePinchCheck(buttonPinchFinger, this) + this.scene.hand.removePinchCheck(buttonPinchFinger, this); } public addPinchCallbacks(pinchCallbacks: PinchCallbacks) { const onTriggerStart = () => { - pinchCallbacks.startPinch?.() + pinchCallbacks.startPinch?.(); this.sound.play({ - volume: config.sonificationLevel - }) - } + volume: config.sonificationLevel, + }); + }; const newCallbacks: PinchCallbacks = { startPinch: onTriggerStart, pinched: pinchCallbacks.pinched, endPinch: pinchCallbacks.endPinch, startHover: pinchCallbacks.startHover, - endHover: pinchCallbacks.endHover - } + endHover: pinchCallbacks.endHover, + }; - this.scene.hand.addPinchCheck(buttonPinchFinger, this, newCallbacks) + this.scene.hand.addPinchCheck(buttonPinchFinger, this, newCallbacks); - this.on('pointerdown', newCallbacks.startPinch!) + this.on('pointerdown', newCallbacks.startPinch!); if (newCallbacks.startHover) { - this.on('pointermove', newCallbacks.startHover) + this.on('pointermove', newCallbacks.startHover); } if (newCallbacks.endHover) { - this.on('pointerout', newCallbacks.endHover) + this.on('pointerout', newCallbacks.endHover); } } } diff --git a/src/ts/ui/checkbox.ts b/src/ts/ui/checkbox.ts index 027e27d..21769af 100644 --- a/src/ts/ui/checkbox.ts +++ b/src/ts/ui/checkbox.ts @@ -1,17 +1,17 @@ -import Phaser from 'phaser' -import { assert } from '../core/common' -import { Graphics, PhaserText, Rectangle, Vector2 } from '../core/phaserTypes' -import { ConfigData } from '../core/interfaces' -import { undefinedText } from '../core/config' +import Phaser from 'phaser'; +import { assert } from '../core/common'; +import { Graphics, PhaserText, Rectangle, Vector2 } from '../core/phaserTypes'; +import { ConfigData } from '../core/interfaces'; +import { undefinedText } from '../core/config'; export class Checkbox extends Graphics { - private isChecked_: boolean - private box: Rectangle - private check: Rectangle - private label: PhaserText | undefined - private readonly labelText: string = undefinedText - private toggleCallback: (checkbox: Checkbox) => void - public readonly key: keyof ConfigData + private isChecked_: boolean; + private box: Rectangle; + private check: Rectangle; + private label: PhaserText | undefined; + private readonly labelText: string = undefinedText; + private toggleCallback: (checkbox: Checkbox) => void; + public readonly key: keyof ConfigData; constructor( scene: Phaser.Scene, @@ -21,91 +21,101 @@ export class Checkbox extends Graphics { labelText: string, configData: ConfigData, key: keyof ConfigData, - toggleCallback: (checkbox: Checkbox) => void + toggleCallback: (checkbox: Checkbox) => void, ) { - super(scene) + super(scene); - this.x = boxPosition.x - this.y = boxPosition.y - this.key = key + this.x = boxPosition.x; + this.y = boxPosition.y; + this.key = key; if (labelOffset != undefined) { - this.labelText = labelText - this.label = this.scene.add.text(boxPosition.x + labelOffset.x, boxPosition.y + labelOffset.y, this.labelText, { - color: '#ffffff', - fontSize: '24px' - }) + this.labelText = labelText; + this.label = this.scene.add.text( + boxPosition.x + labelOffset.x, + boxPosition.y + labelOffset.y, + this.labelText, + { + color: '#ffffff', + fontSize: '24px', + }, + ); } - this.toggleCallback = toggleCallback - assert(typeof configData[this.key] == 'boolean') - this.isChecked_ = configData[this.key] as boolean - this.box = new Rectangle(0, 0, boxSize, boxSize) - this.check = new Rectangle(boxSize * 0.25, boxSize * 0.25, boxSize * 0.5, boxSize * 0.5) + this.toggleCallback = toggleCallback; + assert(typeof configData[this.key] == 'boolean'); + this.isChecked_ = configData[this.key] as boolean; + this.box = new Rectangle(0, 0, boxSize, boxSize); + this.check = new Rectangle( + boxSize * 0.25, + boxSize * 0.25, + boxSize * 0.5, + boxSize * 0.5, + ); - this.setInteractive(this.box, Rectangle.Contains) + this.setInteractive(this.box, Rectangle.Contains); - this.toggleCallback(this) + this.toggleCallback(this); - this.on('pointerdown', this.toggleCheck, this) - this.draw() + this.on('pointerdown', this.toggleCheck, this); + this.draw(); } public reset(configData: ConfigData) { - assert(typeof configData[this.key] == 'boolean') - this.isChecked_ = configData[this.key] as boolean - this.toggleCallback(this) - this.draw() + assert(typeof configData[this.key] == 'boolean'); + this.isChecked_ = configData[this.key] as boolean; + this.toggleCallback(this); + this.draw(); } public resetLabel() { if (this.label != undefined) { - this.label.setText(this.labelText) + this.label.setText(this.labelText); } } public setLabel(text: string) { if (this.label != undefined) { - this.label.setText(text) + this.label.setText(text); } - this.draw() + this.draw(); } private toggleCheck() { - this.isChecked_ = !this.isChecked_ - this.toggleCallback(this) - this.draw() + this.isChecked_ = !this.isChecked_; + this.toggleCallback(this); + this.draw(); } public draw() { - this.clear() + this.clear(); if (this.input != undefined && !this.input.enabled) { - this.setInteractive(this.box, Rectangle.Contains) + this.setInteractive(this.box, Rectangle.Contains); } - this.lineStyle(2, 0xffffff) - this.strokeRectShape(this.box) + this.lineStyle(2, 0xffffff); + this.strokeRectShape(this.box); if (this.isChecked_) { - this.fillStyle(0xffffff) - this.fillRectShape(this.check) + this.fillStyle(0xffffff); + this.fillRectShape(this.check); } } public setToggled(value: number | boolean | string) { - if (typeof value != 'boolean') return - this.isChecked_ = value - this.toggleCallback(this) - this.draw() + if (typeof value != 'boolean') return; + this.isChecked_ = value; + this.toggleCallback(this); + this.draw(); } public hide(): Checkbox { - this.clear() - this.disableInteractive() + this.clear(); + this.disableInteractive(); if (this.label != undefined) { - this.label.setText('') + this.label.setText(''); } - return this + return this; } get isChecked(): boolean { - return this.isChecked_ + return this.isChecked_; } } diff --git a/src/ts/ui/difficultyButton.ts b/src/ts/ui/difficultyButton.ts index b3801e9..4b79089 100644 --- a/src/ts/ui/difficultyButton.ts +++ b/src/ts/ui/difficultyButton.ts @@ -1,19 +1,19 @@ -import { difficultyButtonChosenTint, undefinedText } from '../core/config' -import { PhaserText, Vector2 } from '../core/phaserTypes' -import HandScene from '../scenes/handScene' -import { ToggleButton } from '../ui/toggleButton' +import { difficultyButtonChosenTint, undefinedText } from '../core/config'; +import { PhaserText, Vector2 } from '../core/phaserTypes'; +import HandScene from '../scenes/handScene'; +import { ToggleButton } from '../ui/toggleButton'; -const bpmTextOffset: number = 0.023 +const bpmTextOffset: number = 0.023; export class DifficultyButton extends ToggleButton { - private readonly bpmIndex_: number - private readonly bpm: number - private readonly bpmText: PhaserText + private readonly bpmIndex_: number; + private readonly bpm: number; + private readonly bpmText: PhaserText; private readonly bpmTextOptions = { font: '20px Courier New', - color: 0xffffff - } + color: 0xffffff, + }; constructor( scene: HandScene, @@ -25,40 +25,51 @@ export class DifficultyButton extends ToggleButton { interactable: boolean, bpmIndex: number, bpm: number, - initialState: boolean = true + initialState: boolean = true, ) { - super(scene, position, scale, onSpriteKey, offSpriteKey, soundKey, interactable, initialState) - this.bpmIndex_ = bpmIndex - this.bpm = bpm + super( + scene, + position, + scale, + onSpriteKey, + offSpriteKey, + soundKey, + interactable, + initialState, + ); + this.bpmIndex_ = bpmIndex; + this.bpm = bpm; this.bpmText = this.scene.add.text(0, 0, undefinedText, { - font: this.bpmTextOptions.font - }) - this.bpmText.setText('BPM: ' + this.bpm.toString()) + font: this.bpmTextOptions.font, + }); + this.bpmText.setText('BPM: ' + this.bpm.toString()); // Ensure position is set after text because text varies the displaySize. this.bpmText.setPosition( position.x - this.bpmText.displayWidth / 2, - position.y - this.bpmText.displayHeight / 2 + window.innerHeight * bpmTextOffset - ) + position.y - + this.bpmText.displayHeight / 2 + + window.innerHeight * bpmTextOffset, + ); - this.updateTint() + this.updateTint(); this.on('destroy', () => { - this.bpmText.destroy() - }) + this.bpmText.destroy(); + }); } public updateTint() { if (this.toggleState) { - this.bpmText.setTintFill(difficultyButtonChosenTint) - this.setTintFill(difficultyButtonChosenTint) + this.bpmText.setTintFill(difficultyButtonChosenTint); + this.setTintFill(difficultyButtonChosenTint); } else { - this.bpmText.setTintFill(this.bpmTextOptions.color) - this.clearTint() + this.bpmText.setTintFill(this.bpmTextOptions.color); + this.clearTint(); } } get bpmIndex() { - return this.bpmIndex_ + return this.bpmIndex_; } } diff --git a/src/ts/ui/slider.ts b/src/ts/ui/slider.ts index 96c7779..a82aada 100644 --- a/src/ts/ui/slider.ts +++ b/src/ts/ui/slider.ts @@ -1,24 +1,24 @@ -import Phaser from 'phaser' -import Vector2 = Phaser.Math.Vector2 -import HandScene from '../scenes/handScene' -import { Graphics, PhaserText, Pointer, Rectangle } from '../core/phaserTypes' -import { ConfigData } from '../core/interfaces' -import { assert } from '../core/common' -import RoundTo = Phaser.Math.RoundTo -import { undefinedText } from '../core/config' +import Phaser from 'phaser'; +import Vector2 = Phaser.Math.Vector2; +import HandScene from '../scenes/handScene'; +import { Graphics, PhaserText, Pointer, Rectangle } from '../core/phaserTypes'; +import { ConfigData } from '../core/interfaces'; +import { assert } from '../core/common'; +import RoundTo = Phaser.Math.RoundTo; +import { undefinedText } from '../core/config'; export class Slider extends Graphics { - private value: number - private box: Rectangle - private handle: Rectangle - private isDragging: boolean = false - public label: PhaserText | undefined - public range: Vector2 = new Vector2(0, 1) - private isInteger: boolean = false - public readonly key: keyof ConfigData - private width: number - private height: number - private updateSliderCallback: (slider: Slider) => void + private value: number; + private box: Rectangle; + private handle: Rectangle; + private isDragging: boolean = false; + public label: PhaserText | undefined; + public range: Vector2 = new Vector2(0, 1); + private isInteger: boolean = false; + public readonly key: keyof ConfigData; + private width: number; + private height: number; + private updateSliderCallback: (slider: Slider) => void; constructor( scene: HandScene, @@ -28,127 +28,133 @@ export class Slider extends Graphics { configData: ConfigData, key: keyof ConfigData, range: Vector2, - updateSliderCallback: (slider: Slider) => void + updateSliderCallback: (slider: Slider) => void, ) { - super(scene) - this.x = boxPosition.x - this.y = boxPosition.y - this.width = boxSize.x - this.height = boxSize.y - this.key = key - this.updateSliderCallback = updateSliderCallback - this.range = range - this.value = 0.5 // Start in the middle. - this.box = new Rectangle(0, 0, boxSize.x, boxSize.y) - this.handle = new Rectangle(0, 0, boxSize.y, boxSize.y) // Make the handle a square - - this.updateHandle() + super(scene); + this.x = boxPosition.x; + this.y = boxPosition.y; + this.width = boxSize.x; + this.height = boxSize.y; + this.key = key; + this.updateSliderCallback = updateSliderCallback; + this.range = range; + this.value = 0.5; // Start in the middle. + this.box = new Rectangle(0, 0, boxSize.x, boxSize.y); + this.handle = new Rectangle(0, 0, boxSize.y, boxSize.y); // Make the handle a square + + this.updateHandle(); // Add the labels if (labelOffset != undefined) { - this.label = this.scene.add.text(this.x + labelOffset.x, this.y + labelOffset.y, undefinedText, { - color: '#ffffff', - fontSize: '24px' - }) + this.label = this.scene.add.text( + this.x + labelOffset.x, + this.y + labelOffset.y, + undefinedText, + { + color: '#ffffff', + fontSize: '24px', + }, + ); } - this.setInteractive(this.box, Rectangle.Contains) + this.setInteractive(this.box, Rectangle.Contains); - this.on('pointerdown', this.startDrag, this) - this.scene.input.on('pointerup', this.stopDrag, this) - this.scene.input.on('pointermove', this.doDrag, this) + this.on('pointerdown', this.startDrag, this); + this.scene.input.on('pointerup', this.stopDrag, this); + this.scene.input.on('pointermove', this.doDrag, this); - assert(typeof configData[this.key] == 'number') - this.setValue(configData[this.key] as number) + assert(typeof configData[this.key] == 'number'); + this.setValue(configData[this.key] as number); - this.draw() + this.draw(); } public reset(configData: ConfigData) { - assert(typeof configData[this.key] == 'number') - this.setValue(configData[this.key] as number) - this.draw() + assert(typeof configData[this.key] == 'number'); + this.setValue(configData[this.key] as number); + this.draw(); } private startDrag() { - this.isDragging = true + this.isDragging = true; } private doDrag(pointer: Pointer) { if (!this.isDragging) { - return + return; } this.value = Phaser.Math.Clamp( - (pointer.x - this.x - this.handle.width / 2) / (this.width - this.handle.width), + (pointer.x - this.x - this.handle.width / 2) / + (this.width - this.handle.width), 0, - 1 - ) - this.updateHandle() - this.draw() + 1, + ); + this.updateHandle(); + this.draw(); } private stopDrag() { - this.isDragging = false + this.isDragging = false; } private updateHandle() { - this.handle.x = this.value * (this.width - this.handle.width) + this.handle.x = this.value * (this.width - this.handle.width); } public draw() { - this.clear() + this.clear(); if (this.input != undefined) { if (!this.input.enabled) { - this.setInteractive(this.box, Rectangle.Contains) + this.setInteractive(this.box, Rectangle.Contains); } - this.updateSliderCallback(this) + this.updateSliderCallback(this); } - this.fillStyle(0xaaaaaa) - this.fillRectShape(this.box) - this.fillStyle(0xffffff) - this.fillRectShape(this.handle) + this.fillStyle(0xaaaaaa); + this.fillRectShape(this.box); + this.fillStyle(0xffffff); + this.fillRectShape(this.handle); } public getStringValue() { - const v = this.getValue() + const v = this.getValue(); if (this.isInteger) { - return v.toFixed(0) + return v.toFixed(0); } - return v.toFixed(2) + return v.toFixed(2); } public getValue() { - const value = this.value * (this.range.y - this.range.x) + this.range.x + const value = this.value * (this.range.y - this.range.x) + this.range.x; if (this.isInteger) { - return RoundTo(value, 0) + return RoundTo(value, 0); } - return value + return value; } public setIsInteger(isInteger: boolean): Slider { - this.isInteger = isInteger - this.draw() - return this + this.isInteger = isInteger; + this.draw(); + return this; } public setValue(value: number) { - this.value = (value - this.range.x) / (this.range.y - this.range.x) - this.updateHandle() - this.draw() + this.value = (value - this.range.x) / (this.range.y - this.range.x); + this.updateHandle(); + this.draw(); } public hide(newLabelText?: string): Slider { - this.clear() + this.clear(); if (this.input != undefined) { - this.disableInteractive() + this.disableInteractive(); } if (this.label != undefined && this.label != null && this.label.active) { if (newLabelText != undefined) { - this.label.setText(newLabelText) + this.label.setText(newLabelText); } else { - this.label.setText('') + this.label.setText(''); } } - return this + return this; } } diff --git a/src/ts/ui/toggleButton.ts b/src/ts/ui/toggleButton.ts index 326aaac..b327f9f 100644 --- a/src/ts/ui/toggleButton.ts +++ b/src/ts/ui/toggleButton.ts @@ -1,14 +1,14 @@ -import { difficultyButtonChosenTint } from '../core/config' -import { Vector2 } from '../core/phaserTypes' -import HandScene from '../scenes/handScene' -import { Button } from '../ui/button' +import { difficultyButtonChosenTint } from '../core/config'; +import { Vector2 } from '../core/phaserTypes'; +import HandScene from '../scenes/handScene'; +import { Button } from '../ui/button'; export class ToggleButton extends Button { - protected toggleState: boolean + protected toggleState: boolean; - private onSpriteKey: string - private offSpriteKey: string - private toggleCallback?: (newToggleState: boolean) => void + private onSpriteKey: string; + private offSpriteKey: string; + private toggleCallback?: (newToggleState: boolean) => void; constructor( scene: HandScene, @@ -18,46 +18,46 @@ export class ToggleButton extends Button { offSpriteKey: string, soundKey: string, interactable: boolean, - initialState: boolean = true + initialState: boolean = true, ) { - super(scene, position, scale, onSpriteKey, soundKey, interactable) - this.onSpriteKey = onSpriteKey - this.offSpriteKey = offSpriteKey - this.toggleState = initialState + super(scene, position, scale, onSpriteKey, soundKey, interactable); + this.onSpriteKey = onSpriteKey; + this.offSpriteKey = offSpriteKey; + this.toggleState = initialState; - this.setTexture(this.getSpriteKey()) + this.setTexture(this.getSpriteKey()); } public setToggleState(newToggleState: boolean) { - this.toggleState = newToggleState - this.setTexture(this.getSpriteKey()) - this.toggleCallback?.(this.toggleState) + this.toggleState = newToggleState; + this.setTexture(this.getSpriteKey()); + this.toggleCallback?.(this.toggleState); } public addToggleCallback(toggleCallback: (newToggleState: boolean) => void) { - this.toggleCallback = toggleCallback - this.toggleCallback(this.toggleState) + this.toggleCallback = toggleCallback; + this.toggleCallback(this.toggleState); } public toggle() { - this.toggleState = !this.toggleState - this.setTexture(this.getSpriteKey()) - this.toggleCallback?.(this.toggleState) + this.toggleState = !this.toggleState; + this.setTexture(this.getSpriteKey()); + this.toggleCallback?.(this.toggleState); } public getToggleState(): boolean { - return this.toggleState + return this.toggleState; } public updateTint() { if (this.toggleState) { - this.setTintFill(difficultyButtonChosenTint) + this.setTintFill(difficultyButtonChosenTint); } else { - this.clearTint() + this.clearTint(); } } private getSpriteKey() { - return this.toggleState ? this.onSpriteKey : this.offSpriteKey + return this.toggleState ? this.onSpriteKey : this.offSpriteKey; } } diff --git a/src/ts/util/csvFormat.ts b/src/ts/util/csvFormat.ts index 9e9006b..3850a8d 100644 --- a/src/ts/util/csvFormat.ts +++ b/src/ts/util/csvFormat.ts @@ -1,4 +1,4 @@ -import { LevelStats } from '../core/interfaces' +import { LevelStats } from '../core/interfaces'; /** * Function to convert a levelStats object to a csv string, it's best to keep this in the same file as the interfaces. @@ -6,22 +6,23 @@ import { LevelStats } from '../core/interfaces' * @return the data in a CSV format string. */ export function levelStatsToCSV(levelStats: LevelStats): string { - let csvContent = 'layerID,noteID,loopNumber,playerTime,correctTime,classification\n' // column headers + let csvContent = + 'layerID,noteID,loopNumber,playerTime,correctTime,classification\n'; // column headers for (const layerStats of levelStats.layersStats) { - const layerID = levelStats.layersStats.indexOf(layerStats) + const layerID = levelStats.layersStats.indexOf(layerStats); for (const hitInfo of layerStats.hits) { const row = `${layerID},${hitInfo.noteID},${hitInfo.loopNumber},${millisecondsToSeconds( - hitInfo.playerTime - )},${millisecondsToSeconds(hitInfo.correctTime)},${hitInfo.classification}\n` - csvContent += row + hitInfo.playerTime, + )},${millisecondsToSeconds(hitInfo.correctTime)},${hitInfo.classification}\n`; + csvContent += row; } } - return csvContent + return csvContent; } function millisecondsToSeconds(time: number, roundTo: number = 3): number { - return parseFloat((time / 1000).toFixed(roundTo)) + return parseFloat((time / 1000).toFixed(roundTo)); } //todo this is the old function. diff --git a/src/ts/util/drawUtil.ts b/src/ts/util/drawUtil.ts index 0b9c0e4..81c8af7 100644 --- a/src/ts/util/drawUtil.ts +++ b/src/ts/util/drawUtil.ts @@ -1,32 +1,42 @@ -import { handConnections } from '../objects/handConnections' -import { HandIndex } from '../objects/handLandmarks' -import handTracker from '../objects/handTracker' -import { LandmarkConnectionOptions, LandmarkOptions } from '../core/interfaces' -import { landmarkConnectionDepth, landmarkDepth } from '../core/config' -import { Graphics, Vector2 } from '../core/phaserTypes' +import { handConnections } from '../objects/handConnections'; +import { HandIndex } from '../objects/handLandmarks'; +import handTracker from '../objects/handTracker'; +import { LandmarkConnectionOptions, LandmarkOptions } from '../core/interfaces'; +import { landmarkConnectionDepth, landmarkDepth } from '../core/config'; +import { Graphics, Vector2 } from '../core/phaserTypes'; -export function drawHandLandmarks(graphics: Graphics, options: LandmarkOptions, handIndex: HandIndex = 0) { - graphics.setDepth(landmarkDepth) +export function drawHandLandmarks( + graphics: Graphics, + options: LandmarkOptions, + handIndex: HandIndex = 0, +) { + graphics.setDepth(landmarkDepth); handTracker.forEachLandmarkPosition((position: Vector2) => { - graphics.lineStyle(options.lineWidth, options.color, options.alpha) - graphics.strokeCircle(position.x, position.y, options.radius) - }, handIndex) + graphics.lineStyle(options.lineWidth, options.color, options.alpha); + graphics.strokeCircle(position.x, position.y, options.radius); + }, handIndex); } export function drawHandLandmarkConnections( graphics: Graphics, options: LandmarkConnectionOptions, - handIndex: HandIndex = 0 + handIndex: HandIndex = 0, ) { - graphics.setDepth(landmarkConnectionDepth) + graphics.setDepth(landmarkConnectionDepth); handConnections.forEach(connection => { - const pos1: Vector2 | undefined = handTracker.getLandmarkPosition(connection[0], handIndex) + const pos1: Vector2 | undefined = handTracker.getLandmarkPosition( + connection[0], + handIndex, + ); if (pos1 !== undefined) { - const pos2: Vector2 | undefined = handTracker.getLandmarkPosition(connection[1], handIndex) + const pos2: Vector2 | undefined = handTracker.getLandmarkPosition( + connection[1], + handIndex, + ); if (pos2 !== undefined) { - graphics.lineStyle(options.lineWidth, options.color, options.alpha) - graphics.lineBetween(pos1.x, pos1.y, pos2.x, pos2.y) + graphics.lineStyle(options.lineWidth, options.color, options.alpha); + graphics.lineBetween(pos1.x, pos1.y, pos2.x, pos2.y); } } - }) + }); } diff --git a/src/ts/util/interaction.ts b/src/ts/util/interaction.ts index c5f93ff..24a12e1 100644 --- a/src/ts/util/interaction.ts +++ b/src/ts/util/interaction.ts @@ -1,11 +1,14 @@ -import { MatterSprite } from '../core/phaserTypes' +import { MatterSprite } from '../core/phaserTypes'; -export default function setInteraction(object: MatterSprite, interactWithWorld: boolean) { - if (object == undefined) return - object.setVisible(interactWithWorld) +export default function setInteraction( + object: MatterSprite, + interactWithWorld: boolean, +) { + if (object == undefined) return; + object.setVisible(interactWithWorld); if (interactWithWorld) { - object.setInteractive() + object.setInteractive(); } else { - object.disableInteractive() + object.disableInteractive(); } } diff --git a/webpack/webpack.dev.js b/webpack/webpack.dev.js index fc7ed14..ca6c6de 100644 --- a/webpack/webpack.dev.js +++ b/webpack/webpack.dev.js @@ -1,13 +1,13 @@ -const { merge } = require('webpack-merge') -const common = require('./webpack.common') +const { merge } = require('webpack-merge'); +const common = require('./webpack.common'); const dev = { mode: 'development', stats: 'errors-warnings', devtool: 'eval', devServer: { - open: true - } -} + open: true, + }, +}; -module.exports = merge(common, dev) +module.exports = merge(common, dev); diff --git a/webpack/webpack.prod.js b/webpack/webpack.prod.js index 2c3dae4..324416f 100644 --- a/webpack/webpack.prod.js +++ b/webpack/webpack.prod.js @@ -1,7 +1,7 @@ -const path = require('path') -const { merge } = require('webpack-merge') -const common = require('./webpack.common') -const { InjectManifest } = require('workbox-webpack-plugin') +const path = require('path'); +const { merge } = require('webpack-merge'); +const common = require('./webpack.common'); +const { InjectManifest } = require('workbox-webpack-plugin'); // const WebpackObfuscator = require('webpack-obfuscator') const prod = { @@ -9,16 +9,16 @@ const prod = { stats: 'errors-warnings', output: { filename: '[name].[contenthash].bundle.js', - chunkFilename: '[name].[contenthash].chunk.js' + chunkFilename: '[name].[contenthash].chunk.js', }, optimization: { splitChunks: { cacheGroups: { commons: { - filename: '[name].[contenthash].bundle.js' - } - } - } + filename: '[name].[contenthash].bundle.js', + }, + }, + }, }, plugins: [ // disabled by default (uncomment to active) @@ -32,9 +32,9 @@ const prod = { // ), new InjectManifest({ swSrc: path.resolve(__dirname, '../pwa/sw.js'), - swDest: 'sw.js' - }) - ] -} + swDest: 'sw.js', + }), + ], +}; -module.exports = merge(common, prod) +module.exports = merge(common, prod);