improvements to stroke fighter, game screen layout
This commit is contained in:
parent
9beca6cf72
commit
b2d8fe070c
|
|
@ -24,7 +24,7 @@ export function createRowingGames (canvasElement, clientWidth, clientHeight) {
|
|||
height: clientHeight
|
||||
})
|
||||
// for now show debug infos all the time
|
||||
k.debug.inspect = true
|
||||
// k.debug.inspect = true
|
||||
|
||||
// todo: once there are multiple games, asset loadingshould be moved to the individual games
|
||||
const assets = '/assets'
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ export default function addSpaceBackground (k) {
|
|||
k.add([
|
||||
k.rect(k.width() + 50, k.height() + 50),
|
||||
k.pos(-25, -25),
|
||||
k.color(0, 0, 0),
|
||||
k.color(0, 9, 28),
|
||||
k.layer('background')
|
||||
])
|
||||
|
||||
|
|
@ -45,10 +45,10 @@ export default function addSpaceBackground (k) {
|
|||
])
|
||||
}
|
||||
|
||||
k.onUpdate('star', (component) => {
|
||||
component.move(0, component.speed)
|
||||
if (component.pos.y - component.height > k.height()) {
|
||||
k.destroy(component)
|
||||
k.onUpdate('star', (star) => {
|
||||
star.move(0, star.speed)
|
||||
if (star.pos.y - star.height > k.height()) {
|
||||
k.destroy(star)
|
||||
addStar(true)
|
||||
}
|
||||
})
|
||||
|
|
|
|||
|
|
@ -12,6 +12,14 @@ import addSpaceBackground from './SpaceBackground.js'
|
|||
* @param {import('kaboom').KaboomCtx} k Kaboom Context
|
||||
*/
|
||||
export default function StrokeFighterBattleScene (k) {
|
||||
// how much stroke power is needed to fire high power lasers
|
||||
const THRESHOLD_POWER = 180
|
||||
// training duration in seconds
|
||||
const TARGET_TIME = 5 * 60
|
||||
// strokes per minute at start of training
|
||||
const SPM_START = 14
|
||||
// strokes per minute at end of training
|
||||
const SPM_END = 28
|
||||
const BULLET_SPEED = 1200
|
||||
const ENEMY_SPEED = 60
|
||||
const PLAYER_SPEED = 480
|
||||
|
|
@ -28,6 +36,8 @@ export default function StrokeFighterBattleScene (k) {
|
|||
{ sprite: 'spaceShips_009', health: 2 }
|
||||
]
|
||||
|
||||
let trainingTime = 0
|
||||
|
||||
k.layers([
|
||||
'background',
|
||||
'game',
|
||||
|
|
@ -67,8 +77,8 @@ export default function StrokeFighterBattleScene (k) {
|
|||
}
|
||||
}
|
||||
|
||||
player.onCollide('enemy', (e) => {
|
||||
k.destroy(e)
|
||||
player.onCollide('enemy', (enemy) => {
|
||||
k.destroy(enemy)
|
||||
k.shake(4)
|
||||
k.play('hit', {
|
||||
detune: -1200,
|
||||
|
|
@ -89,13 +99,13 @@ export default function StrokeFighterBattleScene (k) {
|
|||
}
|
||||
})
|
||||
|
||||
function addLaserHit (p, n) {
|
||||
function addLaserHit (pos, 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.pos(pos.sub(0, 10)),
|
||||
k.scale(k.vec2(0.5)),
|
||||
k.lifespan(0.1),
|
||||
grow(k.rand(0.5, 2)),
|
||||
|
|
@ -106,11 +116,11 @@ export default function StrokeFighterBattleScene (k) {
|
|||
}
|
||||
}
|
||||
|
||||
function spawnBullet (p) {
|
||||
function spawnBullet (pos) {
|
||||
k.add([
|
||||
k.sprite('laserRed01'),
|
||||
k.area(),
|
||||
k.pos(p),
|
||||
k.pos(pos),
|
||||
k.origin('center'),
|
||||
k.move(k.UP, BULLET_SPEED),
|
||||
k.cleanup(),
|
||||
|
|
@ -141,8 +151,7 @@ export default function StrokeFighterBattleScene (k) {
|
|||
})
|
||||
}
|
||||
|
||||
function spawnEnemy () {
|
||||
const enemy = k.choose(ENEMIES)
|
||||
function spawnEnemy (enemy) {
|
||||
k.add([
|
||||
k.sprite(enemy.sprite),
|
||||
k.area(),
|
||||
|
|
@ -152,16 +161,18 @@ export default function StrokeFighterBattleScene (k) {
|
|||
'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.on('death', 'enemy', (enemy) => {
|
||||
k.destroy(enemy)
|
||||
k.every('bullet', (bullet) => {
|
||||
addLaserHit(bullet.pos, 1)
|
||||
k.destroy(bullet)
|
||||
})
|
||||
k.shake(2)
|
||||
})
|
||||
|
||||
k.on('hurt', 'enemy', (e) => {
|
||||
k.on('hurt', 'enemy', (enemy) => {
|
||||
k.shake(1)
|
||||
k.play('hit', {
|
||||
detune: k.rand(-1200, 1200),
|
||||
|
|
@ -174,25 +185,24 @@ export default function StrokeFighterBattleScene (k) {
|
|||
k.text('0'),
|
||||
k.pos(12, 32),
|
||||
k.fixed(),
|
||||
k.layer('ui'),
|
||||
{ time: 0 }
|
||||
k.layer('ui')
|
||||
])
|
||||
|
||||
timer.onUpdate(() => {
|
||||
timer.time += k.dt()
|
||||
timer.text = timer.time.toFixed(2)
|
||||
trainingTime += k.dt()
|
||||
timer.text = trainingTime.toFixed(2)
|
||||
})
|
||||
|
||||
k.onCollide('bullet', 'enemy', (b, e) => {
|
||||
k.destroy(b)
|
||||
e.hurt(1)
|
||||
addLaserHit(b.pos, 1)
|
||||
k.onCollide('bullet', 'enemy', (bullet, enemy) => {
|
||||
k.destroy(bullet)
|
||||
enemy.hurt(1)
|
||||
addLaserHit(bullet.pos, 1)
|
||||
})
|
||||
|
||||
k.onUpdate('enemy', (sprite) => {
|
||||
sprite.move(0, sprite.speed)
|
||||
if (sprite.pos.y - sprite.height > k.height()) {
|
||||
k.destroy(sprite)
|
||||
k.onUpdate('enemy', (enemy) => {
|
||||
enemy.move(0, enemy.speed)
|
||||
if (enemy.pos.y - enemy.height > k.height()) {
|
||||
k.destroy(enemy)
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -208,16 +218,31 @@ export default function StrokeFighterBattleScene (k) {
|
|||
}
|
||||
|
||||
function driveFinished (metrics) {
|
||||
if (metrics.power < 120) {
|
||||
if (metrics.power < THRESHOLD_POWER * 0.75) {
|
||||
fireWeapons(1)
|
||||
} else if (metrics.power < 180) {
|
||||
} else if (metrics.power < THRESHOLD_POWER) {
|
||||
fireWeapons(2)
|
||||
} else {
|
||||
fireWeapons(3)
|
||||
}
|
||||
}
|
||||
|
||||
spawnEnemy()
|
||||
function scheduleNextEnemy () {
|
||||
const percentTrainingFinished = trainingTime / TARGET_TIME
|
||||
const currentSPM = SPM_START + (SPM_END - SPM_START) * percentTrainingFinished
|
||||
let maxEnemyHealth = 1
|
||||
if (percentTrainingFinished < 0.4) {
|
||||
maxEnemyHealth = 1
|
||||
} else if (percentTrainingFinished < 0.8) {
|
||||
maxEnemyHealth = 2
|
||||
} else {
|
||||
maxEnemyHealth = 3
|
||||
}
|
||||
spawnEnemy(k.choose(ENEMIES.filter((enemy) => enemy.health <= maxEnemyHealth)))
|
||||
k.wait(60 / currentSPM, scheduleNextEnemy)
|
||||
}
|
||||
|
||||
scheduleNextEnemy()
|
||||
|
||||
return {
|
||||
appState
|
||||
|
|
|
|||
|
|
@ -7,23 +7,77 @@
|
|||
|
||||
import { customElement } from 'lit/decorators.js'
|
||||
import { createRowingGames } from '../arcade/RowingGames.js'
|
||||
import { icon_exit } from '../lib/icons.js'
|
||||
import { AppElement, css, html } from './AppElement.js'
|
||||
|
||||
@customElement('game-component')
|
||||
export class GameComponent extends AppElement {
|
||||
static styles = css`
|
||||
:host {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
display: flex;
|
||||
}
|
||||
#arcade {
|
||||
width: 100vh;
|
||||
height: 100vh;
|
||||
}
|
||||
@media (orientation: portrait) {
|
||||
:host {
|
||||
flex-direction: column
|
||||
}
|
||||
#arcade {
|
||||
width: 100vw;
|
||||
height: 100vw;
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
outline:none;
|
||||
background-color: var(--theme-button-color);
|
||||
border: 0;
|
||||
border-radius: var(--theme-border-radius);
|
||||
color: var(--theme-font-color);
|
||||
margin: 0.2em 0;
|
||||
font-size: 60%;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
width: 3.5em;
|
||||
height: 2.5em;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
button:hover {
|
||||
filter: brightness(150%);
|
||||
}
|
||||
.icon {
|
||||
height: 1.7em;
|
||||
}
|
||||
#controls {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
#buttons {
|
||||
padding: 0.5em 0 0.5em 0;
|
||||
margin: 1vw;
|
||||
background: var(--theme-widget-color);
|
||||
border-radius: var(--theme-border-radius);
|
||||
}
|
||||
`
|
||||
render () {
|
||||
return html`<canvas id="arcade"></canvas>`
|
||||
return html`
|
||||
<canvas id="arcade"></canvas>
|
||||
<div id="controls">
|
||||
<div id="buttons">
|
||||
<button @click=${this.openDashboard}>${icon_exit}</button>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
openDashboard () {
|
||||
this.sendEvent('triggerAction', { command: 'openDashboard' })
|
||||
}
|
||||
|
||||
firstUpdated () {
|
||||
|
|
|
|||
|
|
@ -135,6 +135,10 @@ export function createApp (app) {
|
|||
app.updateState({ ...app.getState(), activeRoute: 'ROWINGGAMES' })
|
||||
break
|
||||
}
|
||||
case 'openDashboard': {
|
||||
app.updateState({ ...app.getState(), activeRoute: 'DASHBOARD' })
|
||||
break
|
||||
}
|
||||
case 'reset': {
|
||||
resetFields()
|
||||
if (socket)socket.send(JSON.stringify({ command: 'reset' }))
|
||||
|
|
|
|||
|
|
@ -26,3 +26,4 @@ export const icon_compress = svg`<svg aria-hidden="true" focusable="false" class
|
|||
export const icon_bluetooth = svg`<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M292.6 171.1L249.7 214l-.3-86 43.2 43.1m-43.2 219.8l43.1-43.1-42.9-42.9-.2 86zM416 259.4C416 465 344.1 512 230.9 512S32 465 32 259.4 115.4 0 228.6 0 416 53.9 416 259.4zm-158.5 0l79.4-88.6L211.8 36.5v176.9L138 139.6l-27 26.9 92.7 93-92.7 93 26.9 26.9 73.8-73.8 2.3 170 127.4-127.5-83.9-88.7z"></path></svg>`
|
||||
export const icon_upload = svg`<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M537.6 226.6c4.1-10.7 6.4-22.4 6.4-34.6 0-53-43-96-96-96-19.7 0-38.1 6-53.3 16.2C367 64.2 315.3 32 256 32c-88.4 0-160 71.6-160 160 0 2.7.1 5.4.2 8.1C40.2 219.8 0 273.2 0 336c0 79.5 64.5 144 144 144h368c70.7 0 128-57.3 128-128 0-61.9-44-113.6-102.4-125.4zM393.4 288H328v112c0 8.8-7.2 16-16 16h-48c-8.8 0-16-7.2-16-16V288h-65.4c-14.3 0-21.4-17.2-11.3-27.3l105.4-105.4c6.2-6.2 16.4-6.2 22.6 0l105.4 105.4c10.1 10.1 2.9 27.3-11.3 27.3z"></path></svg>`
|
||||
export const icon_gamepad = svg`<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M448 64H192C85.96 64 0 149.1 0 256s85.96 192 192 192h256c106 0 192-85.96 192-192S554 64 448 64zM247.1 280h-32v32c0 13.2-10.78 24-23.98 24c-13.2 0-24.02-10.8-24.02-24v-32L136 279.1C122.8 279.1 111.1 269.2 111.1 256c0-13.2 10.85-24.01 24.05-24.01L167.1 232v-32c0-13.2 10.82-24 24.02-24c13.2 0 23.98 10.8 23.98 24v32h32c13.2 0 24.02 10.8 24.02 24C271.1 269.2 261.2 280 247.1 280zM431.1 344c-22.12 0-39.1-17.87-39.1-39.1s17.87-40 39.1-40s39.1 17.88 39.1 40S454.1 344 431.1 344zM495.1 248c-22.12 0-39.1-17.87-39.1-39.1s17.87-40 39.1-40c22.12 0 39.1 17.88 39.1 40S518.1 248 495.1 248z"/></svg>`
|
||||
export const icon_exit = svg`<svg aria-hidden="true" focusable="false" class="icon" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M96 480h64C177.7 480 192 465.7 192 448S177.7 416 160 416H96c-17.67 0-32-14.33-32-32V128c0-17.67 14.33-32 32-32h64C177.7 96 192 81.67 192 64S177.7 32 160 32H96C42.98 32 0 74.98 0 128v256C0 437 42.98 480 96 480zM504.8 238.5l-144.1-136c-6.975-6.578-17.2-8.375-26-4.594c-8.803 3.797-14.51 12.47-14.51 22.05l-.0918 72l-128-.001c-17.69 0-32.02 14.33-32.02 32v64c0 17.67 14.34 32 32.02 32l128 .001l.0918 71.1c0 9.578 5.707 18.25 14.51 22.05c8.803 3.781 19.03 1.984 26-4.594l144.1-136C514.4 264.4 514.4 247.6 504.8 238.5z"/></svg>`
|
||||
|
|
|
|||
Loading…
Reference in New Issue