add fireworks when game finished
This commit is contained in:
parent
f017499cbf
commit
1ac9be4307
3 changed files with 251 additions and 1 deletions
226
game/Fireworks.js
Normal file
226
game/Fireworks.js
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
import Util from './../common/Util.js'
|
||||
|
||||
let minVx = -10
|
||||
let deltaVx = 20
|
||||
let minVy = 2
|
||||
let deltaVy = 15
|
||||
const minParticleV = 5
|
||||
const deltaParticleV = 5
|
||||
|
||||
const gravity = 1
|
||||
|
||||
const explosionRadius = 200
|
||||
const bombRadius = 10
|
||||
const explodingDuration = 100
|
||||
const explosionDividerFactor = 10
|
||||
|
||||
const nBombs = 1
|
||||
const percentChanceNewBomb = 5
|
||||
|
||||
function color() {
|
||||
const r = Util.randomInt(0, 255)
|
||||
const g = Util.randomInt(0, 255)
|
||||
const b = Util.randomInt(0, 255)
|
||||
return 'rgba(' + r + ',' + g + ',' + b + ', 0.8)'
|
||||
}
|
||||
|
||||
// A Bomb. Or firework.
|
||||
class Bomb {
|
||||
constructor() {
|
||||
this.radius = bombRadius
|
||||
this.previousRadius = bombRadius
|
||||
this.explodingDuration = explodingDuration
|
||||
this.hasExploded = false
|
||||
this.alive = true
|
||||
this.color = color()
|
||||
|
||||
this.px = (window.innerWidth / 4) + (Math.random() * window.innerWidth / 2)
|
||||
this.py = window.innerHeight
|
||||
|
||||
this.vx = minVx + Math.random() * deltaVx
|
||||
this.vy = (minVy + Math.random() * deltaVy) * -1 // because y grows downwards
|
||||
|
||||
this.duration = 0
|
||||
}
|
||||
|
||||
update(particlesVector) {
|
||||
if (this.hasExploded) {
|
||||
const deltaRadius = explosionRadius - this.radius
|
||||
this.previousRadius = this.radius
|
||||
this.radius += deltaRadius / explosionDividerFactor
|
||||
this.explodingDuration--
|
||||
if (this.explodingDuration == 0) {
|
||||
this.alive = false
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.vx += 0
|
||||
this.vy += gravity
|
||||
if (this.vy >= 0) { // invertion point
|
||||
this.explode(particlesVector)
|
||||
}
|
||||
|
||||
this.px += this.vx
|
||||
this.py += this.vy
|
||||
}
|
||||
}
|
||||
|
||||
draw(ctx) {
|
||||
ctx.beginPath()
|
||||
ctx.arc(this.px, this.py, this.previousRadius, 0, Math.PI * 2, false)
|
||||
if (!this.hasExploded) {
|
||||
ctx.fillStyle = this.color
|
||||
ctx.lineWidth = 1
|
||||
ctx.fill()
|
||||
}
|
||||
}
|
||||
|
||||
explode(particlesVector) {
|
||||
this.hasExploded = true
|
||||
const e = 3 + Math.floor(Math.random() * 3)
|
||||
for (let j = 0; j < e; j++) {
|
||||
const n = 10 + Math.floor(Math.random() * 21) // 10 - 30
|
||||
const speed = minParticleV + Math.random() * deltaParticleV
|
||||
const deltaAngle = 2 * Math.PI / n
|
||||
const initialAngle = Math.random() * deltaAngle
|
||||
for (let i = 0; i < n; i++) {
|
||||
particlesVector.push(new Particle(this, i * deltaAngle + initialAngle, speed))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Particle {
|
||||
constructor(parent, angle, speed) {
|
||||
this.px = parent.px
|
||||
this.py = parent.py
|
||||
this.vx = Math.cos(angle) * speed
|
||||
this.vy = Math.sin(angle) * speed
|
||||
this.color = parent.color
|
||||
this.duration = 40 + Math.floor(Math.random() * 20)
|
||||
this.alive = true
|
||||
}
|
||||
update() {
|
||||
this.vx += 0
|
||||
this.vy += gravity / 10
|
||||
|
||||
this.px += this.vx
|
||||
this.py += this.vy
|
||||
this.radius = 3
|
||||
|
||||
this.duration--
|
||||
if (this.duration <= 0) {
|
||||
this.alive = false
|
||||
}
|
||||
}
|
||||
|
||||
draw(ctx) {
|
||||
ctx.beginPath()
|
||||
ctx.arc(this.px, this.py, this.radius, 0, Math.PI * 2, false)
|
||||
ctx.fillStyle = this.color
|
||||
ctx.lineWidth = 1
|
||||
ctx.fill()
|
||||
}
|
||||
}
|
||||
|
||||
class Controller {
|
||||
constructor(canvas) {
|
||||
this.canvas = canvas
|
||||
this.ctx = this.canvas.getContext('2d')
|
||||
this.resize()
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
this.resize()
|
||||
})
|
||||
}
|
||||
|
||||
setSpeedParams() {
|
||||
let heightReached = 0
|
||||
let vy = 0
|
||||
|
||||
while (heightReached < this.canvas.height && vy >= 0) {
|
||||
vy += gravity
|
||||
heightReached += vy
|
||||
}
|
||||
|
||||
minVy = vy / 2
|
||||
deltaVy = vy - minVy
|
||||
|
||||
const vx = (1 / 4) * this.canvas.width / (vy / 2)
|
||||
minVx = -vx
|
||||
deltaVx = 2 * vx
|
||||
}
|
||||
|
||||
resize() {
|
||||
this.setSpeedParams()
|
||||
}
|
||||
|
||||
init() {
|
||||
this.readyBombs = []
|
||||
this.explodedBombs = []
|
||||
this.particles = []
|
||||
|
||||
for (let i = 0; i < nBombs; i++) {
|
||||
this.readyBombs.push(new Bomb())
|
||||
}
|
||||
}
|
||||
|
||||
update() {
|
||||
if (Math.random() * 100 < percentChanceNewBomb) {
|
||||
this.readyBombs.push(new Bomb())
|
||||
}
|
||||
|
||||
const aliveBombs = []
|
||||
while (this.explodedBombs.length > 0) {
|
||||
const bomb = this.explodedBombs.shift()
|
||||
bomb.update()
|
||||
if (bomb.alive) {
|
||||
aliveBombs.push(bomb)
|
||||
}
|
||||
}
|
||||
this.explodedBombs = aliveBombs
|
||||
|
||||
const notExplodedBombs = []
|
||||
while (this.readyBombs.length > 0) {
|
||||
const bomb = this.readyBombs.shift()
|
||||
bomb.update(this.particles)
|
||||
if (bomb.hasExploded) {
|
||||
this.explodedBombs.push(bomb)
|
||||
}
|
||||
else {
|
||||
notExplodedBombs.push(bomb)
|
||||
}
|
||||
}
|
||||
this.readyBombs = notExplodedBombs
|
||||
|
||||
const aliveParticles = []
|
||||
while (this.particles.length > 0) {
|
||||
const particle = this.particles.shift()
|
||||
particle.update()
|
||||
if (particle.alive) {
|
||||
aliveParticles.push(particle)
|
||||
}
|
||||
}
|
||||
this.particles = aliveParticles
|
||||
}
|
||||
|
||||
render() {
|
||||
this.ctx.beginPath()
|
||||
this.ctx.fillStyle = 'rgba(0, 0, 0, 0.1)' // Ghostly effect
|
||||
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height)
|
||||
|
||||
for (let i = 0; i < this.readyBombs.length; i++) {
|
||||
this.readyBombs[i].draw(this.ctx)
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.explodedBombs.length; i++) {
|
||||
this.explodedBombs[i].draw(this.ctx)
|
||||
}
|
||||
|
||||
for (let i = 0; i < this.particles.length; i++) {
|
||||
this.particles[i].draw(this.ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Controller
|
||||
19
game/game.js
19
game/game.js
|
|
@ -7,6 +7,7 @@ import Communication from './Communication.js'
|
|||
import Util from './../common/Util.js'
|
||||
import PuzzleGraphics from './PuzzleGraphics.js'
|
||||
import Game from './Game.js'
|
||||
import fireworksController from './Fireworks.js'
|
||||
|
||||
if (typeof GAME_ID === 'undefined') throw '[ GAME_ID not set ]'
|
||||
if (typeof WS_ADDRESS === 'undefined') throw '[ WS_ADDRESS not set ]'
|
||||
|
|
@ -319,6 +320,14 @@ async function main() {
|
|||
|
||||
// Create a dom and attach adapters to it so we can work with it
|
||||
const canvas = addCanvasToDom(Graphics.createCanvas())
|
||||
|
||||
const longFinished = Game.getFinishTs(gameId)
|
||||
let finished = longFinished ? true : false
|
||||
const justFinished = () => !!(finished && !longFinished)
|
||||
|
||||
const fireworks = new fireworksController(canvas)
|
||||
fireworks.init(canvas)
|
||||
|
||||
const ctx = canvas.getContext('2d')
|
||||
|
||||
// initialize some view data
|
||||
|
|
@ -391,6 +400,7 @@ async function main() {
|
|||
} break;
|
||||
}
|
||||
}
|
||||
finished = Game.getFinishTs(gameId)
|
||||
})
|
||||
|
||||
let _last_mouse_down = null
|
||||
|
|
@ -438,6 +448,12 @@ async function main() {
|
|||
}
|
||||
Communication.sendClientEvent(evt)
|
||||
}
|
||||
|
||||
finished = Game.getFinishTs(gameId)
|
||||
if (justFinished()) {
|
||||
fireworks.update()
|
||||
RERENDER = true
|
||||
}
|
||||
}
|
||||
|
||||
const onRender = async () => {
|
||||
|
|
@ -522,6 +538,9 @@ async function main() {
|
|||
if (DEBUG) Debug.checkpoint('scores done')
|
||||
// ---------------------------------------------------------------
|
||||
|
||||
if (justFinished()) {
|
||||
fireworks.render()
|
||||
}
|
||||
|
||||
RERENDER = false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,12 @@ function loadAllGames() {
|
|||
}
|
||||
const file = `${DATA_DIR}/${f}`
|
||||
const contents = fs.readFileSync(file, 'utf-8')
|
||||
const game = JSON.parse(contents)
|
||||
let game
|
||||
try {
|
||||
game = JSON.parse(contents)
|
||||
} catch {
|
||||
console.log(`[ERR] unable to load game from file ${f}`);
|
||||
}
|
||||
if (typeof game.puzzle.data.started === 'undefined') {
|
||||
game.puzzle.data.started = Math.round(fs.statSync(file).ctimeMs)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue