diff --git a/assets/audio/fire.wav b/assets/audio/fire.wav
new file mode 100644
index 0000000..8221026
Binary files /dev/null and b/assets/audio/fire.wav differ
diff --git a/assets/audio/impact-0.wav b/assets/audio/impact-0.wav
new file mode 100644
index 0000000..7888247
Binary files /dev/null and b/assets/audio/impact-0.wav differ
diff --git a/assets/audio/impact-1.wav b/assets/audio/impact-1.wav
new file mode 100644
index 0000000..00fa66c
Binary files /dev/null and b/assets/audio/impact-1.wav differ
diff --git a/assets/audio/impact-2.wav b/assets/audio/impact-2.wav
new file mode 100644
index 0000000..cd88f6d
Binary files /dev/null and b/assets/audio/impact-2.wav differ
diff --git a/assets/audio/music.mp3 b/assets/audio/music.mp3
new file mode 100644
index 0000000..7ebee0a
Binary files /dev/null and b/assets/audio/music.mp3 differ
diff --git a/assets/audio/rumble.wav b/assets/audio/rumble.wav
new file mode 100644
index 0000000..6461359
Binary files /dev/null and b/assets/audio/rumble.wav differ
diff --git a/index.html b/index.html
index e9f65f8..9b6988e 100644
--- a/index.html
+++ b/index.html
@@ -31,7 +31,9 @@
+
+
@@ -44,10 +46,14 @@
Score: 000
+
+ Hits: 00
+
+
diff --git a/lib/color-scheme.min.js b/lib/color-scheme.min.js
new file mode 100644
index 0000000..00229b0
--- /dev/null
+++ b/lib/color-scheme.min.js
@@ -0,0 +1 @@
+(function(){var t,e=[].slice;t=function(){function t(){var e,n;for(e=[],n=1;n<=4;n++)e.push(new t.mutablecolor(60));this.col=e,this._scheme="mono",this._distance=.5,this._web_safe=!1,this._add_complement=!1}var n,r,o,a,i,u;for(i=Array.isArray||function(t){return"[object Array]"==={}.toString.call(t)},t.SCHEMES={},a="mono monochromatic contrast triade tetrade analogic".split(/\s+/),r=0,o=a.length;r=u;n=0<=u?++o:--o)for(r=a=0;a<=3;r=++a)i[4*n+r]=this.col[n].get_hex(this._web_safe,r);return i},t.prototype.colorset=function(){var t,e;for(t=n(this.colors()),e=[];t.length>0;)e.push(t.splice(0,4));return e},t.prototype.from_hue=function(t){if(null==t)throw"from_hue needs an argument";return this.col[0].set_hue(t),this},t.prototype.rgb2ryb=function(){var t,n,r,o,a,u,s,h,l;return s=1<=arguments.length?e.call(arguments,0):[],null!=s[0]&&i(s[0])&&(s=s[0]),u=s[0],n=s[1],t=s[2],h=Math.min(u,n,t),u-=h,n-=h,t-=h,o=Math.max(u,n,t),l=Math.min(u,n),u-=l,n-=l,t>0&&n>0&&(t/=2,n/=2),l+=n,t+=n,a=Math.max(u,l,t),a>0&&(r=o/a,u*=r,l*=r,t*=r),u+=h,l+=h,t+=h,[Math.floor(u),Math.floor(l),Math.floor(t)]},t.prototype.rgb2hsv=function(){var t,n,r,o,a,u,s,h,l,c;return h=1<=arguments.length?e.call(arguments,0):[],null!=h[0]&&i(h[0])&&(h=h[0]),s=h[0],r=h[1],t=h[2],s/=255,r/=255,t/=255,u=Math.min.apply(Math,[s,r,t]),a=Math.max.apply(Math,[s,r,t]),n=a-u,c=a,n>0?(l=n/a,o=s===a?(r-t)/n:r===a?2+(t-s)/n:4+(s-r)/n,o*=60,o%=360,[o,l,c]):[0,0,c]},t.prototype.rgbToHsv=function(){var t,n,r,o,a,u,s,h,l,c;if(h=1<=arguments.length?e.call(arguments,0):[],null!=h[0]&&i(h[0])&&(h=h[0]),s=h[0],r=h[1],t=h[2],s/=255,r/=255,t/=255,a=Math.max(s,r,t),u=Math.min(s,r,t),o=void 0,l=void 0,c=a,n=a-u,l=0===a?0:n/a,a===u)o=0;else{switch(a){case s:o=(r-t)/n+(r= 0";if(t>1)throw"distance("+t+") - argument must be <= 1";return this._distance=t,this},t.prototype.scheme=function(e){if(null==e)return this._scheme;if(null==t.SCHEMES[e])throw"'"+e+"' isn't a valid scheme name";return this._scheme=e,this},t.prototype.variation=function(e){if(null==e)throw"variation needs an argument";if(null==t.PRESETS[e])throw"'$v' isn't a valid variation name";return this._set_variant_preset(t.PRESETS[e]),this},t.prototype._set_variant_preset=function(t){var e,n,r;for(r=[],e=n=0;n<=3;e=++n)r.push(this.col[e].set_variant_preset(t));return r},n=function(t){var e,r,o;if(null==t||"object"!=typeof t)return t;if(t instanceof Date)return new Date(t.getTime());if(t instanceof RegExp)return e="",null!=t.global&&(e+="g"),null!=t.ignoreCase&&(e+="i"),null!=t.multiline&&(e+="m"),null!=t.sticky&&(e+="y"),new RegExp(t.source,e);o=new t.constructor;for(r in t)o[r]=n(t[r]);return o},t.mutablecolor=function(){function e(e){if(null==e)throw"No hue specified";this.saturation=[],this.value=[],this.base_red=0,this.base_green=0,this.base_blue=0,this.base_saturation=0,this.base_value=0,this.set_hue(e),this.set_variant_preset(t.PRESETS.default)}return e.prototype.hue=0,e.prototype.saturation=[],e.prototype.value=[],e.prototype.base_red=0,e.prototype.base_green=0,e.prototype.base_saturation=0,e.prototype.base_value=0,e.prototype.get_hue=function(){return this.hue},e.prototype.set_hue=function(e){var n,r,o,a,i,u,s,h,l,c;n=function(t,e,n){return t+Math.round((e-t)*n)},this.hue=Math.round(e%360),i=this.hue%15+(this.hue-Math.floor(this.hue)),c=i/15,u=this.hue-Math.floor(i),s=(u+15)%360,360===u&&(u=0),360===s&&(s=0),o=t.COLOR_WHEEL[u],a=t.COLOR_WHEEL[s],h={red:0,green:1,blue:2,value:3};for(r in h)l=h[r],this["base_"+r]=n(o[l],a[l],c);return this.base_saturation=n(100,100,c)/100,this.base_value/=100},e.prototype.rotate=function(t){var e;return e=(this.hue+t)%360,this.set_hue(e)},e.prototype.get_saturation=function(t){var e,n;return n=this.saturation[t],e=n<0?-n*this.base_saturation:n,e>1&&(e=1),e<0&&(e=0),e},e.prototype.get_value=function(t){var e,n;return n=this.value[t],e=n<0?-n*this.base_value:n,e>1&&(e=1),e<0&&(e=0),e},e.prototype.set_variant=function(t,e,n){return this.saturation[t]=e,this.value[t]=n},e.prototype.set_variant_preset=function(t){var e,n,r;for(r=[],e=n=0;n<=3;e=++n)r.push(this.set_variant(e,t[2*e],t[2*e+1]));return r},e.prototype.get_hex=function(t,e){var n,r,o,a,i,u,s,h,l,c,f,p,_,m,d,v;for(l=Math.max.apply(Math,function(){var t,e,n,o;for(n=["red","green","blue"],o=[],e=0,t=n.length;e0?v/l:0,p=[],f=["red","green","blue"],h=0,u=f.length;h=0&&e<=1){if(o._volume=e,o._muted)return o;o.usingWebAudio&&o.masterGain.gain.setValueAtTime(e,n.ctx.currentTime);for(var t=0;t=0;o--)e._howls[o].unload();return e.usingWebAudio&&e.ctx&&void 0!==e.ctx.close&&(e.ctx.close(),e.ctx=null,_()),e},codecs:function(e){return(this||n)._codecs[e.replace(/^x-/,"")]},_setup:function(){var e=this||n;if(e.state=e.ctx?e.ctx.state||"suspended":"suspended",e._autoSuspend(),!e.usingWebAudio)if("undefined"!=typeof Audio)try{var o=new Audio;void 0===o.oncanplaythrough&&(e._canPlayEvent="canplay")}catch(n){e.noAudio=!0}else e.noAudio=!0;try{var o=new Audio;o.muted&&(e.noAudio=!0)}catch(e){}return e.noAudio||e._setupCodecs(),e},_setupCodecs:function(){var e=this||n,o=null;try{o="undefined"!=typeof Audio?new Audio:null}catch(n){return e}if(!o||"function"!=typeof o.canPlayType)return e;var t=o.canPlayType("audio/mpeg;").replace(/^no$/,""),r=e._navigator&&e._navigator.userAgent.match(/OPR\/([0-6].)/g),a=r&&parseInt(r[0].split("/")[1],10)<33;return e._codecs={mp3:!(a||!t&&!o.canPlayType("audio/mp3;").replace(/^no$/,"")),mpeg:!!t,opus:!!o.canPlayType('audio/ogg; codecs="opus"').replace(/^no$/,""),ogg:!!o.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),oga:!!o.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),wav:!!o.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),aac:!!o.canPlayType("audio/aac;").replace(/^no$/,""),caf:!!o.canPlayType("audio/x-caf;").replace(/^no$/,""),m4a:!!(o.canPlayType("audio/x-m4a;")||o.canPlayType("audio/m4a;")||o.canPlayType("audio/aac;")).replace(/^no$/,""),mp4:!!(o.canPlayType("audio/x-mp4;")||o.canPlayType("audio/mp4;")||o.canPlayType("audio/aac;")).replace(/^no$/,""),weba:!!o.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/,""),webm:!!o.canPlayType('audio/webm; codecs="vorbis"').replace(/^no$/,""),dolby:!!o.canPlayType('audio/mp4; codecs="ec-3"').replace(/^no$/,""),flac:!!(o.canPlayType("audio/x-flac;")||o.canPlayType("audio/flac;")).replace(/^no$/,"")},e},_unlockAudio:function(){var e=this||n,o=/iPhone|iPad|iPod|Android|BlackBerry|BB10|Silk|Mobi|Chrome|Safari/i.test(e._navigator&&e._navigator.userAgent);if(!e._audioUnlocked&&e.ctx&&o){e._audioUnlocked=!1,e.autoUnlock=!1,e._mobileUnloaded||44100===e.ctx.sampleRate||(e._mobileUnloaded=!0,e.unload()),e._scratchBuffer=e.ctx.createBuffer(1,1,22050);var t=function(n){for(var o=0;o0?i._seek:t._sprite[e][0]/1e3),s=Math.max(0,(t._sprite[e][0]+t._sprite[e][1])/1e3-_),l=1e3*s/Math.abs(i._rate),c=t._sprite[e][0]/1e3,f=(t._sprite[e][0]+t._sprite[e][1])/1e3,p=!(!i._loop&&!t._sprite[e][2]);i._sprite=e,i._ended=!1;var m=function(){i._paused=!1,i._seek=_,i._start=c,i._stop=f,i._loop=p};if(_>=f)return void t._ended(i);var v=i._node;if(t._webAudio){var h=function(){t._playLock=!1,m(),t._refreshBuffer(i);var e=i._muted||t._muted?0:i._volume;v.gain.setValueAtTime(e,n.ctx.currentTime),i._playStart=n.ctx.currentTime,void 0===v.bufferSource.start?i._loop?v.bufferSource.noteGrainOn(0,_,86400):v.bufferSource.noteGrainOn(0,_,s):i._loop?v.bufferSource.start(0,_,86400):v.bufferSource.start(0,_,s),l!==1/0&&(t._endTimers[i._id]=setTimeout(t._ended.bind(t,i),l)),o||setTimeout(function(){t._emit("play",i._id),t._loadQueue()},0)};"running"===n.state?h():(t._playLock=!0,t.once("resume",h),t._clearTimer(i._id))}else{var y=function(){v.currentTime=_,v.muted=i._muted||t._muted||n._muted||v.muted,v.volume=i._volume*n.volume(),v.playbackRate=i._rate;try{var r=v.play();if(r&&"undefined"!=typeof Promise&&(r instanceof Promise||"function"==typeof r.then)?(t._playLock=!0,m(),r.then(function(){t._playLock=!1,v._unlocked=!0,o||(t._emit("play",i._id),t._loadQueue())}).catch(function(){t._playLock=!1,t._emit("playerror",i._id,"Playback was unable to start. This is most commonly an issue on mobile devices and Chrome where playback was not within a user interaction."),i._ended=!0,i._paused=!0})):o||(t._playLock=!1,m(),t._emit("play",i._id),t._loadQueue()),v.playbackRate=i._rate,v.paused)return void t._emit("playerror",i._id,"Playback was unable to start. This is most commonly an issue on mobile devices and Chrome where playback was not within a user interaction.");"__default"!==e||i._loop?t._endTimers[i._id]=setTimeout(t._ended.bind(t,i),l):(t._endTimers[i._id]=function(){t._ended(i),v.removeEventListener("ended",t._endTimers[i._id],!1)},v.addEventListener("ended",t._endTimers[i._id],!1))}catch(e){t._emit("playerror",i._id,e)}},g=window&&window.ejecta||!v.readyState&&n._navigator.isCocoonJS;if(v.readyState>=3||g)y();else{t._playLock=!0;var b=function(){y(),v.removeEventListener(n._canPlayEvent,b,!1)};v.addEventListener(n._canPlayEvent,b,!1),t._clearTimer(i._id)}}return i._id},pause:function(e){var n=this;if("loaded"!==n._state||n._playLock)return n._queue.push({event:"pause",action:function(){n.pause(e)}}),n;for(var o=n._getSoundIds(e),t=0;t=0?o=parseInt(r[0],10):e=parseFloat(r[0])}else r.length>=2&&(e=parseFloat(r[0]),o=parseInt(r[1],10));var a;if(!(void 0!==e&&e>=0&&e<=1))return a=o?t._soundById(o):t._sounds[0],a?a._volume:0;if("loaded"!==t._state||t._playLock)return t._queue.push({event:"volume",action:function(){t.volume.apply(t,r)}}),t;void 0===o&&(t._volume=e),o=t._getSoundIds(o);for(var u=0;u0?t/_:t),l=Date.now();e._fadeTo=o,e._interval=setInterval(function(){var r=(Date.now()-l)/t;l=Date.now(),i+=d*r,i=Math.max(0,i),i=Math.min(1,i),i=Math.round(100*i)/100,u._webAudio?e._volume=i:u.volume(i,e._id,!0),a&&(u._volume=i),(on&&i>=o)&&(clearInterval(e._interval),e._interval=null,e._fadeTo=null,u.volume(o,e._id),u._emit("fade",e._id))},s)},_stopFade:function(e){var o=this,t=o._soundById(e);return t&&t._interval&&(o._webAudio&&t._node.gain.cancelScheduledValues(n.ctx.currentTime),clearInterval(t._interval),t._interval=null,o.volume(t._fadeTo,e),t._fadeTo=null,o._emit("fade",e)),o},loop:function(){var e,n,o,t=this,r=arguments;if(0===r.length)return t._loop;if(1===r.length){if("boolean"!=typeof r[0])return!!(o=t._soundById(parseInt(r[0],10)))&&o._loop;e=r[0],t._loop=e}else 2===r.length&&(e=r[0],n=parseInt(r[1],10));for(var a=t._getSoundIds(n),u=0;u=0?o=parseInt(r[0],10):e=parseFloat(r[0])}else 2===r.length&&(e=parseFloat(r[0]),o=parseInt(r[1],10));var i;if("number"!=typeof e)return i=t._soundById(o),i?i._rate:t._rate;if("loaded"!==t._state||t._playLock)return t._queue.push({event:"rate",action:function(){t.rate.apply(t,r)}}),t;void 0===o&&(t._rate=e),o=t._getSoundIds(o);for(var d=0;d=0?o=parseInt(r[0],10):t._sounds.length&&(o=t._sounds[0]._id,e=parseFloat(r[0]))}else 2===r.length&&(e=parseFloat(r[0]),o=parseInt(r[1],10));if(void 0===o)return t;if("loaded"!==t._state||t._playLock)return t._queue.push({event:"seek",action:function(){t.seek.apply(t,r)}}),t;var i=t._soundById(o);if(i){if(!("number"==typeof e&&e>=0)){if(t._webAudio){var d=t.playing(o)?n.ctx.currentTime-i._playStart:0,_=i._rateSeek?i._rateSeek-i._seek:0;return i._seek+(_+d*Math.abs(i._rate))}return i._node.currentTime}var s=t.playing(o);s&&t.pause(o,!0),i._seek=e,i._ended=!1,t._clearTimer(o),t._webAudio||!i._node||isNaN(i._node.duration)||(i._node.currentTime=e);var l=function(){t._emit("seek",o),s&&t.play(o,!0)};if(s&&!t._webAudio){var c=function(){t._playLock?setTimeout(c,0):l()};setTimeout(c,0)}else l()}return t},playing:function(e){var n=this;if("number"==typeof e){var o=n._soundById(e);return!!o&&!o._paused}for(var t=0;t=0&&n._howls.splice(a,1);var u=!0;for(t=0;t=0){u=!1;break}return r&&u&&delete r[e._src],n.noAudio=!1,e._state="unloaded",e._sounds=[],e=null,null},on:function(e,n,o,t){var r=this,a=r["_on"+e];return"function"==typeof n&&a.push(t?{id:o,fn:n,once:t}:{id:o,fn:n}),r},off:function(e,n,o){var t=this,r=t["_on"+e],a=0;if("number"==typeof n&&(o=n,n=null),n||o)for(a=0;a=0;a--)r[a].id&&r[a].id!==n&&"load"!==e||(setTimeout(function(e){e.call(this,n,o)}.bind(t,r[a].fn),0),r[a].once&&t.off(e,r[a].fn,r[a].id));return t._loadQueue(e),t},_loadQueue:function(e){var n=this;if(n._queue.length>0){var o=n._queue[0];o.event===e&&(n._queue.shift(),n._loadQueue()),e||o.action()}return n},_ended:function(e){var o=this,t=e._sprite;if(!o._webAudio&&e._node&&!e._node.paused&&!e._node.ended&&e._node.currentTime=0;t--){if(o<=n)return;e._sounds[t]._ended&&(e._webAudio&&e._sounds[t]._node&&e._sounds[t]._node.disconnect(0),e._sounds.splice(t,1),o--)}}},_getSoundIds:function(e){var n=this;if(void 0===e){for(var o=[],t=0;t=0;if(n._scratchBuffer&&e.bufferSource&&(e.bufferSource.onended=null,e.bufferSource.disconnect(0),t))try{e.bufferSource.buffer=n._scratchBuffer}catch(e){}return e.bufferSource=null,o}};var t=function(e){this._parent=e,this.init()};t.prototype={init:function(){var e=this,o=e._parent;return e._muted=o._muted,e._loop=o._loop,e._volume=o._volume,e._rate=o._rate,e._seek=0,e._paused=!0,e._ended=!0,e._sprite="__default",e._id=++n._counter,o._sounds.push(e),e.create(),e},create:function(){var e=this,o=e._parent,t=n._muted||e._muted||e._parent._muted?0:e._volume;return o._webAudio?(e._node=void 0===n.ctx.createGain?n.ctx.createGainNode():n.ctx.createGain(),e._node.gain.setValueAtTime(t,n.ctx.currentTime),e._node.paused=!0,e._node.connect(n.masterGain)):(e._node=n._obtainHtml5Audio(),e._errorFn=e._errorListener.bind(e),e._node.addEventListener("error",e._errorFn,!1),e._loadFn=e._loadListener.bind(e),e._node.addEventListener(n._canPlayEvent,e._loadFn,!1),e._node.src=o._src,e._node.preload="auto",e._node.volume=t*n.volume(),e._node.load()),e},reset:function(){var e=this,o=e._parent;return e._muted=o._muted,e._loop=o._loop,e._volume=o._volume,e._rate=o._rate,e._seek=0,e._rateSeek=0,e._paused=!0,e._ended=!0,e._sprite="__default",e._id=++n._counter,e},_errorListener:function(){var e=this;e._parent._emit("loaderror",e._id,e._node.error?e._node.error.code:0),e._node.removeEventListener("error",e._errorFn,!1)},_loadListener:function(){var e=this,o=e._parent;o._duration=Math.ceil(10*e._node.duration)/10,0===Object.keys(o._sprite).length&&(o._sprite={__default:[0,1e3*o._duration]}),"loaded"!==o._state&&(o._state="loaded",o._emit("load"),o._loadQueue()),e._node.removeEventListener(n._canPlayEvent,e._loadFn,!1)}};var r={},a=function(e){var n=e._src;if(r[n])return e._duration=r[n].duration,void d(e);if(/^data:[^;]+;base64,/.test(n)){for(var o=atob(n.split(",")[1]),t=new Uint8Array(o.length),a=0;a0?(r[o._src]=e,d(o,e)):t()};"undefined"!=typeof Promise&&1===n.ctx.decodeAudioData.length?n.ctx.decodeAudioData(e).then(a).catch(t):n.ctx.decodeAudioData(e,a,t)},d=function(e,n){n&&!e._duration&&(e._duration=n.duration),0===Object.keys(e._sprite).length&&(e._sprite={__default:[0,1e3*e._duration]}),"loaded"!==e._state&&(e._state="loaded",e._emit("load"),e._loadQueue())},_=function(){if(n.usingWebAudio){try{"undefined"!=typeof AudioContext?n.ctx=new AudioContext:"undefined"!=typeof webkitAudioContext?n.ctx=new webkitAudioContext:n.usingWebAudio=!1}catch(e){n.usingWebAudio=!1}n.ctx||(n.usingWebAudio=!1);var e=/iP(hone|od|ad)/.test(n._navigator&&n._navigator.platform),o=n._navigator&&n._navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/),t=o?parseInt(o[1],10):null;if(e&&t&&t<9){var r=/safari/.test(n._navigator&&n._navigator.userAgent.toLowerCase());(n._navigator&&n._navigator.standalone&&!r||n._navigator&&!n._navigator.standalone&&!r)&&(n.usingWebAudio=!1)}n.usingWebAudio&&(n.masterGain=void 0===n.ctx.createGain?n.ctx.createGainNode():n.ctx.createGain(),n.masterGain.gain.setValueAtTime(n._muted?0:1,n.ctx.currentTime),n.masterGain.connect(n.ctx.destination)),n._setup()}};"function"==typeof define&&define.amd&&define([],function(){return{Howler:n,Howl:o}}),"undefined"!=typeof exports&&(exports.Howler=n,exports.Howl=o),"undefined"!=typeof window?(window.HowlerGlobal=e,window.Howler=n,window.Howl=o,window.Sound=t):"undefined"!=typeof global&&(global.HowlerGlobal=e,global.Howler=n,global.Howl=o,global.Sound=t)}();
+/*! Spatial Plugin */
+!function(){"use strict";HowlerGlobal.prototype._pos=[0,0,0],HowlerGlobal.prototype._orientation=[0,0,-1,0,1,0],HowlerGlobal.prototype.stereo=function(e){var n=this;if(!n.ctx||!n.ctx.listener)return n;for(var t=n._howls.length-1;t>=0;t--)n._howls[t].stereo(e);return n},HowlerGlobal.prototype.pos=function(e,n,t){var r=this;return r.ctx&&r.ctx.listener?(n="number"!=typeof n?r._pos[1]:n,t="number"!=typeof t?r._pos[2]:t,"number"!=typeof e?r._pos:(r._pos=[e,n,t],void 0!==r.ctx.listener.positionX?(r.ctx.listener.positionX.setTargetAtTime(r._pos[0],Howler.ctx.currentTime,.1),r.ctx.listener.positionY.setTargetAtTime(r._pos[1],Howler.ctx.currentTime,.1),r.ctx.listener.positionZ.setTargetAtTime(r._pos[2],Howler.ctx.currentTime,.1)):r.ctx.listener.setPosition(r._pos[0],r._pos[1],r._pos[2]),r)):r},HowlerGlobal.prototype.orientation=function(e,n,t,r,o,i){var a=this;if(!a.ctx||!a.ctx.listener)return a;var s=a._orientation;return n="number"!=typeof n?s[1]:n,t="number"!=typeof t?s[2]:t,r="number"!=typeof r?s[3]:r,o="number"!=typeof o?s[4]:o,i="number"!=typeof i?s[5]:i,"number"!=typeof e?s:(a._orientation=[e,n,t,r,o,i],void 0!==a.ctx.listener.forwardX?(a.ctx.listener.forwardX.setTargetAtTime(e,Howler.ctx.currentTime,.1),a.ctx.listener.forwardY.setTargetAtTime(n,Howler.ctx.currentTime,.1),a.ctx.listener.forwardZ.setTargetAtTime(t,Howler.ctx.currentTime,.1),a.ctx.listener.upX.setTargetAtTime(e,Howler.ctx.currentTime,.1),a.ctx.listener.upY.setTargetAtTime(n,Howler.ctx.currentTime,.1),a.ctx.listener.upZ.setTargetAtTime(t,Howler.ctx.currentTime,.1)):a.ctx.listener.setOrientation(e,n,t,r,o,i),a)},Howl.prototype.init=function(e){return function(n){var t=this;return t._orientation=n.orientation||[1,0,0],t._stereo=n.stereo||null,t._pos=n.pos||null,t._pannerAttr={coneInnerAngle:void 0!==n.coneInnerAngle?n.coneInnerAngle:360,coneOuterAngle:void 0!==n.coneOuterAngle?n.coneOuterAngle:360,coneOuterGain:void 0!==n.coneOuterGain?n.coneOuterGain:0,distanceModel:void 0!==n.distanceModel?n.distanceModel:"inverse",maxDistance:void 0!==n.maxDistance?n.maxDistance:1e4,panningModel:void 0!==n.panningModel?n.panningModel:"HRTF",refDistance:void 0!==n.refDistance?n.refDistance:1,rolloffFactor:void 0!==n.rolloffFactor?n.rolloffFactor:1},t._onstereo=n.onstereo?[{fn:n.onstereo}]:[],t._onpos=n.onpos?[{fn:n.onpos}]:[],t._onorientation=n.onorientation?[{fn:n.onorientation}]:[],e.call(this,n)}}(Howl.prototype.init),Howl.prototype.stereo=function(n,t){var r=this;if(!r._webAudio)return r;if("loaded"!==r._state)return r._queue.push({event:"stereo",action:function(){r.stereo(n,t)}}),r;var o=void 0===Howler.ctx.createStereoPanner?"spatial":"stereo";if(void 0===t){if("number"!=typeof n)return r._stereo;r._stereo=n,r._pos=[n,0,0]}for(var i=r._getSoundIds(t),a=0;a new Howl({
+ src: [`assets/audio/impact-${i}.wav`],
+ volume: 0.5
+ })),
+}
+
+audio.fire.play();
+audio.music.play();
diff --git a/src/ball.js b/src/ball.js
index d6ae3e2..e405694 100644
--- a/src/ball.js
+++ b/src/ball.js
@@ -1,10 +1,5 @@
const RADIUS = 10;
const SEGMENTS = 40;
-const material = new THREE.MeshLambertMaterial({
- color: 0xFFFFFF,
- emissive: 0xFF0000,
- emissiveIntensity: 0.4,
-});
const ballPhysics = new PHYSICS.SphereModule({
mass: 2,
friction: 0.5,
@@ -20,7 +15,11 @@ const ball = new WHS.Sphere({
z: -30,
y: 10
},
- material,
+ material: new THREE.MeshLambertMaterial({
+ color: 0xFFFFFF,
+ emissive: 0xFF0000,
+ emissiveIntensity: 0.4,
+ }),
modules: [
new WHS.TextureModule({
@@ -30,21 +29,16 @@ const ball = new WHS.Sphere({
],
});
-const fireLight = new WHS.PointLight({
- color: 0xff0000,
- intensity: 5,
-});
-
ball.physics = ballPhysics;
-ball.fireLight = fireLight;
ball.addTo(app);
-ball.add(fireLight);
camera.native.target = ball.native;
ball.checkLife = () => {
- if (ball.position.x > ground.geometry.parameters.width / 2 || ball.position.x < -ground.geometry.parameters.width / 2) {
+ if (ball.position.x > ground.geometry.parameters.width / 2
+ || ball.position.x < -ground.geometry.parameters.width / 2
+ || ball.position.z > -ground.SIZE) {
location.reload();
}
}
@@ -79,66 +73,37 @@ ball.checkLife = () => {
color: 0xffffff,
});
- new THREE.Geometry();
- var smokeCount = 2000,
- smokes = new THREE.Geometry(),
- smokeMaterial = new THREE.PointsMaterial({
- size: 1.5,
- transparent: true,
- color: 0x666666,
- opacity: 0.5,
- });
-
const radius = ball.params.geometry.radius;
const start = ball.position;
for (var p = 0; p < particleCount; p++) {
-
- // create a particle with random
- // position values, -250 -> 250
var pX = start.x + THREE.Math.randFloatSpread(radius),
pY = start.y + THREE.Math.randFloatSpread(radius),
pZ = start.z + THREE.Math.randFloatSpread(radius),
particle = new THREE.Vector3(pX, pY, pZ)
- particle.lifetime = ball.params.geometry.radius + THREE.Math.randFloat(1, 1 + FIRE_HEIGHT_RANDOMNESS) * FIRE_HEIGHT;
- //particle.color.offsetHSL(Math.random(), 0, Math.random());
+ particle.lifetime = Math.random() < 0.5 ? 0 : ball.params.geometry.radius + THREE.Math.randFloat(0.5, 1 + FIRE_HEIGHT_RANDOMNESS) * FIRE_HEIGHT;
- // add it to the geometry
particles.vertices.push(particle);
particles.colors.push(new THREE.Color(1, 0, 0));
}
- particles.colorsNeedUpdate = true;
-
for (var i = 0; i < spriteCount; i++) {
var pX = start.x + THREE.Math.randFloatSpread(radius),
pY = start.y + THREE.Math.randFloatSpread(radius),
pZ = start.z + THREE.Math.randFloatSpread(radius),
particle = new THREE.Vector3(pX, pY, pZ)
- particle.lifetime = ball.params.geometry.radius + THREE.Math.randFloat(1, 1 + FIRE_HEIGHT_RANDOMNESS) * FIRE_HEIGHT;
+ particle.lifetime = Math.random() < 0.5 ? 0 : ball.params.geometry.radius + THREE.Math.randFloat(0.5, 1 + FIRE_HEIGHT_RANDOMNESS) * FIRE_HEIGHT;
sprites.vertices.push(particle);
}
- for (var i = 0; i < smokeCount; i++) {
- var pX = start.x + THREE.Math.randFloatSpread(radius),
- pY = start.y + THREE.Math.randFloat(radius),
- pZ = start.z + THREE.Math.randFloatSpread(radius),
- particle = new THREE.Vector3(pX, pY, pZ);
-
- particle.lifetime = ball.params.geometry.radius + THREE.Math.randFloat(1, 1 + FIRE_HEIGHT_RANDOMNESS) * FIRE_HEIGHT * 1.5;
-
- smokes.vertices.push(particle);
- }
// create the particle system
var points = WHS.MeshComponent.from(new THREE.Points(particles, pMaterial));
var spritesPoints = WHS.MeshComponent.from(new THREE.Points(sprites, spriteMaterial));
- var smokePoints = WHS.MeshComponent.from(new THREE.Points(smokes, smokeMaterial));
- smokePoints.isSmoke = true;
- ball.fire = [points, spritesPoints, smokePoints];
+ ball.fire = [points, spritesPoints];
ball.fire.forEach(fire => {
fire.addTo(app);
})
@@ -154,31 +119,39 @@ ball.checkLife = () => {
);
fire.geometry.vertices.forEach((v, i) => {
- v.y += THREE.Math.randFloat(0, SPREAD_SPEED / 4);
- v.x += THREE.Math.randFloatSpread(SPREAD_SPEED) + velocity.x * -0.02;
- v.z += THREE.Math.randFloatSpread(SPREAD_SPEED) + velocity.z * -0.01;
+ let x = v.x + THREE.Math.randFloatSpread(SPREAD_SPEED) + velocity.x * -0.02;
+ let y = v.y + THREE.Math.randFloat(0, SPREAD_SPEED / 4);
+ let z = v.z + THREE.Math.randFloatSpread(SPREAD_SPEED) + velocity.z * -0.01;
- if (fire.material.vertexColors === THREE.VertexColors) {
+ v.set(x, y, z);
+
+ if (!v.isSmoke && fire.material.vertexColors === THREE.VertexColors) {
fire.geometry.colors[i].offsetHSL(7e-4, 0, 0);
}
if (v.y >= v.lifetime) {
- if (fire.isSmoke) {
- v.y = FIRE_HEIGHT/3 + THREE.Math.randFloat(0, FIRE_HEIGHT_RANDOMNESS);
+ if (!v.isSmoke && fire.material.vertexColors === THREE.VertexColors) {
+ v.isSmoke = true;
+ fire.geometry.colors[i].setHex(0x444444)
+ v.lifetime += FIRE_HEIGHT * 0.5;
} else {
- v.y = Math.min(radius, radius * velocity.z / -50);
- }
+ y = Math.min(radius, radius * velocity.z / -50);
- v.x = 0;
- v.z = -ball.params.geometry.radius * 3;
+ x = 0;
+ z = -ball.params.geometry.radius * 3;
- v.lifetime = ball.params.geometry.radius + THREE.Math.randFloat(1, 1 + FIRE_HEIGHT_RANDOMNESS) * FIRE_HEIGHT * (fire.isSmoke ? 1.5 : 1);
+ v.lifetime = ball.params.geometry.radius + THREE.Math.randFloat(1, 1 + FIRE_HEIGHT_RANDOMNESS) * FIRE_HEIGHT;
- if (fire.material.vertexColors === THREE.VertexColors) {
- const r = THREE.Math.randFloat(0.8, 1);
- const g = THREE.Math.randFloat(0, 0.2);
- const b = THREE.Math.randFloat(0, 0.2);
- fire.geometry.colors[i].setRGB(r, g, b);
+ v.set(x, y, z);
+
+ v.isSmoke = false;
+
+ if (fire.material.vertexColors === THREE.VertexColors) {
+ const r = THREE.Math.randFloat(0.8, 1);
+ const g = THREE.Math.randFloat(0, 0.2);
+ const b = THREE.Math.randFloat(0, 0.2);
+ fire.geometry.colors[i].setRGB(r, g, b);
+ }
}
}
});
@@ -189,5 +162,19 @@ ball.checkLife = () => {
fire.geometry.verticesNeedUpdate = true;
});
});
+
+ const fireLight = new WHS.PointLight({
+ color: 0xff0000,
+ intensity: 5,
+ });
+ ball.fireLight = fireLight;
+ ball.add(fireLight);
}());
+let hits = 0;
+ball.on('collision', (o, v, r, cn) => {
+ if (o.uuid === ground.native.uuid) return;
+ rand(audio.impacts).play();
+ hits++;
+ GUI.setHits(hits);
+});
diff --git a/src/controls.js b/src/controls.js
index 735e95a..a1e5ac9 100644
--- a/src/controls.js
+++ b/src/controls.js
@@ -1,14 +1,18 @@
-const DFORCE = 3e3;
+const DFORCE = 6e3;
const AFORCE = 500;
const VFORCE = 1e4;
Mousetrap.bind('left', () => {
if (!ball.started) return;
+ const v = ball.physics.getLinearVelocity();
+ ball.physics.setLinearVelocity({ x: Math.min(0, v.x), y: v.y, z: v.z });
ball.physics.applyCentralForce({ x: -DFORCE, y: 0, z: 0 });
});
Mousetrap.bind('right', () => {
if (!ball.started) return;
+ const v = ball.physics.getLinearVelocity();
+ ball.physics.setLinearVelocity({ x: Math.max(0, v.x), y: v.y, z: v.z });
ball.physics.applyCentralForce({ x: DFORCE, y: 0, z: 0 });
});
@@ -19,20 +23,20 @@ Mousetrap.bind('up', () => {
//ball.methods.updateVelocity();
});
-Mousetrap.bind('space', () => {
- if (!ball.started) return;
- if (ball.physics.data.touches.length) {
- ball.physics.applyCentralForce({ x: 0, y: VFORCE, z: 0 });
- }
- //ball.speed = Math.min(config.MAX_SPEED, Math.max(config.FINAL_SPEED, ball.speed + config.SPEED_INCREASE));
- //ball.methods.updateVelocity();
-});
+//Mousetrap.bind('space', () => {
+ //if (!ball.started) return;
+ //if (ball.physics.data.touches.length) {
+ //ball.physics.applyCentralForce({ x: 0, y: VFORCE, z: 0 });
+ //}
+ ////ball.speed = Math.min(config.MAX_SPEED, Math.max(config.FINAL_SPEED, ball.speed + config.SPEED_INCREASE));
+ ////ball.methods.updateVelocity();
+//});
-Mousetrap.bind('down', () => {
- if (!ball.started) return;
- ball.physics.applyCentralForce({ x: 0, y: 0, z: AFORCE * 3 });
- //ball.speed = Math.min(config.MAX_SPEED, Math.max(config.FINAL_SPEED, ball.speed - config.SPEED_INCREASE));
- //ball.methods.updateVelocity();
-});
+//Mousetrap.bind('down', () => {
+ //if (!ball.started) return;
+ //ball.physics.applyCentralForce({ x: 0, y: 0, z: AFORCE * 3 });
+ ////ball.speed = Math.min(config.MAX_SPEED, Math.max(config.FINAL_SPEED, ball.speed - config.SPEED_INCREASE));
+ ////ball.methods.updateVelocity();
+//});
diff --git a/src/gui.js b/src/gui.js
index 1f3196f..3cc4e21 100644
--- a/src/gui.js
+++ b/src/gui.js
@@ -3,6 +3,7 @@ const GUI = {
score: document.getElementById('score'),
speed: document.getElementById('speed'),
time: document.getElementById('time'),
+ hits: document.getElementById('hits'),
},
setScore(s) {
GUI.elements.score.textContent = Math.floor(s).toString().padStart(3, '0');
@@ -13,6 +14,9 @@ const GUI = {
setStartingTime() {
GUI.startingTime = moment();
},
+ setHits(s) {
+ GUI.elements.hits.textContent = Math.floor(s).toString().padStart(2, '0');
+ },
tickTime() {
const now = moment();
const diff = moment.duration(now.diff(GUI.startingTime));
diff --git a/src/levels/0-basic.js b/src/levels/0-basic.js
index 476ea81..c73c153 100644
--- a/src/levels/0-basic.js
+++ b/src/levels/0-basic.js
@@ -1,36 +1,127 @@
function level0() {
- function jumpBoard(x, z) {
- const cylinder = new WHS.Cylinder({
+ const step = rad(10);
+ const tan = Math.tan(step);
+ const BOX_SIZE = 100;
+
+ function add({ x, y, z } = {}, { width, depth, height } = {}, rotation = {}) {
+ y = y || (tan * z) + BOX_SIZE/2;
+ width = width || BOX_SIZE;
+ height = height || BOX_SIZE;
+ depth = depth || BOX_SIZE;
+
+ rotation.x = rotation.x || -step;
+ const box = new WHS.Box({
geometry: {
- radiusTop: 50,
- radiusBottom: 1,
- radiusSegments: 3,
- heightSegments: 1,
- height: 2,
+ width,
+ depth,
+ height,
},
material: new THREE.MeshPhongMaterial({
- color: 0xffff00,
+ color: 0xffffff,
}),
- position: [x, 0, z],
- rotation: [Math.PI, rad(-35), 0],
+ position: [x, y, z],
+
+ rotation,
modules: [
- new PHYSICS.CylinderModule({
+ new PHYSICS.BoxModule({
mass: 0,
+ friction: 1,
+ restitution: 0,
})
]
});
- //cylinder.addTo(app);
- ground.add(cylinder);
+ box.addTo(app);
+ return box;
}
- //jumpBoard(5, -50);
- //jumpBoard(5, -100);
- //
- //ring({ x: 0, y: 10, z: -150 });
+ const SPACE_UNIT = 250;
+
+ const patterns = [{
+ items: [3,4,5],
+ randoms: {
+ startX() {
+ return Math.random() > 0.5 ? 200 : -200;
+ },
+ },
+ x(i, props) {
+ return props.startX + i * 50 * (props.startX === 200 ? -1 : 1);
+ },
+ z(i) {
+ return i * -BOX_SIZE;
+ },
+ }, {
+ items: [3,4,6],
+ props: {
+ side: 150,
+ },
+ rot: {
+ y(i, props) {
+ if (i % 3 == 0) {
+ return THREE.Math.randFloatSpread(Math.PI);
+ }
+ return 0;
+ }
+ },
+ size(i, props) {
+ return THREE.Math.randInt(BOX_SIZE * 0.8, BOX_SIZE * 1.2);
+ },
+ x(i, props) {
+ if (i % 3 == 0) {
+ return 0;
+ } else {
+ return i % 3 == 1 ? props.side : -props.side;
+ }
+ },
+ z(i) {
+ if (i % 3 == 0) {
+ return i * -SPACE_UNIT;
+ } else {
+ return i % 3 == 1 ? i * -SPACE_UNIT : (i - 1) * -SPACE_UNIT;
+ }
+ }
+ }];
+
+
+ const applyPattern = (sz, { items, x, z, rot, props, randoms, space, size } = {}) => {
+ props = props || {};
+ randoms = randoms || {};
+ space = space || SPACE_UNIT;
+ rot = rot || {};
+ const count = rand(items);
+
+ const rs = {};
+ Object.entries(randoms).forEach(([key, value]) => {
+ rs[key] = value();
+ });
+
+ Object.assign(props, rs);
+
+ let zz;
+
+ for (let i = 0; i < count; i++) {
+ zz = sz + z(i, props);
+ const rotation = {};
+ Object.entries(rot).forEach(([key, value]) => {
+ rotation[key] = value(i, props);
+ });
+ let boxSize = BOX_SIZE;
+ if (size) {
+ boxSize = size(i, props);
+ }
+ add({ x: x(i, props), z: zz }, { width: boxSize, height: boxSize, depth: boxSize }, rotation);
+ }
+
+ return zz - space;
+ }
+
+ let sz = -300;
+ while (-sz < ground.SIZE) {
+ const pattern = rand(patterns);
+ sz = applyPattern(sz, pattern);
+ }
}
-level0();
diff --git a/src/scenes.js b/src/scenes.js
index ca68b92..1d47f9f 100644
--- a/src/scenes.js
+++ b/src/scenes.js
@@ -40,9 +40,13 @@ const entrance = () => {
GUI.setStartingTime();
const diff = camera.position.y - ball.position.y;
ball.started = true;
+ level0();
+
+ audio.rumble.play();
app.physics.addEventListener('update', function() {
- camera.position.set(camera.position.x, ball.position.y + diff, ball.position.z + 130);
+ //camera.position.set(camera.position.x, ball.position.y + diff, ball.position.z + 130);
+ camera.position.set(ball.position.x, ball.position.y + diff, ball.position.z + 130);
camera.native.lookAt(ball.position);
directionalLight.position.set(directionalLight.position.x, directionalLight.position.y, ball.position.z - 300);
GUI.setScore(-ball.position.z / 100);
diff --git a/src/utils.js b/src/utils.js
index cf60bd2..c7f225b 100644
--- a/src/utils.js
+++ b/src/utils.js
@@ -1,2 +1,4 @@
const rad = (deg) => deg * Math.PI / 180;
+const rand = (items) => items[Math.floor(Math.random() * items.length)];
+
diff --git a/src/world.js b/src/world.js
index 336d68a..25784dc 100644
--- a/src/world.js
+++ b/src/world.js
@@ -34,7 +34,7 @@ let ground;
(function groundinit() {
const WIDTH = 500;
const SIZE = 1e5;
- const mesh = new WHS.Plane({
+ ground = new WHS.Plane({
geometry: {
width: WIDTH,
height: SIZE,
@@ -51,12 +51,14 @@ let ground;
}),
new PHYSICS.PlaneModule({
mass: 0,
+ restitution: 0,
}),
],
material: new THREE.MeshPhongMaterial({ color: 0xffffff })
});
- ground = mesh;
+ ground.SIZE = SIZE;
+
ground.addTo(app);
}());