implements stroke fighter insane mode

This commit is contained in:
Lars Berning 2022-02-21 20:22:19 +01:00
parent 434c67e46b
commit 156591fb51
No known key found for this signature in database
GPG Key ID: 028E73C9E1D8A0B3
5 changed files with 66 additions and 28 deletions

View File

@ -28,7 +28,7 @@ export function createRowingGames (rootComponent, clientWidth, clientHeight) {
font: 'sinko'
})
// todo: once there are multiple games, asset loadingshould be moved to the individual games
// todo: once there are multiple games, asset loading should be moved to the individual games
const assets = '/assets'
const sprites = ['enemyBlack1', 'enemyBlue2', 'enemyGreen3', 'enemyRed4', 'enemyRed5', 'playerShip2_orange',
'playerLife2_orange', 'spaceShips_004', 'spaceShips_006', 'spaceShips_007', 'spaceShips_009', 'star1', 'star2',
@ -56,8 +56,9 @@ export function createRowingGames (rootComponent, clientWidth, clientHeight) {
}
}
// todo: currently we move to an empty scene to dispose the game as there does not seem to be
// a mechanism in kaboom to dispose the instance.
/**
* clean up the game resources
*/
function dispose () {
k.quit()
}

View File

@ -8,7 +8,7 @@
import addSpaceBackground from './SpaceBackground.js'
/**
* Creates the main scene of Storke Fighter
* Creates the main scene of Stroke Fighter
* @param {import('kaboom').KaboomCtx} k Kaboom Context
*/
export default function StrokeFighterBattleScene (k, args) {
@ -21,7 +21,7 @@ export default function StrokeFighterBattleScene (k, args) {
// strokes per minute at end of training
const SPM_END = 28
const BULLET_SPEED = 1200
const ENEMY_SPEED = 60
const ENEMY_SPEED = 50
const PLAYER_SPEED = 480
const PLAYER_LIFES = 3
const SPRITE_WIDTH = 90
@ -72,13 +72,10 @@ export default function StrokeFighterBattleScene (k, args) {
k.area(),
k.opacity(0.4),
k.pos(player.pos),
k.follow(player),
k.origin('center')
])
shield.onUpdate(() => {
shield.pos = player.pos
})
shield.onCollide('enemy', (enemy) => {
k.destroy(enemy)
k.shake(4)
@ -331,8 +328,10 @@ export default function StrokeFighterBattleScene (k, args) {
function scheduleNextEnemy () {
const percentTrainingFinished = trainingTime / TARGET_TIME
const currentSPM = SPM_START + (SPM_END - SPM_START) * percentTrainingFinished
// linearly increase the SPM over time
let currentSPM = SPM_START + (SPM_END - SPM_START) * percentTrainingFinished
let maxEnemyHealth = 1
let minEnemyHealth = 1
if (percentTrainingFinished < 0.4) {
maxEnemyHealth = 1
} else if (percentTrainingFinished < 0.8) {
@ -340,7 +339,16 @@ export default function StrokeFighterBattleScene (k, args) {
} else {
maxEnemyHealth = 3
}
spawnEnemy(k.choose(ENEMIES.filter((enemy) => enemy.health <= maxEnemyHealth)))
// insane mode (keep on rowing after winning)
if (percentTrainingFinished > 1) {
// cap SPM at 20% above SPM_END (for insane mode)
currentSPM = Math.max(currentSPM, SPM_END * 1.2)
minEnemyHealth = 2
if (percentTrainingFinished > 1.3) {
minEnemyHealth = 3
}
}
spawnEnemy(k.choose(ENEMIES.filter((enemy) => enemy.health >= minEnemyHealth && enemy.health <= maxEnemyHealth)))
k.wait(60 / currentSPM, scheduleNextEnemy)
}

View File

@ -39,33 +39,61 @@ export default function StrokeFighterEndScene (k, args) {
k.pos(k.width() / 2, 320),
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.origin('center')
k.scale(1),
k.origin('center'),
k.color(255, 255, 255)
])
if (args?.overtimePossible) {
if (args?.gameState === 'LOST') {
k.add([
k.text('or keep rowing to continue your workout', { size: 18 }),
k.pos(k.width() / 2, 550),
k.pos(k.width() / 2, 650),
k.origin('center')
])
} else {
k.add([
k.text('or keep rowing for an insane challenge', { size: 18 }),
k.pos(k.width() / 2, 550),
k.pos(k.width() / 2, 650),
k.origin('center')
])
}
}
restartButton.onClick(() => {
console.log('click')
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) {

View File

@ -8,7 +8,7 @@
import addSpaceBackground from './SpaceBackground.js'
/**
* Creates the main scene of Storke Fighter
* Creates the main scene of Stroke Fighter
* @param {import('kaboom').KaboomCtx} k Kaboom Context
*/
export default function StrokeFighterStartScene (k, args) {
@ -19,13 +19,8 @@ export default function StrokeFighterStartScene (k, args) {
k.pos(k.width() / 2, 50),
k.origin('center')
])
k.add([
k.text('start rowing...', { size: 40 }),
k.pos(k.width() / 2, 110),
k.origin('center')
])
const shipsPos = k.vec2(450, 260)
const shipsPos = k.vec2(520, 240)
const ship1 = k.add([
k.sprite('playerShip2_orange'),
k.scale(0.5),
@ -53,7 +48,7 @@ export default function StrokeFighterStartScene (k, args) {
addBullet(ship3.pos.sub(20, 40))
addBullet(ship3.pos.sub(-20, 40))
const explainPos = k.vec2(40, 260)
const explainPos = k.vec2(60, 240)
k.add([
k.text('light stroke = ', { size: 28 }),
k.pos(explainPos),
@ -79,6 +74,12 @@ export default function StrokeFighterStartScene (k, args) {
])
}
k.add([
k.text('start rowing to charge lasers', { size: 28 }),
k.pos(k.width() / 2, 650),
k.origin('center')
])
let motionDetectionEnabled = false
k.wait(5, () => {
motionDetectionEnabled = true

View File

@ -119,7 +119,7 @@ export class GameComponent extends AppElement {
// depending on the resolution
// using a square screen has the advantage that it works well on portrait and landscape screens
// for now will set it to a fixed square resolution and let css take care of scaling it
const gameSize = 600
const gameSize = 700
const arcade = this.renderRoot.querySelector('#arcade')
// this.rowingGames = createRowingGames(arcade, canvas.clientWidth, canvas.clientHeight)
// @ts-ignore
@ -130,10 +130,10 @@ export class GameComponent extends AppElement {
// change notifiers available in this component), then the state changes will be processed by the
// game with a certain delay. This is pretty weird, since they are processed by this component at
// the correct time. Also when we look at timestamps in the games callback, then it seems that they
// are called completely in sync with the event and without dely.
// This problem only occures, when the update events are created from a web request (i.e. by receiving
// are called completely in sync with the event and without delay.
// This problem only occurs, when the update events are created from a web request (i.e. by receiving
// new rowing metrics via web socket).
// By delivering the app state updates directly here from index.js, this problem does not occure.
// By delivering the app state updates directly here from index.js, this problem does not occur.
this.sendEvent('setGameStateUpdater', (appState) => { this.gameAppState(appState) })
}