openrowingmonitor/app/client/arcade/StrokeFighterBattleScene.js

226 lines
4.9 KiB
JavaScript

'use strict'
/*
Open Rowing Monitor, https://github.com/laberning/openrowingmonitor
Implements the Battle Action of the Stroke Fighter Game
*/
import addSpaceBackground from './SpaceBackground.js'
/**
* Creates the main scene of Storke Fighter
* @param {import('kaboom').KaboomCtx} k Kaboom Context
*/
export default function StrokeFighterBattleScene (k) {
const BULLET_SPEED = 1200
const ENEMY_SPEED = 60
const PLAYER_SPEED = 480
const SPRITE_WIDTH = 90
const ENEMIES = [
{ sprite: 'enemyBlack1', health: 1 },
{ sprite: 'enemyBlue2', health: 1 },
{ sprite: 'enemyGreen3', health: 1 },
{ sprite: 'enemyRed4', health: 1 },
{ sprite: 'enemyRed5', health: 1 },
{ sprite: 'spaceShips_004', health: 3 },
{ sprite: 'spaceShips_006', health: 2 },
{ sprite: 'spaceShips_007', health: 3 },
{ sprite: 'spaceShips_009', health: 2 }
]
k.layers([
'background',
'game',
'ui'
], 'game')
addSpaceBackground(k)
function grow (rate) {
return {
update () {
const n = rate * k.dt()
this.scale.x += n
this.scale.y += n
}
}
}
const player = k.add([
k.sprite('playerShip2_orange'),
k.area(),
k.pos(k.width() / 2, k.height() - 64),
k.origin('center')
])
function moveLeft () {
player.move(-PLAYER_SPEED, 0)
if (player.pos.x < 0) {
player.pos.x = k.width()
}
}
function moveRight () {
player.move(PLAYER_SPEED, 0)
if (player.pos.x > k.width()) {
player.pos.x = 0
}
}
player.onCollide('enemy', (e) => {
k.destroy(e)
k.shake(4)
k.play('hit', {
detune: -1200,
volume: 0.3,
speed: k.rand(0.5, 2)
})
})
player.onUpdate(() => {
const tolerance = 10
const closestEnemy = k.get('enemy').reduce((prev, enemy) => { return enemy?.pos.y > prev?.pos.y ? enemy : prev }, { pos: { y: 0 } })
if (closestEnemy?.pos?.x) {
if (closestEnemy.pos.x > player.pos.x + tolerance) {
moveRight()
} else if (closestEnemy.pos.x < player.pos.x - tolerance) {
moveLeft()
}
}
})
function addLaserHit (p, n) {
for (let i = 0; i < n; i++) {
k.wait(k.rand(n * 0.1), () => {
for (let i = 0; i < 2; i++) {
k.add([
k.sprite('laserRed09'),
k.pos(p.sub(0, 10)),
k.scale(k.vec2(0.5)),
k.lifespan(0.1),
grow(k.rand(0.5, 2)),
k.origin('center')
])
}
})
}
}
function spawnBullet (p) {
k.add([
k.sprite('laserRed01'),
k.area(),
k.pos(p),
k.origin('center'),
k.move(k.UP, BULLET_SPEED),
k.cleanup(),
'bullet'
])
}
k.onKeyPress('space', () => { fireWeapons(2) })
/**
* fires the weapons of our spaceship
* @param {number} destructivePower the deadliness the weapon
*/
function fireWeapons (destructivePower) {
if (destructivePower <= 1) {
spawnBullet(player.pos.sub(0, 20))
} else if (destructivePower <= 2) {
spawnBullet(player.pos.sub(16, 15))
spawnBullet(player.pos.add(16, -15))
} else {
spawnBullet(player.pos.sub(0, 20))
spawnBullet(player.pos.sub(16, 15))
spawnBullet(player.pos.add(16, -15))
}
k.play('shoot', {
volume: 0.3,
detune: k.rand(-1200, 1200)
})
}
function spawnEnemy () {
const enemy = k.choose(ENEMIES)
k.add([
k.sprite(enemy.sprite),
k.area(),
k.pos(k.rand(0 + SPRITE_WIDTH / 2, k.width() - SPRITE_WIDTH / 2), 0),
k.health(enemy.health),
k.origin('bot'),
'enemy',
{ speed: k.rand(ENEMY_SPEED * 0.5, ENEMY_SPEED * 1.5) }
])
k.wait(3, spawnEnemy)
}
k.on('death', 'enemy', (e) => {
k.destroy(e)
k.every('bullet', (bullet) => { k.destroy(bullet) })
k.shake(2)
})
k.on('hurt', 'enemy', (e) => {
k.shake(1)
k.play('hit', {
detune: k.rand(-1200, 1200),
volume: 0.1,
speed: k.rand(0.2, 2)
})
})
const timer = k.add([
k.text('0'),
k.pos(12, 32),
k.fixed(),
k.layer('ui'),
{ time: 0 }
])
timer.onUpdate(() => {
timer.time += k.dt()
timer.text = timer.time.toFixed(2)
})
k.onCollide('bullet', 'enemy', (b, e) => {
k.destroy(b)
e.hurt(1)
addLaserHit(b.pos, 1)
})
k.onUpdate('enemy', (sprite) => {
sprite.move(0, sprite.speed)
if (sprite.pos.y - sprite.height > k.height()) {
k.destroy(sprite)
}
})
let lastStrokeState = 'DRIVING'
function appState (appState) {
if (appState?.metrics.strokeState === undefined) {
return
}
if (lastStrokeState === 'DRIVING' && appState.metrics.strokeState === 'RECOVERY') {
driveFinished(appState.metrics)
}
lastStrokeState = appState.metrics.strokeState
}
function driveFinished (metrics) {
if (metrics.power < 120) {
fireWeapons(1)
} else if (metrics.power < 180) {
fireWeapons(2)
} else {
fireWeapons(3)
}
}
spawnEnemy()
return {
appState
}
}