diff --git a/src/app/collision_resolver.js b/src/app/collision_resolver.js index 9f02bcf..8021fe4 100644 --- a/src/app/collision_resolver.js +++ b/src/app/collision_resolver.js @@ -74,9 +74,10 @@ function createCollisionResolver(spec) { if ((entity.meta.status & MetaStatus.active) !== 0) { item.meta.health -= entity.meta.damage } - if (item.meta.health <= 0) { + if (item.meta.health <= 0 && (item.meta.status & MetaStatus.active) !== 0) { item.meta.status &= ~MetaStatus.visible item.meta.status &= ~MetaStatus.active + emitter.emit('CollisionResolver:enemyDied', item) } } else if (entity.meta.type === MetaType.enemy && item.meta.type === MetaType.hero) { if ((entity.meta.status & MetaStatus.active) !== 0 && (item.meta.status & MetaStatus.invulnerable) === 0) { diff --git a/src/app/game_input_controller.js b/src/app/game_input_controller.js index c26dca9..65b2eb9 100644 --- a/src/app/game_input_controller.js +++ b/src/app/game_input_controller.js @@ -40,11 +40,11 @@ function createGameInputController(spec) { return paused } - docWindow.addEventListener('keyup', (e) => { + const onKeyUp = (e) => { keyboard[e.code] = false - }) + } - docWindow.addEventListener('keydown', (e) => { + const onKeyDown = (e) => { keyboard[e.code] = true if (e.code === KeyMapping.dash && e.target === graphics.canvas) { e.preventDefault() @@ -55,24 +55,38 @@ function createGameInputController(spec) { if (!isPaused()) { emitter.emit('GameInputController:keydown', e) } - }) + } - graphics.canvas.addEventListener('mousemove', (e) => { + const onMouseMove = (e) => { setMousePoint(e.clientX, e.clientY) - }) + } - graphics.canvas.addEventListener('mousedown', (e) => { + const onMouseDown = (e) => { setMousePoint(e.clientX, e.clientY) mouse.down = true if (!isPaused()) { emitter.emit('GameInputController:mousedown', mouse) } - }) + } - graphics.canvas.addEventListener('mouseup', (e) => { + const onMouseUp = (e) => { setMousePoint(e.clientX, e.clientY) mouse.down = false - }) + } + + const unregisterListeners = () => { + docWindow.removeEventListener('keyup', onKeyUp) + docWindow.removeEventListener('keydown', onKeyDown) + graphics.canvas.removeEventListener('mousemove', onMouseMove) + graphics.canvas.removeEventListener('mousedown', onMouseDown) + graphics.canvas.removeEventListener('mouseup', onMouseUp) + } + + docWindow.addEventListener('keyup', onKeyUp) + docWindow.addEventListener('keydown', onKeyDown) + graphics.canvas.addEventListener('mousemove', onMouseMove) + graphics.canvas.addEventListener('mousedown', onMouseDown) + graphics.canvas.addEventListener('mouseup', onMouseUp) return { isUp: isUp, @@ -81,7 +95,8 @@ function createGameInputController(spec) { isRight: isRight, isDash: isDash, isPaused: isPaused, - mouse: mouse + mouse: mouse, + unregisterListeners: unregisterListeners } } diff --git a/src/app/hero.js b/src/app/hero.js index bc90c10..3ed463f 100644 --- a/src/app/hero.js +++ b/src/app/hero.js @@ -18,33 +18,6 @@ function createHero(spec) { let alphaResetTimerId = null let alpha = 1.0 - emitter.on('GameInputController:keydown', (e) => { - if (dashResetTimerId || !inputController.isDash()) { - return - } - meta.status |= MetaStatus.invulnerable - isDashing = true - setTimeout(() => { - isDashing = false - meta.status &= ~MetaStatus.invulnerable - }, dashTimeout) - dashResetTimerId = setTimeout(() => { - dashResetTimerId = null - }, dashResetTimeout) - }) - - emitter.on('GameInputController:mousedown', (mouse) => { - if (ammunition.length <= 0) { - return - } - const projectile = ammunition.pop() - projectile.fire(mouse.x, mouse.y) - }) - - emitter.on('CollisionResolver:heroTouchedProjectile', (projectile) => { - ammunition.push(projectile) - }) - const getAlpha = () => { if (isDashing && !alphaResetTimerId) { alpha = 0.1 @@ -80,12 +53,52 @@ function createHero(spec) { graphics.ctx.restore() } + const onKeyDown = (e) => { + if (dashResetTimerId || !inputController.isDash()) { + return + } + meta.status |= MetaStatus.invulnerable + isDashing = true + emitter.emit('Hero:isDashing') + setTimeout(() => { + isDashing = false + meta.status &= ~MetaStatus.invulnerable + }, dashTimeout) + dashResetTimerId = setTimeout(() => { + dashResetTimerId = null + }, dashResetTimeout) + } + + const onMouseDown = (mouse) => { + emitter.emit('Hero:onMouseDown', ammunition) + if (ammunition.length <= 0) { + return + } + const projectile = ammunition.pop() + projectile.fire(mouse.x, mouse.y) + } + + const onTouchProjectile = (projectile) => { + ammunition.push(projectile) + } + + const unregisterListeners = () => { + emitter.removeListener('GameInputController:keydown', onKeyDown) + emitter.removeListener('GameInputController:mousedown', onMouseDown) + emitter.removeListener('CollisionResolver:heroTouchedProjectile', onTouchProjectile) + } + + emitter.on('GameInputController:keydown', onKeyDown) + emitter.on('GameInputController:mousedown', onMouseDown) + emitter.on('CollisionResolver:heroTouchedProjectile', onTouchProjectile) + return { frame: frame, update: update, render: render, meta: meta, - ammunition: ammunition + ammunition: ammunition, + unregisterListeners: unregisterListeners } } diff --git a/src/app/level.js b/src/app/level.js index 54155b5..cbafa37 100644 --- a/src/app/level.js +++ b/src/app/level.js @@ -67,6 +67,8 @@ function createLevel(spec) { const complete = (callback) => { graphics.fadeOut(callback) + gameInputController.unregisterListeners() + hero.unregisterListeners() } return { diff --git a/src/app/level_factory.js b/src/app/level_factory.js index c04cd4c..cca8dc5 100644 --- a/src/app/level_factory.js +++ b/src/app/level_factory.js @@ -18,6 +18,7 @@ import createLevel from './level' function createLevelFactory(spec) { const emitter = spec.emitter const graphics = spec.graphics + const soundController = spec.soundController const makeLevel = (levelCount) => { const mazeGenerator = createMazeGenerator() @@ -108,6 +109,7 @@ function createLevelFactory(spec) { return createLevel({ emitter: emitter, gameInputController: gameInputController, + soundController: soundController, hero: hero, enemies: enemies, projectiles: projectiles, diff --git a/src/app/main.js b/src/app/main.js index 33d56dc..3c3b665 100644 --- a/src/app/main.js +++ b/src/app/main.js @@ -3,13 +3,19 @@ import EventEmitter from 'events' import createGraphics from './graphics' import createGame from './game' import createLevelFactory from './level_factory' +import createSoundController from './sound_controller' document.addEventListener('DOMContentLoaded', function() { const canvas = document.getElementById('canvas') const emitter = new EventEmitter() const graphics = createGraphics({ canvas: canvas, emitter: emitter }) + const soundController = createSoundController({ emitter: emitter }) const runLoop = createRunLoop({ emitter: emitter }) - const levelFactory = createLevelFactory({ emitter: emitter, graphics: graphics }) + const levelFactory = createLevelFactory({ + emitter: emitter, + graphics: graphics, + soundController: soundController + }) const game = createGame({ emitter: emitter, levelFactory: levelFactory }) emitter.on('RunLoop:begin', (timeStamp, frameDelta) => { diff --git a/src/app/sound_controller.js b/src/app/sound_controller.js new file mode 100644 index 0000000..ae8e8b9 --- /dev/null +++ b/src/app/sound_controller.js @@ -0,0 +1,79 @@ +/* eslint-disable no-sparse-arrays */ +/* eslint-disable comma-spacing */ +import jsfxr from 'jsfxr' +import { randomIntInRange } from './utils' + +function createSoundController(spec) { + const emitter = spec.emitter + + const play = (sound) => { + const player = new Audio() + player.src = sound + player.play() + } + + emitter.on('Hero:onMouseDown', (ammunition) => { + if (ammunition.length) { + play(Sounds.hero.fire[randomIntInRange(0, Sounds.hero.fire.length)]) + } else { + play(Sounds.hero.fireEmpty[randomIntInRange(0, Sounds.hero.fireEmpty.length)]) + } + }) + + emitter.on('Hero:isDashing', () => { + play(Sounds.hero.dash[randomIntInRange(0, Sounds.hero.dash.length)]) + }) + + emitter.on('CollisionResolver:heroTouchedProjectile', () => { + play(Sounds.hero.pickup[randomIntInRange(0, Sounds.hero.pickup.length)]) + }) + + emitter.on('CollisionResolver:heroTouchedEnemy', () => { + play(Sounds.hero.hurt[randomIntInRange(0, Sounds.hero.hurt.length)]) + }) + + emitter.on('CollisionResolver:enemyDied', () => { + play(Sounds.enemy.died[randomIntInRange(0, Sounds.enemy.died.length)]) + }) + + return { + play: play + } +} + +const Sounds = Object.freeze({ + hero: { + fire: [ + jsfxr([2,,0.138,0.2833,0.3363,0.8296,0.3507,-0.3135,,,,,,0.3683,0.0045,,,,1,,,0.1446,,0.5]), + jsfxr([2,,0.1096,0.2833,0.3363,0.8697,0.3609,-0.2655,-0.0371,,0.0505,-0.0091,,0.3119,0.0139,0.0224,,-0.0162,1,,0.0403,0.1926,0.0421,0.5]), + jsfxr([2,,0.1251,0.2802,0.3448,0.8455,0.3976,-0.3491,0.0315,,0.0088,,,0.2863,0.0009,,-0.0659,0.0129,0.9717,0.0212,0.0236,0.1267,-0.0284,0.5]) + ], + fireEmpty: [ + jsfxr([3,,0.0451,,0.287,0.3837,,-0.5973,,,,,,,,,,,1,,,0.0039,,0.44]), + jsfxr([3,,0.0451,0.0344,0.2436,0.3837,,-0.5575,0.0192,,,,,,0.043,,0.0443,,0.9888,0.0214,,0.0213,0.013,0.31]) + ], + dash: [ + jsfxr([0,,0.3845,,0.2544,0.543,,0.1433,,,,,,0.3354,,,,,0.6125,,,,,0.35]), + jsfxr([0,,0.4298,,0.311,0.6176,0.018,0.1433,0.0482,,,0.0041,0.0496,0.3003,0.0458,,0.04,0.007,0.6125,,0.0008,0.0115,-0.0065,0.35]) + ], + pickup: [ + jsfxr([0,,0.0101,0.5708,0.2528,0.5521,,,,,,0.3545,0.5621,,,,,,1,,,,,0.5]), + jsfxr([0,0.0074,0.01,0.6028,0.2642,0.5521,0.038,,-0.0054,,,0.3896,0.5173,0.0287,-0.0159,0.0239,,-0.0176,0.9738,,,0.036,0.0484,0.5]) + ], + hurt: [ + jsfxr([3,,0.1216,0.4737,0.3359,0.0444,,0.0488,,,,,,,,,0.102,-0.1476,1,,,,,0.5]), + jsfxr([3,,0.1114,0.4446,0.3265,0.0391,,0.0088,-0.03,,,0.0264,,,-0.0284,0.0162,0.102,-0.1667,1,,0.0309,0.0422,0.0105,0.5]) + ] + }, + enemy: { + died: [ + jsfxr([0,0.0355,0.0514,,0.1975,0.2944,,-0.2884,0.0285,,,,,0.3462,-0.0323,,-0.0342,-0.0466,0.9699,-0.0228,,,0.0472,0.5]), + jsfxr([0,,0.0778,,0.1975,0.2944,0.0156,-0.2884,0.0285,,,,,0.3293,-0.0323,,-0.0378,-0.0848,0.9699,-0.0104,0.0358,,0.0814,0.5]) + ] + } +}) + +export { + createSoundController as default, + Sounds +}