From d9a88aaee040d1c9bdd1e8cf9ec3a21b566aa5c9 Mon Sep 17 00:00:00 2001 From: Lars Berning <151194+laberning@users.noreply.github.com> Date: Mon, 7 Mar 2022 19:40:10 +0100 Subject: [PATCH] adds option to select game duration for stroke fighter --- app-webclient/arcade/RowingGames.js | 3 + .../arcade/StrokeFighterBattleScene.js | 15 +++-- app-webclient/arcade/StrokeFighterEndScene.js | 50 ++++---------- .../arcade/StrokeFighterStartScene.js | 66 +++++++++++++++++-- app-webclient/arcade/arcadeHelper.js | 44 +++++++++++++ app/server.js | 4 +- 6 files changed, 133 insertions(+), 49 deletions(-) create mode 100644 app-webclient/arcade/arcadeHelper.js diff --git a/app-webclient/arcade/RowingGames.js b/app-webclient/arcade/RowingGames.js index 4394208..121b3c0 100644 --- a/app-webclient/arcade/RowingGames.js +++ b/app-webclient/arcade/RowingGames.js @@ -44,6 +44,7 @@ export function createRowingGames (rootComponent, clientWidth, clientHeight) { k.scene('strokeFighterBattle', (args) => { activeScene = StrokeFighterBattleScene(k, args) }) k.scene('strokeFighterStart', (args) => { activeScene = StrokeFighterStartScene(k, args) }) k.scene('strokeFighterEnd', (args) => { activeScene = StrokeFighterEndScene(k, args) }) + k.scene('disposed', () => { activeScene = undefined }) k.go('strokeFighterStart') @@ -59,6 +60,8 @@ export function createRowingGames (rootComponent, clientWidth, clientHeight) { * clean up the game resources */ function dispose () { + // k.quit does not seem to end the game 'yet', so we move to an empty scene to stop all events... + k.go('disposed') k.quit() } diff --git a/app-webclient/arcade/StrokeFighterBattleScene.js b/app-webclient/arcade/StrokeFighterBattleScene.js index 1ce91fa..89a8e27 100644 --- a/app-webclient/arcade/StrokeFighterBattleScene.js +++ b/app-webclient/arcade/StrokeFighterBattleScene.js @@ -14,8 +14,6 @@ import addSpaceBackground from './SpaceBackground.js' export default function StrokeFighterBattleScene (k, args) { // how much stroke power is needed to fire high power lasers const THRESHOLD_POWER = 180 - // training duration in seconds - const TARGET_TIME = 10 * 60 // strokes per minute at start of training const SPM_START = 18 // strokes per minute at end of training @@ -39,6 +37,8 @@ export default function StrokeFighterBattleScene (k, args) { ] let trainingTime = args?.trainingTime || 0 + // training duration in seconds + const targetTime = args?.targetTime || 10 * 60 let playerLifes = args?.gameState === 'LOST' ? 0 : args?.playerLifes ? args?.playerLifes : PLAYER_LIFES const ui = k.add([ @@ -131,6 +131,7 @@ export default function StrokeFighterBattleScene (k, args) { if (args?.gameState === 'WON') { k.go('strokeFighterEnd', { trainingTime, + targetTime, gameState: 'WON', overtimePossible: false }) @@ -138,6 +139,7 @@ export default function StrokeFighterBattleScene (k, args) { } else { k.go('strokeFighterEnd', { trainingTime, + targetTime, gameState: 'LOST', overtimePossible: true }) @@ -269,13 +271,15 @@ export default function StrokeFighterBattleScene (k, args) { trainingTime += k.dt() const newTrainingTimeRounded = Math.round(trainingTime) if (trainingTimeRounded !== newTrainingTimeRounded) { - timer.text = `${secondsToTimeString(newTrainingTimeRounded)} / ${k.debug.fps()}fps` + const time = newTrainingTimeRounded >= targetTime ? newTrainingTimeRounded : targetTime - newTrainingTimeRounded + timer.text = `${secondsToTimeString(time)} / ${k.debug.fps()}fps` trainingTimeRounded = newTrainingTimeRounded - if (trainingTimeRounded >= TARGET_TIME) { + if (newTrainingTimeRounded >= targetTime) { // if we already lost the game before, go back to loose message without possibility for overtime if (args?.gameState === 'LOST') { k.go('strokeFighterEnd', { trainingTime, + targetTime, playerLifes, gameState: 'LOST', overtimePossible: false @@ -285,6 +289,7 @@ export default function StrokeFighterBattleScene (k, args) { if (!(args?.gameState)) { k.go('strokeFighterEnd', { trainingTime, + targetTime, playerLifes, gameState: 'WON', overtimePossible: true @@ -355,7 +360,7 @@ export default function StrokeFighterBattleScene (k, args) { } function scheduleNextEnemy () { - const percentTrainingFinished = trainingTime / TARGET_TIME + const percentTrainingFinished = trainingTime / targetTime // linearly increase the SPM over time let currentSPM = SPM_START + (SPM_END - SPM_START) * percentTrainingFinished let maxEnemyHealth = 1 diff --git a/app-webclient/arcade/StrokeFighterEndScene.js b/app-webclient/arcade/StrokeFighterEndScene.js index 0253c81..d579c38 100644 --- a/app-webclient/arcade/StrokeFighterEndScene.js +++ b/app-webclient/arcade/StrokeFighterEndScene.js @@ -6,9 +6,10 @@ */ import addSpaceBackground from './SpaceBackground.js' +import { addButton } from './arcadeHelper.js' /** - * Creates the main scene of Storke Fighter + * Creates the game over screen scene of Storke Fighter * @param {import('kaboom').KaboomCtx} k Kaboom Context * @param {Object} args the game state */ @@ -40,23 +41,15 @@ export default function StrokeFighterEndScene (k, args) { k.origin('center') ]) - const restartButton = k.add([ - k.rect(300, 60), - k.area(), - k.pos(k.width() / 2, 440), - k.scale(1), - k.origin('center'), - k.outline(2, k.rgb(255, 255, 255)), - k.color(54, 80, 128) - ]) - const restartText = k.add([ - k.text('Restart', { size: 40 }), - k.area({ cursor: 'pointer' }), - k.pos(k.width() / 2, 440), - k.scale(1), - k.origin('center'), - k.color(255, 255, 255) - ]) + addButton({ + k, + pos: k.vec2(k.width() / 2, 440), + width: 300, + height: 60, + text: 'Restart', + textOptions: { size: 40 }, + onClick: () => { k.go('strokeFighterStart') } + }) if (args?.overtimePossible) { if (args?.gameState === 'LOST') { @@ -73,27 +66,6 @@ export default function StrokeFighterEndScene (k, args) { ]) } } - restartButton.onClick(() => { - k.go('strokeFighterStart') - }) - restartButton.onUpdate(() => { - if (restartButton.isHovering()) { - k.cursor('pointer') - restartButton.scale = k.vec2(1.2) - restartText.scale = k.vec2(1.2) - const t = k.time() * 10 - restartButton.color = k.rgb( - k.wave(0, 255, t), - k.wave(0, 255, t + 2), - k.wave(0, 255, t + 4) - ) - } else { - k.cursor('default') - restartButton.scale = k.vec2(1) - restartText.scale = k.vec2(1) - restartButton.color = k.rgb(54, 80, 128) - } - }) let motionDetectionEnabled = false if (args?.overtimePossible) { diff --git a/app-webclient/arcade/StrokeFighterStartScene.js b/app-webclient/arcade/StrokeFighterStartScene.js index bc7eba8..bffb9fc 100644 --- a/app-webclient/arcade/StrokeFighterStartScene.js +++ b/app-webclient/arcade/StrokeFighterStartScene.js @@ -6,9 +6,10 @@ */ import addSpaceBackground from './SpaceBackground.js' +import { addButton } from './arcadeHelper.js' /** - * Creates the main scene of Stroke Fighter + * Creates the start screen scene of Stroke Fighter * @param {import('kaboom').KaboomCtx} k Kaboom Context */ export default function StrokeFighterStartScene (k, args) { @@ -20,7 +21,64 @@ export default function StrokeFighterStartScene (k, args) { k.origin('center') ]) - const shipsPos = k.vec2(520, 240) + const GAME_DURATION_OPTIONS = [3, 5, 10, 15, 20, 30] + let selectedGameDuration = 1 + + const selectorPos = k.vec2(150, 140) + + k.add([ + k.text('time:', { size: 28 }), + k.pos(selectorPos), + k.origin('center') + ]) + + addButton({ + k, + pos: selectorPos.add(100, 0), + width: 60, + height: 48, + text: '-', + textOptions: { size: 28 }, + onClick: () => { + selectedGameDuration = Math.max(selectedGameDuration - 1, 0) + durationSelectorText.text = displayTime(selectedGameDuration) + } + }) + + const durationSelectorText = k.add([ + k.text(displayTime(selectedGameDuration), { size: 28 }), + k.pos(selectorPos.add(168, 0)), + k.origin('center') + ]) + + addButton({ + k, + pos: selectorPos.add(230, 0), + width: 60, + height: 48, + text: '+', + textOptions: { size: 28 }, + onClick: () => { + selectedGameDuration = Math.min(selectedGameDuration + 1, GAME_DURATION_OPTIONS.length - 1) + durationSelectorText.text = displayTime(selectedGameDuration) + } + }) + + k.add([ + k.text('minutes', { size: 28 }), + k.pos(selectorPos.add(280, 0)), + k.origin('left') + ]) + + function displayTime (option) { + let time = GAME_DURATION_OPTIONS[option].toString() + if (time.length === 1) { + time = '0' + time + } + return time + } + + const shipsPos = k.vec2(550, 280) const ship1 = k.add([ k.sprite('playerShip'), k.scale(0.5), @@ -48,7 +106,7 @@ export default function StrokeFighterStartScene (k, args) { addBullet(ship3.pos.sub(20, 40)) addBullet(ship3.pos.sub(-20, 40)) - const explainPos = k.vec2(60, 240) + const explainPos = k.vec2(90, 280) k.add([ k.text('light stroke = ', { size: 28 }), k.pos(explainPos), @@ -99,7 +157,7 @@ export default function StrokeFighterStartScene (k, args) { } function driveFinished (metrics) { - k.go('strokeFighterBattle') + k.go('strokeFighterBattle', { targetTime: GAME_DURATION_OPTIONS[selectedGameDuration] * 60 }) } return { diff --git a/app-webclient/arcade/arcadeHelper.js b/app-webclient/arcade/arcadeHelper.js new file mode 100644 index 0000000..9f3997a --- /dev/null +++ b/app-webclient/arcade/arcadeHelper.js @@ -0,0 +1,44 @@ +'use strict' +/* + Open Rowing Monitor, https://github.com/laberning/openrowingmonitor + + Implements some common helpers for the games +*/ +export function addButton ({ k, node = k, pos, width, height, text, textOptions, onClick }) { + const button = node.add([ + k.rect(width, height), + k.area(), + k.pos(pos), + k.origin('center'), + k.outline(2, k.rgb(0, 0, 0)), + k.color(54, 80, 128) + ]) + node.add([ + k.text(text, textOptions), + k.area({ cursor: 'pointer' }), + k.pos(button.pos.add(2, 1)), + k.origin('center'), + k.color(255, 255, 255) + ]) + + button.onClick(() => { + onClick() + }) + + button.onUpdate(() => { + if (button.isHovering()) { + k.cursor('pointer') + const t = k.time() * 10 + button.color = k.rgb( + k.wave(0, 255, t), + k.wave(0, 255, t + 2), + k.wave(0, 255, t + 4) + ) + } else { + // todo: resetting the cursor here will not work as expected if we have multiple buttons on the page + // seems like kaboom does not yet have an elegant way of how to do this... + k.cursor('default') + button.color = k.rgb(54, 80, 128) + } + }) +} diff --git a/app/server.js b/app/server.js index 9a8d95d..32ce762 100644 --- a/app/server.js +++ b/app/server.js @@ -186,8 +186,10 @@ function getConfig () { } } +/* replayRowingSession(handleRotationImpulse, { - filename: 'data/recordings/2022-02-18_12-51-00_raw.csv', + filename: 'recordings/WRX700_2magnets.csv', realtime: true, loop: true }) +*/