diff --git a/README.md b/README.md index 52007ed..09a9d57 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,22 @@ # 404 Orbiting Asteroids for JS13k 2020 +Play the latest version: https://deathraygames.github.io/404-js13k-2020/dist/ -Sounds +## JS13k -I considered [jsfxr](https://github.com/mneubrand/jsfxr), but then found that [ZzFX micro](https://github.com/KilledByAPixel/ZzFX/blob/master/ZzFX.micro.js) is smaller and offered simple sound effects that I was looking for. I created the sounds by experimenting with [the ZzFX tool](https://killedbyapixel.github.io/ZzFX/), and put everything into a `sounds.js` file. +* [JS13k Rules](http://2020.js13kgames.com/#rules): Make a game with a package size less than 13k (13,312 bytes) in one month +* Theme: *"404"* +* Entry for this game: ...(TBD)... +* See all the entries for the competition at http://2020.js13kgames.com/ + +## Post Mortem + +I didn't have a lot of inspiration with the theme of "404", so I decided to use this challenge as an excuse to [learn WebGL](https://xem.github.io/articles/webgl-guide.html) and to improve [my physics library](https://github.com/rocket-boots/physics). I enjoyed working with basic spaceship flying as part of [Return to the Moon](https://github.com/deathraygames/lunar-lander-13k) in 2019, so settled on making a simple [Asteroids](https://en.wikipedia.org/wiki/Asteroids_(video_game)) "clone". Because there wasn't a lot of originality to the gameplay, I ended up using a very literal title: *"404 Orbiting Asteroids"*. + +I spent most of the month learning WebGL and working on a **starfield background**, which resulted in [webgl-starfield](https://github.com/rocket-boots/webgl-starfield) and a helper library [webglp](https://github.com/rocket-boots/webglp) to help smooth out some of the ugly WebGL js code. + +As a clone of classic the **gameplay** didn't require too much thought: *Fly around, shoot asteroids*. I knew I wanted some gravity to make flying more interesting, and since it [didn't make too much sense](https://twitter.com/deathraygames/status/1300966473280753664/photo/1) to have the asteroids with a strong force of gravity, I decided to add a big mass in the center. I originally wanted a sun and a few planets (like [Star Hopper Glitch Jump](https://github.com/deathraygames/star-hopper-glitch)), but knew it would be too complicated to keep the planets in orbit, so I just stuck with one mass: a sun. + +When it game time to work on the **controls** I decided to copy Reassembly, a game I really enjoy. Rotation of the ship follows the mouse, clicking fires the weapons. To make the game playable entirely by mouse I made the right-click fire the engines. Later on I also added some keyboard controls: "w" to fire engines and space bar to fire. + +After the game was playable I realized it desperately needed some **sounds** to give the experience more depth. I considered [jsfxr](https://github.com/mneubrand/jsfxr), but then found that [ZzFX micro](https://github.com/KilledByAPixel/ZzFX/blob/master/ZzFX.micro.js) is smaller and offered simple sound effects that I was looking for. I created the sounds by experimenting with [the ZzFX tool](https://killedbyapixel.github.io/ZzFX/), and put everything into a `sounds.js` file. diff --git a/dist/404-orbiting-asteroids-13k.zip b/dist/404-orbiting-asteroids-13k.zip new file mode 100644 index 0000000..7f59afe Binary files /dev/null and b/dist/404-orbiting-asteroids-13k.zip differ diff --git a/dist/game-window.js b/dist/game-window.js index 5e2c876..76ada9a 100644 --- a/dist/game-window.js +++ b/dist/game-window.js @@ -1 +1 @@ -window.stars=function(t){var e={};function s(r){if(e[r])return e[r].exports;var i=e[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,s),i.l=!0,i.exports}return s.m=t,s.c=e,s.d=function(t,e,r){s.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},s.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},s.t=function(t,e){if(1&e&&(t=s(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(s.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)s.d(r,i,function(e){return t[e]}.bind(null,i));return r},s.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return s.d(e,"a",e),e},s.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},s.p="",s(s.s=2)}([function(t,e){!function(t,e){for(var s in e)t[s]=e[s]}(e,function(t){var e={};function s(r){if(e[r])return e[r].exports;var i=e[r]={i:r,l:!1,exports:{}};return t[r].call(i.exports,i,i.exports,s),i.l=!0,i.exports}return s.m=t,s.c=e,s.d=function(t,e,r){s.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},s.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},s.t=function(t,e){if(1&e&&(t=s(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(s.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var i in t)s.d(r,i,function(e){return t[e]}.bind(null,i));return r},s.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return s.d(e,"a",e),e},s.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},s.p="",s(s.s=0)}([function(t,e,s){"use strict";s.r(e);class r{constructor(t=0,e=0){this.x=t,this.y=e}set({x:t,y:e}){return this.x=t||0,this.y=e||0,this}clear(){this.x=0,this.y=0}fix(){return!!this.check()||(this.x=Number(this.x),this.y=Number(this.y),!!this.check()||(this.x=0,this.y=0,!1))}add({x:t,y:e}){return this.x+=t||0,this.y+=e||0,this}subtract({x:t,y:e}){return this.x-=t||0,this.y-=e||0,this}multiply(t){return this.x*=t||1,this.y*=t||1,this}abs(){return this.x=Math.abs(this.x),this.y=Math.abs(this.y),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this}reverse(){return this.multiply(-1)}normalize(){const t=this.getMagnitude();return 0===t?(this.x=0,this.y=0,this):(this.x=this.x/t,this.y=this.y/t,this)}setMagnitude(t){return this.normalize().multiply(t)}setTangent(){return this.set(this.y,this.x)}getDistance({x:t,y:e}){return Math.sqrt(Math.pow(this.x-t,2)+Math.pow(this.y-e,2))}getAbsoluteDistance(t={}){return Math.abs(this.getDistance(t))}getMagnitude(){return Math.sqrt(Math.pow(this.x,2)+Math.pow(this.y,2))}clone(){return new r(this.x,this.y)}getMultiply(t){return new r(this.x*t,this.y*t)}getDot({x:t,y:e}){return this.x*t+this.y*e}getUnitVector(t={}){const e=this.getAbsoluteDistance(t);if(0===e)return new r(0,0);const s=t.x-this.x,i=t.y-this.y;return new r(s/e,i/e)}getUnitVectorTangent(t={}){const e=this.getAbsoluteDistance(t);if(0===e)return new r(0,0);const s=(t.y-this.y)/e,i=(this.x-t.x)/e;return new r(s,i)}getPerpendicularVector(t=!1){return t?new r(-1*this.y,this.x):new r(this.y,-1*this.x)}getTangent(){return new r(this.y,this.x)}check(){return"number"==typeof this.x&&"number"==typeof this.y&&!isNaN(this.x)&&!isNaN(this.y)}isEqual({x:t,y:e}){return this.x===t&&this.y===e}isEqualInteger({x:t,y:e}){return Math.round(this.x)===Math.round(t)&&Math.round(this.y)===Math.round(e)}setByPolarCoords(t,e){return this.set({x:t*Math.cos(e),y:t*Math.sin(e)})}rotate(t,e){return this.subtract(e),this.setByPolarCoords(this.r,this.theta+t),this.add(e),this}getRadius(){return this.getDistance({x:0,y:0})}setRadius(t){return this.setByPolarCoords(t,this.getTheta()),this}getTheta(){return Math.atan2(this.y,this.x)}setTheta(t){this.setByPolarCoords(this.getRadius(),t)}getDegrees(){return this.getTheta()*(180/Math.PI)}equals(t={}){return this.isEqual(t)}copy(){return this.clone()}getCopy(){return this.clone()}getClone(){return this.clone()}}var i=r;e.default={Coords:i}}]).default)},,function(t,e,s){"use strict";s.r(e);class r{constructor(t){this.verts=t}static getRegularPolygonVerts(t=3,e=1,s=0){const r=[],i=2*Math.PI;for(let o=0;o[t[0]+s[0]/e,t[1]+s[1]/e],[0,0])}static getRadius([t,e]){return Math.sqrt(Math.pow(t,2)+Math.pow(e,2))}static getRadii(t){let e=1/0;const s=t.reduce((t,s)=>{const i=r.getRadius(s);return it?i:t},0);return{inner:e,outer:s}}}var i=r,o=s(0);function n(t,e){t.forEach(s=>{n.isPhysical(s)&&(s.collide&&s.collide(t),s.gravitate&&s.gravitate(e,t),s.move&&s.move(e))})}function a(){return new n.Coords}n.Coords=o.Coords,n.bigG=5,n.collidingVelocityMultiplier=.95,n.collidePushbackMultipler=.2,n.elasticity=.98,n.MIN_GRAVITY_RADIUS=1,n.isPhysical=t=>t.pos instanceof n.Coords&&t.vel instanceof n.Coords,n.getOrbitalVelocity=function(t,e,s=!1,r=n.bigG){const i=e.mass,o=t.pos.getDistance(e.pos),a=Math.sqrt(r*i/o);return t.pos.getUnitVector(e.pos).getPerpendicularVector(s).multiply(a)},n.canCollide=t=>({colliding:[],collideDetect(e){const s="number"==typeof t.innerRadius?t.innerRadius:0,r="number"==typeof e.innerRadius?e.innerRadius:0,i=t.pos.getDistance(e.pos)-s-r;return{isColliding:!(i>0),edgeToEdgeDistance:i}},collidePushback(e,s,r=n.collidePushbackMultipler){const i=s*r,o=t.mass>e.mass?t:e,a=o===t?e:t,c=a.pos.getUnitVector(o.pos).multiply(i);a.pos.add(c)},collideBounce(t,e=n.elasticity){var s=this;if(s.mass<=0||t.mass<=0)return!1;s.mass,s.vel.getMagnitude(),t.mass,t.vel.getMagnitude();var r=s.pos.getUnitVector(t.pos),i=2*(s.vel.getDot(r)-t.vel.getDot(r))/(s.mass+t.mass);return s.vel.add(r.getMultiply(-1*i*t.mass)),s.vel.multiply(e),t.vel.add(r.getMultiply(i*s.mass)),s.vel.multiply(e),!0},collideDamage(e){if(!t.damage)return;const s=t.vel.clone().add(e.vel).getMagnitude(),r=s<1?0:Math.ceil(Math.pow(s,1.4)/10);r&&t.damage(r,e)},collide(e){t.isColliding=!1,t.colliding.length=0,e.forEach(e=>{if(t===e||!n.isPhysical(e))return!1;const{isColliding:s,edgeToEdgeDistance:r}=t.collideDetect(e);return!!s&&(t.colliding.push(e),t.collidePushback(e,r),t.collideBounce(e),t.collideDamage(e),!0)}),t.isColliding=t.colliding.length>0,t.isColliding&&t.vel.multiply(n.collidingVelocityMultiplier)}}),n.canMove=t=>({lastPos:a(),pos:a(),force:a(),acc:a(),vel:a(),move(e){if(t.lastPos.set(t.pos),0!==t.mass){const e=new n.Coords(t.force.x/t.mass,t.force.y/t.mass);t.acc.add(e)}const s=t.acc.getMultiply(e);t.vel.add(s);const r=t.vel.getMultiply(e/2);t.pos.add(r),t.force.clear(),t.acc.clear()}}),n.canGravitate=(t,e=n.bigG)=>({gravitate(s,r){if(0===t.mass)return!1;const i=e*t.mass;r.forEach(e=>t.gravitateOne(e,i))},gravitateOne(e,s){if(e===t||0===e.mass||0===t.mass||t.isColliding||!t.gravitate||!n.isPhysical(e))return!1;const r=t.pos.getDistance(e.pos);if(r({rotation:0,rotVel:0,torque:0,momentOfInertia:0,inverseMomentOfInertia:0,rotate(e){t.rotation+=t.rotVel*e}}),n.physical=(t,{mass:e=1,bigG:s=n.bigG})=>(Object.assign(t,{mass:e},n.canCollide(t),n.canMove(t),n.canGravitate(t,s),n.canRotate(t)),t);var c=n;c.bigG=1e-6;var l=class extends i{constructor(t=[]){super([]);const e=()=>.2*Math.random()+.4,s=this;Object.assign(s,{baseVerts:t,baseColor:[e(),e(),e()],hitColor:[.7,0,0],boundingBox:[],hit:!1,verts:[],vc:null,r:0,innerRadius:0,children:[]}),c.physical(s,{mass:10}),s.alignToCenter(),s.calcRadii(),s.calcVerts(),s.calcMass(),s.Coords=c.Coords}alignToCenter(){const t=i.getCenter(this.baseVerts);this.baseVerts.forEach(e=>{[0,1].forEach(s=>e[s]=e[s]-t[s])})}calcRadii(){let t=1/0;this.r=this.baseVerts.reduce((e,s)=>{const r=s.r=i.getRadius(s);return re?r:e},0),this.innerRadius=t}calcMass(){this.mass=Math.PI*Math.pow((this.innerRadius+this.r)/2,2)*50}getVertColors(){return this.vc}getColor(){const t=this.baseColor;return[t[0]+(this.hit?.1:0),t[1]+(this.isColliding?.1:0),t[2]]}calcVerts(){let t=[];this.verts.length=0,this.baseVerts.forEach((e,s)=>{this.verts[s]=[e[0]+this.pos.x,e[1]+this.pos.y,0],t=t.concat(this.verts[s]).concat(this.getColor())}),this.vc=new Float32Array(t)}calcVertWithRotation(t,e,s){const r=Math.cos(this.rotation),i=Math.sin(this.rotation);return this.verts[s]=[r*e[0]-i*e[1]+this.pos.x,r*e[1]+i*e[0]+this.pos.y,0],t.concat(this.verts[s]).concat(this.getColor())}calcVertsWithRotation(){this.verts.length=0;let t=this.baseVerts.reduce((t,e,s)=>this.calcVertWithRotation(t,e,s),[]);this.vc=new Float32Array(t)}static rotate(t,e,s){let r=t[0]-s[0];r+=s[0]}setOrbitalVelocity(t){this.vel.set(c.getOrbitalVelocity(this,t))}};const h=[[.2,-.1,0],[.35,0,0],[.45,.1,0],[.4,.3,0],[.3,.4,0],[.2,.8,0],[.1,.4,0],[0,.3,0],[-.05,.1,0],[.05,0,0]];var u=class extends l{constructor(){super(h.map(t=>t.map(t=>1.5*t))),Object.assign(this,{shipScale:1.5,engaged:!1,facingRotationOffset:-Math.PI/2,thrustPowerUpMax:2,thrustPowerUpStart:.75,thrustPowerUp:.75,thrustPowerUpMultiplier:.5,thrustPowerDownMultiplier:2}),this.mass*=3,this.thrustMagnitude=100*this.mass,this.baseColor=[.6,1,.3]}setRotation(t){this.rotation=t+this.facingRotationOffset}getFacingUnitVector(){const t=this.rotation+this.facingRotationOffset,e=Math.cos(t),s=Math.sin(t);return new this.Coords(e,s)}ongoing(t){this.engaged?this.thrust(t):this.cooldownThrust(t)}engage(){this.engaged=!0,this.thrust(.02)}disengage(){this.engaged=!1}thrust(t){this.thrustPowerUp;this.thrustPowerUp+=t*this.thrustPowerUpMultiplier,this.thrustPowerUp=Math.min(this.thrustPowerUpMax,this.thrustPowerUp);const e=this.getFacingUnitVector().getMultiply(t*-this.thrustMagnitude*this.thrustPowerUp);this.force.add(e)}cooldownThrust(t){this.thrustPowerUp<=this.thrustPowerUpStart||(this.thrustPowerUp-=t*this.thrustPowerDownMultiplier,this.thrustPowerUp=Math.max(this.thrustPowerUpStart,this.thrustPowerUp))}fire(){return 22+2*Math.random()}};var d=class extends l{constructor(){const t=1.5+.5*Math.random()-.5*Math.random();super(l.getRegularPolygonVerts(3+Math.floor(7*Math.random()),t,.2*t))}};var g=class extends l{constructor(){super(l.getRegularPolygonVerts(40,40)),this.pos.set({x:0,y:0}),this.mass*=1e4,this.innerRadius*=.94}move(){}};let p=(t=1,e=.05,s=220,r=0,i=0,o=.1,n=0,a=1,c=0,l=0,h=0,u=0,d=0,g=0,p=0,v=0,M=0,b=1,w=0,x=0)=>{let P,R,C=2*Math.PI,V=c*=500*C/m**2,S=(0U?0:(Uu&&(s+=h,T+=h,D=0),!d||++_%d||(s=T,c=V,D=D||1);return(t=y.createBuffer(1,R,m)).getChannelData(0).set(O),(s=y.createBufferSource()).buffer=t,s.connect(y.destination),s.start(),s},f=.3,y=new(window.AudioContext||webkitAudioContext),m=44100;const v=[,,470,,,.13,4,1.78,-.3,,-50,.04,.02,.1,-.2,.4,,.51,.04,.01],M=[,,651,,.2,.88,4,1.22,.3,,,,,.6,,1,,.51,.01],b=[.6,,980,,.51,1.62,4,2.35,,,,,,.3,.7,.9,.13,.73,.06],w=[1.3,,117,.12,.27,1.21,4,2,.7,,,,,.8,,.9,.34,1.1,.04,.2],x=[,,452,,.08,.46,2,.73,2.4,,,,,,,,.04,.51];var P={gun(){p(...v)},explode(){p(...M)},thrust(){p(...b)},death(){p(...w)},jump(){p(...x)}};const R=new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]),C=(...t)=>console.log(...t);class V{constructor(t,e){Object.assign(this,{gl:t,p:e,i:0,aV:e.map(()=>({})),uV:e.map(()=>({}))})}use(t){return this.i=t,this.gl.useProgram(this.p[t]),this}v(t,e,s,r,i){const o=this,n=o[t][o.i][r]||o.gl[e](o.p[o.i],r);return o.gl[`${s}${i.length}f`](n,...i),o[t][this.i][r]=n}attr(t,...e){return this.v("aV","getAttribLocation","vertexAttrib",t,e)}unif(t,...e){return this.v("uV","getUniformLocation","uniform",t,e)}ua(t){t.forEach(t=>this.unif(...t))}buff(t,e,{size:s=2,type:r=this.gl.FLOAT,norm:i=!1,stride:o=0,offset:n=0}={}){const{gl:a,p:c}=this;a.bindBuffer(a.ARRAY_BUFFER,a.createBuffer()),a.bufferData(a.ARRAY_BUFFER,e,a.STATIC_DRAW);const l=e.BYTES_PER_ELEMENT,h=a.getAttribLocation(c[this.i],t);return a.vertexAttribPointer(h,s,r,i,o*l,n*l),a.enableVertexAttribArray(h),h}ba(t,e){e.forEach(e=>this.buff(e[0],t,e[1]))}clear(){this.gl.clearColor(0,0,0,1),this.gl.clear(this.gl.COLOR_BUFFER_BIT)}draw({uniforms:t=[],i:e=this.i,buffs:s=[],verts:r=R,vertSize:i=2,vertsToDraw:o,type:n=this.gl.TRIANGLES,clear:a=!0}){const c=this;return c.use(e),c.ua(t),c.ba(r,s),a&&c.clear(),void 0===o&&(o=r.length/i),c.gl.drawArrays(n,0,o),c}drawAll(t={}){this.p.forEach((e,s)=>this.draw(Object.assign({i:s,clear:!s},t)))}}const S={Glp:V,STV:R,STNPV:2,getRenderingContext:(t,e=!1)=>{const s=document.querySelector(t).getContext("webgl",{antialias:e});return s||alert("Unable to initialize WebGL. Your browser or machine may not support it."),s},loadText:t=>fetch(t).then(t=>t.text()),loadShaders:t=>Promise.all(t.map(t=>S.loadText(t))),compileShader:(t,e,s)=>{const r=t.createShader(e);return t.shaderSource(r,s),t.compileShader(r),r},fullscreen:(t,e=window)=>{t.canvas.width=e.innerWidth,t.canvas.height=e.innerHeight,S.setViewport(t)},setViewport:t=>{t.viewport(0,0,t.canvas.width,t.canvas.height)},compile:(t,e)=>{const s=t.createProgram(),r=[t.VERTEX_SHADER,t.FRAGMENT_SHADER],i=["vertex","fragment"];return e.map((e,o)=>{const n=S.compileShader(t,r[o],e);t.attachShader(s,n),C(i[o]+" shader:",t.getShaderInfoLog(n)||"OK")}),t.linkProgram(s),t.useProgram(s),C("program:",t.getProgramInfoLog(s)||"OK"),s},init:async(t,e,{fullscreen:s}={})=>{const r="string"==typeof t?S.getRenderingContext(t):t,i=e.map(t=>S.loadShaders(t).then(t=>S.compile(r,t))),o=await Promise.all(i);return s&&S.fullscreen(r),new V(r,o)}};var T=S;var O=class extends l{constructor(t,e,s=[1,.5,0],r=.2,i=8){super(l.getRegularPolygonVerts(i,r)),this.pos.set(t),this.vel.set(e),Object.assign(this,{mass:0,baseColor:s,baseR:r,maxR:10*r,r:r,explosionSpeed:6+3*Math.random()})}ongoing(t){this.r+=t*this.explosionSpeed,this.r>this.maxR&&(this.delete=!0),this.baseVerts=l.getRegularPolygonVerts(8,this.r),this.calcVerts()}};const A=()=>20*Math.random()*(Math.random()<.5?1:-1);var E=class extends l{constructor(t,e=2){const s=.75*t.r/e,r=Math.max(3,Math.round(t.verts.length*Math.random()));super(l.getRegularPolygonVerts(r,s,s)),this.baseColor=t.baseColor.map(t=>Math.max(0,t-.3)),this.pos.set(t.pos);const i={x:A(),y:A()};this.vel.set(t.vel).add(i)}};const U=[["v.glsl","stars-f.glsl"],["space-v.glsl","space-f.glsl"]];let D,_,I=20,j=404,L=!1,F=!1;const k=t=>document.getElementById(t),B=(t=1)=>Math.random()*t,G={},N=[],z=function(){const t=new g;return N.push(t),t}(),q=(function(t){for(let e=0;e<404;e++){const e=new d;st(e,t),e.damage=(s,r)=>{r===t&&st(e,t)},N.push(e)}}(z),function(t){const e=new u;return e.pos.set({x:120,y:0}),e.setOrbitalVelocity(t),e.damage=(e,s)=>{s===t&&$("sun")},N.push(e),e}(z)),W=()=>{const t=[["iResolution",D.gl.canvas.width,D.gl.canvas.height],["zoom",I],["viewerPosition",q.pos.x,q.pos.y,0],["iTime",0]];D.use(0).draw({uniforms:t,buffs:[["position"]]});const e=[["position",{size:3,stride:6}],["color",{size:3,stride:6,offset:3}]];D.use(1).draw({uniforms:t,buffs:e,verts:new Float32Array([]),vertSize:6,type:D.gl.TRIANGLE_FAN,clear:!1}),N.forEach(t=>{D.draw({buffs:e,verts:t.getVertColors(),vertSize:6,type:D.gl.TRIANGLE_FAN,clear:!1})})};function Y(t){if(G[t])return;G[t]=!0;const e=k("ach-"+t);e&&e.classList.add("unlocked"),Object.keys(G).length>=5&&k("intro").classList.add("closed")}function H(t,e){return t.pos.getDistance(z.pos)>e&&(st(t,z),!0)}const K=t=>{let e=0;const s=[];return N.forEach((r,i)=>{r.delete?s.push(i):(r.ongoing&&r.ongoing(t),r.rotate(t),r.calcVertsWithRotation(),!r.gravitate||r instanceof g||r.gravitate(t,[z]),r.move(t),r.collide(N),r instanceof d?(e++,H(r,250)):r instanceof u&&H(r,800)&&P.jump())}),((t=[])=>{for(let e=t.length-1;e>=0;e--){const s=t[e];N.splice(s,1)}})(s),{asteroidCount:e}};function X(t,e=8){t.decayTime=e,t.ongoing=e=>{t.decayTime-=e,t.decayTime<0&&(t.delete=!0)}}function $(t){F=!0,q.delete=!0,et(q),Q(q.pos,q.vel),P.death(),function(t="sun"){k("main").classList.remove("go"),F&&(k("intro").classList.add("closed"),k("dead").classList.remove("closed"),k(t).style.display="block")}(t)}const J=(t,e)=>{const s=new l([[0,.2,0],[-.1,-.1,0],[0,-.2,0],[.1,-.1,0]]);s.rotation=t.rotation;const r=t.getFacingUnitVector().multiply(-1);s.pos.set(t.pos).add(r.getMultiply(t.shipScale)),s.vel.set(t.vel).add(r.getMultiply(e)),s.mass*=.5,X(s,10),s.damage=(t,e)=>{var r;s.decayTime*=.5,e instanceof d?((r=e).delete=!0,et(r),Q(r.pos,r.vel),P.explode()):e instanceof u&&$("bullet")},s.gravitate=null,N.push(s)};function Q(t,e){const s=new O(t,e),r=new O(t,e,[1,1,0],.05,3);N.push(s),N.push(r)}function Z(){const t=.07+B(.05),e=l.getRegularPolygonVerts(3,t),s=new l(e);s.baseColor=[.5,.3,.5],s.rotation=B(2*Math.PI);const r=q.getFacingUnitVector();s.pos.set(q.pos).add(r.getMultiply(.5*q.shipScale)),s.vel.set(q.vel).add(r.getMultiply(14)),X(s,10),s.gravitate=null,N.push(s)}function tt(t,e){const s=new E(t,e);s.damage=(t,e)=>{e===z&&(s.delete=!0)},X(s,30),rt(s),N.push(s)}function et(t){const e=2+Math.floor(B(4));for(let s=0;s{let s=0;_=r=>{window.requestAnimationFrame(i=>{const o=void 0===r?(i-s)/1e3*1:r;t.engaged&&Z();const{asteroidCount:n}=K(o);var a;j!==(a=n)&&(e.innerHTML=a,j=a,0===a&&(k("win").style.display="block")),W(),s=i,_()})},K(0),W()};const ot=(t,e)=>{const s=[t.width,t.height],r=()=>{e.engage(),P.thrust(),Z(),Y("thrust")},i=()=>{const t=e.fire();J(e,t),P.gun(),Y("shoot")};window.addEventListener("wheel",t=>{const e=.001*Math.min(600,Math.abs(t.deltaY))*I,s=t.deltaY<0?-1:1;I=Math.max(.4,Math.min(5e3,I+s*e)),L||W(),Y("zoom")}),window.addEventListener("keydown",t=>{switch(t.key.toUpperCase()){case"W":r()}}),window.addEventListener("keyup",t=>{switch(t.key.toUpperCase()){case"W":e.disengage();break;case" ":i()}}),window.oncontextmenu=t=>t.preventDefault(),t.onmousedown=t.ontouchstart=t=>{F||3===t.which&&r()},t.onmouseup=t.ontouchend=t=>{F||(L?3!==t.which?i():e.disengage():L||(L=!0,k("main").classList.add("go"),Y("start"),_(0)))},t.onmousemove=t.ontouchmove=t=>{if(F)return;if(!L)return;const r=(t=>{const e=t.type.startsWith("touch")?t.targetTouches[0]:t;return[e.pageX,e.pageY]})(t).map((t,e)=>(t-s[e]/2)*(1===e?-1:1)),i=Math.atan2(r[1],r[0]);e.rotation=i-Math.PI/2,Y("rotate")}};document.addEventListener("DOMContentLoaded",async()=>(D=await T.init("#canvas",U,{fullscreen:!0}),window.z.glp=D,console.log(D),ot(D.gl.canvas,q),it(q,k("count")),D)),window.z={SpaceObject:l,glp:D,objects:N}}]).default; +window.stars=function(t){var e={};function s(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,s),o.l=!0,o.exports}return s.m=t,s.c=e,s.d=function(t,e,r){s.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},s.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},s.t=function(t,e){if(1&e&&(t=s(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(s.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)s.d(r,o,function(e){return t[e]}.bind(null,o));return r},s.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return s.d(e,"a",e),e},s.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},s.p="",s(s.s=1)}([function(t,e){!function(t,e){for(var s in e)t[s]=e[s]}(e,function(t){var e={};function s(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,s),o.l=!0,o.exports}return s.m=t,s.c=e,s.d=function(t,e,r){s.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},s.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},s.t=function(t,e){if(1&e&&(t=s(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(s.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)s.d(r,o,function(e){return t[e]}.bind(null,o));return r},s.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return s.d(e,"a",e),e},s.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},s.p="",s(s.s=0)}([function(t,e,s){"use strict";s.r(e);class r{constructor(t=0,e=0){this.x=t,this.y=e}set({x:t,y:e}){return this.x=t||0,this.y=e||0,this}clear(){this.x=0,this.y=0}fix(){return!!this.check()||(this.x=Number(this.x),this.y=Number(this.y),!!this.check()||(this.x=0,this.y=0,!1))}add({x:t,y:e}){return this.x+=t||0,this.y+=e||0,this}subtract({x:t,y:e}){return this.x-=t||0,this.y-=e||0,this}multiply(t){return this.x*=t||1,this.y*=t||1,this}abs(){return this.x=Math.abs(this.x),this.y=Math.abs(this.y),this}round(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this}reverse(){return this.multiply(-1)}normalize(){const t=this.getMagnitude();return 0===t?(this.x=0,this.y=0,this):(this.x=this.x/t,this.y=this.y/t,this)}setMagnitude(t){return this.normalize().multiply(t)}setTangent(){return this.set(this.y,this.x)}getDistance({x:t,y:e}){return Math.sqrt(Math.pow(this.x-t,2)+Math.pow(this.y-e,2))}getAbsoluteDistance(t={}){return Math.abs(this.getDistance(t))}getMagnitude(){return Math.sqrt(Math.pow(this.x,2)+Math.pow(this.y,2))}clone(){return new r(this.x,this.y)}getMultiply(t){return new r(this.x*t,this.y*t)}getDot({x:t,y:e}){return this.x*t+this.y*e}getUnitVector(t={}){const e=this.getAbsoluteDistance(t);if(0===e)return new r(0,0);const s=t.x-this.x,o=t.y-this.y;return new r(s/e,o/e)}getUnitVectorTangent(t={}){const e=this.getAbsoluteDistance(t);if(0===e)return new r(0,0);const s=(t.y-this.y)/e,o=(this.x-t.x)/e;return new r(s,o)}getPerpendicularVector(t=!1){return t?new r(-1*this.y,this.x):new r(this.y,-1*this.x)}getTangent(){return new r(this.y,this.x)}check(){return"number"==typeof this.x&&"number"==typeof this.y&&!isNaN(this.x)&&!isNaN(this.y)}isEqual({x:t,y:e}){return this.x===t&&this.y===e}isEqualInteger({x:t,y:e}){return Math.round(this.x)===Math.round(t)&&Math.round(this.y)===Math.round(e)}setByPolarCoords(t,e){return this.set({x:t*Math.cos(e),y:t*Math.sin(e)})}rotate(t,e){return this.subtract(e),this.setByPolarCoords(this.r,this.theta+t),this.add(e),this}getRadius(){return this.getDistance({x:0,y:0})}setRadius(t){return this.setByPolarCoords(t,this.getTheta()),this}getTheta(){return Math.atan2(this.y,this.x)}setTheta(t){this.setByPolarCoords(this.getRadius(),t)}getDegrees(){return this.getTheta()*(180/Math.PI)}equals(t={}){return this.isEqual(t)}copy(){return this.clone()}getCopy(){return this.clone()}getClone(){return this.clone()}}var o=r;e.default={Coords:o}}]).default)},function(t,e,s){"use strict";s.r(e);class r{constructor(t){this.verts=t}static getRegularPolygonVerts(t=3,e=1,s=0){const r=[],o=2*Math.PI;for(let i=0;i[t[0]+s[0]/e,t[1]+s[1]/e],[0,0])}static getRadius([t,e]){return Math.sqrt(Math.pow(t,2)+Math.pow(e,2))}static getRadii(t){let e=1/0;const s=t.reduce((t,s)=>{const o=r.getRadius(s);return ot?o:t},0);return{inner:e,outer:s}}}var o=r,i=s(0);function n(t,e){t.forEach(s=>{n.isPhysical(s)&&(s.collide&&s.collide(t),s.gravitate&&s.gravitate(e,t),s.move&&s.move(e))})}function a(){return new n.Coords}n.Coords=i.Coords,n.bigG=5,n.collidingVelocityMultiplier=.95,n.collidePushbackMultipler=.2,n.elasticity=.98,n.MIN_GRAVITY_RADIUS=1,n.checkAabbCollision=(t,e)=>{const s=t.outerRadius||t.innerRadius,r=e.outerRadius||e.innerRadius,o=t.pos.x+s,i=t.pos.y+s,n=e.pos.x-r,a=e.pos.y-r;if(n>o||a>i)return!1;const c=t.pos.x-s,l=t.pos.y-s,h=e.pos.x+r,u=e.pos.y+r;return!(c>h||l>u)},n.checkRadiusCollision=(t,e)=>{const s="number"==typeof t.innerRadius?t.innerRadius:0,r="number"==typeof e.innerRadius?e.innerRadius:0,o=t.pos.getDistance(e.pos)-s-r;return{isColliding:!(o>0),edgeToEdgeDistance:o}},n.isPhysical=t=>t.pos instanceof n.Coords&&t.vel instanceof n.Coords,n.getOrbitalVelocity=function(t,e,s=!1,r=n.bigG){const o=e.mass,i=t.pos.getDistance(e.pos),a=Math.sqrt(r*o/i);return t.pos.getUnitVector(e.pos).getPerpendicularVector(s).multiply(a)},n.canCollide=t=>({colliding:[],collideDetect(t){return!!n.checkAabbCollision(this,t)&&n.checkRadiusCollision(this,t)},collidePushback(e,s,r=n.collidePushbackMultipler){const o=t.mass>e.mass?t:e,i=o===t?e:t,a=i.pos.getUnitVector(o.pos).multiply(s*r);return i.pos.add(a),{pusher:o,pushee:i,push:a}},collideBounce(t,e=n.elasticity){var s=this;if(s.mass<=0||t.mass<=0)return!1;s.mass,s.vel.getMagnitude(),t.mass,t.vel.getMagnitude();var r=s.pos.getUnitVector(t.pos),o=2*(s.vel.getDot(r)-t.vel.getDot(r))/(s.mass+t.mass);return s.vel.add(r.getMultiply(-1*o*t.mass)),s.vel.multiply(e),t.vel.add(r.getMultiply(o*s.mass)),s.vel.multiply(e),!0},collideDamage(e){if(!t.damage)return;const s=t.vel.clone().add(e.vel).getMagnitude(),r=s<1?0:Math.ceil(Math.pow(s,1.4)/10);r&&t.damage(r,e)},collide(e){t.isColliding=!1,t.colliding.length=0,e.forEach(e=>{if(t===e||!n.isPhysical(e))return!1;const{isColliding:s,edgeToEdgeDistance:r}=t.collideDetect(e);return!!s&&(t.colliding.push(e),t.collidePushback(e,r),t.collideBounce(e),t.collideDamage(e),!0)}),t.isColliding=t.colliding.length>0,t.isColliding&&t.vel.multiply(n.collidingVelocityMultiplier)}}),n.canMove=t=>({lastPos:a(),pos:a(),force:a(),acc:a(),vel:a(),move(e){if(t.lastPos.set(t.pos),0!==t.mass){const e=new n.Coords(t.force.x/t.mass,t.force.y/t.mass);t.acc.add(e)}const s=t.acc.getMultiply(e);t.vel.add(s);const r=t.vel.getMultiply(e/2);t.pos.add(r),t.force.clear(),t.acc.clear()}}),n.canGravitate=(t,e=n.bigG)=>({gravitate(s,r){if(0===t.mass)return!1;const o=e*t.mass;r.forEach(e=>t.gravitateOne(e,o))},gravitateOne(e,s){if(e===t||0===e.mass||0===t.mass||t.isColliding||!t.gravitate||!n.isPhysical(e))return!1;const r=t.pos.getDistance(e.pos);if(r({rotation:0,rotVel:0,torque:0,momentOfInertia:0,inverseMomentOfInertia:0,rotate(e){t.rotation+=t.rotVel*e}}),n.physical=(t,{mass:e=1,bigG:s=n.bigG})=>(Object.assign(t,{mass:e},n.canCollide(t),n.canMove(t),n.canGravitate(t,s),n.canRotate(t)),t);var c=n;c.bigG=1e-6;var l=class extends o{constructor(t=[]){super([]);const e=()=>.2*Math.random()+.4,s=this;Object.assign(s,{baseVerts:t,baseColor:[e(),e(),e()],hitColor:[.7,0,0],hit:!1,verts:[],vc:null,r:0,outerRadius:0,innerRadius:0,children:[]}),c.physical(s,{mass:10}),s.alignToCenter(),s.calcRadii(),s.calcVerts(),s.calcMass(),s.Coords=c.Coords}alignToCenter(){const t=o.getCenter(this.baseVerts);this.baseVerts.forEach(e=>{[0,1].forEach(s=>e[s]=e[s]-t[s])})}calcRadii(){let t=1/0;this.outerRadius=this.r=this.baseVerts.reduce((e,s)=>{const r=s.r=o.getRadius(s);return re?r:e},0),this.innerRadius=t}calcMass(){this.mass=Math.PI*Math.pow((this.innerRadius+this.r)/2,2)*50}getVertColors(){return this.vc}getColor(t,e,s){const r=this.baseColor;return[r[0]+(this.hit?.1:0),r[1]+(this.isColliding?.1:0),r[2]]}calcVerts(){let t=[];this.verts.length=0,this.baseVerts.forEach((e,s)=>{const r=this.verts[s]=[e[0]+this.pos.x,e[1]+this.pos.y,0];t=t.concat(r).concat(this.getColor(r,e,s))}),this.vc=new Float32Array(t)}calcVertWithRotation(t,e,s){const r=Math.cos(this.rotation),o=Math.sin(this.rotation);return this.verts[s]=[r*e[0]-o*e[1]+this.pos.x,r*e[1]+o*e[0]+this.pos.y,0],t.concat(this.verts[s]).concat(this.getColor())}calcVertsWithRotation(){this.verts.length=0;let t=this.baseVerts.reduce((t,e,s)=>this.calcVertWithRotation(t,e,s),[]);this.vc=new Float32Array(t)}static rotate(t,e,s){let r=t[0]-s[0];r+=s[0]}setOrbitalVelocity(t){this.vel.set(c.getOrbitalVelocity(this,t))}};const h=[[.2,-.1,0],[.35,0,0],[.45,.1,0],[.4,.3,0],[.3,.4,0],[.2,.8,0],[.1,.4,0],[0,.3,0],[-.05,.1,0],[.05,0,0]];var u=class extends l{constructor(){super(h.map(t=>t.map(t=>1.5*t))),Object.assign(this,{shipScale:1.5,engaged:!1,facingRotationOffset:-Math.PI/2,thrustPowerUpMax:2,thrustPowerUpStart:.75,thrustPowerUp:.75,thrustPowerUpMultiplier:.5,thrustPowerDownMultiplier:2}),this.mass*=3,this.thrustMagnitude=100*this.mass,this.baseColor=[.6,1,.3]}setRotation(t){this.rotation=t+this.facingRotationOffset}getFacingUnitVector(){const t=this.rotation+this.facingRotationOffset,e=Math.cos(t),s=Math.sin(t);return new this.Coords(e,s)}ongoing(t){this.engaged?this.thrust(t):this.cooldownThrust(t)}engage(){this.engaged=!0,this.thrust(.02)}disengage(){this.engaged=!1}thrust(t){this.thrustPowerUp;this.thrustPowerUp+=t*this.thrustPowerUpMultiplier,this.thrustPowerUp=Math.min(this.thrustPowerUpMax,this.thrustPowerUp);const e=this.getFacingUnitVector().getMultiply(t*-this.thrustMagnitude*this.thrustPowerUp);this.force.add(e)}cooldownThrust(t){this.thrustPowerUp<=this.thrustPowerUpStart||(this.thrustPowerUp-=t*this.thrustPowerDownMultiplier,this.thrustPowerUp=Math.max(this.thrustPowerUpStart,this.thrustPowerUp))}fire(){return 22+2*Math.random()}};var d=class extends l{constructor(){const t=1.5+.5*Math.random()-.5*Math.random();super(l.getRegularPolygonVerts(3+Math.floor(7*Math.random()),t,.2*t))}};var g=class extends l{constructor(){super(l.getRegularPolygonVerts(40,40)),this.pos.set({x:0,y:0}),this.mass*=1e4,this.innerRadius*=.94}move(){}};let p=(t=1,e=.05,s=220,r=0,o=0,i=.1,n=0,a=1,c=0,l=0,h=0,u=0,d=0,g=0,p=0,v=0,M=0,b=1,w=0,x=0)=>{let P,R,C=2*Math.PI,V=c*=500*C/m**2,S=(0U?0:(Uu&&(s+=h,T+=h,D=0),!d||++_%d||(s=T,c=V,D=D||1);return(t=y.createBuffer(1,R,m)).getChannelData(0).set(E),(s=y.createBufferSource()).buffer=t,s.connect(y.destination),s.start(),s},f=.3,y=new(window.AudioContext||webkitAudioContext),m=44100;const v=[,,470,,,.13,4,1.78,-.3,,-50,.04,.02,.1,-.2,.4,,.51,.04,.01],M=[,,651,,.2,.88,4,1.22,.3,,,,,.6,,1,,.51,.01],b=[.6,,980,,.51,1.62,4,2.35,,,,,,.3,.7,.9,.13,.73,.06],w=[1.3,,117,.12,.27,1.21,4,2,.7,,,,,.8,,.9,.34,1.1,.04,.2],x=[,,452,,.08,.46,2,.73,2.4,,,,,,,,.04,.51];var P={gun(){p(...v)},explode(){p(...M)},thrust(){p(...b)},death(){p(...w)},jump(){p(...x)}};const R=new Float32Array([-1,-1,1,-1,-1,1,-1,1,1,-1,1,1]),C=(...t)=>console.log(...t);class V{constructor(t,e){Object.assign(this,{gl:t,p:e,i:0,aV:e.map(()=>({})),uV:e.map(()=>({}))})}use(t){return this.i=t,this.gl.useProgram(this.p[t]),this}v(t,e,s,r,o){const i=this,n=i[t][i.i][r]||i.gl[e](i.p[i.i],r);return i.gl[`${s}${o.length}f`](n,...o),i[t][this.i][r]=n}attr(t,...e){return this.v("aV","getAttribLocation","vertexAttrib",t,e)}unif(t,...e){return this.v("uV","getUniformLocation","uniform",t,e)}ua(t){t.forEach(t=>this.unif(...t))}buff(t,e,{size:s=2,type:r=this.gl.FLOAT,norm:o=!1,stride:i=0,offset:n=0}={}){const{gl:a,p:c}=this;a.bindBuffer(a.ARRAY_BUFFER,a.createBuffer()),a.bufferData(a.ARRAY_BUFFER,e,a.STATIC_DRAW);const l=e.BYTES_PER_ELEMENT,h=a.getAttribLocation(c[this.i],t);return a.vertexAttribPointer(h,s,r,o,i*l,n*l),a.enableVertexAttribArray(h),h}ba(t,e){e.forEach(e=>this.buff(e[0],t,e[1]))}clear(){this.gl.clearColor(0,0,0,1),this.gl.clear(this.gl.COLOR_BUFFER_BIT)}draw({uniforms:t=[],i:e=this.i,buffs:s=[],verts:r=R,vertSize:o=2,vertsToDraw:i,type:n=this.gl.TRIANGLES,clear:a=!0}){const c=this;return c.use(e),c.ua(t),c.ba(r,s),a&&c.clear(),void 0===i&&(i=r.length/o),c.gl.drawArrays(n,0,i),c}drawAll(t={}){this.p.forEach((e,s)=>this.draw(Object.assign({i:s,clear:!s},t)))}}const S={Glp:V,STV:R,STNPV:2,getRenderingContext:(t,e=!1)=>{const s=document.querySelector(t).getContext("webgl",{antialias:e});return s||alert("Unable to initialize WebGL. Your browser or machine may not support it."),s},loadText:t=>fetch(t).then(t=>t.text()),loadShaders:t=>Promise.all(t.map(t=>S.loadText(t))),compileShader:(t,e,s)=>{const r=t.createShader(e);return t.shaderSource(r,s),t.compileShader(r),r},fullscreen:(t,e=window)=>{t.canvas.width=e.innerWidth,t.canvas.height=e.innerHeight,S.setViewport(t)},setViewport:t=>{t.viewport(0,0,t.canvas.width,t.canvas.height)},compile:(t,e)=>{const s=t.createProgram(),r=[t.VERTEX_SHADER,t.FRAGMENT_SHADER],o=["vertex","fragment"];return e.map((e,i)=>{const n=S.compileShader(t,r[i],e);t.attachShader(s,n),C(o[i]+" shader:",t.getShaderInfoLog(n)||"OK")}),t.linkProgram(s),t.useProgram(s),C("program:",t.getProgramInfoLog(s)||"OK"),s},init:async(t,e,{fullscreen:s}={})=>{const r="string"==typeof t?S.getRenderingContext(t):t,o=e.map(t=>S.loadShaders(t).then(t=>S.compile(r,t))),i=await Promise.all(o);return s&&S.fullscreen(r),new V(r,i)}};var T=S;var E=class extends l{constructor(t,e,s=[1,.5,0],r=.2,o=8){super(l.getRegularPolygonVerts(o,r)),this.pos.set(t),this.vel.set(e),Object.assign(this,{mass:0,baseColor:s,baseR:r,maxR:10*r,r:r,explosionSpeed:6+3*Math.random()})}ongoing(t){this.r+=t*this.explosionSpeed,this.r>this.maxR&&(this.delete=!0),this.baseVerts=l.getRegularPolygonVerts(8,this.r),this.calcVerts()}};const O=()=>20*Math.random()*(Math.random()<.5?1:-1);var A=class extends l{constructor(t,e=2){const s=.75*t.r/e,r=Math.max(3,Math.round(t.verts.length*Math.random()));super(l.getRegularPolygonVerts(r,s,s)),this.baseColor=t.baseColor.map(t=>Math.max(0,t-.3)),this.pos.set(t.pos);const o={x:O(),y:O()};this.vel.set(t.vel).add(o)}};const U=[["v.glsl","stars-f.glsl"],["space-v.glsl","space-f.glsl"]];let D,_,I=20,j=404,L=!1,k=!1;const F=t=>document.getElementById(t),B=(t=1)=>Math.random()*t,G={},N=[],z=[],q=function(){const t=new g,e=t.baseColor.map(t=>Math.max(0,t-.3)),s=1.1*t.r;return[8,8,4,4,4,3].forEach(t=>function(t,e,s){const r=l.getRegularPolygonVerts(t,e),o=new l(r);o.baseColor=s,o.mass=0,o.vel=null,o.move=()=>{},o.collide=()=>{},o.gravitate=null,ot(o,.1),z.push(o)}(t,s,e)),N.push(t),t}(),W=(function(t){for(let e=0;e<404;e++){const e=new d;rt(e,t),e.damage=(s,r)=>{r===t&&rt(e,t)},N.push(e)}}(q),function(t){const e=new u;return e.pos.set({x:120,y:0}),e.setOrbitalVelocity(t),e.damage=(e,s)=>{s===t&&J("sun")},N.push(e),e}(q)),Y=()=>{const t=[["iResolution",D.gl.canvas.width,D.gl.canvas.height],["zoom",I],["viewerPosition",W.pos.x,W.pos.y,0],["iTime",0]];D.use(0).draw({uniforms:t,buffs:[["position"]]});const e=[["position",{size:3,stride:6}],["color",{size:3,stride:6,offset:3}]];D.use(1).draw({uniforms:t,buffs:e,verts:new Float32Array([]),vertSize:6,type:D.gl.TRIANGLE_FAN,clear:!1}),z.concat(N).forEach(t=>{D.draw({buffs:e,verts:t.getVertColors(),vertSize:6,type:D.gl.TRIANGLE_FAN,clear:!1})})};function H(t){if(G[t])return;G[t]=!0;const e=F("ach-"+t);e&&e.classList.add("unlocked"),Object.keys(G).length>=5&&F("intro").classList.add("closed")}function K(t,e){return t.pos.getDistance(q.pos)>e&&(rt(t,q),!0)}const X=t=>{let e=0;const s=[];return N.forEach((r,o)=>{r.delete?s.push(o):(r.ongoing&&r.ongoing(t),r.rotate(t),r.calcVertsWithRotation(),!r.gravitate||r instanceof g||r.gravitate(t,[q]),r.move(t),r.collide(N),r instanceof d?(e++,K(r,250)):r instanceof u&&K(r,800)&&P.jump())}),z.forEach(e=>{e.rotate(t),e.calcVertsWithRotation()}),((t=[])=>{for(let e=t.length-1;e>=0;e--){const s=t[e];N.splice(s,1)}})(s),{asteroidCount:e}};function $(t,e=8){t.decayTime=e,t.ongoing=e=>{t.decayTime-=e,t.decayTime<0&&(t.delete=!0)}}function J(t){k=!0,W.delete=!0,st(W,10),Z(W.pos,W.vel,5),P.death(),function(t="sun"){F("main").classList.remove("go"),k&&(F("intro").classList.add("closed"),F("dead").classList.remove("closed"),F(t).style.display="block")}(t)}const Q=(t,e)=>{const s=new l([[0,.2,0],[-.1,-.1,0],[0,-.2,0],[.1,-.1,0]]);s.rotation=t.rotation;const r=t.getFacingUnitVector().multiply(-1);s.pos.set(t.pos).add(r.getMultiply(t.shipScale)),s.vel.set(t.vel).add(r.getMultiply(e)),s.mass*=.5,$(s,10),s.damage=(t,e)=>{var r;s.baseColor[0]=.9,s.decayTime*=.5,e instanceof d?((r=e).delete=!0,st(r),Z(r.pos,r.vel),P.explode()):e instanceof u&&J("bullet")},s.gravitate=null,N.push(s)};function Z(t,e,s=1){const r=new E(t,e,[1,.5,0],.2*s,8),o=new E(t,e,[1,1,0],.05*s,3);N.push(r),N.push(o)}function tt(){const t=.07+B(.05),e=l.getRegularPolygonVerts(3,t),s=new l(e);s.baseColor=[.5,.3,.5],s.rotation=B(2*Math.PI);const r=W.getFacingUnitVector();s.pos.set(W.pos).add(r.getMultiply(.5*W.shipScale)),s.vel.set(W.vel).add(r.getMultiply(14)),$(s,10),s.gravitate=null,N.push(s)}function et(t,e){const s=new A(t,e);s.damage=(t,e)=>{e===q&&(s.delete=!0)},$(s,30),ot(s),N.push(s)}function st(t,e=4){const s=2+Math.floor(B(e));for(let e=0;e{let s=0;_=r=>{window.requestAnimationFrame(o=>{const i=void 0===r?(o-s)/1e3*1:r;t.engaged&&tt();const{asteroidCount:n}=X(i);var a;j!==(a=n)&&(e.innerHTML=a,j=a,0===a&&(F("win").style.display="block")),Y(),s=o,_()})},X(0),Y()};const nt=(t,e)=>{const s=[t.width,t.height],r=()=>{e.engage(),P.thrust(),tt(),H("thrust")},o=()=>{const t=e.fire();Q(e,t),P.gun(),H("shoot")};window.addEventListener("wheel",t=>{const e=.001*Math.min(600,Math.abs(t.deltaY))*I,s=t.deltaY<0?-1:1;I=Math.max(.4,Math.min(5e3,I+s*e)),L||Y(),H("zoom")}),window.addEventListener("keydown",t=>{switch(t.key.toUpperCase()){case"W":r()}}),window.addEventListener("keyup",t=>{switch(t.key.toUpperCase()){case"W":e.disengage();break;case" ":o()}}),window.oncontextmenu=t=>t.preventDefault(),t.onmousedown=t.ontouchstart=t=>{k||3===t.which&&r()},t.onmouseup=t.ontouchend=t=>{k||(L?3!==t.which?o():e.disengage():L||(L=!0,F("main").classList.add("go"),H("start"),_(0)))},t.onmousemove=t.ontouchmove=t=>{if(k)return;if(!L)return;const r=(t=>{const e=t.type.startsWith("touch")?t.targetTouches[0]:t;return[e.pageX,e.pageY]})(t).map((t,e)=>(t-s[e]/2)*(1===e?-1:1)),o=Math.atan2(r[1],r[0]);e.rotation=o-Math.PI/2,H("rotate")},document.addEventListener("click",t=>{console.log(t.target,t),"restart"===t.target.id&&location.reload()})};document.addEventListener("DOMContentLoaded",async()=>(D=await T.init("#canvas",U,{fullscreen:!0}),window.z.glp=D,console.log(D),nt(D.gl.canvas,W),it(W,F("count")),D)),window.z={SpaceObject:l,glp:D,objects:N}}]).default; diff --git a/dist/index.html b/dist/index.html index 19aa856..21d3c0e 100644 --- a/dist/index.html +++ b/dist/index.html @@ -3,9 +3,9 @@ 404 Orbiting Asteroids - A js13k game for 2020 - + - + @@ -137,7 +146,8 @@ You died.

The sun is hot! 🌞+🚀=💀

Ricochet! 🚀+🔫=💀

-

🔄 Refresh the page (F5) to start over.

+

Click below or 🔄 Refresh the page (F5) to start over.

+ diff --git a/package-lock.json b/package-lock.json index 3791759..fecd014 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2979,8 +2979,8 @@ "from": "git+https://github.com/rocket-boots/coords.git#v0.6.0" }, "rocket-boots-physics": { - "version": "git+https://github.com/rocket-boots/physics.git#a37264bc8c08e418d397e244b917b3d2189f2762", - "from": "git+https://github.com/rocket-boots/physics.git#v0.4.0", + "version": "git+https://github.com/rocket-boots/physics.git#6d514a8ee641dbc4f35e9a23d8864e2f831f4f10", + "from": "git+https://github.com/rocket-boots/physics.git#v0.5.0", "requires": { "rocket-boots-coords": "git+https://github.com/rocket-boots/Coords.git#v0.6.0" }, diff --git a/package.json b/package.json index af22047..54b2734 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ }, "dependencies": { "rocket-boots-coords": "git+https://github.com/rocket-boots/coords.git#v0.6.0", - "rocket-boots-physics": "git+https://github.com/rocket-boots/physics.git#v0.4.0", + "rocket-boots-physics": "git+https://github.com/rocket-boots/physics.git#v0.5.0", "webglp": "git+https://github.com/rocket-boots/webglp.git#v0.1.0" } } diff --git a/src/SpaceObject.js b/src/SpaceObject.js index 73ec4ce..fe854f1 100644 --- a/src/SpaceObject.js +++ b/src/SpaceObject.js @@ -1,10 +1,10 @@ import Polygon from './Polygon.js'; // import physics from '../../../rocket-boots-repos/physics/src/physics.js'; -// import { physics } from 'rocket-boots-physics'; import physics from '../node_modules/rocket-boots-physics/src/physics.js'; -// import { Coords } from 'rocket-boots-coords'; +// import { Coords } from 'rocket-boots-coords'; // physics.Coords = Coords; + physics.bigG = .000001; class SpaceObject extends Polygon { @@ -22,7 +22,7 @@ class SpaceObject extends Polygon { c(), ], hitColor: [.7, 0., 0.], - boundingBox: [], + // boundingBox: [], hit: false, verts: [], // set in calc vc: null, // set in calc @@ -32,6 +32,7 @@ class SpaceObject extends Polygon { // acc: new Coords(), // vel: new Coords(), r: 0, + outerRadius: 0, innerRadius: 0, children: [], } @@ -61,7 +62,7 @@ class SpaceObject extends Polygon { calcRadii() { let inner = Infinity; // Outer/largest radius - this.r = this.baseVerts.reduce((n, v) => { + this.outerRadius = this.r = this.baseVerts.reduce((n, v) => { // Store radius on each base vertex for quicker computation later const r = v.r = Polygon.getRadius(v); if (r < inner) { inner = r; } @@ -79,7 +80,7 @@ class SpaceObject extends Polygon { return this.vc; } - getColor() { + getColor(v, bv, i) { const bc = this.baseColor; return [ bc[0] + (this.hit ? .1 : 0.), @@ -92,8 +93,8 @@ class SpaceObject extends Polygon { let vc = []; this.verts.length = 0; this.baseVerts.forEach((bv, i) => { - this.verts[i] = [bv[0] + this.pos.x, bv[1] + this.pos.y, 0]; // bv[2] + this.pos.z]; - vc = vc.concat(this.verts[i]).concat(this.getColor()); + const v = this.verts[i] = [bv[0] + this.pos.x, bv[1] + this.pos.y, 0]; // bv[2] + this.pos.z]; + vc = vc.concat(v).concat(this.getColor(v, bv, i)); }); this.vc = new Float32Array(vc); } diff --git a/src/game.js b/src/game.js index f35a1dd..dd8f38c 100644 --- a/src/game.js +++ b/src/game.js @@ -41,6 +41,7 @@ const rand = (n = 1) => Math.random() * n; const achievements = {}; const objects = []; +const effects = []; const sun = setupSun(); const asteroids = setupAsteroids(sun); const ship = setupShip(sun) @@ -58,7 +59,7 @@ const draw = () => { // Draw stars background glp.use(0).draw({ uniforms, buffs: [['position']] }); - // Draw galaxy + // Draw "space" galaxy const buffs = [ ['position', { size: 3, stride: 6 }], ['color', { size: 3, stride: 6, offset: 3 }], @@ -72,7 +73,7 @@ const draw = () => { clear: false, }); - objects.forEach((o) => { + effects.concat(objects).forEach((o) => { // glp.unif('translation', 0, 0, 0); // o.x, o.y, o.z); glp.draw({ // uniforms: [], @@ -99,7 +100,7 @@ function achieve(what) { } } -//--------------- OBJECTS +//--------------- OBJECT MANAGEMENT const removeDeletedObjects = (deleteIndices = []) => { for(let d = deleteIndices.length - 1; d >= 0; d--) { @@ -145,10 +146,16 @@ const objectLoop = (t) => { } } }); + effects.forEach((o) => { + o.rotate(t); + o.calcVertsWithRotation(); + }); removeDeletedObjects(deleteIndices); return { asteroidCount }; }; +//-------------------- OBJECT CREATION + function makeDecay(o, n = 8) { o.decayTime = n; o.ongoing = (t) => { @@ -160,8 +167,8 @@ function makeDecay(o, n = 8) { function die(reason) { isDead = true; ship.delete = true; - makeFragments(ship); - makeBlast(ship.pos, ship.vel); + makeFragments(ship, 10); + makeBlast(ship.pos, ship.vel, 5); sounds.death(); endGame(reason); } @@ -188,6 +195,7 @@ const makeBullet = (ship, bulletPower) => { b.mass *= 0.5; makeDecay(b, 10); b.damage = (dmg, objHit) => { + b.baseColor[0] = .9; // red-ify the richoceting bullets // console.log(dmg / 10); b.decayTime *= 0.5; if (objHit instanceof Asteroid) { @@ -200,9 +208,9 @@ const makeBullet = (ship, bulletPower) => { objects.push(b); }; -function makeBlast(pos, vel) { - const b = new Blast(pos, vel); - const b2 = new Blast(pos, vel, [1., 1., 0.], 0.05, 3); +function makeBlast(pos, vel, scale = 1) { + const b = new Blast(pos, vel, [1., 0.5, 0.], .2 * scale, 8); + const b2 = new Blast(pos, vel, [1., 1., 0.], 0.05 * scale, 3); objects.push(b); objects.push(b2); } @@ -233,8 +241,8 @@ function makeFragment(o, n) { objects.push(f); } -function makeFragments(o) { - const n = 2 + Math.floor(rand(4)); +function makeFragments(o, extra = 4) { + const n = 2 + Math.floor(rand(extra)); for(let i = 0; i < n; i++) { makeFragment(o, n); } } @@ -288,23 +296,24 @@ function setupAsteroids(sun) { } }; -// function makeOuterSun(s, r, color) { -// const baseVerts = SpaceObject.getRegularPolygonVerts(s, r); -// const outerSun = new SpaceObject(baseVerts); -// outerSun.baseColor = color; -// outerSun.mass = 0; -// outerSun.vel = null; -// outerSun.move = () => {}; -// outerSun.collide = () => {}; -// outerSun.gravitate = null; -// giveSpin(outerSun, .1); -// objects.push(outerSun); -// } +function makeOuterSun(s, r, color) { + const baseVerts = SpaceObject.getRegularPolygonVerts(s, r); + const outerSun = new SpaceObject(baseVerts); + outerSun.baseColor = color; + outerSun.mass = 0; + outerSun.vel = null; + outerSun.move = () => {}; + outerSun.collide = () => {}; + outerSun.gravitate = null; + giveSpin(outerSun, .1); + effects.push(outerSun); +} function setupSun() { const sun = new Sun(); - // const color = sun.baseColor.map((c) => Math.max(0, c - 0.2)); - // const r = sun.r * 1.05; + const color = sun.baseColor.map((c) => Math.max(0, c - 0.3)); + const r = sun.r * 1.1; + [8,8,4,4,4,3].forEach((s) => makeOuterSun(s, r, color)); // makeOuterSun(8, r, color); // makeOuterSun(8, r, color); // makeOuterSun(3, r, color); @@ -429,6 +438,12 @@ const setupInput = (canvas, ship) => { ship.rotation = theta - Math.PI/2; achieve('rotate'); }; + document.addEventListener('click', (e) => { + console.log(e.target, e); + if (e.target.id === 'restart') { + location.reload(); + } + }); }; // Create glp