├── .gitignore ├── README.md ├── package.json ├── screenshots ├── EvilGlitchLogo.jpg ├── bigScreenshot-min.png └── smallScreenshot.png ├── scripts ├── compileglsfiles.sh ├── concat.sh └── wrapjs.sh ├── src ├── banner.js ├── buttons.js ├── clickEvents.js ├── colors.js ├── controllers.js ├── corruption.js ├── effects.js ├── enemies.js ├── env_dev.js ├── env_prod.js ├── gameLoop.js ├── gameState.js ├── handleTrigger.js ├── index.html ├── keyEvents.js ├── lib │ ├── TinyMusic.js │ ├── audio.js │ ├── ease.js │ ├── font.js │ ├── geometry.js │ ├── jsfxr.js │ ├── quadTree.js │ ├── spatialhashing.js │ ├── stats.min.js │ └── webgl.js ├── music.js ├── post.js ├── pre.js ├── setup.js ├── shaders │ ├── badcolor.frag │ ├── crt.frag │ ├── cut.frag │ ├── glow.frag │ ├── slit.frag │ ├── static.vert │ ├── swell.frag │ └── twist.frag ├── sounds.js └── splash.js ├── target ├── b.js └── index.html └── todo.todo /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | target.zip 40 | 41 | .DS_Store 42 | build/ 43 | scripts/total* 44 | utility -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Evil glitch 2 | ![alt text](https://raw.githubusercontent.com/agar3s/devil-glitches/master/screenshots/EvilGlitchLogo.jpg "logo") 3 | ==== 4 | 5 | Play it now: http://js13kgames.com/entries/evil-glitch 6 | Recent Arcade Version with Global Scores: http://evilglitch.io 7 | 8 | Stranger glitches appear in your dimension, could you stop them to assimilate your world? 9 | 10 | [![smallgiffdemo.gif](https://s10.postimg.org/ha1vbgkbd/smallgiffdemo.gif)](https://postimg.org/image/a6tzvuevp/) 11 | 12 | Compressed size: 13225bytes 12.92Kb 99.35% // thanks advzip 13 | 14 | I had this obsession about creating a game with sprites like neon, finally using webgl I found a way to made it. 15 | 16 | The game is an arcade shoot 'em up 2d with a retro aesthetics inspired in 80's media, lore and enemies are inspired in the crazy sacred geometry stuff. 17 | 18 | ![alt text](https://raw.githubusercontent.com/agar3s/devil-glitches/master/screenshots/bigScreenshot-min.png "screenshot") 19 | 20 | 21 | 22 | ## Acknowledgments 23 | I recieve a lot of feedback and advices from friends and communities, this game was possible because of their comments. 24 | 25 | Lorena Marquez (https://www.behance.net/prinfrexita) <3 helps me with game style: color and visual references, game logo is made by her. 26 | 27 | David Roncancio (https://twitter.com/kuryaki), my friend and the best player ever, maybe he plays more time this game than me. :P 28 | 29 | David Arcila (https://www.facebook.com/pcmasterdave/), master helps me with a lot of game references and ideas. 30 | 31 | 32 | The awesome Juegos-indies team, constantly feedback and advise about gameplay, narrative, difficulty progression. 33 | 34 | Camilo Torres (http://gatostao.wix.com/catdesign#!__portafolio/game-design) 35 | 36 | Santiago Castillo (https://twitter.com/castillo7979) 37 | 38 | Mateo Robayo (http://cepillosdedientes.tumblr.com/) 39 | 40 | Jose Romero (https://plus.google.com/113467524045285963773) 41 | 42 | Santiago Menelao (https://twitter.com/SanMenelao) 43 | 44 | Fredy Mena (https://twitter.com/xfry) 45 | 46 | 47 | ColombiaDev community: https://twitter.com/colombia_dev 48 | 49 | GameDevLatam community on slack: https://gamedevlatam.slack.com 50 | 51 | Comunidad de desarrolladores de Videojuegos: https://www.facebook.com/groups/comunidad.duval/ 52 | 53 | JuegosIndies group on facebook: https://www.facebook.com/groups/juegosindies 54 | 55 | 56 | Devil Daggers - http://devildaggers.com/ : that game inspires me to create the same sensation of beating the game at all costs. 57 | 58 | ## References from: 59 | 60 | Glitches 61 | 62 | http://www.zachstronaut.com/projects/ggj2015/js/vendor/GlitchFilter.js 63 | https://cdn.rawgit.com/ktingvoar/PixiGlitch/master/examples/dashboard/index.html 64 | https://github.com/ktingvoar/PixiGlitch/blob/master/src/GlowFilter.js 65 | https://github.com/evanw/glfx.js 66 | http://www.zachstronaut.com/posts/2012/08/17/webgl-fake-crt-html5.html 67 | 68 | 69 | music with 70 | 71 | https://github.com/jamesseanwright/nanotunes 72 | 73 | 74 | Sounds with 75 | 76 | https://github.com/evanw/glfx.js 77 | 78 | 79 | your death animation sucks 80 | 81 | https://www.youtube.com/watch?v=pmSAG51BybY 82 | 83 | 84 | References old crt tv: 85 | 86 | 87 | You Have to Win the Game 88 | 89 | http://store.steampowered.com/app/286100/ 90 | 91 | 92 | super win the game 93 | 94 | http://store.steampowered.com/app/310700/ 95 | 96 | 97 | her story 98 | 99 | http://store.steampowered.com/app/368370/ 100 | 101 | 102 | Pinterest board with style references: 103 | 104 | https://es.pinterest.com/agares/game-js13k-inspiration/ 105 | 106 | 107 | I base the background music on this awesome song: 108 | https://www.youtube.com/watch?v=BP1C-W8A69s Neo-Tokyo 109 | 110 | 111 | Code not writed by me has comments on the file's header. 112 | 113 | I took the 2015 1st place winner code https://github.com/gre/behind-asteroids as a template for my entry, removing the game itself and keeping only the bootstrap part. 114 | building system 115 | processing and rendering shaders 116 | 117 | 118 | ## Follow me on: 119 | 120 | https://twitter.com/agar3s 121 | 122 | https://agar3s.itch.io 123 | 124 | Personal channel(spanish) https://www.youtube.com/channel/UCw7MCnJ_p1aGZ-eAVeXJm9g 125 | 126 | 127 | PlayList with progression of the game. 128 | 129 | https://www.youtube.com/watch?v=mmoZVpAYpAI&list=PL7SN2wN7O0VlwpIBY0TX0hx4aGU7gVNVT 130 | 131 | ## Spolier Alert! 132 | You can watch full gameplay in : 133 | https://www.youtube.com/watch?v=eBC8ThmHZms 134 | 135 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "devil-glitches", 3 | "version": "0.0.1", 4 | "description": "glitches are invading our world, help the guardian to keep it free", 5 | "main": "index.js", 6 | "scripts": { 7 | "clean": "rm -rf build/; mkdir -p build target", 8 | "compileglsl": "./scripts/compileglsfiles.sh src/shaders build", 9 | "concat": "./scripts/concat.sh > build/build.js", 10 | "uglify": "uglifyjs build/build.js -c --screw-ie8 -m -o build/build.min.js", 11 | "minify": "ccjs build/build.js --compilation_level=ADVANCED_OPTIMIZATIONS > build/build.min.js", 12 | "nominify": "cp build/build.js build/build.min.js", 13 | "gen": "cp src/index.html target/index.html && cp build/build.min.js target/b.js", 14 | "build-nominify": "npm run clean && npm run compileglsl && npm run concat && npm run nominify && npm run gen", 15 | "zip": "cd target; zip -r ../target.zip .; cd ..; wc -c target.zip", 16 | "build": "export NODE_ENV=production; npm run clean && npm run compileglsl && npm run concat && npm run minify && npm run gen && npm run zip", 17 | "watch": "npm run build-nominify; wr 'npm run build-nominify' src/ scripts/", 18 | "liveserver": "mkdir -p target; cd target; live-server --no-browser", 19 | "start": "npm run watch & npm run liveserver" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "git+https://github.com/agar3s/devil-glitches.git" 24 | }, 25 | "keywords": [ 26 | "js13k" 27 | ], 28 | "author": "Agar3s", 29 | "license": "ISC", 30 | "bugs": { 31 | "url": "https://github.com/agar3s/devil-glitches/issues" 32 | }, 33 | "homepage": "https://github.com/agar3s/devil-glitches#readme", 34 | "devDependencies": { 35 | "glslmin": "0.0.0", 36 | "nodemon": "^1.10.0", 37 | "uglifyjs": "^2.4.10", 38 | "wr": "^1.3.1" 39 | }, 40 | "dependencies": { 41 | "closurecompiler": "^1.6.0" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /screenshots/EvilGlitchLogo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agar3s/devil-glitches/4796dd2806b5a13f4d02f73106771d15389e0512/screenshots/EvilGlitchLogo.jpg -------------------------------------------------------------------------------- /screenshots/bigScreenshot-min.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agar3s/devil-glitches/4796dd2806b5a13f4d02f73106771d15389e0512/screenshots/bigScreenshot-min.png -------------------------------------------------------------------------------- /screenshots/smallScreenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/agar3s/devil-glitches/4796dd2806b5a13f4d02f73106771d15389e0512/screenshots/smallScreenshot.png -------------------------------------------------------------------------------- /scripts/compileglsfiles.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # original script from @gre 3 | # https://github.com/gre/behind-asteroids/blob/master/scripts/compileglsfiles.sh 4 | 5 | if [ "$#" -ne 2 ]; then 6 | echo "Invalid arguments. Usage: $0 fromDir toDir" >&2; 7 | exit 1; 8 | fi; 9 | if [ "$1" == "$2" ]; then 10 | echo "fromDir and toDir must be different" >&2; 11 | exit 2; 12 | fi; 13 | if [ ! -d "$1" ]; then 14 | echo "fromDir must be a directory" >&2; 15 | exit 3; 16 | fi; 17 | if [ ! -d "$2" ]; then 18 | echo "toDir must be a directory" >&2; 19 | exit 4; 20 | fi; 21 | 22 | for glsl in $1/*.frag $1/*.vert; do 23 | name=`basename $glsl`; 24 | cat $glsl | glslmin > $2/$name; 25 | done; 26 | -------------------------------------------------------------------------------- /scripts/concat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # original script from @gre 3 | # https://github.com/gre/behind-asteroids/blob/master/scripts/concat.sh 4 | cat src/pre.js 5 | 6 | if [ "$NODE_ENV" == "production" ]; then 7 | cat src/env_prod.js 8 | else 9 | cat src/env_dev.js 10 | cat src/lib/stats.min.js 11 | fi; 12 | 13 | # libs 14 | 15 | cat src/lib/geometry.js 16 | cat src/lib/webgl.js 17 | cat src/lib/jsfxr.js 18 | cat src/lib/audio.js 19 | cat src/lib/TinyMusic.js 20 | cat src/lib/font.js 21 | cat src/lib/ease.js 22 | cat src/lib/spatialhashing.js 23 | 24 | # shaders 25 | 26 | cd build; 27 | for glsl in *.frag *.vert; do 28 | name=`echo $glsl | tr '.' '_' | tr '[:lower:]' '[:upper:]'` 29 | cat $glsl | ../scripts/wrapjs.sh $name 30 | echo 31 | done 32 | cd ..; 33 | 34 | # game 35 | 36 | cat src/setup.js 37 | cat src/colors.js 38 | cat src/effects.js 39 | cat src/clickEvents.js 40 | cat src/keyEvents.js 41 | cat src/music.js 42 | cat src/sounds.js 43 | cat src/gameState.js 44 | cat src/splash.js 45 | cat src/banner.js 46 | cat src/controllers.js 47 | cat src/buttons.js 48 | cat src/handleTrigger.js 49 | cat src/corruption.js 50 | cat src/enemies.js 51 | cat src/gameLoop.js 52 | cat src/post.js 53 | -------------------------------------------------------------------------------- /scripts/wrapjs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # original script from @gre 3 | # https://github.com/gre/behind-asteroids/blob/master/scripts/wrapjs.sh 4 | # usage: cat glslfile | ./wrapjs.sh varname 5 | 6 | echo -n "var $1 ='" 7 | perl -p -e 's/\n/\\n/'; 8 | echo -ne "';" 9 | -------------------------------------------------------------------------------- /src/banner.js: -------------------------------------------------------------------------------- 1 | var bannerScreen = false; 2 | var bannerMessage=''; 3 | var bannerCounter = 0; 4 | function launchBanner(message,length){ 5 | GLITCHS[6]=30; 6 | bannerCounter=length||30; 7 | glitchTime = 10; 8 | bannerMessage = message; 9 | } 10 | 11 | function drawBanner(){ 12 | var widthSequence = [20,20,20,20,20,20,20,20,20,20,20,20]; 13 | ctx.save(); 14 | ctx.beginPath(); 15 | setContextAtrribute(2,1); 16 | ctx.fillRect(0,0,FW, FH); 17 | displayWord(bannerMessage, 430,180,bannerMessage.length<5?120:70,widthSequence); 18 | ctx.closePath(); 19 | ctx.fill(); 20 | ctx.restore(); 21 | } 22 | 23 | var endMessages = [ 24 | '', 25 | 'now i see', 26 | 'i am creation', 27 | 'you are destruction', 28 | 'we are going to be', 29 | 'in this battle', 30 | 'forever']; 31 | 32 | var endBannerCounter = 0; 33 | var bannerEndMessage; 34 | function updateEnds(){ 35 | switch(endBannerCounter){ 36 | case 90: 37 | launchBanner('what',30); 38 | break; 39 | case 120: 40 | launchBanner('have',30); 41 | break; 42 | case 150: 43 | launchBanner('you',30); 44 | break; 45 | case 180: 46 | launchBanner('done?',120); 47 | break; 48 | case 185: 49 | GLITCHS=[100,100,100,0,0,0,0]; 50 | break 51 | case 215: 52 | GLITCHS=[100,100,100,100,100,100,100]; 53 | sequence1.stop(); 54 | sequence2.stop(); 55 | sequence2.tempo=1; 56 | sequence2.play(); 57 | sequence3.stop(); 58 | sequence3.tempo=1; 59 | sequence3.play(); 60 | sequence4.stop(); 61 | break; 62 | case 320: 63 | sequence2.stop(); 64 | sequence3.stop(); 65 | sequence2.tempo=138; 66 | sequence3.tempo=138; 67 | bannerMessage=''; 68 | break; 69 | case 434: 70 | totemDieShakes=0; 71 | break; 72 | } 73 | if(endBannerCounter>435&&endBannerCounter<1694){ 74 | bannerMessage=endMessages[~~((endBannerCounter-435)/180)]; 75 | } 76 | if(endBannerCounter>1694){ 77 | bannerEndMessage= false; 78 | score=300000; 79 | wave=7; 80 | enemies = []; 81 | bullets=[]; 82 | godMode = true; 83 | storage.setItem('agar3sjs13k-gm', 'qyui'); 84 | loadGod(); 85 | } 86 | endBannerCounter++; 87 | } 88 | function drawBannerEnds(){ 89 | if(endBannerCounter<300)return; 90 | var widthSequence = [0,0,0,0]; 91 | ctx.save(); 92 | ctx.beginPath(); 93 | if(endBannerCounter<436){ 94 | setContextAtrribute(-1,1,'rgba(0,0,0,'+(1-(436-endBannerCounter)/120)+')'); 95 | ctx.fillRect(0,0,FW,FH); 96 | 97 | }else{ 98 | setContextAtrribute(23,1); 99 | ctx.fillRect(0,0,FW,FH); 100 | displayWord(bannerMessage,400,250,16,widthSequence); 101 | } 102 | ctx.closePath(); 103 | ctx.fill(); 104 | ctx.restore(); 105 | } 106 | -------------------------------------------------------------------------------- /src/buttons.js: -------------------------------------------------------------------------------- 1 | // x, y, width, visible, color, message, clicked, hover, action 2 | var buttons = [[250,320,300, false,10, 'start again',false, false, startGame], 3 | [120,460,250, false,11, 'twitter',false, false, shareTwitter], 4 | [430,460,250, false,12, 'facebook',false, false, shareFacebook], 5 | [240,380,320, true,13, 'fire to start', false, false, startGame], 6 | [280,440,240, true,13, 'controls', false, false, toggleControls], 7 | [280,130,240, godModeAvailable,16,'evil mode', false, false, startGodMode]]; 8 | 9 | function drawButtons(){ 10 | // absolute position 11 | for (var i = 0; i < buttons.length; i++) { 12 | var button = buttons[i]; 13 | if(!button[3]) continue; 14 | var colorIndex = button[7]?14:button[4]; 15 | setContextAtrribute(colorIndex); 16 | setContextAtrribute(-1,2,2); 17 | ctx.strokeRect(button[0], button[1],button[2],42); 18 | displayWord(button[5], button[0]+button[2]/2, button[1]+9,12, [0,colorIndex]); 19 | } 20 | } 21 | 22 | function updateButtons(){ 23 | for (var i = 0; i < buttons.length; i++) { 24 | var button = buttons[i]; 25 | if(!button[3]||coords[0]button[0]+button[2]||coords[1]button[1]+42){ 26 | button[6] = button[7] = false; 27 | continue; 28 | } 29 | button[7] = true; 30 | if(coords[2]==1){ 31 | button[6] = true; 32 | }else if(coords[2]==0&&button[6]){ 33 | button[6] = false; 34 | button[8](); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /src/clickEvents.js: -------------------------------------------------------------------------------- 1 | // mouse states 2 | // x, y, down 3 | var coords = [0, 0, 0]; 4 | 5 | c.onmousedown = function(e){ 6 | coords[2] = e.which==3?0:1; 7 | coords[3] = e.which==3?1:0; 8 | e.preventDefault(); 9 | } 10 | 11 | c.onmouseup = function(e){ 12 | coords[2] = 0; 13 | coords[3] = 0; 14 | e.preventDefault(); 15 | } 16 | 17 | c.onmousemove = function(e){ 18 | if(GLITCHS[6]>0) return; 19 | coords[0] = e.offsetX*FW/c.offsetWidth; 20 | coords[1] = e.offsetY*FH/c.offsetHeight; 21 | } 22 | -------------------------------------------------------------------------------- /src/colors.js: -------------------------------------------------------------------------------- 1 | // colors 2 | var colors = [ 3 | '#FFF',//0 white 4 | 'rgba(40,77,153,0.6)', //1 normal 5 | 'rgba(234,34,37,0.6)',//2 corruption or 'rgba(231,197,11,0.3)'; 6 | 'rgba(180,0,50,0.3)',//3 red eye for flower of life 7 | '#F952FF',//4 cursor color 8 | 'rgba(0,77,153,0.6)',//5 soft white line to glow 9 | 'rgb(72,255,206)',//6 hero 10 | 'rgba(0,0,0,0.1)',//7f screen cleaner 11 | 'rgba(7,8,12,0.2)',// 8f corruption background 12 | 'rgb(40,145,160)',//9f bullet color 13 | '#F66', //10 button start again 14 | '#69F', //11 button twitter 15 | '#32F', //12 button facebook 16 | '#6FF', //13 button fire to start& controls 17 | '#066', //14 stroke line buttons 18 | '#0FF', //15 hit enemy strokeline 19 | 'rgba(235,118,71,0.8)', //16 enemy stroke release 20 | '#559', //17 enemy stroke charging, 21 | '#F6F', //18 colors glow , 22 | '#2F2', //19 score color, 23 | '#000',//20 message color1 24 | '#973',//21 message color1, 25 | 'rgba(0,0,0,0.71)',//22 darken 26 | 'rgb(2,1,2)',//23 splash background 27 | 'rgba(255,102,192,0.8)',//24 enemy 1 28 | 'rgba(255,102,102,0.8)',//25 enemy 2 29 | //'rgba(235,118,71,0.8)',//25 enemy 2 30 | 'rgba(252,233,128,0.8)',//26 enemy 3 31 | 'rgba(150,127,254,0.8)',//27 enemy 4 32 | 'rgba(179,72,108,0.8)',//28 enemy 5 guardian 33 | 'rgba(179,88,52,0.8)',//29 enemy 6 bullet 34 | 'rgba(128,108,26,0.8)',//30 enemy 7 35 | 'rgba(128,155,15,0.8)',//31 enemy 8 36 | 'rgba(128,131,51,0.8)',//32 enemy 9 37 | 'hsla(324,50%, 60%, 0.88)',//33 enemy 10 38 | 'hsla(360,50%, 60%, 0.88)',//34 enemy 11 39 | 'hsla(10,50%, 60%, 0.88)',//35 enemy 12 40 | 'hsla(20,50%, 60%, 0.88)',//36 enemy 13 41 | 'hsla(30,50%, 60%, 0.88)',//37 enemy 14 42 | 'rgba(7,8,12,0.2)' //38 43 | ]; 44 | 45 | function setContextAtrribute(index,attribute, custom){ 46 | ctx[['strokeStyle','fillStyle','lineWidth'][attribute||0]] = custom||colors[index]; 47 | } 48 | /**enemies must have colors?*/ 49 | function setRandomColor(r,r2,g,g2,b,b2,a,a2){ 50 | var value = 'rgba('+~~getRandomValue(r,r2)+','+~~getRandomValue(g,g2)+','+~~getRandomValue(b,b2)+','+getRandomValue(a,a2)+')'; 51 | setContextAtrribute(-1,1,value); 52 | } -------------------------------------------------------------------------------- /src/controllers.js: -------------------------------------------------------------------------------- 1 | function checkRecord(){ 2 | newRecord = score>record; 3 | if(!newRecord) return; 4 | record = score; 5 | storage.setItem('agar3sjs13k-record', score); 6 | buttons[1][3] = true; 7 | buttons[2][3] = true; 8 | } 9 | 10 | //temp 11 | var locationref = 'http://js13kgames.com/entries/evil-glitch'; 12 | 13 | function baseMessage(){ 14 | return 'I reached '+score.toFixed()+' '+(godMode?'#evilMode ':'')+'points in #evilGlitch #js13k #js13kgames by @agar3s '; 15 | } 16 | function shareTwitter(){ 17 | var message = baseMessage()+locationref; 18 | var link = encodeURIComponent(message); 19 | window.open('https://twitter.com/home?status='+link); 20 | } 21 | function shareFacebook(){ 22 | var link = encodeURIComponent(locationref)+'&description='+encodeURIComponent(baseMessage()); 23 | window.open('https://www.facebook.com/sharer/sharer.php?u='+link); 24 | } 25 | var fullscreen = false; 26 | function toggleFullscreen(evt){ 27 | if (document.fullscreenEnabled) { 28 | fullscreen?document.exitFullscreen():document.body.requestFullscreen(); 29 | } else if (document['webkitFullscreenEnabled']) { 30 | fullscreen?document.webkitExitFullscreen():document.body.webkitRequestFullscreen(); 31 | } else if (document.mozFullScreenEnabled) { 32 | fullscreen?document.mozCancelFullScreen():document.body.mozRequestFullScreen(); 33 | } 34 | fullscreen=!fullscreen; 35 | evt.preventDefault(); 36 | } 37 | document.getElementById('f').onclick=toggleFullscreen; 38 | 39 | function getRandomValue(value, offset){ 40 | return Math.random()*(value||1) + (offset||0); 41 | } 42 | function randomSign(){ 43 | return getRandomValue()>0.5?1:-1; 44 | } 45 | function randomGlitch(){ 46 | var tempDuration = getRandomValue(10,5); 47 | GLITCHS=[tempDuration,tempDuration,tempDuration,getRandomValue(10,5),getRandomValue(10,5),getRandomValue(10,5),0]; 48 | } 49 | 50 | function toggleControls(){ 51 | play(heroSpeedUp); 52 | controlHelp = !controlHelp; 53 | buttons[3][3] = !controlHelp; 54 | buttons[5][3] = !controlHelp&&godModeAvailable; 55 | buttons[4][5] = controlHelp?'go back':'controls'; 56 | } 57 | -------------------------------------------------------------------------------- /src/corruption.js: -------------------------------------------------------------------------------- 1 | //corrupt algorithm 2 | function corruptZone(x, y, radius){ 3 | var col = ~~(x/tileset); 4 | var row = ~~(y/tileset); 5 | var r = Math.ceil(radius/tileset); 6 | for(var j = row-r; j < row+r; j++){ 7 | if(typeof(map[j])=='undefined') continue; 8 | for(var i = col-r; i < col+r; i++){ 9 | if(map[j][i]==1||getHypo((j+0.5)*tileset-y, (i+0.5)*tileset-x)>=radius) continue; 10 | map[j][i]=1; 11 | } 12 | } 13 | } 14 | 15 | function removeCorruption(x, y, radius){ 16 | var col = ~~(x/tileset); 17 | var row = ~~(y/tileset); 18 | var r = Math.ceil(radius/tileset); 19 | for(var j = row-r; j < row+r; j++){ 20 | if(typeof(map[j])=='undefined') continue; 21 | for(var i = col-r; i < col+r; i++){ 22 | if(map[j][i]==1||getHypo((j+0.5)*tileset-y, (i+0.5)*tileset-x)<=radius){ 23 | map[j][i]=0; 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /src/effects.js: -------------------------------------------------------------------------------- 1 | /* 2 | * original script from @gre 3 | * https://github.com/gre/behind-asteroids/blob/master/src/effects.sh 4 | */ 5 | 6 | function setFrameBuffer(param1, param2, shader,time,colors){ 7 | glBindFBO(param1); 8 | glBindShader(shader); 9 | gl.uniform1i(glUniformLocation(shader, 'tex'), glBindTexture(param2, 0)); 10 | if(time!=undefined){ 11 | gl.uniform1f(glUniformLocation(shader, 'time'), time); 12 | } 13 | if(colors){ 14 | gl.uniform3fv(glUniformLocation(shader, 'colors'),colors); 15 | } 16 | gl.drawArrays(gl.TRIANGLES, 0, 6); 17 | } 18 | function drawPostProcessing (time) { 19 | glSetTexture(textureGame, g); 20 | glitchTime--; 21 | for (var i = 0; i < GLITCHS.length; i++){GLITCHS[i]--;} 22 | 23 | setFrameBuffer(fbo1,textureGame, badColorShader, (frame/60)%180, [(glitchTime+1>0||GLITCHS[0]>0)?1:0,(glitchTime+2>0||GLITCHS[1]>0)?1:0,(glitchTime>0||GLITCHS[2]>0)?1:0]); 24 | setFrameBuffer(fbo2,glGetFBOTexture(fbo1), cutShader, (glitchTime>0||GLITCHS[3]>0)?15:0); 25 | 26 | setFrameBuffer(fbo1, glGetFBOTexture(fbo2), twistShader, (glitchTime+1>0||GLITCHS[4]>0)?1:0); 27 | // swell free for effects 28 | setFrameBuffer(fbo2, glGetFBOTexture(fbo1), swellShader, (GLITCHS[7]>0&&frame%3==0)?0:1); 29 | setFrameBuffer(fbo1, glGetFBOTexture(fbo2),slitShader, (glitchTime>0||GLITCHS[5]>0)?9:1); 30 | // glow 31 | setFrameBuffer(fbo2, glGetFBOTexture(fbo1), glowShader, frame); 32 | setFrameBuffer(fbo1,glGetFBOTexture(fbo2), crtShader); 33 | 34 | 35 | // Final draw 36 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 37 | gl.drawArrays(gl.TRIANGLES, 0, 6); 38 | gl.flush(); 39 | } 40 | -------------------------------------------------------------------------------- /src/enemies.js: -------------------------------------------------------------------------------- 1 | var necronomicon = [ 2 | // normal enemies 3 | //size, angle, summonTime, type, hits, xpoints, ypoints, customData:angleTarget, customData: angleMomentum, anglediff, speed 4 | // 0: basic square 5 | [15, 0, 0, 0, 1, [1,0.25,-1,0.25],[0,-0.75,0,0.75], 0, 3,0.1,1.1], 6 | // 1: simple killer 7 | [15, 0, 0, 1, 4, [1,0.3,0,-2,0,0.3], [0,1,0.3,0,-0.3,-1], 0, 3,0.05,0.8], 8 | // 2: follower 9 | [8, 0, 0, 2, 2, [1,0.25,-1,0.25],[0,-0.5,0,0.5],0, 3.5,0.15,1.6], 10 | // 3: heavy killer 11 | [20, 0, 0, 3, 9, [0,0.25,0.75,0.75,1,0.75,0.75,0.25,0,-0.25,-0.75,-0.75,-1,-0.75,-0.75,-0.25], [-1,-0.75,-0.75,-0.25,0,0.25,0.75,0.75,1,0.75,0.75,0.25,0,-0.25,-0.75,-0.75], 0, 1,0.12,1.05], 12 | //4: guardian 13 | [12, 0, 0, 4, 5, [0,0.25,1,0.25,0,-0.25,-1,-0.25], [-1,-0.25,0,0.25,1,0.25,0,-0.25], 0, 3,0.03,2.5, 0,0,0], // last two parameters, x y to turnon radiis 14 | //5: bullet basic triangle 15 | [3, 0, 0, 5, 150, [1,-1,-1], [0,1,-1],0, 0,0,1.4], // last two parameters, x y to turnon radiis 16 | //6: totem seed - countdown=13 17 | [16, 0, 0, 6, 9, [1,0.25,-1,0.25],[0,-0.75,0,0.75], 0,0,0,0.6,3.5], 18 | //7: totem seed - countdown=13 19 | [18, 0, 0, 7, 8, [1,0.25,-1,0.25],[0,0.75,0,-0.75], 0,0,0,0.8,2.5], 20 | //8: nothing decided 21 | [20, 0, 0, 8, 7, [1,0.25,-1,0.25],[0,0.75,0,-0.75], 0,0,0,1.2,1.5], 22 | //9: nothing decided 23 | , 24 | // spawners 10 -- por si acaso 25 | //size, angle, hitEffect, type, hits, xpoints, ypoints, customData:nextInvocation, corruptionPower, corruptionRatio 26 | //pyramid solid 27 | [tileset/2, 0, 0, 10, 9, [[-1,0,0],[0,0,1],[-1,1,0]], [[-1.5,-0.5,0.5],[-0.5,0.5,-1.5],[-1.5,-1.5,-0.5]], 100, 0,7], 28 | //cube solid 29 | [tileset/2, 0, 0, 11, 10, [[-1,0,0,-1],[1,0,0,1],[-1,0,1,0],[-1,0,1,0],[-1,0,0,-1],[1,0,0,1]], [[-1.25,-0.5,0.8,0.25],[-1.25,-0.5,0.8,0.25],[-1.25,-0.5,-1.25,-1.8],[0.25,-0.5,0.25,0.8],[0.25,-0.5,-1.8,-1.25],[0.25,-0.5,-1.8,-1.25]], 100, 0,6], 30 | //prism solid 31 | [tileset*0.8, 0, 0, 12, 15, [[-0.5,0,0.5,0],[-0.5,0,0],[0.5,0,0],[-0.5,0,0],[0.5,0,0]], [[-0.75,-1,-0.75,-0.5],[-0.75,-0.5,0.25],[-0.75,-0.5,0.25],[-0.75,-1.75,-0.5],[-0.75,-1.75,-0.5]], 0.9, 0,4,0.004], 32 | // st 33 | [tileset*1.2, 0, 0, 13, 50, [[0,-0.75,0],[0,0.75,0],[-0.75,0.75,0],[-0.75,0.75,0],[-0.35,0.35,0]], [[-1,0.5,0],[-1,0.5,0],[0.5,0.5,0],[-0.5,-0.5,1],[0.25,0.25,-0.5]], 0.9, 0,13,0.1], 34 | // flower of fucking life - summon counter=13 35 | [tileset*2.5, 0, 0, 14, 200, [], [], 0.9, 0,60,0.003, 1,0,[6,7,6,7,8]] 36 | ]; 37 | 38 | var invocationTimes={ 39 | 10: 2800, 40 | 11: 2600, 41 | 12: 60, 42 | 13:200 43 | } 44 | 45 | var totemDieShakes = 0; 46 | 47 | function summonGuardian(enemy, j){ 48 | var newEnemy = createEnemy([enemy[0]+Math.cos(Math.PI*j/3)*10,enemy[1]+Math.sin(Math.PI*j/3)*10, 4]) 49 | newEnemy[13] = enemy; 50 | newEnemy[9] = getAngle(newEnemy, enemy); 51 | newEnemy[3] = newEnemy[9]+enemy[11]; 52 | newEnemy[15]=0; 53 | newEnemy[16]=0; 54 | enemies.push(newEnemy); 55 | } 56 | 57 | // values: x, y, type 58 | function createEnemy(values){ 59 | var enemy = values.slice(0,2).concat(necronomicon[values[2]].slice(0)); 60 | 61 | if(enemy[5]==12||enemy[5]==14){ 62 | for (var j = 0; j<6; j++) { 63 | summonGuardian(enemy, j); 64 | } 65 | } 66 | // for (var j = 0; j<12; j++) { for the last enemy 67 | // var newEnemy = createEnemy([enemy[0]+Math.cos(Math.PI*2*j/12+0.1)*10,enemy[1]+Math.sin(Math.PI*2*j/12+0.1)*10, 4]) 68 | return enemy; 69 | } 70 | 71 | //draw methods 72 | 73 | function drawFace(xPath, yPath, size, index, color){ 74 | ctx.beginPath(); 75 | var value = 125-index*20; 76 | setContextAtrribute(-1,1,'rgba('+color+')'); 77 | path(xPath, yPath,size) 78 | ctx.closePath(); 79 | ctx.fill() 80 | ctx.stroke() 81 | } 82 | 83 | function pathEnemy(enemy){ 84 | ctx.rotate(enemy[9]) 85 | path(enemy[7], enemy[8], enemy[2]) 86 | //ctx.strokeRect(enemy[7][0], enemy[8][1], enemy[2]*2, enemy[2]*2) 87 | ctx.rotate(-enemy[9]) 88 | } 89 | var flowerOfpoints = []; 90 | for (var i = 0; i < 6; i++) { 91 | var angle = (i-3)*Math.PI/3+Math.PI/6; 92 | var x = Math.cos(angle); 93 | var y = Math.sin(angle); 94 | flowerOfpoints.push(x,y); 95 | } 96 | function drawFlowerOfLife(enemy){ 97 | var color = 'hsla('+enemy[3]*20+',50%,60%, 0.5)'; 98 | setContextAtrribute(-1,2,2); 99 | ctx.beginPath(); 100 | setContextAtrribute(-1,0,enemy[4]>0?colors[3]:color); 101 | var relativeSize = enemy[2]/3.5; 102 | ctx.arc(0,0, relativeSize/2, 0, Math.PI*2, false); 103 | ctx.stroke(); 104 | ctx.beginPath(); 105 | ctx.bezierCurveTo(-relativeSize, 0, 0, -relativeSize, relativeSize, 0); 106 | ctx.bezierCurveTo(relativeSize, 0, 0, relativeSize, -relativeSize, 0); 107 | ctx.stroke(); 108 | ctx.rotate(enemy[3]); 109 | for (var i = 0; i < 6; i++) { 110 | var x= flowerOfpoints[i*2]; 111 | var y= flowerOfpoints[i*2+1] 112 | var angle = (i-3)*Math.PI/3; 113 | ctx.beginPath(); 114 | ctx.arc(4*x*relativeSize,4*y*relativeSize, relativeSize, 0, Math.PI*2, false); 115 | ctx.stroke(); 116 | ctx.beginPath(); 117 | ctx.arc(2*x*relativeSize,2*y*relativeSize, relativeSize, 0, Math.PI*2, false); 118 | ctx.stroke(); 119 | ctx.beginPath(); 120 | ctx.moveTo(x*relativeSize*4,y*relativeSize*4); 121 | ctx.lineTo(relativeSize*4*flowerOfpoints[(i*2+2)%12],relativeSize*4*flowerOfpoints[(i*2+3)%12]); 122 | ctx.lineTo(relativeSize*4*flowerOfpoints[(i*2+6)%12],relativeSize*4*flowerOfpoints[(i*2+7)%12]); 123 | ctx.moveTo(x*relativeSize*2,y*relativeSize*2); 124 | ctx.lineTo(relativeSize*2*flowerOfpoints[(i*2+2)%12],relativeSize*2*flowerOfpoints[(i*2+3)%12]); 125 | ctx.lineTo(relativeSize*2*flowerOfpoints[(i*2+6)%12],relativeSize*2*flowerOfpoints[(i*2+7)%12]); 126 | ctx.stroke(); 127 | } 128 | ctx.beginPath(); 129 | ctx.rotate(-enemy[3]); 130 | 131 | } 132 | 133 | // greater the percentage blink fast 134 | function blink(percentage, i){ 135 | if(percentage>0.99) return 1; 136 | var value = percentage*100; 137 | var umbral = 1/(percentage); 138 | return value%(umbral)>(umbral/2)?1:0; 139 | } 140 | var blinkValues={}; 141 | for (var i = 0; i < 10000; i++) { 142 | blinkValues[(i/10000).toFixed(4)] = blink(i/10000, i) 143 | } 144 | 145 | function pathTotem(totem){ 146 | var loading = (invocationTimes[totem[5]]-totem[9])/invocationTimes[totem[5]]; 147 | var green = totem[4]>0?-55:~~(200*loading)*blinkValues[loading.toFixed(4)]; 148 | //setContextAtrribute(totem[4]>0?15:green==0?16:17) 149 | setContextAtrribute(16) 150 | for(var i = 0; i < totem[7].length; i++){ 151 | drawFace(totem[7][i], totem[8][i], totem[2], i, [80+totem[4],55+green,130+~~(green/2),totem[4]>0?0.9:0.2]); 152 | } 153 | } 154 | 155 | function drawEnemy(enemy){ 156 | if(enemy[0]+viewPort[0]<20||enemy[0]+viewPort[0]>W-20||enemy[1]+viewPort[1]<20||enemy[1]+viewPort[1]>H-20) return; 157 | var offsetX = enemy[0]+viewPort[0]+shakeScreen[0]+randomSign()*enemy[4]/40; // 20 /2 width/2 158 | var value = (enemy[5]>9?Math.sin((frame/50)%(Math.PI*2))*5+5:0); 159 | var offsetY = enemy[1]+viewPort[1]+shakeScreen[1]+randomSign()*enemy[4]/40-value; 160 | ctx.translate(offsetX, offsetY); 161 | ctx.beginPath(); 162 | if(enemy[5]<10){ 163 | setContextAtrribute(enemy[5]+24);//temporal 164 | //setContextAtrribute(-1,0,'hsla('+enemy[5]*36+',50%,60%,0.8)'); 165 | setContextAtrribute(-1,2,2); 166 | pathEnemy(enemy); 167 | }else if(enemy[5]==14){ 168 | drawFlowerOfLife(enemy,1); 169 | }else { 170 | setContextAtrribute(-1,2,2); 171 | pathTotem(enemy); 172 | } 173 | ctx.closePath(); 174 | ctx.stroke(); 175 | ctx.translate(-offsetX, -offsetY) 176 | } 177 | 178 | var spawns = { 179 | 10: function(enemy){ 180 | for (var j = 0; j<9; j++) { 181 | if(j==4) continue; //summon especial 182 | var x = enemy[0]+(j%3-1)*tileset; 183 | var y = enemy[1]+(~~(j/3)-1)*tileset; 184 | scheduleSummon(x,y,0.65,[x,y,j==1?1:0]); 185 | //var newEnemy = createEnemy([enemy[0]+(j%3-1)*tileset,enemy[1]+(~~(j/3)-1)*tileset, j==1?1:0]) 186 | //enemies.push(newEnemy); 187 | } 188 | enemy[9]=invocationTimes[10]; // time to summon again 189 | }, 190 | 11: function(enemy){ 191 | for (var j = 0; j<12; j++) { 192 | if(j==4) continue; //summon especial 193 | var x = enemy[0]+(j%3-1)*tileset; 194 | var y = enemy[1]+(~~(j/3)-1)*tileset; 195 | scheduleSummon(x,y,0.65,[x,y,j==1?3:2]) 196 | 197 | //if(j==1) bigKiller = newEnemy; 198 | //enemies.push(newEnemy); 199 | } 200 | enemy[9]=invocationTimes[11]; 201 | }, 202 | 12: function(enemy){ // 203 | for (var i = 0; i < 2; i++) { 204 | var newEnemy = createEnemy([enemy[0],enemy[1], 5]) 205 | newEnemy[9] = enemy[3]+i*Math.PI; 206 | enemies.push(newEnemy); 207 | } 208 | enemy[9]=invocationTimes[12]; //crazy 0.3 209 | }, 210 | 13: function(enemy){ 211 | for (var i = 0; i < 6; i++) { 212 | var newEnemy = createEnemy([enemy[0],enemy[1], 5]) 213 | newEnemy[9] = enemy[3]+(i-3)*Math.PI/3; 214 | newEnemy[12]+=0.5; 215 | enemies.push(newEnemy); 216 | } 217 | enemy[9]=45; 218 | }, 219 | 14:function(enemy){ 220 | for (var i = 0; i < 6; i++) { 221 | var newEnemy = createEnemy([enemy[0],enemy[1], 5]) 222 | newEnemy[9] = enemy[3]+(i-3)*Math.PI/3+Math.PI/6; 223 | newEnemy[12]-=0.6; 224 | enemies.push(newEnemy); 225 | } 226 | if(enemy[13]%16==0){ 227 | var newEnemy = createEnemy([enemy[0],enemy[1], enemy[15][enemy[14]%enemy[15].length]]); 228 | newEnemy[9] = enemy[3]/2; 229 | enemies.push(newEnemy); 230 | enemy[14]++; 231 | } 232 | if(enemy[13]%100==0){ 233 | for (var j = 0; j<6; j++) { 234 | summonGuardian(enemy, j); 235 | } 236 | } 237 | enemy[9]=70; 238 | enemy[13]++; 239 | } 240 | } 241 | 242 | // update methods 243 | function updateEnemy(enemy,index){ 244 | // have zero life 245 | if(enemy[6]<=0){ 246 | enemies.splice(index,1); 247 | if(enemy[5]==5) return; 248 | if(enemy[5]==14){ 249 | bannerEndMessage = true; 250 | } 251 | createParticles(enemy[0], enemy[1], enemy[2], necronomicon[enemy[5]][0],necronomicon[enemy[5]][0]*2,enemy[5]+24); 252 | if(enemy[5]>9){ 253 | removeCorruption(enemy[0], enemy[1], enemy[10]); 254 | totemDieShakes=4; 255 | play(totemDestroyed); 256 | }else{ 257 | play(enemyDie); 258 | } 259 | return; 260 | } 261 | 262 | if(enemy[4]>0){ 263 | enemy[4]-=50; 264 | } 265 | // miniom 266 | if(enemy[5]<10){ 267 | if(enemy[10]*(enemy[9]-enemy[3])>0){ 268 | if(enemy[5]==2){//follower 269 | enemy[3] = getAngle(enemy, bigKiller||[0,0]); 270 | }else if(enemy[5]==4){ 271 | enemy[3]+=enemy[9]+enemy[11]; 272 | }else{ 273 | enemy[3] = getAngle(enemy, hero); 274 | } 275 | enemy[10] = enemy[3]>enemy[9]?enemy[11]:-enemy[11]; 276 | } 277 | 278 | var otherEnemy = collideElements(enemy); 279 | if(enemy[5]==4){ 280 | enemy[9] +=enemy[10]*t; 281 | }else if(enemy[5]!=3||(otherEnemy&&otherEnemy[5]==3)){ //type 3 dont collide with followers and collide with himself 282 | enemy[9] +=(otherEnemy?-1:1)*enemy[10]; 283 | }else{ 284 | enemy[9] +=enemy[10]; 285 | } 286 | 287 | if(enemy[5]==5){ 288 | enemy[6]-=t/10; 289 | } 290 | 291 | if(enemy[5]>5&&enemy[5]<10){ 292 | enemy[13]-=dt; 293 | if(enemy[13]<0){ 294 | var coords = [(~~(enemy[0]/tileset)+0.5)*tileset, (~~(enemy[1]/tileset)+0.5)*tileset,enemy[5]+4]; 295 | scheduleSummon(coords[0], coords[1], 1, coords); 296 | enemies.splice(index,1); 297 | return; 298 | } 299 | } 300 | 301 | if(enemy[5]!=4){ 302 | enemy[0] += Math.cos(enemy[9])*t*enemy[12]; 303 | enemy[1] += Math.sin(enemy[9])*t*enemy[12]; 304 | }else{ 305 | // guardians moves 306 | if(enemy[13][6]<1){ 307 | enemy[10]*=0.99; 308 | } 309 | enemy[15] = tileset*2*(-Math.cos(enemy[16])+1.2); 310 | enemy[16]+=t/200; 311 | enemy[0] = enemy[13][0]+Math.cos(enemy[9])*enemy[15]; 312 | enemy[1] = enemy[13][1]+Math.sin(enemy[9])*enemy[15]; 313 | } 314 | 315 | // spawner 316 | }else{ 317 | enemy[9]-=t; 318 | if(enemy[5]>=12)enemy[3]+=enemy[12]*t; 319 | if(enemy[9]<0&&!gameOver){ 320 | spawns[enemy[5]](enemy); 321 | } 322 | enemy[10]+=dt*enemy[11]; 323 | corruptZone(enemy[0], enemy[1], enemy[10]); 324 | } 325 | addItem(enemy); 326 | } 327 | -------------------------------------------------------------------------------- /src/env_dev.js: -------------------------------------------------------------------------------- 1 | const DEBUG = true; // eslint-disable-line no-unused-vars testing 2 | -------------------------------------------------------------------------------- /src/env_prod.js: -------------------------------------------------------------------------------- 1 | const DEBUG = false; // eslint-disable-line no-unused-vars 2 | -------------------------------------------------------------------------------- /src/gameLoop.js: -------------------------------------------------------------------------------- 1 | /** 2 | * read the last events in game, update world 3 | */ 4 | 5 | function die(killer){ 6 | play(heroDie); 7 | createParticles(hero[0], hero[1], hero[2], 10, 80,6); 8 | heroShape=[[], []] 9 | gameOver = true; 10 | sequence1.stop(); 11 | sequence2.stop(); 12 | sequence3.stop(); 13 | sequence4.stop(); 14 | buttons[0][3] = true; 15 | t = dt*30; 16 | checkRecord(); 17 | frame=0; 18 | } 19 | 20 | function drawPointer(){ 21 | ctx.save(); 22 | ctx.beginPath(); 23 | setContextAtrribute(-1,2,2); 24 | ctx.translate(coords[0], coords[1]); 25 | setContextAtrribute(6); 26 | ctx.translate(-10, -10); 27 | crossLine(10,0,20); 28 | ctx.stroke(); 29 | ctx.closePath(); 30 | ctx.restore(); 31 | } 32 | 33 | function playerUdate(dt){ 34 | 35 | // apply speed to hero movement 36 | t = dt*hero[4]*(hero[8]>0?slowMotion:1); 37 | // move depending on keypressed 38 | var speed = dt*hero[4]*(hero[8]>0?1.4:1); 39 | if(map[Math.round(hero[1]/tileset)]&&map[Math.round(hero[1]/tileset)][Math.round(hero[0]/tileset)]==1){ 40 | speed-=0.5; 41 | } 42 | if(keyMap&keys[65]){ 43 | hero[0]-=speed; 44 | if(hero[0]viewPort[2]&&hero[0]32)viewPort[0]=32; 47 | } 48 | if(keyMap&keys[87]){ 49 | hero[1]-=speed; 50 | if(hero[1]viewPort[3]&&hero[1]27)viewPort[1]=27; 53 | } 54 | 55 | if(keyMap&keys[83]){ 56 | hero[1]+=speed; 57 | if(hero[1]>mapPixels - hero[2]) hero[1] = mapPixels - hero[2]; 58 | if(hero[1]>viewPort[3]&&hero[1]mapPixels - hero[2]) hero[0] = mapPixels - hero[2]; 64 | if(hero[0]>viewPort[2]&&hero[0]= 0; i--) { 100 | var bullet = bullets[i]; 101 | bullet[0] += Math.cos(bullet[3])*t*bullet[2]; // bullet speed *2 102 | bullet[1] += Math.sin(bullet[3])*t*bullet[2]; 103 | if(bullet[0]<-20||bullet[0]>mapPixels+20||bullet[1]<-20||bullet[1]>mapPixels+20) bullets.splice(i,1); 104 | 105 | var enemy = collideElements(bullet); 106 | if(enemy){ 107 | if(--enemy[6]>0){ 108 | createParticles(bullet[0], bullet[1], -bullet[3], 2,10,9); 109 | } 110 | bullets.splice(i,1); 111 | enemy[4]=200 112 | if(enemy[5]>9) 113 | play(hitSounds[~~(getRandomValue(hitSounds.length))]); 114 | } 115 | } 116 | 117 | //update particles 118 | for(var i=0;i0)totemDieShakes-=0.1; 127 | spatialhashing = {}; 128 | for (var i = enemies.length-1; i >=0; i--) { 129 | updateEnemy(enemies[i], i); 130 | } 131 | updateTrigger(); 132 | updateSummons(); 133 | } 134 | 135 | function shake(cond, val){ 136 | return cond?getRandomValue(val*2,-val):0; 137 | } 138 | 139 | /** 140 | helper function to draw paths. 141 | */ 142 | function path(xpts, ypts, size){ 143 | ctx.moveTo(xpts[0]*size, ypts[0]*size); 144 | for (var i = 1; i0, totemDieShakes+2), shake(coords[2]||totemDieShakes>0, totemDieShakes+2)]:[0,0]; 171 | setContextAtrribute(-1,1,'rgba(7,8,12,'+ (0.2-(hero[8]>0?0.1:0)) +')'); 172 | ctx.translate(viewPort[0]+shakeScreen[0], viewPort[1]+shakeScreen[1]); 173 | ctx.fillRect(0, 0, mapPixels, mapPixels); 174 | 175 | setContextAtrribute(1); 176 | ctx.beginPath(); 177 | for(var i = 0; i <= mapSize; i++){ 178 | crossLine(i*tileset-0.5, 0, mapPixels); 179 | } 180 | ctx.stroke(); 181 | ctx.beginPath(); 182 | setContextAtrribute(5); 183 | for(var i = 0; i <= mapSize; i++){ 184 | crossLine(i*tileset+0.5, 0, mapPixels); 185 | } 186 | ctx.stroke(); 187 | ctx.restore(); 188 | 189 | // draw corruption 190 | ctx.save(); 191 | ctx.beginPath(); 192 | setContextAtrribute(8,1); //fillstyle 193 | setContextAtrribute(2); //stroke style 194 | for (var j = 0; j < mapSize; j++) { 195 | for (var i = 0; i < mapSize; i++) { 196 | if(map[j][i]==0) continue; 197 | ctx.fillRect(i*tileset+viewPort[0]+shakeScreen[0], j*tileset+viewPort[1]+shakeScreen[1], tileset, tileset); 198 | ctx.strokeRect(i*tileset+viewPort[0]+shakeScreen[0]-0.5, j*tileset+viewPort[1]+shakeScreen[1]-0.5, tileset+2, tileset+2); 199 | //ctx.strokeRect(i*tileset+viewPort[0]+shakeScreen[0]+1.5, j*tileset+viewPort[1]+shakeScreen[1]+1.5, tileset-2, tileset-2); 200 | } 201 | } 202 | ctx.stroke(); 203 | ctx.beginPath(); 204 | setContextAtrribute(2); 205 | for (var j = 0; j < mapSize; j++) { 206 | for (var i = 0; i < mapSize; i++) { 207 | if(map[j][i]==0) continue; 208 | ctx.strokeRect(i*tileset+viewPort[0]+shakeScreen[0]+0.5, j*tileset+viewPort[1]+shakeScreen[1]+0.5, tileset, tileset); 209 | } 210 | } 211 | ctx.fill(); 212 | ctx.closePath(); 213 | ctx.restore(); 214 | 215 | // draw summons 216 | ctx.save(); 217 | drawSummons(); 218 | ctx.restore(); 219 | 220 | // draw hero 221 | ctx.save(); 222 | ctx.translate(hero[0] + viewPort[0], hero[1] + viewPort[1]); 223 | ctx.rotate(hero[3]+Math.PI/2); 224 | // strokeWidth to 3 225 | setContextAtrribute(-1,2,2); 226 | // strokeStyle to colors[6] 227 | setContextAtrribute(6); 228 | ctx.beginPath(); 229 | path(heroShape[0], heroShape[1],hero[2]); 230 | ctx.closePath(); 231 | ctx.stroke(); 232 | ctx.restore(); 233 | 234 | // draw enemies 235 | ctx.save(); 236 | for (var i = 0; i < enemies.length; i++) { 237 | drawEnemy(enemies[i]) 238 | } 239 | ctx.closePath(); 240 | ctx.restore(); 241 | 242 | // draw bullets 243 | ctx.save(); 244 | setContextAtrribute(9,1);//fill 245 | for (var i = 0; i < bullets.length; i++) { 246 | var bullet = bullets[i]; 247 | if(bullet[0]+viewPort[0]<20||bullet[0]+viewPort[0]>W-20||bullet[1]+viewPort[1]<20||bullet[1]+viewPort[1]>H-20) continue 248 | ctx.beginPath(); 249 | ctx.arc(bullet[0]+viewPort[0], bullet[1]+viewPort[1], bullet[2], 0, 2 * Math.PI, false); 250 | ctx.closePath(); 251 | ctx.fill(); 252 | } 253 | ctx.restore(); 254 | 255 | 256 | //draw particles 257 | ctx.save(); 258 | //setRandomColor(125,50, 125,50,125,50,0,1); 259 | // setContextAtrribute(1) 260 | for (var i = 0; i < particles.length; i++) { 261 | var particle = particles[i]; 262 | if(particle[0]+viewPort[0]<5||particle[0]+viewPort[0]>W-5||particle[1]+viewPort[1]<5||particle[1]+viewPort[1]>H-5) continue 263 | ctx.beginPath(); 264 | //ctx.globalAlpha = particle[3]/100; 265 | setContextAtrribute(particle[4],1); 266 | //setRandomColor(125,particle[4], 125,particle[5],125,particle[6],0,particle[3]/100); 267 | ctx.arc(particle[0]+viewPort[0]+shakeScreen[0], particle[1]+viewPort[1]+shakeScreen[1], 2, 0, 2 * Math.PI, false); 268 | ctx.closePath(); 269 | ctx.fill(); 270 | } 271 | ctx.restore(); 272 | 273 | // ui 274 | ctx.save(); 275 | displayWord(message, 401, 501,14, [26,21,21]); 276 | //displayWord(viewPort[0]+' '+viewPort[1], 402, 100,14, '#D6AE45'); 277 | if(gameOver){ 278 | setContextAtrribute(22,1); 279 | ctx.fillRect(0,0,mapPixels, mapPixels); 280 | if(godMode){ 281 | displayWord('evil mode', 400, 80,22, [0,16]); 282 | } 283 | if(newRecord){ 284 | displayWord('-new record-', 400, 240,22, [10,18]); 285 | displayWord('-share it-', 400, 400,14, [24,18]); 286 | }else{ 287 | displayWord('game over', 400, 240,20, [0,13]); 288 | } 289 | displayWord(score.toFixed(0), 400, 160,newRecord?20:16, [0,9]); 290 | }else{ 291 | //wave 292 | displayWord(wave>6?'evil':(wave+'/6'), 400, 60,9, [0,3]); 293 | //score 294 | displayWord(score.toFixed(0), 750, 60,18, [32,9],1); 295 | //record 296 | var lrd = score>record?'record':record.toFixed(0); 297 | displayWord(lrd, 750, 110,9, [24,3],1); 298 | } 299 | ctx.restore(); 300 | 301 | } 302 | 303 | 304 | var lastTime; 305 | function loop(t){ 306 | // webgl postprocessing 307 | if(DEBUG){ 308 | _fps_.begin(); 309 | _processing_.begin(); 310 | _memory_.begin(); 311 | _enemies_.begin(); 312 | } 313 | 314 | if(!lastTime) lastTime = t; 315 | dt = (Math.min(100, t-lastTime)/1000); 316 | lastTime = t; 317 | frame++; 318 | 319 | // update changes 320 | if(splashScreen)updateSplash(dt); 321 | else if(bannerEndMessage)updateEnds(); 322 | else if(GLITCHS[6]<0)update(dt); 323 | 324 | if(splashScreen||gameOver){ 325 | switch(frame){ 326 | case 240: 327 | case 280: 328 | case 500: 329 | randomGlitch(); 330 | play(openingGlitch); 331 | break; 332 | case 700: 333 | frame=0; 334 | break; 335 | } 336 | } 337 | 338 | //update buttons 339 | updateButtons(); 340 | 341 | // draw changes 342 | ctx.save(); 343 | // draw game 344 | if(splashScreen)drawSplash(); 345 | else if(!bannerScreen) draw(dt); 346 | 347 | // draw buttons 348 | if(!bannerScreen&&!bannerEndMessage)drawPointer(); 349 | ctx.save(); 350 | drawButtons(); 351 | ctx.restore(); 352 | if(bannerScreen)drawBanner(); 353 | else if(bannerEndMessage)drawBannerEnds(); 354 | 355 | if(fade>0){ 356 | fade+=0.05; 357 | if(fade==0.51)play(gameStarts); 358 | } 359 | if(fade>1){ 360 | splashScreen=false; 361 | fade=0; 362 | init(); 363 | } 364 | if(fade>0&&fade<1){ 365 | setContextAtrribute(-1,1,'rgba(220,220,220,'+fade+')'); 366 | ctx.fillRect(0,0,FW, FH); 367 | } 368 | bannerScreen = bannerCounter>0; 369 | if(bannerScreen){ 370 | bannerCounter-=1; 371 | } 372 | 373 | 374 | ctx.restore(); 375 | 376 | drawPostProcessing(~~(t)); 377 | if(!gameOver&&!splashScreen) score += dt*1000*(hero[8]>0?slowMotion:1); 378 | 379 | if(DEBUG){ 380 | _fps_.end(); 381 | _processing_.end(); 382 | _memory_.end(); 383 | _enemies_.end(); 384 | enemiesPanel.update(enemies?enemies.length:0, 1000); 385 | } 386 | requestAnimationFrame(loop); 387 | } 388 | 389 | requestAnimationFrame(loop); 390 | -------------------------------------------------------------------------------- /src/gameState.js: -------------------------------------------------------------------------------- 1 | var spatialhashing,mapSize,tileset,gameOver,mapPixels,map,slowMotion,viewPort,enemies,hero,heroShape,bullets,particles,message,particleZ,score,glitchStoped,triggers,bigKiller,times,newRecord; 2 | function init(){ 3 | spatialhashing = {}, 4 | mapSize = 21, 5 | tileset = 40, 6 | gameOver = false, 7 | frame=0, 8 | mapPixels = mapSize*tileset, 9 | map = [], 10 | slowMotion = 0.3, 11 | viewPort = [(FW-mapPixels)/2, (FH-mapPixels)/2 , FW/2-30, FH/2-30], // [x, y, leftOffset, topOffset] 12 | // [0x, 1y, 2size, 3angle, 4speed, 5crossFireAngle, 6countDown, 7bulletRatio, 8dashCountDown, 9dashcolddown] 13 | enemies = [], 14 | hero = [tileset*10.5, tileset*10.5, 16, 0, 150, 0, 0, 12,0,0], 15 | heroShape = [[0,1,0,-1],[-1,1,0.5,1]], 16 | //0x, 1y, 2size, 3angle 17 | bullets = [], 18 | // enemy description 19 | // 0x, 1y, 2size, 4angleIncrement, 3angle, 5angleMomentum, 6xpath, 7ypath, 8hit 20 | // 0x, 1y, 2size, 3angle, 4index, 5type, 6hits, 7path, 8path, 9customdata 21 | // totem description 22 | // totem spawns enemies 23 | // 0x, 1y, 2size, 3angle, 4xpath, 5ypath, 6hitpoints, 7nextInvocation 24 | particles = [], 25 | message = '', 26 | particleZ = Math.PI/2, 27 | score = 0, 28 | glitchTime = 0, 29 | wave = 1, 30 | //012=rgb, 3, 4, 5, 6stop, 7 nada, 31 | GLITCHS=[0,0,0,0,0,0,0,0], 32 | glitchStoped = false, 33 | triggers = { 34 | //1st wave 35 | 500:[1,sequence4], 36 | 2500:[0,10,5,10], 37 | //2st wave 38 | 8999:[5,'what are you doing?'], 39 | 10500:[1,sequence3], 40 | 10800:[8,2], 41 | 11000:[0,5,5,10], 42 | 18000:[0,15,15,10], 43 | 18001:[5,''], 44 | 25000:[0,15,5,10], 45 | 31000:[0,5,15,10], 46 | 37000:[0,10,11,11], 47 | 37500:[1,sequence1], 48 | 46000:[5,'are you trying to stop us?'], 49 | // some broken in the matrix 50 | 48500:[2, 10,0,0,0,10,10,10], 51 | 49000:[2, 10,10,10,15,10,20,10], 52 | 49001:[4, sequence3, 1], 53 | 49002:[4, sequence1, 1], 54 | 49003:[4, sequence4, 1], 55 | 49500:[8,3], 56 | 50004:[2, 60,60,68,55,50,45,60], 57 | 51000:[4, sequence3, 138], 58 | 51001:[4, sequence1, 138], 59 | 51002:[4, sequence4, 138], 60 | 51003:[5,'we are perfection'], 61 | //3st wave 62 | 52000:[0,4,10,11], 63 | 53000:[0,14,10,10], 64 | 55550:[5,''], 65 | 61000:[0,10,16,11], 66 | 61003:[5,'we are creation'], 67 | 66550:[5,''], 68 | 67000:[0,16,10,11], 69 | 68000:[0,6,10,10], 70 | 72000:[0,10,4,11], 71 | // some cool effect for the summon in the middle 72 | 80501:[2, 1000,0,0,0,0,0,10], 73 | 83000:[0,10,9,12], 74 | 83500:[1,sequence2], 75 | 95050:[5,'you must stop this'], 76 | 99950:[5,'it\'s inevitable!'], 77 | 99980:[8,4], 78 | 100000:[0,10, 1,12], 79 | 103050:[5,''], 80 | 106000:[0,1, 10,12], 81 | 111000:[0,19, 10,12], 82 | 116000:[0,10, 19,12], 83 | // some cool effect 84 | 140000:[8,5], 85 | 140001:[5,'can\'t you understand?'], 86 | //5st wave 87 | 141600:[0,10,12,10], 88 | 141601:[0,12,13,10], 89 | 141602:[0,12,15,10], 90 | 141603:[0,10,16,10], 91 | 141604:[0,8,15,10], 92 | 141605:[0,8,13,10], 93 | 145050:[5,''], 94 | 156800:[6,'stop'], 95 | 156900:[6,'now'], 96 | 157000:[0,10,14,13],//3 97 | 180000:[6,'you'], 98 | 180100:[6,'are'], 99 | 180200:[6,'the'], 100 | 180300:[6,'glitch'], 101 | 181000:[8,6], 102 | //6th wave 103 | 182000:[7,sequence1], 104 | 182050:[7,sequence2], 105 | 182100:[7,sequence3], 106 | 182150:[7,sequence4], 107 | 185100:[2,300,0,300,0,0,0,0], 108 | // mega special summon 109 | 187000:[0,10,10,14], 110 | 189000:[1,sequence1], 111 | 189001:[1,sequence2], 112 | 189002:[1,sequence3], 113 | 189003:[1,sequence4], 114 | //god mode 115 | 300100:[1,sequence4], 116 | 305000:[0,10,5,10], 117 | 308000:[1,sequence3], 118 | 310000:[0,10,6,11], 119 | 311000:[1,sequence4], 120 | 315000:[0,14,6,10], 121 | 317000:[0,14,14,10], 122 | 319000:[0,6,14,10], 123 | 320000:[0,6,6,10], 124 | 335000:[0,11,11,12], 125 | 336000:[0,9,11,12], 126 | 337000:[0,11,9,12], 127 | 338000:[0,9,9,12], 128 | 350000:[0,19,19,11], 129 | 352000:[0,1,19,11], 130 | 354000:[0,1,1,11], 131 | 355000:[0,19,1,11], 132 | 365000:[0,10,8,10], 133 | 366000:[0,11,9,10], 134 | 367000:[0,12,10,10], 135 | 368000:[0,11,11,10], 136 | 369000:[0,10,12,10], 137 | 370000:[0,9,11,10], 138 | 371000:[0,8,10,10], 139 | 372000:[0,9,9,10], 140 | 395000:[0,1,1,12], 141 | 395001:[0,1,19,12], 142 | 395002:[0,19,19,12], 143 | 395003:[0,20,1,12], 144 | 395004:[0,10,10,12], 145 | 425000:[0,0,10,13], 146 | 425001:[0,20,10,13], 147 | 570001:[0,10,10,13] 148 | }, 149 | bigKiller = undefined; // reference for enemy followers 150 | 151 | for(var i=0;i5 totems 170 | */ 171 | sequence1.stop(); 172 | sequence2.stop(); 173 | sequence3.stop(); 174 | sequence4.stop(); 175 | record = parseFloat(storage.getItem('agar3sjs13k-record')||0); 176 | for (var i = 0; buttons&&i < buttons.length; i++) { 177 | buttons[i][3] = false; 178 | } 179 | newRecord = false; 180 | randomGlitch(); 181 | loadGod(); 182 | } 183 | 184 | function loadGod(){ 185 | if(godMode){ 186 | heroShape=[[0,-0.5,-0.25,-1,-0.5,-0.4,-0.5,-0.25,0,0.25,0.5,0.4,0.5,1,0.25,0.5,],[-0.25,0,-1,0.25,0.75,0.5,0.25,0.2,0.8,0.2,0.25,0.5,0.75,0.25,-1,0]]; 187 | hero[4]=160; 188 | hero[2]=20; 189 | hero[7]=22; 190 | } 191 | if(startFromGodMode){ 192 | score = 300000; 193 | wave = 7; 194 | for (var i = times.length-1; i>=0; i--) { 195 | if(times[i]<300000){ 196 | times.splice(i,1); 197 | } 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/handleTrigger.js: -------------------------------------------------------------------------------- 1 | // x, y, timeleft, baseTime, summonElement, baseanimation... 2 | var summons = []; 3 | 4 | function updateSummons(){ 5 | for (var i = 0; i < summons.length; i++) { 6 | var summon = summons[i]; 7 | summon[2]-=dt; 8 | if(summon[2]<0){ 9 | enemies.push(createEnemy(summon[4])); 10 | if(summon[4][2]>9){ 11 | glitchTime = 10; 12 | play(totemAppears); 13 | } 14 | if(summon[4][2]==3){ 15 | bigKiller = enemies[enemies.length-1]; 16 | } 17 | summons.splice(i, 1); 18 | } 19 | } 20 | } 21 | 22 | function drawSummonBoss(x,y,percentage, size){ 23 | ctx.translate(x,y); 24 | ctx.beginPath(); 25 | setContextAtrribute(-1,1,'rgba(210,0,0,0.9)') 26 | ctx.arc(0,0,mapPixels*(1-percentage),0, Math.PI*2); 27 | ctx.stroke(); 28 | ctx.fill(); 29 | ctx.beginPath(); 30 | setContextAtrribute(0); 31 | if(percentage<0.3){ 32 | ctx.moveTo(-size*percentage/0.3,0); 33 | ctx.lineTo(size*percentage/0.3,0); 34 | }else{ 35 | setContextAtrribute(0,1); 36 | ctx.bezierCurveTo(-size, 0, 0, -size*percentage/3.5, size, 0); 37 | ctx.bezierCurveTo(size, 0, 0, size*percentage/3.5, -size, 0); 38 | ctx.fill(); 39 | } 40 | ctx.closePath(); 41 | ctx.translate(-x,-y); 42 | } 43 | 44 | function drawSummons(){ 45 | ctx.beginPath(); 46 | for (var i = 0; i < summons.length; i++) { 47 | var summon=summons[i]; 48 | var enemyType = summon[4][2]; 49 | var size = necronomicon[enemyType][0]; 50 | var percentage = easeOutQuad(summon[2],1,-1, summon[3]); 51 | var x = summon[0]+viewPort[0]+shakeScreen[0]; 52 | var y = summon[1]+viewPort[1]+shakeScreen[1]; 53 | if(enemyType==14){ 54 | drawSummonBoss(x, y,percentage, size); 55 | continue 56 | } 57 | setContextAtrribute(-1,0,'rgba(38,82,255,'+percentage+')'); 58 | ctx.fillRect(x-percentage*size, y-percentage*size, percentage*size*2, percentage*size*2); 59 | } 60 | ctx.closePath(); 61 | ctx.fill(); 62 | ctx.stroke(); 63 | } 64 | 65 | function scheduleSummon(x,y,time, element){ 66 | if(element[2]==14){ 67 | time *=2; 68 | play(bossSummon); 69 | } 70 | summons.push([x, y, time, time, element]) 71 | } 72 | 73 | var alSequence=[10,10,11,11,11,12,12,10,10,11,13,10,11,12,12]; 74 | var alIndex =0; 75 | 76 | function updateTrigger(){ 77 | if(times.length>0&&score>times[0]){ 78 | var trigger = triggers[times.splice(0,1)[0]]; 79 | var type = trigger.splice(0,1)[0]; 80 | switch(type){ 81 | case 0: 82 | trigger[0]=(trigger[0]+0.5)*tileset; 83 | trigger[1]=(trigger[1]+0.5)*tileset; 84 | scheduleSummon(trigger[0], trigger[1], 1, trigger); 85 | break; 86 | case 1: 87 | trigger[0].play(); 88 | break; 89 | case 2: 90 | GLITCHS = trigger; 91 | break; 92 | case 3: 93 | play(trigger[0]) 94 | break; 95 | case 4: 96 | trigger[0].tempo = trigger[1]; 97 | if(trigger[1]==138){trigger[0].stop();trigger[0].play()} 98 | break; 99 | case 5: 100 | message = trigger[0]; 101 | break; 102 | case 6: 103 | launchBanner(trigger[0]); 104 | break; 105 | case 7: 106 | trigger[0].stop() 107 | break; 108 | case 8: 109 | wave = trigger[0] 110 | break; 111 | } 112 | 113 | }else if(times.length==0){ // to infinite mode 114 | if(alIndex++>=alSequence.length){ 115 | alIndex=0; 116 | } 117 | triggers[score+5000] = [0,~~(getRandomValue(21)),~~(getRandomValue(21)),alSequence[alIndex]] 118 | times.push(score+5000) 119 | } 120 | } -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | Evil Glitch
FullScreen -------------------------------------------------------------------------------- /src/keyEvents.js: -------------------------------------------------------------------------------- 1 | var keyMap = 0; 2 | var keys = { 3 | '65':1, // left 4 | '87':2, // up 5 | '68':4, // right 6 | '83':8 // down 7 | } 8 | 9 | document.onkeydown = function(e){ 10 | var key = e.keyCode|| e.which; 11 | if(keys[key]){ 12 | keyMap|=keys[key]; 13 | e.preventDefault(); 14 | } 15 | } 16 | 17 | document.onkeyup = function(e){ 18 | var key = e.keyCode ? e.keyCode : e.which; 19 | if(keyMap&keys[key]){ 20 | keyMap^=keys[key]; 21 | e.preventDefault(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/lib/TinyMusic.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Private stuffz 3 | */ 4 | 5 | var enharmonics = 'B#-C|C#-Db|D|D#-Eb|E-Fb|E#-F|F#-Gb|G|G#-Ab|A|A#-Bb|B-Cb', 6 | middleC = 440 * Math.pow( Math.pow( 2, 1 / 12 ), -9 ), 7 | numeric = /^[0-9.]+$/, 8 | octaveOffset = 4, 9 | space = /\s+/, 10 | num = /(\d+)/, 11 | offsets = {}; 12 | 13 | // populate the offset lookup (note distance from C, in semitones) 14 | enharmonics.split('|').forEach(function( val, i ) { 15 | val.split('-').forEach(function( note ) { 16 | offsets[ note ] = i; 17 | }); 18 | }); 19 | 20 | /* 21 | * Note class 22 | * 23 | * new Note ('A4 q') === 440Hz, quarter note 24 | * new Note ('- e') === 0Hz (basically a rest), eigth note 25 | * new Note ('A4 es') === 440Hz, dotted eighth note (eighth + sixteenth) 26 | * new Note ('A4 0.0125') === 440Hz, 32nd note (or any arbitrary 27 | * divisor/multiple of 1 beat) 28 | * 29 | */ 30 | 31 | // create a new Note instance from a string 32 | function Note( str ) { 33 | var couple = str.split( space ); 34 | // frequency, in Hz 35 | this.frequency = Note.getFrequency( couple[ 0 ] ) || 0; 36 | // duration, as a ratio of 1 beat (quarter note = 1, half note = 0.5, etc.) 37 | this.duration = Note.getDuration( couple[ 1 ] ) || 0; 38 | } 39 | 40 | // convert a note name (e.g. 'A4') to a frequency (e.g. 440.00) 41 | Note.getFrequency = function( name ) { 42 | var couple = name.split( num ), 43 | distance = offsets[ couple[ 0 ] ], 44 | octaveDiff = ( couple[ 1 ] || octaveOffset ) - octaveOffset, 45 | freq = middleC * Math.pow( Math.pow( 2, 1 / 12 ), distance ); 46 | return freq * Math.pow( 2, octaveDiff ); 47 | }; 48 | 49 | // convert a duration string (e.g. 'q') to a number (e.g. 1) 50 | // also accepts numeric strings (e.g '0.125') 51 | // and compund durations (e.g. 'es' for dotted-eight or eighth plus sixteenth) 52 | Note.getDuration = function( symbol ) { 53 | return numeric.test( symbol ) ? parseFloat( symbol ) : 54 | symbol.toLowerCase().split('').reduce(function( prev, curr ) { 55 | return prev + ( curr === 'w' ? 4 : curr === 'h' ? 2 : 56 | curr === 'q' ? 1 : curr === 'e' ? 0.5 : 57 | curr === 's' ? 0.25 : 0 ); 58 | }, 0 ); 59 | }; 60 | 61 | /* 62 | * Sequence class 63 | */ 64 | 65 | // create a new Sequence 66 | function Sequence( ac, tempo, arr ) { 67 | this.ac = ac || new AudioContext(); 68 | this.createFxNodes(); 69 | this.tempo = tempo || 120; 70 | this.loop = true; 71 | this.smoothing = 0; 72 | this.staccato = 0; 73 | this.notes = []; 74 | this.push.apply( this, arr || [] ); 75 | } 76 | 77 | // create gain and EQ nodes, then connect 'em 78 | Sequence.prototype.createFxNodes = function() { 79 | var eq = [ [ 'bass', 100 ], [ 'mid', 1000 ], [ 'treble', 2500 ] ], 80 | prev = this.gain = this.ac.createGain(); 81 | eq.forEach(function( config, filter ) { 82 | filter = this[ config[ 0 ] ] = this.ac.createBiquadFilter(); 83 | filter.type = 'peaking'; 84 | filter.frequency.value = config[ 1 ]; 85 | prev.connect( prev = filter ); 86 | }.bind( this )); 87 | prev.connect( this.ac.destination ); 88 | return this; 89 | }; 90 | 91 | // accepts Note instances or strings (e.g. 'A4 e') 92 | Sequence.prototype.push = function() { 93 | Array.prototype.forEach.call( arguments, function( note ) { 94 | this.notes.push( note instanceof Note ? note : new Note( note ) ); 95 | }.bind( this )); 96 | return this; 97 | }; 98 | 99 | // create a custom waveform as opposed to "sawtooth", "triangle", etc 100 | Sequence.prototype.createCustomWave = function( real, imag ) { 101 | // Allow user to specify only one array and dupe it for imag. 102 | if ( !imag ) { 103 | imag = real; 104 | } 105 | 106 | // Wave type must be custom to apply period wave. 107 | this.waveType = 'custom'; 108 | 109 | // Reset customWave 110 | this.customWave = [ new Float32Array( real ), new Float32Array( imag ) ]; 111 | }; 112 | 113 | // recreate the oscillator node (happens on every play) 114 | Sequence.prototype.createOscillator = function() { 115 | this.stop(); 116 | this.osc = this.ac.createOscillator(); 117 | 118 | // customWave should be an array of Float32Arrays. The more elements in 119 | // each Float32Array, the dirtier (saw-like) the wave is 120 | if ( this.customWave ) { 121 | this.osc.setPeriodicWave( 122 | this.ac.createPeriodicWave.apply( this.ac, this.customWave ) 123 | ); 124 | } else { 125 | this.osc.type = this.waveType || 'square'; 126 | } 127 | 128 | this.osc.connect( this.gain ); 129 | return this; 130 | }; 131 | 132 | // schedules this.notes[ index ] to play at the given time 133 | // returns an AudioContext timestamp of when the note will *end* 134 | Sequence.prototype.scheduleNote = function( index, when ) { 135 | var duration = 60 / this.tempo * this.notes[ index ].duration, 136 | cutoff = duration * ( 1 - ( this.staccato || 0 ) ); 137 | 138 | this.setFrequency( this.notes[ index ].frequency, when ); 139 | 140 | if ( this.smoothing && this.notes[ index ].frequency ) { 141 | this.slide( index, when, cutoff ); 142 | } 143 | 144 | this.setFrequency( 0, when + cutoff ); 145 | return when + duration; 146 | }; 147 | 148 | // get the next note 149 | Sequence.prototype.getNextNote = function( index ) { 150 | return this.notes[ index < this.notes.length - 1 ? index + 1 : 0 ]; 151 | }; 152 | 153 | // how long do we wait before beginning the slide? (in seconds) 154 | Sequence.prototype.getSlideStartDelay = function( duration ) { 155 | return duration - Math.min( duration, 60 / this.tempo * this.smoothing ); 156 | }; 157 | 158 | // slide the note at into the next note at the given time, 159 | // and apply staccato effect if needed 160 | Sequence.prototype.slide = function( index, when, cutoff ) { 161 | var next = this.getNextNote( index ), 162 | start = this.getSlideStartDelay( cutoff ); 163 | this.setFrequency( this.notes[ index ].frequency, when + start ); 164 | this.rampFrequency( next.frequency, when + cutoff ); 165 | return this; 166 | }; 167 | 168 | // set frequency at time 169 | Sequence.prototype.setFrequency = function( freq, when ) { 170 | this.osc.frequency.setValueAtTime( freq, when ); 171 | return this; 172 | }; 173 | 174 | // ramp to frequency at time 175 | Sequence.prototype.rampFrequency = function( freq, when ) { 176 | this.osc.frequency.linearRampToValueAtTime( freq, when ); 177 | return this; 178 | }; 179 | 180 | // run through all notes in the sequence and schedule them 181 | Sequence.prototype.play = function( when ) { 182 | when = typeof when === 'number' ? when : this.ac.currentTime; 183 | this.createOscillator(); 184 | this.osc.start( when ); 185 | 186 | this.notes.forEach(function( note, i ) { 187 | when = this.scheduleNote( i, when ); 188 | }.bind( this )); 189 | 190 | this.osc.stop( when ); 191 | this.osc.onended = this.loop ? this.play.bind( this, when ) : null; 192 | 193 | return this; 194 | }; 195 | 196 | // stop playback, null out the oscillator, cancel parameter automation 197 | Sequence.prototype.stop = function() { 198 | if ( this.osc ) { 199 | this.osc.onended = null; 200 | this.osc.disconnect(); 201 | this.osc = null; 202 | } 203 | return this; 204 | }; 205 | -------------------------------------------------------------------------------- /src/lib/audio.js: -------------------------------------------------------------------------------- 1 | /* global jsfxr */ 2 | 3 | var audioCtx, audioDest, audio, play; // eslint-disable-line 4 | 5 | var AudioContext = window.AudioContext || window.webkitAudioContext; 6 | 7 | if (AudioContext) { 8 | audioCtx = new AudioContext(); 9 | audioDest = audioCtx.createDynamicsCompressor(); 10 | var gain = audioCtx.createGain(); 11 | gain.gain.value = !!window.chrome?0.2:0.4; 12 | audioDest.connect(gain); 13 | gain.connect(audioCtx.destination); 14 | 15 | audio = function (conf) { // eslint-disable-line no-unused-vars 16 | var o = []; 17 | jsfxr(conf, audioCtx, function (buf) { 18 | o.push(buf); 19 | }); 20 | return o; 21 | }; 22 | play = function (o) { // eslint-disable-line no-unused-vars 23 | if (!o[0]) return; 24 | var source = audioCtx.createBufferSource(); 25 | //o[0].sampleRate+=Math.round(Math.random()*500) 26 | source.context.sampleRate+=~~getRandomValue(500); 27 | source.buffer = o[0]; 28 | source.start(0); 29 | source.connect(audioDest); 30 | setTimeout(function () { 31 | source.disconnect(audioDest); 32 | }, o[0].duration * 1000 + 300); 33 | }; 34 | } 35 | else { 36 | audio = play = function(){}; 37 | } 38 | -------------------------------------------------------------------------------- /src/lib/ease.js: -------------------------------------------------------------------------------- 1 | //ease functions from http://gizma.com/easing 2 | var linearTween = function (t, b, c, d) { 3 | return c*t/d + b; 4 | }; 5 | var easeInQuad = function (t, b, c, d) { 6 | t /= d; 7 | return c*t*t + b; 8 | }; 9 | var easeOutQuad = function (t, b, c, d) { 10 | t /= d; 11 | return -c * t*(t-2) + b; 12 | }; 13 | 14 | var easeOutCirc = function (t, b, c, d) { 15 | t /= d; 16 | t--; 17 | return c * Math.sqrt(1 - t*t) + b; 18 | }; -------------------------------------------------------------------------------- /src/lib/font.js: -------------------------------------------------------------------------------- 1 | /** 2 | * custom font 14 segment letter 3 | */ 4 | var mapLetters = '0123456789?abcdefghijklmnopqrstuvwxyz .-\'/'; 5 | var letters = [8767,518,1115,1039,1126,1133,1149,7,1151,1135,5123,1143,5391,57,4367,121,113,1085,1142,4361,30,2672,56,694,2230,63,1139,2111,3187,1133,4353,62,8752,10294,10880,4736,8713,0,16,1088,256,8704]; 6 | 7 | function drawSegment(xi,yi,offsetX,offsetY){ 8 | ctx.moveTo(xi,yi); 9 | ctx.lineTo(xi+offsetX,yi+offsetY); 10 | } 11 | 12 | function drawLetter14Segments(letter, x, y, size){ 13 | var size4 = size-4; 14 | var size24 = size/2-4; 15 | // ***** 16 | // |\|/| 17 | // -- -- 18 | // |\|/| 19 | // ***** 20 | if(letter&1){ 21 | drawSegment(x+2, y-1,size4, 0); 22 | } 23 | // --- 24 | // |\/* 25 | // -- 26 | // |\/| 27 | // --- 28 | if(letter&2){ 29 | drawSegment(size+x+1, y,0, size-1); 30 | } 31 | // --- 32 | // |\/| 33 | // -- 34 | // |\/* 35 | // --- 36 | if(letter&4){ 37 | drawSegment(size+x+1, size+y+1,0, size-1); 38 | } 39 | // --- 40 | // |\/| 41 | // -- 42 | // |\/| 43 | // *** 44 | if(letter&8){ 45 | drawSegment(x+2, size*2+y+1,size4, 0); 46 | } 47 | // --- 48 | // |\/| 49 | // -- 50 | // *\/| 51 | // --- 52 | if(letter&16){ 53 | drawSegment(x-1, y+size+1,0, size-1); 54 | } 55 | // --- 56 | // *\/| 57 | // -- 58 | // |\/| 59 | // --- 60 | if(letter&32){ 61 | drawSegment(x-1, y,0, size-1); 62 | } 63 | // --- 64 | // |\/| 65 | // *- 66 | // |\/| 67 | // --- 68 | if(letter&64){ 69 | drawSegment(x+2, size+y,size24, 0); 70 | } 71 | // --- 72 | // |*/| 73 | // -- 74 | // |/|\| 75 | // --- 76 | if(letter&128){ 77 | drawSegment(x+2, y+2,size24, size4); 78 | } 79 | // ----- 80 | // |\*/| 81 | // -- -- 82 | // |/|\| 83 | // ----- 84 | if(letter&256){ 85 | drawSegment(size/2+x, y+2,0, size4); 86 | } 87 | // ----- 88 | // |\|*| 89 | // -- -- 90 | // |/|\| 91 | // ----- 92 | if(letter&512){ 93 | drawSegment(size+x-2, y+2,-size24, size4); 94 | //drawSegment(size/2+x+2, size+y-2,size24, size+4); 95 | } 96 | // ----- 97 | // |\|/| 98 | // -- ** 99 | // |/|\| 100 | // ----- 101 | if(letter&1024){ 102 | drawSegment(size/2+x+2, size+y,size24, 0); 103 | } 104 | // ----- 105 | // |\|/| 106 | // -- -- 107 | // |/|*| 108 | // ----- 109 | if(letter&2048){ 110 | drawSegment(size/2+x+2, size+y+2,size24, size4); 111 | } 112 | // ----- 113 | // |\|/| 114 | // -- -- 115 | // |/*\| 116 | // ----- 117 | if(letter&4096){ 118 | drawSegment(size/2+x, size+y+2,0, size4); 119 | } 120 | // ----- 121 | // |\|/| 122 | // -- -- 123 | // |*|\| 124 | // ----- 125 | if(letter&8192){ 126 | drawSegment(x+2, size*2+y-2,size24, -size+4); 127 | } 128 | } 129 | function drawWord(word, x, y, size, colorIndex, spacing){ 130 | ctx.save(); 131 | ctx.beginPath(); 132 | setContextAtrribute(colorIndex); 133 | for (var i = 0; i < word.length; i++) { 134 | drawLetter14Segments(letters[mapLetters.indexOf(word[i])], shakeScreen[0]+x-(size+spacing)*(word.length-i), shakeScreen[1]+y, size); 135 | } 136 | ctx.closePath(); 137 | ctx.stroke(); 138 | ctx.restore(); 139 | } 140 | function drawWordCenter(word, x, y, size, colorIndex,spacing){ 141 | x += (size+spacing)*word.length/2 142 | drawWord(word, x, y, size, colorIndex, spacing) 143 | } 144 | function drawWordLeft(word, x, y, size, colorIndex, spacing){ 145 | x += (size+spacing)*word.length; 146 | drawWord(word, x, y, size, colorIndex, spacing) 147 | } 148 | var wordAligns=[drawWordCenter,drawWord]; 149 | 150 | function displayWord(word, x, y, size, colorIndexes, side, width){ 151 | width = width||colorIndexes.length; 152 | side = side||0; 153 | var spacing = size<25?10:size*0.5; 154 | for (var i = 0; i < width; i++) { 155 | wordAligns[side](word, x+i, y+i, size, colorIndexes[i]||colorIndexes[0], spacing); 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/lib/geometry.js: -------------------------------------------------------------------------------- 1 | 2 | function getAngle(p1, p2){ 3 | return Math.atan2(p2[1] - p1[1], p2[0] - p1[0]); 4 | } 5 | 6 | function getHypo(p1, p2){ 7 | return Math.sqrt(p1*p1+p2*p2) 8 | } -------------------------------------------------------------------------------- /src/lib/jsfxr.js: -------------------------------------------------------------------------------- 1 | /** 2 | * SfxrParams 3 | * 4 | * Copyright 2010 Thomas Vian 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | * 18 | * @author Thomas Vian 19 | */ 20 | 21 | /* eslint-disable */ 22 | 23 | /** @constructor */ 24 | function SfxrParams() { 25 | //-------------------------------------------------------------------------- 26 | // 27 | // Settings String Methods 28 | // 29 | //-------------------------------------------------------------------------- 30 | 31 | /** 32 | * Parses a settings array into the parameters 33 | * @param array Array of the settings values, where elements 0 - 23 are 34 | * a: waveType 35 | * b: attackTime 36 | * c: sustainTime 37 | * d: sustainPunch 38 | * e: decayTime 39 | * f: startFrequency 40 | * g: minFrequency 41 | * h: slide 42 | * i: deltaSlide 43 | * j: vibratoDepth 44 | * k: vibratoSpeed 45 | * l: changeAmount 46 | * m: changeSpeed 47 | * n: squareDuty 48 | * o: dutySweep 49 | * p: repeatSpeed 50 | * q: phaserOffset 51 | * r: phaserSweep 52 | * s: lpFilterCutoff 53 | * t: lpFilterCutoffSweep 54 | * u: lpFilterResonance 55 | * v: hpFilterCutoff 56 | * w: hpFilterCutoffSweep 57 | * x: masterVolume 58 | * @return If the string successfully parsed 59 | */ 60 | this.ss = function(values) 61 | { 62 | for ( var i = 0; i < 24; i++ ) 63 | { 64 | this[String.fromCharCode( 97 + i )] = values[i] || 0; 65 | } 66 | 67 | // I moved this here from the r(true) function 68 | if (this['c'] < .01) { 69 | this['c'] = .01; 70 | } 71 | 72 | var totalTime = this['b'] + this['c'] + this['e']; 73 | if (totalTime < .18) { 74 | var multiplier = .18 / totalTime; 75 | this['b'] *= multiplier; 76 | this['c'] *= multiplier; 77 | this['e'] *= multiplier; 78 | } 79 | } 80 | } 81 | 82 | /** 83 | * SfxrSynth 84 | * 85 | * Copyright 2010 Thomas Vian 86 | * 87 | * Licensed under the Apache License, Version 2.0 (the "License"); 88 | * you may not use this file except in compliance with the License. 89 | * You may obtain a copy of the License at 90 | * 91 | * http://www.apache.org/licenses/LICENSE-2.0 92 | * 93 | * Unless required by applicable law or agreed to in writing, software 94 | * distributed under the License is distributed on an "AS IS" BASIS, 95 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 96 | * See the License for the specific language governing permissions and 97 | * limitations under the License. 98 | * 99 | * @author Thomas Vian 100 | */ 101 | /** @constructor */ 102 | function SfxrSynth() { 103 | // All variables are kept alive through function closures 104 | 105 | //-------------------------------------------------------------------------- 106 | // 107 | // Sound Parameters 108 | // 109 | //-------------------------------------------------------------------------- 110 | 111 | this._p = new SfxrParams(); // Params instance 112 | 113 | //-------------------------------------------------------------------------- 114 | // 115 | // Synth Variables 116 | // 117 | //-------------------------------------------------------------------------- 118 | 119 | var _envelopeLength0, // Length of the attack stage 120 | _envelopeLength1, // Length of the sustain stage 121 | _envelopeLength2, // Length of the decay stage 122 | 123 | _period, // Period of the wave 124 | _maxPeriod, // Maximum period before sound stops (from minFrequency) 125 | 126 | _slide, // Note slide 127 | _deltaSlide, // Change in slide 128 | 129 | _changeAmount, // Amount to change the note by 130 | _changeTime, // Counter for the note change 131 | _changeLimit, // Once the time reaches this limit, the note changes 132 | 133 | _squareDuty, // Offset of center switching point in the square wave 134 | _dutySweep; // Amount to change the duty by 135 | 136 | //-------------------------------------------------------------------------- 137 | // 138 | // Synth Methods 139 | // 140 | //-------------------------------------------------------------------------- 141 | 142 | /** 143 | * rs the runing variables from the params 144 | * Used once at the start (total r) and for the repeat effect (partial r) 145 | */ 146 | this.r = function() { 147 | // Shorter reference 148 | var p = this._p; 149 | 150 | _period = 100 / (p['f'] * p['f'] + .001); 151 | _maxPeriod = 100 / (p['g'] * p['g'] + .001); 152 | 153 | _slide = 1 - p['h'] * p['h'] * p['h'] * .01; 154 | _deltaSlide = -p['i'] * p['i'] * p['i'] * .000001; 155 | 156 | if (!p['a']) { 157 | _squareDuty = .5 - p['n'] / 2; 158 | _dutySweep = -p['o'] * .00005; 159 | } 160 | 161 | _changeAmount = 1 + p['l'] * p['l'] * (p['l'] > 0 ? -.9 : 10); 162 | _changeTime = 0; 163 | _changeLimit = p['m'] == 1 ? 0 : (1 - p['m']) * (1 - p['m']) * 20000 + 32; 164 | } 165 | 166 | // I split the r() function into two functions for better readability 167 | this.tr = function() { 168 | this.r(); 169 | 170 | // Shorter reference 171 | var p = this._p; 172 | 173 | // Calculating the length is all that remained here, everything else moved somewhere 174 | _envelopeLength0 = p['b'] * p['b'] * 100000; 175 | _envelopeLength1 = p['c'] * p['c'] * 100000; 176 | _envelopeLength2 = p['e'] * p['e'] * 100000 + 12; 177 | // Full length of the volume envelop (and therefore sound) 178 | // Make sure the length can be divided by 3 so we will not need the padding "==" after base64 encode 179 | return ((_envelopeLength0 + _envelopeLength1 + _envelopeLength2) / 3 | 0) * 3; 180 | } 181 | 182 | /** 183 | * Writes the wave to the supplied buffer ByteArray 184 | * @param buffer A ByteArray to write the wave to 185 | * @return If the wave is finished 186 | */ 187 | this.sw = function(buffer, length) { 188 | // Shorter reference 189 | var p = this._p; 190 | 191 | // If the filters are active 192 | var _filters = p['s'] != 1 || p['v'], 193 | // Cutoff multiplier which adjusts the amount the wave position can move 194 | _hpFilterCutoff = p['v'] * p['v'] * .1, 195 | // Speed of the high-pass cutoff multiplier 196 | _hpFilterDeltaCutoff = 1 + p['w'] * .0003, 197 | // Cutoff multiplier which adjusts the amount the wave position can move 198 | _lpFilterCutoff = p['s'] * p['s'] * p['s'] * .1, 199 | // Speed of the low-pass cutoff multiplier 200 | _lpFilterDeltaCutoff = 1 + p['t'] * .0001, 201 | // If the low pass filter is active 202 | _lpFilterOn = p['s'] != 1, 203 | // masterVolume * masterVolume (for quick calculations) 204 | _masterVolume = p['x'] * p['x'], 205 | // Minimum frequency before stopping 206 | _minFreqency = p['g'], 207 | // If the phaser is active 208 | _phaser = p['q'] || p['r'], 209 | // Change in phase offset 210 | _phaserDeltaOffset = p['r'] * p['r'] * p['r'] * .2, 211 | // Phase offset for phaser effect 212 | _phaserOffset = p['q'] * p['q'] * (p['q'] < 0 ? -1020 : 1020), 213 | // Once the time reaches this limit, some of the iables are r 214 | _repeatLimit = p['p'] ? ((1 - p['p']) * (1 - p['p']) * 20000 | 0) + 32 : 0, 215 | // The punch factor (louder at begining of sustain) 216 | _sustainPunch = p['d'], 217 | // Amount to change the period of the wave by at the peak of the vibrato wave 218 | _vibratoAmplitude = p['j'] / 2, 219 | // Speed at which the vibrato phase moves 220 | _vibratoSpeed = p['k'] * p['k'] * .01, 221 | // The type of wave to generate 222 | _waveType = p['a']; 223 | 224 | var _envelopeLength = _envelopeLength0, // Length of the current envelope stage 225 | _envelopeOverLength0 = 1 / _envelopeLength0, // (for quick calculations) 226 | _envelopeOverLength1 = 1 / _envelopeLength1, // (for quick calculations) 227 | _envelopeOverLength2 = 1 / _envelopeLength2; // (for quick calculations) 228 | 229 | // Damping muliplier which restricts how fast the wave position can move 230 | var _lpFilterDamping = 5 / (1 + p['u'] * p['u'] * 20) * (.01 + _lpFilterCutoff); 231 | if (_lpFilterDamping > .8) { 232 | _lpFilterDamping = .8; 233 | } 234 | _lpFilterDamping = 1 - _lpFilterDamping; 235 | 236 | var _finished = false, // If the sound has finished 237 | _envelopeStage = 0, // Current stage of the envelope (attack, sustain, decay, end) 238 | _envelopeTime = 0, // Current time through current enelope stage 239 | _envelopeVolume = 0, // Current volume of the envelope 240 | _hpFilterPos = 0, // Adjusted wave position after high-pass filter 241 | _lpFilterDeltaPos = 0, // Change in low-pass wave position, as allowed by the cutoff and damping 242 | _lpFilterOldPos, // Previous low-pass wave position 243 | _lpFilterPos = 0, // Adjusted wave position after low-pass filter 244 | _periodTemp, // Period modified by vibrato 245 | _phase = 0, // Phase through the wave 246 | _phaserInt, // Integer phaser offset, for bit maths 247 | _phaserPos = 0, // Position through the phaser buffer 248 | _pos, // Phase expresed as a Number from 0-1, used for fast sin approx 249 | _repeatTime = 0, // Counter for the repeats 250 | _sample, // Sub-sample calculated 8 times per actual sample, averaged out to get the super sample 251 | _superSample, // Actual sample writen to the wave 252 | _vibratoPhase = 0; // Phase through the vibrato sine wave 253 | 254 | // Buffer of wave values used to create the out of phase second wave 255 | var _phaserBuffer = new Array(1024), 256 | // Buffer of random values used to generate noise 257 | _noiseBuffer = new Array(32); 258 | for (var i = _phaserBuffer.length; i--; ) { 259 | _phaserBuffer[i] = 0; 260 | } 261 | for (var i = _noiseBuffer.length; i--; ) { 262 | _noiseBuffer[i] = getRandomValue(2,-1); 263 | } 264 | 265 | for (var i = 0; i < length; i++) { 266 | if (_finished) { 267 | return i; 268 | } 269 | 270 | // Repeats every _repeatLimit times, partially rting the sound parameters 271 | if (_repeatLimit) { 272 | if (++_repeatTime >= _repeatLimit) { 273 | _repeatTime = 0; 274 | this.r(); 275 | } 276 | } 277 | 278 | // If _changeLimit is reached, shifts the pitch 279 | if (_changeLimit) { 280 | if (++_changeTime >= _changeLimit) { 281 | _changeLimit = 0; 282 | _period *= _changeAmount; 283 | } 284 | } 285 | 286 | // Acccelerate and apply slide 287 | _slide += _deltaSlide; 288 | _period *= _slide; 289 | 290 | // Checks for frequency getting too low, and stops the sound if a minFrequency was set 291 | if (_period > _maxPeriod) { 292 | _period = _maxPeriod; 293 | if (_minFreqency > 0) { 294 | _finished = true; 295 | } 296 | } 297 | 298 | _periodTemp = _period; 299 | 300 | // Applies the vibrato effect 301 | if (_vibratoAmplitude > 0) { 302 | _vibratoPhase += _vibratoSpeed; 303 | _periodTemp *= 1 + Math.sin(_vibratoPhase) * _vibratoAmplitude; 304 | } 305 | 306 | _periodTemp |= 0; 307 | if (_periodTemp < 8) { 308 | _periodTemp = 8; 309 | } 310 | 311 | // Sweeps the square duty 312 | if (!_waveType) { 313 | _squareDuty += _dutySweep; 314 | if (_squareDuty < 0) { 315 | _squareDuty = 0; 316 | } else if (_squareDuty > .5) { 317 | _squareDuty = .5; 318 | } 319 | } 320 | 321 | // Moves through the different stages of the volume envelope 322 | if (++_envelopeTime > _envelopeLength) { 323 | _envelopeTime = 0; 324 | 325 | switch (++_envelopeStage) { 326 | case 1: 327 | _envelopeLength = _envelopeLength1; 328 | break; 329 | case 2: 330 | _envelopeLength = _envelopeLength2; 331 | } 332 | } 333 | 334 | // Sets the volume based on the position in the envelope 335 | switch (_envelopeStage) { 336 | case 0: 337 | _envelopeVolume = _envelopeTime * _envelopeOverLength0; 338 | break; 339 | case 1: 340 | _envelopeVolume = 1 + (1 - _envelopeTime * _envelopeOverLength1) * 2 * _sustainPunch; 341 | break; 342 | case 2: 343 | _envelopeVolume = 1 - _envelopeTime * _envelopeOverLength2; 344 | break; 345 | case 3: 346 | _envelopeVolume = 0; 347 | _finished = true; 348 | } 349 | 350 | // Moves the phaser offset 351 | if (_phaser) { 352 | _phaserOffset += _phaserDeltaOffset; 353 | _phaserInt = _phaserOffset | 0; 354 | if (_phaserInt < 0) { 355 | _phaserInt = -_phaserInt; 356 | } else if (_phaserInt > 1023) { 357 | _phaserInt = 1023; 358 | } 359 | } 360 | 361 | // Moves the high-pass filter cutoff 362 | if (_filters && _hpFilterDeltaCutoff) { 363 | _hpFilterCutoff *= _hpFilterDeltaCutoff; 364 | if (_hpFilterCutoff < .00001) { 365 | _hpFilterCutoff = .00001; 366 | } else if (_hpFilterCutoff > .1) { 367 | _hpFilterCutoff = .1; 368 | } 369 | } 370 | 371 | _superSample = 0; 372 | for (var j = 8; j--; ) { 373 | // Cycles through the period 374 | _phase++; 375 | if (_phase >= _periodTemp) { 376 | _phase %= _periodTemp; 377 | 378 | // Generates new random noise for this period 379 | if (_waveType == 3) { 380 | for (var n = _noiseBuffer.length; n--; ) { 381 | _noiseBuffer[n] = getRandomValue(2,-1); 382 | } 383 | } 384 | } 385 | 386 | // Gets the sample from the oscillator 387 | switch (_waveType) { 388 | case 0: // Square wave 389 | _sample = ((_phase / _periodTemp) < _squareDuty) ? .5 : -.5; 390 | break; 391 | case 1: // Saw wave 392 | _sample = 1 - _phase / _periodTemp * 2; 393 | break; 394 | case 2: // Sine wave (fast and accurate approx) 395 | _pos = _phase / _periodTemp; 396 | _pos = (_pos > .5 ? _pos - 1 : _pos) * 6.28318531; 397 | _sample = 1.27323954 * _pos + .405284735 * _pos * _pos * (_pos < 0 ? 1 : -1); 398 | _sample = .225 * ((_sample < 0 ? -1 : 1) * _sample * _sample - _sample) + _sample; 399 | break; 400 | case 3: // Noise 401 | _sample = _noiseBuffer[Math.abs(_phase * 32 / _periodTemp | 0)]; 402 | } 403 | 404 | // Applies the low and high pass filters 405 | if (_filters) { 406 | _lpFilterOldPos = _lpFilterPos; 407 | _lpFilterCutoff *= _lpFilterDeltaCutoff; 408 | if (_lpFilterCutoff < 0) { 409 | _lpFilterCutoff = 0; 410 | } else if (_lpFilterCutoff > .1) { 411 | _lpFilterCutoff = .1; 412 | } 413 | 414 | if (_lpFilterOn) { 415 | _lpFilterDeltaPos += (_sample - _lpFilterPos) * _lpFilterCutoff; 416 | _lpFilterDeltaPos *= _lpFilterDamping; 417 | } else { 418 | _lpFilterPos = _sample; 419 | _lpFilterDeltaPos = 0; 420 | } 421 | 422 | _lpFilterPos += _lpFilterDeltaPos; 423 | 424 | _hpFilterPos += _lpFilterPos - _lpFilterOldPos; 425 | _hpFilterPos *= 1 - _hpFilterCutoff; 426 | _sample = _hpFilterPos; 427 | } 428 | 429 | // Applies the phaser effect 430 | if (_phaser) { 431 | _phaserBuffer[_phaserPos % 1024] = _sample; 432 | _sample += _phaserBuffer[(_phaserPos - _phaserInt + 1024) % 1024]; 433 | _phaserPos++; 434 | } 435 | 436 | _superSample += _sample; 437 | } 438 | 439 | // Averages out the super samples and applies volumes 440 | _superSample *= .125 * _envelopeVolume * _masterVolume; 441 | 442 | // Clipping if too loud 443 | buffer[i] = _superSample >= 1 ? 32767 : _superSample <= -1 ? -32768 : _superSample * 32767 | 0; 444 | } 445 | 446 | return length; 447 | } 448 | } 449 | 450 | // Adapted from http://codebase.es/riffwave/ 451 | var synth = new SfxrSynth(); 452 | // Export for the Closure Compiler 453 | function jsfxr (settings, audioCtx, cb) { 454 | // Initialize SfxrParams 455 | synth._p.ss(settings); 456 | // Synthesize Wave 457 | var envelopeFullLength = synth.tr(); 458 | var data = new Uint8Array(((envelopeFullLength + 1) / 2 | 0) * 4 + 44); 459 | 460 | var used = synth.sw(new Uint16Array(data.buffer, 44), envelopeFullLength) * 2; 461 | 462 | var dv = new Uint32Array(data.buffer, 0, 44); 463 | // Initialize header 464 | dv[0] = 0x46464952; // "RIFF" 465 | dv[1] = used + 36; // put total size here 466 | dv[2] = 0x45564157; // "WAVE" 467 | dv[3] = 0x20746D66; // "fmt " 468 | dv[4] = 0x00000010; // size of the following 469 | dv[5] = 0x00010001; // Mono: 1 channel, PCM format 470 | dv[6] = 0x0000AC44; // 44,100 samples per second 471 | dv[7] = 0x00015888; // byte rate: two bytes per sample 472 | dv[8] = 0x00100002; // 16 bits per sample, aligned on every two bytes 473 | dv[9] = 0x61746164; // "data" 474 | dv[10] = used; // put number of samples here 475 | 476 | // Base64 encoding written by me, @maettig 477 | used += 44; 478 | var i = 0, 479 | base64Characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/', 480 | output = 'data:audio/wav;base64,'; 481 | for (; i < used; i += 3) 482 | { 483 | var a = data[i] << 16 | data[i + 1] << 8 | data[i + 2]; 484 | output += base64Characters[a >> 18] + base64Characters[a >> 12 & 63] + base64Characters[a >> 6 & 63] + base64Characters[a & 63]; 485 | } 486 | 487 | audioCtx && audioCtx.decodeAudioData(data.buffer, cb); 488 | 489 | return output; 490 | } 491 | -------------------------------------------------------------------------------- /src/lib/quadTree.js: -------------------------------------------------------------------------------- 1 | // quad tree has the form 2 | // xi, yi, xf, yf, elements, ul, ur, br, bl 3 | //let maxCapacity = 5; 4 | //let quadTree = createQuad([0, 0, mapPixels, mapPixels]); //declaration on gamestate 5 | 6 | // element [x, y], quadTree, created by splitting 7 | function insertQuad(element, quad, deep){ 8 | // check if the element is in this quad 9 | if(!isInQuad(quad, element)) return false; 10 | // if quadtree have children 11 | if(!!quad[5]){ 12 | // for each children check where is the position 13 | for (var i = 5; i < quad.length; i++) { 14 | if(insertQuad(element, quad[i])) return true; 15 | } 16 | console.log('esto no deberia pasar por aca') 17 | } 18 | // insert the element 19 | quad[4].push(element); 20 | // if quad exced capacity split 21 | if(quad[4].length>25&&!deep){ 22 | // x, y, width/2 23 | let half = [quad[0]/2+quad[2]/2, quad[1]/2+quad[3]/2]; 24 | quad[5] = createQuad([quad[0], quad[1], half[0], half[1]]) 25 | quad[6] = createQuad([quad[0], half[1], half[0], quad[3]]) 26 | quad[7] = createQuad([half[0], quad[1], quad[2], half[1]]) 27 | quad[8] = createQuad([half[0], half[1], quad[2], quad[3]]) 28 | 29 | for (var i = 0; i < quad[4].length; i++) { 30 | insertQuad(quad[4][i], quad[5], true); 31 | insertQuad(quad[4][i], quad[6], true); 32 | insertQuad(quad[4][i], quad[7], true); 33 | insertQuad(quad[4][i], quad[8], true); 34 | } 35 | quad[4]=[]; 36 | } 37 | return true; 38 | } 39 | 40 | function createQuad(values){ 41 | return values.concat([[],,,,]); 42 | } 43 | 44 | function isInQuad(quad, point){ 45 | return quad[0]<=point[0]&&quad[2]>point[0]&&quad[1]<=point[1]&&quad[3]>point[1]; 46 | } 47 | // check if element A collides with element B 48 | function intersectRect(elementA, elementB) { 49 | return (elementA[0]-elementA[2] <= elementA[0]+elementA[2] && 50 | elementB[0]-elementB[2] <= elementA[0]+elementA[2] && 51 | elementA[1]-elementA[3] <= elementB[1]+elementB[3] && 52 | elementB[1]-elementB[3] <= elementA[1]+elementA[3]) 53 | } 54 | 55 | function collides(element, quad){ 56 | if(!isInQuad(quad, element)) return; 57 | if(!!quad[5]){ 58 | for (var i = 5; i < quad.length; i++) { 59 | let collideElement = collides(element, quad[i]); 60 | if(collideElement) return collideElement; 61 | } 62 | } 63 | for (var i = 0; i < quad[4].length; i++) { 64 | if(intersectRect(quad[4][i], element)) return quad[4][i]; 65 | } 66 | return; 67 | } 68 | 69 | function getTotal(quad){ 70 | let total = quad[4].length; 71 | if(!!quad[5]){ 72 | for (var i = 5; i < quad.length; i++) { 73 | total+=getTotal(quad[i]); 74 | } 75 | } 76 | return total; 77 | } 78 | 79 | function removeQuad(element, quad, force){ 80 | if(!force&&!isInQuad(quad, element)) return false; 81 | if(!!quad[5]){ 82 | // for each children check where is the position 83 | for (var i = 5; i < quad.length; i++) { 84 | removeQuad(element, quad[i], force); 85 | } 86 | if(getTotal(quad)==0){ 87 | quad[5]=quad[6]=quad[7]=quad[8]=undefined; 88 | } 89 | 90 | return true; 91 | } 92 | let index = quad[4].indexOf(element); 93 | if(index==-1) return false; 94 | //console.log('eliminado', quad); 95 | quad[4].splice(index, 1); 96 | } 97 | 98 | function updateQuad(quad){ 99 | let removed = []; 100 | if(!!quad[5]){ 101 | for (var i = 5; i < quad.length; i++) { 102 | removed = removed.concat(updateQuad(quad[i])); 103 | } 104 | if(getTotal(quad)==0){ 105 | quad[5]=quad[6]=quad[7]=quad[8]=undefined; 106 | } 107 | return removed; 108 | } 109 | for (var i = quad[4].length-1; i >=0; i--) { 110 | // if element move to another quad remove it 111 | if(!isInQuad(quad, quad[4][i])){ 112 | removed.push(quad[4][i]); 113 | removeQuad(quad[4][i], quad, true); 114 | } 115 | } 116 | return removed; 117 | } 118 | function checkUpdateQuad(root){ 119 | let removed = updateQuad(root); 120 | 121 | for (var i = 0; i < removed.length; i++) { 122 | insertQuad(removed[i], root); 123 | } 124 | 125 | } 126 | 127 | if(DEBUG){ 128 | function drawQuad(quad, ctx){ 129 | ctx.strokeStyle='green'; 130 | ctx.strokeRect(quad[0]+viewPort[0], quad[1]+viewPort[1], quad[2]-quad[0], quad[3]-quad[1]); 131 | if(!!quad[5]){ 132 | drawQuad(quad[5], ctx); 133 | drawQuad(quad[6], ctx); 134 | drawQuad(quad[7], ctx); 135 | drawQuad(quad[8], ctx); 136 | } 137 | ctx.strokeStyle='red'; 138 | for (var i = 0; i < quad[4].length; i++) { 139 | let element = quad[4][i]; 140 | ctx.fillRect(element[0]-element[2]+viewPort[0], element[1]-element[3]+viewPort[1], element[2], element[3]); 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/lib/spatialhashing.js: -------------------------------------------------------------------------------- 1 | // custom spatialhashing implementation 2 | 3 | var spatialhashing = {}; 4 | var ceilHeight = 84; 5 | function getHashItem(item){ 6 | return Math.round(item[0]/ceilHeight)+'-'+Math.round(item[1]/ceilHeight); 7 | } 8 | /* add element to spatialhash*/ 9 | function addItem(item){ 10 | var hash = getHashItem(item); 11 | spatialhashing[hash]=spatialhashing[hash]||[]; 12 | spatialhashing[hash].push(item); 13 | } 14 | 15 | function getHash(x,y){ 16 | return getHashItem([x, y]); 17 | } 18 | /* return elements that collides with element*/ 19 | /* only the first element that collides*/ 20 | function collideElements(item){ 21 | var list = {}; 22 | var elements = []; 23 | for (var i = 0; i < 9; i++) { 24 | var hash = getHash(item[0]+(i%3-1)*ceilHeight, item[1]+(~~(i/3)-1)*ceilHeight); 25 | if(!list[hash]){ 26 | list[hash]=1; 27 | var elements = spatialhashing[hash]; 28 | for (var i = 0; elements&&i < elements.length; i++) { 29 | if(getHypo(item[1]-elements[i][1], item[0]-elements[i][0])e+1E3&&(r.update(1E3*a/(c-e),100),e=c,a=0,t)){var d=performance.memory;t.update(d.usedJSHeapSize/1048576,d.jsHeapSizeLimit/1048576)}return c},update:function(){g=this.end()},domElement:c,setMode:k}}; 4 | Stats.Panel=function(h,k,l){var c=Infinity,g=0,e=Math.round,a=e(window.devicePixelRatio||1),r=80*a,f=48*a,t=3*a,u=2*a,d=3*a,m=15*a,n=74*a,p=30*a,q=document.createElement("canvas");q.width=r;q.height=f;q.style.cssText="width:80px;height:48px";var b=q.getContext("2d");b.font="bold "+9*a+"px Helvetica,Arial,sans-serif";b.textBaseline="top";b.fillStyle=l;b.fillRect(0,0,r,f);b.fillStyle=k;b.fillText(h,t,u);b.fillRect(d,m,n,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d,m,n,p);return{dom:q,update:function(f, 5 | v){c=Math.min(c,f);g=Math.max(g,f);b.fillStyle=l;b.globalAlpha=1;b.fillRect(0,0,r,m);b.fillStyle=k;b.fillText(e(f)+" "+h+" ("+e(c)+"-"+e(g)+")",t,u);b.drawImage(q,d+a,m,n-a,p,d,m,n-a,p);b.fillRect(d+n-a,m,a,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d+n-a,m,a,e((1-f/v)*p))}}};"object"===typeof module&&(module.exports=Stats); 6 | -------------------------------------------------------------------------------- /src/lib/webgl.js: -------------------------------------------------------------------------------- 1 | /* 2 | * original script from @gre 3 | * https://github.com/gre/behind-asteroids/blob/master/src/lib/webgl.sh 4 | */ 5 | function glCreateShader (vert, frag) { 6 | var handle, type = gl.VERTEX_SHADER, src = vert; 7 | handle = gl.createShader(type); 8 | gl.shaderSource(handle, src); 9 | gl.compileShader(handle); 10 | var vertex = handle; 11 | if (DEBUG) { 12 | if (!gl.getShaderParameter(handle, gl.COMPILE_STATUS)) 13 | throw gl.getShaderInfoLog(handle); 14 | } 15 | 16 | type = gl.FRAGMENT_SHADER; 17 | src = frag; 18 | handle = gl.createShader(type); 19 | gl.shaderSource(handle, src); 20 | gl.compileShader(handle); 21 | var fragment = handle; 22 | 23 | if (DEBUG) { 24 | if (!gl.getShaderParameter(handle, gl.COMPILE_STATUS)) 25 | throw gl.getShaderInfoLog(handle); 26 | } 27 | 28 | var program = gl.createProgram(); 29 | gl.attachShader(program, vertex); 30 | gl.attachShader(program, fragment); 31 | gl.linkProgram(program); 32 | 33 | if (DEBUG) { 34 | if (!gl.getProgramParameter(program, gl.LINK_STATUS)) 35 | throw gl.getProgramInfoLog(program); 36 | } 37 | 38 | gl.useProgram(program); 39 | var p = gl.getAttribLocation(program, "p"); 40 | gl.enableVertexAttribArray(p); 41 | gl.vertexAttribPointer(p, 2, gl.FLOAT, false, 0, 0); 42 | return [program]; 43 | } 44 | function glBindShader (shader) { 45 | gl.useProgram(shader[0]); 46 | } 47 | function glUniformLocation(shader, name) { 48 | return shader[name] || (shader[name] = gl.getUniformLocation(shader[0], name)); 49 | } 50 | function glCreateTexture () { 51 | var tex = gl.createTexture(); 52 | gl.bindTexture(gl.TEXTURE_2D, tex); 53 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 54 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 55 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 56 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 57 | return tex; 58 | } 59 | function glSetTexture (t, value) { 60 | gl.bindTexture(gl.TEXTURE_2D, t); 61 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, value); 62 | } 63 | function glBindTexture (t, unit) { 64 | gl.activeTexture(gl.TEXTURE0 + unit); 65 | gl.bindTexture(gl.TEXTURE_2D, t); 66 | return unit; 67 | } 68 | function glCreateFBO () { 69 | var handle = gl.createFramebuffer(); 70 | gl.bindFramebuffer(gl.FRAMEBUFFER, handle); 71 | var color = glCreateTexture(); 72 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, W, H, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); 73 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, color, 0); 74 | return [handle, color]; 75 | } 76 | function glBindFBO (fbo) { 77 | gl.bindFramebuffer(gl.FRAMEBUFFER, fbo[0]); 78 | } 79 | function glGetFBOTexture (fbo) { 80 | return fbo[1]; 81 | } 82 | -------------------------------------------------------------------------------- /src/music.js: -------------------------------------------------------------------------------- 1 | // create the audio context 2 | var ac = new AudioContext(), 3 | // get the current Web Audio timestamp (this is when playback should begin) 4 | when = ac.currentTime, 5 | // set the tempo 6 | tempo = 138, 7 | // initialize some vars 8 | sequence1, 9 | sequence2, 10 | sequence3, 11 | sequence4, 12 | // create an array of "note strings" that can be passed to a sequence 13 | bb1='Bb1 s', 14 | d2 = 'D2 s', 15 | ab1 ='Ab1 s', 16 | g2be = 'G2b e' 17 | g2e = 'G2 e', 18 | c2e = 'C2 e', 19 | 20 | lead = [ 21 | bb1, 22 | d2, 23 | bb1, 24 | d2, 25 | bb1, 26 | d2, 27 | bb1, 28 | d2, 29 | bb1, 30 | d2, 31 | bb1, 32 | d2, 33 | bb1, 34 | d2, 35 | bb1, 36 | d2, 37 | ab1, 38 | d2, 39 | ab1, 40 | d2, 41 | ab1, 42 | d2, 43 | ab1, 44 | d2, 45 | ab1, 46 | d2, 47 | ab1, 48 | d2, 49 | ab1, 50 | d2, 51 | ab1, 52 | d2, 53 | ], 54 | harmony = [ 55 | g2e, 56 | g2be, 57 | g2e, 58 | g2be, 59 | g2e, 60 | g2be, 61 | g2e, 62 | g2be, 63 | c2e, 64 | g2be, 65 | c2e, 66 | g2be, 67 | c2e, 68 | g2be, 69 | c2e, 70 | g2be 71 | ], 72 | bass2 = [ 73 | '- w', 74 | 'D1 s', 75 | '- s', 76 | 'D1 e', 77 | '- q', 78 | '- m', 79 | '- w' 80 | ], 81 | basebass = [ 82 | 'C1 e', 83 | '- e', 84 | 'A1 e', 85 | '- e' 86 | ], 87 | bass = basebass; 88 | 89 | // create 3 new sequences (one for lead, one for harmony, one for bass) 90 | sequence1 = new Sequence( ac, tempo, lead ); 91 | sequence2 = new Sequence( ac, tempo, harmony ); 92 | sequence3 = new Sequence( ac, tempo, bass ); 93 | sequence4 = new Sequence( ac, tempo, bass2 ); 94 | 95 | // set staccato and smoothing values for maximum coolness 96 | sequence1.staccato = 0.81; 97 | sequence2.staccato = 0.55; 98 | sequence3.staccato = 0.05; 99 | sequence3.smoothing = 0.35; 100 | sequence4.staccato = 0.05; 101 | 102 | // adjust the levels so the bass and harmony aren't too loud 103 | sequence1.gain.gain.value = 0.12; 104 | sequence2.gain.gain.value = 0.09; 105 | sequence3.gain.gain.value = 0.11; 106 | sequence4.gain.gain.value = 0.1; 107 | 108 | 109 | //sequence1.play(when + 40); 110 | //sequence3.play(when + 10); 111 | //sequence4.play(when + 1); 112 | //sequence2.play(when + 100); 113 | -------------------------------------------------------------------------------- /src/post.js: -------------------------------------------------------------------------------- 1 | }()); 2 | -------------------------------------------------------------------------------- /src/pre.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | -------------------------------------------------------------------------------- /src/setup.js: -------------------------------------------------------------------------------- 1 | /* 2 | * original setup script from @gre 3 | * https://github.com/gre/behind-asteroids/blob/master/src/setup.js 4 | */ 5 | if(DEBUG){ 6 | var _fps_ = new Stats(); 7 | var _processing_ = new Stats(); 8 | var _memory_ = new Stats(); 9 | var _enemies_ = new Stats(); 10 | var enemiesPanel = _enemies_.addPanel( new Stats.Panel( 'enemies', '#ff8', '#221' ) ); 11 | _fps_.dom.style.left = '0px'; 12 | _processing_.dom.style.left = '100px'; 13 | _memory_.dom.style.left = '200px'; 14 | _enemies_.dom.style.left = '300px'; 15 | _fps_.showPanel(0); 16 | _processing_.showPanel(1); 17 | _memory_.showPanel(2); 18 | _enemies_.showPanel(3); 19 | document.body.appendChild(_fps_.dom); 20 | document.body.appendChild(_processing_.dom); 21 | document.body.appendChild(_memory_.dom); 22 | document.body.appendChild(_enemies_.dom); 23 | console.log('new loaded', new Date()) 24 | } 25 | var glprops = {preserveDrawingBuffer: true}; 26 | var gl = c.getContext('webgl',glprops) || c.getContext('experimental-webgl', glprops), 27 | ctx = g.getContext('2d'), 28 | FW = 800, 29 | FH = 600, 30 | GAME_MARGIN = 0, 31 | GAME_Y_MARGIN = GAME_MARGIN, 32 | GAME_INC_PADDING = 80, 33 | W = FW - 2 * GAME_MARGIN, 34 | H = FH - 2 * GAME_Y_MARGIN, 35 | borderLength = 2*(W+H+2*GAME_INC_PADDING), 36 | storage = localStorage, 37 | shakeScreen=[0,0], 38 | glitchTime = 0, 39 | frame=0, 40 | GLITCHS=[0,0,0,0,0,0,0], 41 | godMode = false, 42 | godModeAvailable = !!storage.getItem('agar3sjs13k-gm'), 43 | startFromGodMode = false; 44 | // DOM setup 45 | d.style.webkitTransformOrigin = d.style.transformOrigin = "0 0"; 46 | 47 | g.width = c.width = W; 48 | g.height = c.height = H; 49 | c.style.top = GAME_Y_MARGIN + "px"; 50 | c.style.left = GAME_MARGIN + "px"; 51 | document.oncontextmenu = function (e) { 52 | e.preventDefault(); 53 | }; 54 | 55 | 56 | // WebGL setup 57 | gl.viewport(0, 0, W, H); 58 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true); 59 | 60 | var buffer = gl.createBuffer(); 61 | gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 62 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 63 | -1.0, -1.0, 64 | 1.0, -1.0, 65 | -1.0, 1.0, 66 | -1.0, 1.0, 67 | 1.0, -1.0, 68 | 1.0, 1.0 69 | ]), gl.STATIC_DRAW); 70 | 71 | var glowShader = glCreateShader(STATIC_VERT, GLOW_FRAG); 72 | gl.uniform2f(glUniformLocation(glowShader, 'dim'), W, H); 73 | var crtShader = glCreateShader(STATIC_VERT, CRT_FRAG); 74 | gl.uniform2f(glUniformLocation(crtShader, 'dim'), W, H); 75 | var badColorShader = glCreateShader(STATIC_VERT, BADCOLOR_FRAG); 76 | gl.uniform2f(glUniformLocation(badColorShader, 'dim'), W, H); 77 | var cutShader = glCreateShader(STATIC_VERT, CUT_FRAG); 78 | gl.uniform2f(glUniformLocation(cutShader, 'dim'), W, H); 79 | var twistShader = glCreateShader(STATIC_VERT, TWIST_FRAG); 80 | gl.uniform2f(glUniformLocation(twistShader, 'dim'), W, H); 81 | var swellShader = glCreateShader(STATIC_VERT, SWELL_FRAG); 82 | gl.uniform2f(glUniformLocation(swellShader, 'dim'), W, H); 83 | var slitShader = glCreateShader(STATIC_VERT, SLIT_FRAG); 84 | gl.uniform2f(glUniformLocation(slitShader, 'dim'), W, H); 85 | 86 | 87 | var fbo1 = glCreateFBO(); 88 | var fbo2 = glCreateFBO(); 89 | 90 | var textureGame = glCreateTexture(); 91 | -------------------------------------------------------------------------------- /src/shaders/badcolor.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | uniform vec2 dim; 3 | uniform sampler2D tex; 4 | varying vec2 uv; 5 | uniform float time; 6 | uniform vec3 colors; 7 | 8 | // bad color 9 | // play with rand, 10 | // and colors to display, rgb, rb, rg, gb, r, g, b 11 | // this glitch requires a black cortain like the one in gameover 12 | void main (){ 13 | float rand = sin(time); 14 | vec4 col = texture2D(tex, uv); 15 | vec4 col_r = texture2D(tex, uv + vec2((-15. / dim.x), 0)); 16 | vec4 col_l = texture2D(tex, uv + vec2((15. / dim.x), 0)); 17 | vec4 col_g = texture2D(tex, uv + vec2((-7.5 / dim.x), 0)); 18 | if(colors.r==1.){ 19 | col.r = col.r + col_l.r * max(1., sin(uv.y * dim.y * 1.2)) * rand; 20 | } 21 | if(colors.g==1.){ 22 | col.b = col.b + col_r.b * max(1., sin(uv.y * dim.y * 1.2)) * rand; 23 | } 24 | if(colors.b==1.){ 25 | col.g = col.g + col_g.g * max(1., sin(uv.y * dim.y * 1.2)) * rand; 26 | } 27 | gl_FragColor.rgba = col.rgba; 28 | } -------------------------------------------------------------------------------- /src/shaders/crt.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | uniform vec2 dim; 3 | uniform sampler2D tex; 4 | varying vec2 uv; 5 | uniform float time; 6 | uniform vec3 colors; 7 | 8 | void main() { 9 | vec2 coord = uv * dim; 10 | coord -= dim/2.; 11 | float dis = length(coord); 12 | if (dis < 600.) { 13 | float percent = dis / 600.; 14 | coord *= mix(1., smoothstep(0.0, 600. / dis, percent), .125); 15 | } 16 | coord += dim/2.; 17 | vec4 color = texture2D(tex, coord / dim); 18 | 19 | float dist = distance(uv, vec2(.5, .5)); 20 | color.rgb *= smoothstep(.8, .2*.8, dist); 21 | 22 | gl_FragColor = color; 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/shaders/cut.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | uniform vec2 dim; 3 | uniform sampler2D tex; 4 | varying vec2 uv; 5 | uniform float time; 6 | uniform vec3 colors; 7 | 8 | // cut slide nice glitch! 9 | // play with rand and val1 val2 10 | void main (){ 11 | float val1 = 5.; 12 | float val2 = .5; 13 | vec2 pos = uv * dim; 14 | vec2 posOffset = pos + vec2(floor(sin(pos.y / val1 * time + time * time)) * val2 * time, 0); 15 | posOffset = posOffset / dim; 16 | vec4 col = texture2D(tex, posOffset); 17 | gl_FragColor.rgba = col.rgba; 18 | } -------------------------------------------------------------------------------- /src/shaders/glow.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | uniform vec2 dim; 3 | uniform sampler2D tex; 4 | varying vec2 uv; 5 | uniform float time; 6 | uniform vec3 colors; 7 | 8 | void main (){ 9 | vec2 pos = uv * dim; 10 | vec4 col = texture2D(tex, uv); 11 | vec4 gws = vec4(.0); 12 | float rand = .02*sin(time)+.3; 13 | float weight = .03; 14 | 15 | vec4 col_r = texture2D(tex, uv + vec2((-15. / dim.x) * rand, 0)); 16 | //glow 17 | for (int i = 0; i <9; i++) { 18 | float miw = float(mod(float(i), 4.)); 19 | float idw = float(i / 3); 20 | vec2 v1 = vec2(pos.x + miw, pos.y + idw); 21 | vec2 v2 = vec2(pos.x - miw, pos.y + idw); 22 | vec2 v3 = vec2(pos.x + miw, pos.y - idw); 23 | vec2 v4 = vec2(pos.x - miw, pos.y - idw); 24 | gws += texture2D(tex, v1 / dim) * weight; 25 | gws += texture2D(tex, v2 / dim) * weight; 26 | gws += texture2D(tex, v3 / dim) * weight; 27 | gws += texture2D(tex, v4 / dim) * weight; 28 | } 29 | col += gws; 30 | // chromatic distorsion 31 | vec4 col_l = texture2D(tex, uv + vec2((8. / dim.x) * rand, 0)); 32 | vec4 col_g = texture2D(tex, uv + vec2((-7.5 / dim.x) * rand, 0)); 33 | float val = max(1., sin(uv.y * dim.y * 1.2) * 2.5) * rand; 34 | col.r = col.r + col_l.r * val; 35 | col.b = col.b + col_r.b * val; 36 | col.g = col.g + col_g.g * val; 37 | 38 | // Noise color using random number 39 | vec2 pos2 = uv*sin(time); 40 | float r = fract(sin(dot(pos2.xy ,vec2(12.,78.))) * 43758.); 41 | vec3 noise = vec3(r); 42 | col.rgb = mix(col.rgb, noise, .015); 43 | 44 | 45 | 46 | gl_FragColor.rgba = col; 47 | } 48 | -------------------------------------------------------------------------------- /src/shaders/slit.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | uniform vec2 dim; 3 | uniform sampler2D tex; 4 | varying vec2 uv; 5 | uniform float time; 6 | uniform vec3 colors; 7 | 8 | // SlitScanFilter 9 | // play with time 10 | void main (){ 11 | vec2 pos = uv * dim; 12 | vec2 texCoord = vec2(3.+floor(pos.x/time)*time ,pos.y); 13 | vec4 col = texture2D(tex, texCoord / dim); 14 | gl_FragColor.rgba = col.rgba; 15 | } 16 | -------------------------------------------------------------------------------- /src/shaders/static.vert: -------------------------------------------------------------------------------- 1 | attribute vec2 p; 2 | varying vec2 uv; 3 | 4 | void main() { 5 | gl_Position = vec4(p,.0,1.); 6 | uv = .5 * (p+1.); 7 | } 8 | -------------------------------------------------------------------------------- /src/shaders/swell.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | uniform vec2 dim; 3 | uniform sampler2D tex; 4 | varying vec2 uv; 5 | uniform float time; 6 | uniform vec3 colors; 7 | 8 | // effects for enemies, totems and corruption 9 | void main (){ 10 | vec4 col_s = texture2D(tex, uv); 11 | if(time==.0){ 12 | gl_FragColor.rgba = col_s.bgra; 13 | }else{ 14 | gl_FragColor.rgba = col_s.rgba; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/shaders/twist.frag: -------------------------------------------------------------------------------- 1 | precision highp float; 2 | uniform vec2 dim; 3 | uniform sampler2D tex; 4 | varying vec2 uv; 5 | uniform float time; 6 | uniform vec3 colors; 7 | 8 | // twist glitch temp 9 | // 4 values to play... 10 | void main (){ 11 | float rand = .3; //2.3 12 | float timer = .3; // 11.0 13 | float val2 = 10.*time; //2.5 14 | float val3 = 10.*time; //167.0 15 | float trueWidth = dim.x; 16 | float trueHeight = dim.y; 17 | vec2 pos = uv * dim; 18 | vec2 texCoord = vec2(max(3., min(float(trueWidth), pos.x + sin(pos.y / (153.25 * rand * rand) * rand + rand * val2 + timer * 3.) * val3)), max(3., min(float(trueHeight), pos.y + cos(pos.x/(251.57 * rand * rand) * rand + rand * val2 + timer * 2.4) * val3)- 3.)); 19 | vec4 col = texture2D(tex, texCoord / dim); 20 | gl_FragColor.rgba = col.rgba; 21 | } -------------------------------------------------------------------------------- /src/sounds.js: -------------------------------------------------------------------------------- 1 | // the frequency is the sixth value index 5 2 | 3 | function mutates(sound,mutations){ 4 | var sounds = []; 5 | for (var i = 0; i < mutations; i++) { 6 | var newSound = sound.slice(); 7 | newSound[5] = (i-mutations/2)*0.05 + sound[5]; 8 | sounds.push(audio(newSound)) 9 | } 10 | return sounds; 11 | } 12 | 13 | var gameStarts = audio([3,0.2421,0.1876,0.1891,0.2844,0.5008,,-0.0619,0.2484,,0.0432,-0.7113,0.3743,0.007,0.0008,0.0474,-0.0023,0.705,0.7098,0.0034,0.011,0.0259,0.0005,0.42]); 14 | var baseFireSound = [0,,0.12,0.14,0.3,0.8,,-0.3399,0.04,,,-0.04,,0.51,-0.02,,-0.74,,0.21,0.24,,,0.02,0.41]; 15 | var fireSounds = mutates(baseFireSound, 6); 16 | var totemAppears = audio([1,,0.38,,0.03,0.03,,0.8799,0.96,0.9411,0.9785,-0.9219,0.82,0.7513,0.6049,0.8,-0.6041,-0.8402,0.28,0.7,0.78,0.1423,-0.7585,0.5]); 17 | var enemyDie = audio([3,0.0597,0.11,0.2,0.2513,0.5277,,0.5841,-0.0248,-0.076,0.5312,-0.2978,0.7065,-0.9091,0.4202,0.966,0.7036,0.4575,1,-0.9064,0.6618,0.0266,-0.0655,0.42]); 18 | var heroSpeedUp = audio([2,,0.09,0.06,0.45,0.27,0.02,-0.28,0.82,0.41,0.58,-0.88,0.07,0.448,-0.355,1,0.54,-0.073,1,,,,,0.42]); 19 | var totemDestroyed = audio([3,0.002,0.6302,0.499,0.0804,0.5224,,-0.0324,0.0004,0.5448,,-0.7762,-0.1765,0.6762,-0.4386,0.7747,-0.0347,-0.2051,0.931,-0.0732,0.4693,0.1444,,0.42]); 20 | var heroDie = audio([1,0.145,0.2094,0.4645,0.4954,0.7134,,-0.1659,-0.8866,0.9733,,-0.572,-0.7927,-0.1186,0.4699,0.6044,0.4604,0.1762,0.9998,0.0236,0.1554,,0.659,0.42]); 21 | var glitchStop = audio([1,0.0076,0.66,,,0.09,,0.96,0.32,0.1,0.97,-1,,0.0615,-0.1587,1,,-0.02,0.83,0.12,0.23,0.0231,-0.02,0.96]); 22 | var baseEnemyHit = [3,0.0691,0.183,0.0949,0.5678,0.46,,-0.0001,,,,-0.542,-0.2106,-0.2402,-0.1594,,-0.3133,-0.0707,0.1592,-0.4479,0.5788,0.0169,-0.919,0.42]; 23 | var hitSounds = mutates(baseEnemyHit,8) 24 | var openingGlitch = audio([3,0.0258,0.16,0.0251,0.16,0.05,,-0.86,-0.4088,0.0956,0.256,-0.62,,-0.0006,-0.0352,,-0.0882,-0.0443,0.9219,-0.0531,0.8727,0.031,0.0002,0.6]); 25 | var bossSummon = audio([0,0.95,0.34,0.03,0.05,0.51,,0.96,0.84,0.05,0.51,-0.84,0.99,0.82,,1,,-0.88,0.87,1,0.5,0.21,0.94,0.65]); -------------------------------------------------------------------------------- /src/splash.js: -------------------------------------------------------------------------------- 1 | var splashScreen = true; 2 | var animationLine = 0; 3 | var distanceLine = 30; 4 | var controlHelp = false; 5 | var fade = 0; 6 | function drawDiagonal(x, y, width, vertical, offset){ 7 | ctx.moveTo(x,y); 8 | ctx.lineTo(x+(vertical?offset:width),y+(vertical?width:offset)); 9 | } 10 | function drawLine(x,y,width, vertical){ 11 | drawDiagonal(x,y,width,vertical,0); 12 | } 13 | function crossLine(x,y,width){ 14 | drawLine(x,y,width,true); 15 | drawLine(y,x,width); 16 | } 17 | 18 | 19 | function drawSplash(){ 20 | ctx.save(); 21 | ctx.beginPath(); 22 | setContextAtrribute(23,1); 23 | ctx.fillRect(0,0,FW, FH); 24 | 25 | setContextAtrribute(0); 26 | // 27 | var halfHeight = FH/2; 28 | var horizon = distanceLine*2; 29 | ctx.beginPath(); 30 | for (var i = 0; i < halfHeight/distanceLine; i++){ 31 | var dis = easeInQuad(i*distanceLine+animationLine, halfHeight+horizon, halfHeight, halfHeight); 32 | setContextAtrribute(1); 33 | drawLine(0,dis+0.5,FW); 34 | } 35 | ctx.stroke(); 36 | ctx.beginPath(); 37 | for (var i = 0; i < halfHeight/distanceLine; i++){ 38 | var dis = easeInQuad(i*distanceLine+animationLine, halfHeight+horizon, halfHeight, halfHeight); 39 | setContextAtrribute(2); 40 | drawLine(0,FH-dis-0.5,FW); 41 | } 42 | ctx.stroke(); 43 | var limit = halfHeight-horizon; 44 | setContextAtrribute(2); 45 | ctx.beginPath(); 46 | drawLine(0,limit,FW) 47 | drawLine(FW/2,limit,-limit, true); 48 | ctx.stroke(); 49 | 50 | setContextAtrribute(1); 51 | ctx.beginPath(); 52 | drawLine(0,FH-limit,FW) 53 | drawLine(FW/2,FH-limit, limit, true); 54 | ctx.stroke(); 55 | ctx.beginPath(); 56 | for (var i = 1; i < FW/(distanceLine*2); i++) { 57 | var offset = i*i*5+25; 58 | setContextAtrribute(2); 59 | drawDiagonal(i*distanceLine+FW/2,limit,-limit,true,offset); 60 | drawDiagonal(-i*distanceLine+FW/2,limit,-limit,true,-offset); 61 | } 62 | ctx.stroke(); 63 | ctx.beginPath(); 64 | for (var i = 1; i < FW/(distanceLine*2); i++) { 65 | var offset = i*i*5+25; 66 | setContextAtrribute(1); 67 | drawDiagonal(i*distanceLine+FW/2,FH-limit,limit,true,offset); 68 | drawDiagonal(-i*distanceLine+FW/2,FH-limit,limit,true,-offset); 69 | } 70 | ctx.stroke(); 71 | 72 | if(controlHelp){ 73 | displayWord('controls', 400, 130,12, [0,16]); 74 | displayWord('move awsd', 400, 251,12, [0,0]); 75 | displayWord('fire left click', 400, 290,12, [0,0]); 76 | displayWord('warptime right click', 400, 330,12, [0,0]); 77 | }else{ 78 | displayWord('winners don\'t use drugs', 401, 50,9, [0,0]); 79 | displayWord('evil glitch', 400, 270-fade*50,30*(1+fade), [0,9,0,9]); 80 | //displayWord('glitch', 400, 310+fade*50,20*(1+fade), [0,1,0]); 81 | } 82 | 83 | displayWord('made by agar3s', 401, 520,9, [0,10]); 84 | 85 | ctx.closePath(); 86 | ctx.fill(); 87 | ctx.stroke(); 88 | ctx.restore(); 89 | } 90 | 91 | function updateSplash(){ 92 | animationLine++; 93 | if(animationLine>distanceLine){ 94 | animationLine=0; 95 | } 96 | } 97 | 98 | function startGame(){ 99 | fade=0.01; 100 | } 101 | 102 | function startGodMode(){ 103 | startFromGodMode = true; 104 | godMode = true; 105 | startGame(); 106 | } 107 | init(); 108 | -------------------------------------------------------------------------------- /target/b.js: -------------------------------------------------------------------------------- 1 | (function(){function cb(a){a/=300;return 300*a*a+360}function Ha(a,b){return Math.atan2(b[1]-a[1],b[0]-a[0])}function Ra(a,b){return Math.sqrt(a*a+b*b)}function Q(a){var b;b=k.VERTEX_SHADER;b=k.createShader(b);k.shaderSource(b,"attribute vec2 p;varying vec2 uv;void main(){gl_Position=vec4(p,.0,1.);uv=.5*(p+1.);}");k.compileShader(b);var e=b;b=k.FRAGMENT_SHADER;b=k.createShader(b);k.shaderSource(b,a);k.compileShader(b);a=k.createProgram();k.attachShader(a,e);k.attachShader(a,b);k.linkProgram(a);k.useProgram(a); 2 | e=k.getAttribLocation(a,"p");k.enableVertexAttribArray(e);k.vertexAttribPointer(e,2,k.FLOAT,!1,0,0);return[a]}function ha(a,b){return a[b]||(a[b]=k.getUniformLocation(a[0],b))}function db(){var a=k.createTexture();k.bindTexture(k.TEXTURE_2D,a);k.texParameteri(k.TEXTURE_2D,k.TEXTURE_MIN_FILTER,k.LINEAR);k.texParameteri(k.TEXTURE_2D,k.TEXTURE_MAG_FILTER,k.LINEAR);k.texParameteri(k.TEXTURE_2D,k.TEXTURE_WRAP_S,k.CLAMP_TO_EDGE);k.texParameteri(k.TEXTURE_2D,k.TEXTURE_WRAP_T,k.CLAMP_TO_EDGE);return a}function Jb(a){k.activeTexture(k.TEXTURE0+ 3 | 0);k.bindTexture(k.TEXTURE_2D,a);return 0}function eb(){var a=k.createFramebuffer();k.bindFramebuffer(k.FRAMEBUFFER,a);var b=db();k.texImage2D(k.TEXTURE_2D,0,k.RGBA,R,H,0,k.RGBA,k.UNSIGNED_BYTE,null);k.framebufferTexture2D(k.FRAMEBUFFER,k.COLOR_ATTACHMENT0,k.TEXTURE_2D,b,0);return[a,b]}function Kb(){this.T=function(a){for(var b=0;24>b;b++)this[String.fromCharCode(97+b)]=a[b]||0;.01>this.c&&(this.c=.01);a=this.b+this.c+this.e;.18>a&&(a=.18/a,this.b*=a,this.c*=a,this.e*=a)}}function Lb(a,b,e){Sa.G.T(a); 4 | var h=Sa.V();a=new Uint8Array(4*((h+1)/2|0)+44);var h=2*Sa.U(new Uint16Array(a.buffer,44),h),m=new Uint32Array(a.buffer,0,44);m[0]=1179011410;m[1]=h+36;m[2]=1163280727;m[3]=544501094;m[4]=16;m[5]=65537;m[6]=44100;m[7]=88200;m[8]=1048578;m[9]=1635017060;m[10]=h;h+=44;for(m=0;mh?10:.5*h,p=0;ph;h++)if(e=gb([a[0]+84*(h%3-1),a[1]+84*(~~(h/3)-1)]),!b[e])for(b[e]=1,e=ia[e],h=0;e&&hV[a]&&V.splice(a,1)}}function M(a,b,q,h,f){e.moveTo(a,b);e.lineTo(a+(h?f:q),b+(h?q:f))}function Xa(a,b){M(a,0,b,!0,0);M(0,a,b,void 0,0)}function ob(){N=.01}function Aa(a,b){E[6]=30;Ya=b||30;aa=10;sa=a}function pb(){return"I reached "+J.toFixed()+" "+(za?"#evilMode ":"")+"points in #evilGlitch #js13k #js13kgames by @agar3s "}function w(a,b){return Math.random()*(a||1)+(b||0)}function lb(){var a=w(10,5);E=[a,a,a,w(10,5),w(10, 14 | 5),w(10,5),0]}function La(a,b,e,h){14==h[2]&&(e*=2,O(Pb));ta.push([a,b,e,e,h])}function qb(a,b){var e=ua([a[0]+10*Math.cos(Math.PI*b/3),a[1]+10*Math.sin(Math.PI*b/3),4]);e[13]=a;e[9]=Ha(e,a);e[3]=e[9]+a[11];e[15]=0;e[16]=0;F.push(e)}function ua(a){a=a.slice(0,2).concat(Ma[a[2]].slice(0));if(12==a[5]||14==a[5])for(var b=0;6>b;b++)qb(a,b);return a}function Qb(a){if(.99b/2?1:0}function Ba(a,b){return a?w(2*b,-b):0}function Za(a,b,f){e.moveTo(a[0]*f,b[0]*f);for(var h= 15 | 1;hka&&(sa=Rb[~~((ka-435)/180)]);1694E[6]){a=dt;if(!ea){t=a*f[4]*(0l[2]&&f[0]l[3]&&f[1]v-f[2]&&(f[1]=v-f[2]),f[1]>l[3]&&f[1]l[1]&&(l[1]=-272));ca&da[68]&&(f[0]+=b,f[0]>v-f[2]&&(f[0]=v-f[2]),f[0]>l[2]&&f[0]l[0]&&(l[0]=-67));f[3]=Ha([f[0]+l[0],f[1]+l[1]],B);f[5]+=25*t*(8*B[2]+1);f[5]%=360;if(Ta(f)){O(Sb);$a(f[0],f[1],f[2],10,80,6);ya=[[],[]];ea=!0;fa.stop();L.stop();z.stop();K.stop();W[0][3]=!0;t=30*dt;if(Ka=J>record)record=J,Ja.setItem("agar3sjs13k-record", 18 | J),W[1][3]=!0,W[2][3]=!0;ja=0}B[2]&&0>=f[6]&&0>=f[8]?(U.push([f[0]+Ba(1,2+f[7]/30),f[1]+Ba(1,2+f[7]/30),2,f[3]+Ba(1,.05+.001*f[7])]),O(sb[~~w(sb.length)]),f[6]=1/f[7]):f[6]-=a;B[3]&&0>=f[8]&&0>=f[9]?(O(tb),f[8]=.55,f[9]=1.2):(f[8]-=a,f[9]-=a)}a=U.length-1;for(;0<=a;a--){b=U[a];b[0]+=Math.cos(b[3])*t*b[2];b[1]+=Math.sin(b[3])*t*b[2];(-20>b[0]||b[0]>v+20||-20>b[1]||b[1]>v+20)&&U.splice(a,1);var q=Ta(b);q&&(0<--q[6]&&$a(b[0],b[1],-b[3],2,10,9),U.splice(a,1),q[4]=200,9--b[3]&&oa.splice(a,1);0=b[6]){if(F.splice(q,1),5!=b[5])if(14==b[5]&&(Ea=!0),$a(b[0],b[1],b[2],Ma[b[5]][0],2*Ma[b[5]][0],b[5]+24),9b[5]){0b[9]?b[11]:-b[11]);h=Ta(b);b[9]=4==b[5]?b[9]+b[10]*t:3!=b[5]||h&&3==h[5]?b[9]+(h?-1:1)*b[10]:b[9]+b[10];5==b[5]&&(b[6]-=t/10);if(5b[5]&&(b[13]-=dt,0>b[13])){b=[(~~(b[0]/n)+.5)*n,(~~(b[1]/n)+.5)*n,b[5]+4];La(b[0],b[1],1,b);F.splice(q,1);break a}4!=b[5]?(b[0]+=Math.cos(b[9])*t*b[12],b[1]+=Math.sin(b[9])*t*b[12]):(1>b[13][6]&&(b[10]*=.99),b[15]=2*n*(-Math.cos(b[16])+ 21 | 1.2),b[16]+=t/200,b[0]=b[13][0]+Math.cos(b[9])*b[15],b[1]=b[13][1]+Math.sin(b[9])*b[15])}else{b[9]-=t;12<=b[5]&&(b[3]+=b[12]*t);if(0>b[9]&&!ea)Vb[b[5]](b);b[10]+=dt*b[11];q=b[0];h=b[1];m=b[10];x=~~(q/n);X=~~(h/n);G=Math.ceil(m/n);for(Y=X-G;Y=m||(T[Y][u]=1)}q=gb(b);ia[q]=ia[q]||[];ia[q].push(b)}if(0V[0])switch(a=ra[V.splice(0,1)[0]],a.splice(0,1)[0]){case 0:a[0]=(a[0]+.5)*n;a[1]=(a[1]+.5)* 22 | n;La(a[0],a[1],1,a);break;case 1:a[0].play();break;case 2:E=a;break;case 3:O(a[0]);break;case 4:a[0].D=a[1];138==a[1]&&(a[0].stop(),a[0].play());break;case 5:Va=a[0];break;case 6:Aa(a[0]);break;case 7:a[0].stop();break;case 8:wave=a[0]}else 0==V.length&&(ab++>=vb.length&&(ab=0),ra[J+5E3]=[0,~~w(21),~~w(21),vb[ab]],V.push(J+5E3));for(a=0;ab[2]&&(F.push(ua(b[4])),9b[0]+b[2]||B[1]b[1]+42?b[6]=b[7]=!1:(b[7]=!0,1==B[2]?b[6]=!0:0==B[2]&&b[6]&&(b[6]=!1,b[8]()));e.save();if(Ca){e.save();e.beginPath();p(23,1);e.fillRect(0,0,800,600);p(0);e.beginPath();for(a=0;10>a;a++)b=cb(30*a+Da),p(1),M(0,b+.5,800,void 0,0);e.stroke();e.beginPath();for(a=0;10>a;a++)b=cb(30*a+Da),p(2),M(0,600-b-.5,800,void 0,0);e.stroke();p(2);e.beginPath();M(0,240,800,void 0,0);M(400,240,-240,!0,0);e.stroke(); 24 | p(1);e.beginPath();M(0,360,800,void 0,0);M(400,360,240,!0,0);e.stroke();e.beginPath();for(a=1;a<800/60;a++)b=a*a*5+25,p(2),M(30*a+400,240,-240,!0,b),M(30*-a+400,240,-240,!0,-b);e.stroke();e.beginPath();for(a=1;a<800/60;a++)b=a*a*5+25,p(1),M(30*a+400,360,240,!0,b),M(30*-a+400,360,240,!0,-b);e.stroke();va?(C("controls",400,130,12,[0,16]),C("move awsd",400,251,12,[0,0]),C("fire left click",400,290,12,[0,0]),C("warptime right click",400,330,12,[0,0])):(C("winners don't use drugs",401, 25 | 50,9,[0,0]),C("evil glitch",400,270-50*N,30*(1+N),[0,9,0,9]));C("made by agar3s",401,520,9,[0,10]);e.closePath();e.fill();e.stroke();e.restore()}else if(!Oa){p(7,1);e.fillRect(0,0,800,600);p(-1,1,"rgba("+~~w(180,0)+","+~~w(185,0)+","+~~w(185,0)+","+w(0,1)+")");for(a=0;6>a;a++)e.fillRect(~~w(800),~~w(600),2,2);e.save();e.beginPath();D=ea?[0,0]:[Ba(B[2]||0q?(e.moveTo(-b*q/.3,0),e.lineTo(b*q/.3,0)):(p(0,1),e.bezierCurveTo(-b,0,0,-b*q/3.5,b,0),e.bezierCurveTo(b,0,0,b*q/3.5,-b,0),e.fill()),e.closePath(),e.translate(-h,-m)):(p(-1,0,"rgba(38,82,255,"+q+")"),e.fillRect(x-q*b,m-q*b,q*b*2,q*b*2));e.closePath();e.fill();e.stroke(); 28 | e.restore();e.save();e.translate(f[0]+l[0],f[1]+l[1]);e.rotate(f[3]+Math.PI/2);p(-1,2,2);p(6);e.beginPath();Za(ya[0],ya[1],f[2]);e.closePath();e.stroke();e.restore();e.save();for(a=0;ah[0]+l[0]||h[0]+l[0]>R-20||20>h[1]+l[1]||h[1]+l[1]>H-20)){b=h[0]+l[0]+D[0]+(.5h[5])p(h[5]+24),p(-1,2,2),e.rotate(h[9]),Za(h[7],h[8],h[2]),e.rotate(-h[9]); 29 | else if(14==h[5]){m="hsla("+20*h[3]+",50%,60%, 0.5)";p(-1,2,2);e.beginPath();p(-1,0,0x;x++)X=ga[2*x],G=ga[2*x+1],e.beginPath(),e.arc(4*X*m,4*G*m,m,0,2*Math.PI,!1),e.stroke(),e.beginPath(),e.arc(2*X*m,2*G*m,m,0,2*Math.PI,!1),e.stroke(),e.beginPath(),e.moveTo(X*m*4,G*m*4),e.lineTo(4*m*ga[(2*x+2)%12],4*m*ga[(2*x+3)%12]),e.lineTo(4* 30 | m*ga[(2*x+6)%12],4*m*ga[(2*x+7)%12]),e.moveTo(X*m*2,G*m*2),e.lineTo(2*m*ga[(2*x+2)%12],2*m*ga[(2*x+3)%12]),e.lineTo(2*m*ga[(2*x+6)%12],2*m*ga[(2*x+7)%12]),e.stroke();e.beginPath();e.rotate(-h[3])}else for(p(-1,2,2),m=(Fa[h[5]]-h[9])/Fa[h[5]],m=0b[0]+l[0]||b[0]+l[0]>R-20||20>b[1]+l[1]||b[1]+l[1]>H-20||(e.beginPath(),e.arc(b[0]+l[0],b[1]+l[1],b[2],0,2*Math.PI,!1),e.closePath(),e.fill());e.restore();e.save();for(a=0;ab[0]+l[0]||b[0]+l[0]>R-5||5>b[1]+l[1]||b[1]+l[1]>H-5||(e.beginPath(),p(b[4],1),e.arc(b[0]+l[0]+D[0],b[1]+l[1]+D[1],2,0,2*Math.PI,!1),e.closePath(),e.fill());e.restore();e.save();C(Va,401,501,14,[26,21,21]);ea?(p(22,1),e.fillRect(0,0,v,v),za&& 32 | C("evil mode",400,80,22,[0,16]),Ka?(C("-new record-",400,240,22,[10,18]),C("-share it-",400,400,14,[24,18])):C("game over",400,240,20,[0,13]),C(J.toFixed(0),400,160,Ka?20:16,[0,9])):(C(6record?"record":record.toFixed(0),750,110,9,[24,3],1));e.restore()}Oa||Ea||(e.save(),e.beginPath(),p(-1,2,2),e.translate(B[0],B[1]),p(6),e.translate(-10,-10),Xa(10,20),e.stroke(),e.closePath(),e.restore());e.save();for(a=0;asa.length?120:70,[20,20,20,20,20,20,20,20,20,20,20,20]),e.closePath(),e.fill(),e.restore()):!Ea||300>ka||(a=[0,0,0,0],e.save(),e.beginPath(),436>ka?(p(-1,1,"rgba(0,0,0,"+(1-(436-ka)/120)+")"),e.fillRect(0,0,800,600)):(p(23,1),e.fillRect(0,0,800,600),C(sa,400,250,16,a)),e.closePath(),e.fill(),e.restore()); 34 | 0N&&(p(-1,1,"rgba(220,220,220,"+N+")"),e.fillRect(0,0,800,600));(Oa=0r.q?-1020:1020),L=r.p?((1-r.p)*(1-r.p)*2E4|0)+32:0,W=r.d,S=r.j/2,Z=r.k*r.k*.01,M=r.a,N=a,fa=1/a,ga=1/b,ha=1/e,r=5/(1+r.u*r.u*20)*(.01+A);.8=L&&(oa=0,this.r());u&&++p>=u&&(u=0,h*=n);k+=l;h*=k;h>f&&(h=f,0na&&(na=8);M||(v+=B,0>v?v=0:.5N)switch(U=0,++ja){case 1:N=b;break;case 2:N=e}switch(ja){case 0:V=U*fa;break;case 1:V=1+2*(1-U*ga)*W;break;case 2:V=1-U*ha;break;case 3:V=0,aa=!0}H&&(K+=T,Q=K|0,0>Q?Q=-Q:1023z?z=1E-5:.1=na&&(P%=na,3==M))for(ba=ia.length;ba--;)ia[ba]=w(2,-1);switch(M){case 0:y=P/nay?1:-1);y=.225*((0>y?-1:1)*y*y-y)+y;break;case 3:y=ia[Math.abs(32*P/na|0)]}E&&(ba=da,A*=I,0>A?A=0:.1=ca?-32768:32767*ca|0}return D}},xa,Pa,S,O, 39 | Ia=window.AudioContext||window.webkitAudioContext;if(Ia){xa=new Ia;Pa=xa.createDynamicsCompressor();var bb=xa.createGain();bb.gain.value=window.chrome?.2:.4;Pa.connect(bb);bb.connect(xa.destination);S=function(a){var b=[];Lb(a,xa,function(a){b.push(a)});return b};O=function(a){if(a[0]){var b=xa.createBufferSource();b.context.sampleRate+=~~w(500);b.buffer=a[0];b.start(0);b.connect(Pa);setTimeout(function(){b.disconnect(Pa)},1E3*a[0].duration+300)}}}else S=O=function(){};var Zb=440*Math.pow(Math.pow(2, 40 | 1/12),-9),$b=/^[0-9.]+$/,Mb=/\s+/,ac=/(\d+)/,Fb={};"B#-C C#-Db D D#-Eb E-Fb E#-F F#-Gb G G#-Ab A A#-Bb B-Cb".split(" ").forEach(function(a,b){a.split("-").forEach(function(a){Fb[a]=b})});la.M=function(a){a=a.split(ac);return Zb*Math.pow(Math.pow(2,1/12),Fb[a[0]])*Math.pow(2,(a[1]||4)-4)};la.L=function(a){return $b.test(a)?parseFloat(a):a.toLowerCase().split("").reduce(function(a,e){return a+("w"===e?4:"h"===e?2:"q"===e?1:"e"===e?.5:"s"===e?.25:0)},0)};A.prototype.J=function(){var a=this.gain=this.C.createGain(); 41 | [["bass",100],["mid",1E3],["treble",2500]].forEach(function(b,e){e=this[b[0]]=this.C.createBiquadFilter();e.type="peaking";e.frequency.value=b[1];a.connect(a=e)}.bind(this));a.connect(this.C.destination)};A.prototype.push=function(){Array.prototype.forEach.call(arguments,function(a){this.B.push(a instanceof la?a:new la(a))}.bind(this));return this};A.prototype.createOscillator=function(){this.stop();this.A=this.C.createOscillator();this.K?this.A.setPeriodicWave(this.C.createPeriodicWave.apply(this.C, 42 | this.K)):this.A.type=this.W||"square";this.A.connect(this.gain);return this};A.prototype.R=function(a,b){var e=60/this.D*this.B[a].duration,f=e*(1-(this.F||0));this.H(this.B[a].frequency,b);this.I&&this.B[a].frequency&&this.S(a,b,f);this.H(0,b+f);return b+e};A.prototype.N=function(a){return this.B[aP;P++){var Ib=(P-3)*Math.PI/3+Math.PI/6;ga.push(Math.cos(Ib),Math.sin(Ib))}for(var wb= 60 | {},P=0;1E4>P;P++)wb[(P/1E4).toFixed(4)]=Qb(P/1E4);var Vb={10:function(a){for(var b=0;9>b;b++)if(4!=b){var e=a[0]+(b%3-1)*n,f=a[1]+(~~(b/3)-1)*n;La(e,f,.65,[e,f,1==b?1:0])}a[9]=Fa[10]},11:function(a){for(var b=0;12>b;b++)if(4!=b){var e=a[0]+(b%3-1)*n,f=a[1]+(~~(b/3)-1)*n;La(e,f,.65,[e,f,1==b?3:2])}a[9]=Fa[11]},12:function(a){for(var b=0;2>b;b++){var e=ua([a[0],a[1],5]);e[9]=a[3]+b*Math.PI;F.push(e)}a[9]=Fa[12]},13:function(a){for(var b=0;6>b;b++){var e=ua([a[0],a[1],5]);e[9]=a[3]+(b-3)*Math.PI/3;e[12]+= 61 | .5;F.push(e)}a[9]=45},14:function(a){for(var b=0;6>b;b++){var e=ua([a[0],a[1],5]);e[9]=a[3]+(b-3)*Math.PI/3+Math.PI/6;e[12]-=.6;F.push(e)}0==a[13]%16&&(e=ua([a[0],a[1],a[15][a[14]%a[15].length]]),e[9]=a[3]/2,F.push(e),a[14]++);if(0==a[13]%100)for(b=0;6>b;b++)qb(a,b);a[9]=70;a[13]++}},Na;requestAnimationFrame(rb)})(); 62 | -------------------------------------------------------------------------------- /target/index.html: -------------------------------------------------------------------------------- 1 | Evil Glitch
FullScreen -------------------------------------------------------------------------------- /todo.todo: -------------------------------------------------------------------------------- 1 | todo 2 | 3 | --- save god mode in localstorage 4 | +----- efecto raro en el siguiente boss, aplicacion de filtermode en la textura 5 | +--- technologic song from daft punk idea to make glitches 6 | +---- falta un summon bacano del ultimo enemigo... o algo como un fade o algo... 7 | +---- spetial summon 8 | +---- muerte del final boss 9 | 10 | +!----- seleccionar colores para enemigos y ui 11 | --- el enemigo bala se confunde con las balas de uno.... 12 | +--el color de las particulas no me termina de convencer... 13 | 14 | +--- glitch en gameover 15 | +- Vería si hay alguna lib que pueda inclinar un poco la pantalla (que la angule un poco sobre el eje Z) 16 | +----- sacar un listado de nombres y hacer un cuestionario 17 | +--- wave number 18 | +--- god mode 19 | -- mejorar beats y frequencia de la musica 20 | -- mejorar el sonido de explosion 21 | - soporte de gamepad por favorrrrrrrrrrrrrrrrr 22 | - save statitics 23 | 24 | ----- server mode 25 | -- multiplayer leaderboard 26 | --- server para highscore global 27 | 28 | +--- un ding de audio o un flash en el enemigo cuando le pegas para los que requieren mas de un disparo 29 | +----- clickeable content // buttons 30 | +---- highscore 31 | +---- listado de record y current score, 32 | +---- cuando new record share on twitter 33 | +---- share highscore 34 | 35 | +----- pantalla de inicio 36 | +- hacer el logo con algo como: https://www.youtube.com/watch?v=y8brTm5aslo 37 | +--- hacer un estilo para el intro 38 | +-- shader para el intro: https://www.shadertoy.com/view/MslGWN 39 | +---- incluí si o si un "fire to start" 40 | +---- los controles sean dibujitos de como funcionan 41 | --- dejar claro el warp space powerup 42 | +!--- refactoring to reduce space 43 | +----- summon animation proportional to the enemy size 44 | +---- animar totems 45 | +---- small particles when hit but not kill 46 | +---- greater particles when enemy is harder 47 | +---- when kill totem shakes screen harder 48 | +-- animar summon con la silueta de lo que aparecera y con el tamanio de lo que aparecera //nop 49 | +---- fullscreen 50 | +---- enemigos de colores 51 | +!---- imgr https://github.com/eirikb/gifie/commit/3ca5219deb272d8edf12dd0d5cdb830a25cdc27c 52 | +---- client id d65571b4543e280 53 | +---- click en vez de f5 54 | +--- quizas darle un poco mas de tiempo a los summons... 55 | +--- lso summons deberian tener el tamanio y ojala el shap del enemigo nop 56 | +- use ease functions for blink//nop 57 | 58 | +--- save last 6 frames before death and post to imgr 59 | +!--- problemas de rendimiento en firefox? 60 | +-- aviso webgl not available 61 | +--- efecto vhs cuando este en el inicio o algo asi... // no se pudo 62 | +- mejorar glow? 63 | 64 | +-- mejorar los tiempos antes de empezar 65 | +--- si podes poner un previo de un micro segundo atnes de que se muestra ayuda 66 | +-- stop music on gameover 67 | +-- reasignar posiciones de enemigos 68 | +!---- summon warning 69 | ---- summon totem 70 | -- summon enemy 71 | +---- los guardians deben aparecer desde el centro. 72 | +---- hit en enemy more notorio 73 | +---- enemigo enjambre? // maybe not 74 | +---- enemigo penultimo, movimiento y spawn 75 | +---- enemigo ultimo, disenio y todo 76 | +- Las letras glitcheadas en la pestaña es un detalle increible. 77 | +- Cuando le disparo al triangulo no tiene feedback de si lo estoy dañando o no. 78 | +- El score me parece raro pq suma todo el tiempo. Si es tiempo me copa tiempo, si es puntaje pq me lo gane por matar que sea puntaje. 79 | +- La estetica me gustwwwa! 80 | +- Ponele musica! 81 | +- Hay un momento que parece que esta narrando algo donde no spawnean enemigos, es aburrido 82 | +- Proba cambiandole el pitch rando a cada sonido de disparo para que no sea tan monotono 83 | +- El sonido de la explosion de los enemigos es mas largo que el feedback visual. 84 | +- El left button parece un slow motion mas que un speed up 85 | +- me paso con uno que le disparaba, no hacia nada y pense que era un pick up 86 | +- muy lindo el efecto de pantalla de tubo :slightly_smiling_face: 87 | +- Me encanta la estetica! :clap: 88 | +- Musica ftw, te va a cambiar todo. 89 | +- En la version que probé recien no vendria mal un countdown o un poquito de preparacion antes de que se me vengan encima :stuck_out_tongue: Le agarro la mano igual despues de un par de veces, pero es medio abrupto. 90 | +-- Me resulta medio incomodo que deje de disparar despues de mandar el slow `motion`. 91 | +-- Para alguien que no desarrolla NADA en web, en que lo hiciste? Que usaste? 92 | +-- Tambien estaria bueno tener un toque de anticipacion cuando los triangulos locos van a largar enemigos. Como un shake o algo asi. Esas cosas me ponen nervioso y suman. 93 | +-- y en una me acerque mucho a un triangulo y me spawneo los bichos y fue un insta death --------------------------------------------------------------------------------