adds option to select game duration for stroke fighter
This commit is contained in:
parent
be545e445b
commit
d9a88aaee0
|
|
@ -44,6 +44,7 @@ export function createRowingGames (rootComponent, clientWidth, clientHeight) {
|
||||||
k.scene('strokeFighterBattle', (args) => { activeScene = StrokeFighterBattleScene(k, args) })
|
k.scene('strokeFighterBattle', (args) => { activeScene = StrokeFighterBattleScene(k, args) })
|
||||||
k.scene('strokeFighterStart', (args) => { activeScene = StrokeFighterStartScene(k, args) })
|
k.scene('strokeFighterStart', (args) => { activeScene = StrokeFighterStartScene(k, args) })
|
||||||
k.scene('strokeFighterEnd', (args) => { activeScene = StrokeFighterEndScene(k, args) })
|
k.scene('strokeFighterEnd', (args) => { activeScene = StrokeFighterEndScene(k, args) })
|
||||||
|
k.scene('disposed', () => { activeScene = undefined })
|
||||||
|
|
||||||
k.go('strokeFighterStart')
|
k.go('strokeFighterStart')
|
||||||
|
|
||||||
|
|
@ -59,6 +60,8 @@ export function createRowingGames (rootComponent, clientWidth, clientHeight) {
|
||||||
* clean up the game resources
|
* clean up the game resources
|
||||||
*/
|
*/
|
||||||
function dispose () {
|
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()
|
k.quit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,6 @@ import addSpaceBackground from './SpaceBackground.js'
|
||||||
export default function StrokeFighterBattleScene (k, args) {
|
export default function StrokeFighterBattleScene (k, args) {
|
||||||
// how much stroke power is needed to fire high power lasers
|
// how much stroke power is needed to fire high power lasers
|
||||||
const THRESHOLD_POWER = 180
|
const THRESHOLD_POWER = 180
|
||||||
// training duration in seconds
|
|
||||||
const TARGET_TIME = 10 * 60
|
|
||||||
// strokes per minute at start of training
|
// strokes per minute at start of training
|
||||||
const SPM_START = 18
|
const SPM_START = 18
|
||||||
// strokes per minute at end of training
|
// strokes per minute at end of training
|
||||||
|
|
@ -39,6 +37,8 @@ export default function StrokeFighterBattleScene (k, args) {
|
||||||
]
|
]
|
||||||
|
|
||||||
let trainingTime = args?.trainingTime || 0
|
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
|
let playerLifes = args?.gameState === 'LOST' ? 0 : args?.playerLifes ? args?.playerLifes : PLAYER_LIFES
|
||||||
|
|
||||||
const ui = k.add([
|
const ui = k.add([
|
||||||
|
|
@ -131,6 +131,7 @@ export default function StrokeFighterBattleScene (k, args) {
|
||||||
if (args?.gameState === 'WON') {
|
if (args?.gameState === 'WON') {
|
||||||
k.go('strokeFighterEnd', {
|
k.go('strokeFighterEnd', {
|
||||||
trainingTime,
|
trainingTime,
|
||||||
|
targetTime,
|
||||||
gameState: 'WON',
|
gameState: 'WON',
|
||||||
overtimePossible: false
|
overtimePossible: false
|
||||||
})
|
})
|
||||||
|
|
@ -138,6 +139,7 @@ export default function StrokeFighterBattleScene (k, args) {
|
||||||
} else {
|
} else {
|
||||||
k.go('strokeFighterEnd', {
|
k.go('strokeFighterEnd', {
|
||||||
trainingTime,
|
trainingTime,
|
||||||
|
targetTime,
|
||||||
gameState: 'LOST',
|
gameState: 'LOST',
|
||||||
overtimePossible: true
|
overtimePossible: true
|
||||||
})
|
})
|
||||||
|
|
@ -269,13 +271,15 @@ export default function StrokeFighterBattleScene (k, args) {
|
||||||
trainingTime += k.dt()
|
trainingTime += k.dt()
|
||||||
const newTrainingTimeRounded = Math.round(trainingTime)
|
const newTrainingTimeRounded = Math.round(trainingTime)
|
||||||
if (trainingTimeRounded !== newTrainingTimeRounded) {
|
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
|
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 we already lost the game before, go back to loose message without possibility for overtime
|
||||||
if (args?.gameState === 'LOST') {
|
if (args?.gameState === 'LOST') {
|
||||||
k.go('strokeFighterEnd', {
|
k.go('strokeFighterEnd', {
|
||||||
trainingTime,
|
trainingTime,
|
||||||
|
targetTime,
|
||||||
playerLifes,
|
playerLifes,
|
||||||
gameState: 'LOST',
|
gameState: 'LOST',
|
||||||
overtimePossible: false
|
overtimePossible: false
|
||||||
|
|
@ -285,6 +289,7 @@ export default function StrokeFighterBattleScene (k, args) {
|
||||||
if (!(args?.gameState)) {
|
if (!(args?.gameState)) {
|
||||||
k.go('strokeFighterEnd', {
|
k.go('strokeFighterEnd', {
|
||||||
trainingTime,
|
trainingTime,
|
||||||
|
targetTime,
|
||||||
playerLifes,
|
playerLifes,
|
||||||
gameState: 'WON',
|
gameState: 'WON',
|
||||||
overtimePossible: true
|
overtimePossible: true
|
||||||
|
|
@ -355,7 +360,7 @@ export default function StrokeFighterBattleScene (k, args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function scheduleNextEnemy () {
|
function scheduleNextEnemy () {
|
||||||
const percentTrainingFinished = trainingTime / TARGET_TIME
|
const percentTrainingFinished = trainingTime / targetTime
|
||||||
// linearly increase the SPM over time
|
// linearly increase the SPM over time
|
||||||
let currentSPM = SPM_START + (SPM_END - SPM_START) * percentTrainingFinished
|
let currentSPM = SPM_START + (SPM_END - SPM_START) * percentTrainingFinished
|
||||||
let maxEnemyHealth = 1
|
let maxEnemyHealth = 1
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import addSpaceBackground from './SpaceBackground.js'
|
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 {import('kaboom').KaboomCtx} k Kaboom Context
|
||||||
* @param {Object} args the game state
|
* @param {Object} args the game state
|
||||||
*/
|
*/
|
||||||
|
|
@ -40,23 +41,15 @@ export default function StrokeFighterEndScene (k, args) {
|
||||||
k.origin('center')
|
k.origin('center')
|
||||||
])
|
])
|
||||||
|
|
||||||
const restartButton = k.add([
|
addButton({
|
||||||
k.rect(300, 60),
|
k,
|
||||||
k.area(),
|
pos: k.vec2(k.width() / 2, 440),
|
||||||
k.pos(k.width() / 2, 440),
|
width: 300,
|
||||||
k.scale(1),
|
height: 60,
|
||||||
k.origin('center'),
|
text: 'Restart',
|
||||||
k.outline(2, k.rgb(255, 255, 255)),
|
textOptions: { size: 40 },
|
||||||
k.color(54, 80, 128)
|
onClick: () => { k.go('strokeFighterStart') }
|
||||||
])
|
})
|
||||||
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)
|
|
||||||
])
|
|
||||||
|
|
||||||
if (args?.overtimePossible) {
|
if (args?.overtimePossible) {
|
||||||
if (args?.gameState === 'LOST') {
|
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
|
let motionDetectionEnabled = false
|
||||||
if (args?.overtimePossible) {
|
if (args?.overtimePossible) {
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import addSpaceBackground from './SpaceBackground.js'
|
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
|
* @param {import('kaboom').KaboomCtx} k Kaboom Context
|
||||||
*/
|
*/
|
||||||
export default function StrokeFighterStartScene (k, args) {
|
export default function StrokeFighterStartScene (k, args) {
|
||||||
|
|
@ -20,7 +21,64 @@ export default function StrokeFighterStartScene (k, args) {
|
||||||
k.origin('center')
|
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([
|
const ship1 = k.add([
|
||||||
k.sprite('playerShip'),
|
k.sprite('playerShip'),
|
||||||
k.scale(0.5),
|
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))
|
||||||
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.add([
|
||||||
k.text('light stroke = ', { size: 28 }),
|
k.text('light stroke = ', { size: 28 }),
|
||||||
k.pos(explainPos),
|
k.pos(explainPos),
|
||||||
|
|
@ -99,7 +157,7 @@ export default function StrokeFighterStartScene (k, args) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function driveFinished (metrics) {
|
function driveFinished (metrics) {
|
||||||
k.go('strokeFighterBattle')
|
k.go('strokeFighterBattle', { targetTime: GAME_DURATION_OPTIONS[selectedGameDuration] * 60 })
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -186,8 +186,10 @@ function getConfig () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
replayRowingSession(handleRotationImpulse, {
|
replayRowingSession(handleRotationImpulse, {
|
||||||
filename: 'data/recordings/2022-02-18_12-51-00_raw.csv',
|
filename: 'recordings/WRX700_2magnets.csv',
|
||||||
realtime: true,
|
realtime: true,
|
||||||
loop: true
|
loop: true
|
||||||
})
|
})
|
||||||
|
*/
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue