├── .gitignore ├── Gruntfile.js ├── LICENSE ├── README.md ├── bin-debug ├── game.js └── index.html ├── bin-release ├── bin-release.zip ├── game.js └── index.html ├── chessPursuit.zip ├── older-versions └── 01-render │ ├── game.js │ └── index.html ├── package.json ├── photos └── 1-pawned.png ├── screens ├── screen-160x160.png └── screen-400x250.png └── src ├── game-audio.js ├── game.js ├── index.html ├── jsfxr.js ├── requestAnimationFame.js ├── stats-wrapper.js └── stats.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .idea/ 3 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | grunt.initConfig({ 4 | pkg: grunt.file.readJSON('package.json'), 5 | concat: { 6 | options: { 7 | separator: ';' 8 | }, 9 | distDebug: { 10 | src: ['src/jsfxr.js','src/*.js'], 11 | dest: 'bin-debug/game.js' 12 | }, 13 | distRelease: { 14 | src: ['src/requestAnimation.js','src/jsfxr.js','src/game-audio.js','src/game.js'], 15 | dest: 'bin-release/game.js' 16 | } 17 | }, 18 | uglify: { 19 | dist: { 20 | files: { 21 | 'bin-release/game.js': 'bin-release/game.js' 22 | } 23 | } 24 | }, 25 | jshint: { 26 | files: ['Gruntfile.js', 'src/game.js'], 27 | options: { 28 | // options here to override JSHint defaults 29 | globals: { 30 | jQuery: true, 31 | console: true, 32 | module: true, 33 | document: true 34 | } 35 | } 36 | }, 37 | copy: { 38 | 'dist-debug': { 39 | expand:true, 40 | cwd : 'src/', 41 | src: ['*.html'], 42 | dest: 'bin-debug/' 43 | }, 44 | 'dist-release': { 45 | expand:true, 46 | cwd : 'src/', 47 | src: ['*.html'], 48 | dest: 'bin-release/' 49 | } 50 | }, 51 | watch: { 52 | files: ['Gruntfile.js','src/**/*'], 53 | tasks: ['jshint', 'concat', 'uglify','copy'] 54 | } 55 | }); 56 | 57 | grunt.loadNpmTasks('grunt-contrib-copy'); 58 | grunt.loadNpmTasks('grunt-contrib-uglify'); 59 | grunt.loadNpmTasks('grunt-contrib-jshint'); 60 | grunt.loadNpmTasks('grunt-contrib-qunit'); 61 | grunt.loadNpmTasks('grunt-contrib-watch'); 62 | grunt.loadNpmTasks('grunt-contrib-concat'); 63 | 64 | grunt.registerTask('test', ['jshint']); 65 | 66 | grunt.registerTask('default', ['jshint', 'concat', 'uglify','copy']); 67 | 68 | }; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2017 ChessPursuit authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Chess Pursuit 2 | 3 | This is the game I created for the JS13K contest of 2015, the theme was "Reversed". 4 | 5 | ### Concept 6 | 7 | My interpretation of the theme was quite loose. The idea was to turn the chess game upside down, making it an action game instead of a strategy game. 8 | For once, I started with the graphical concept, the perspective and the scrolling and tried to make some game out of it. 9 | The result is a little weird, the 'action' part and the 'puzzle' part don't blend so well, but I kinda like it anyway. 10 | 11 | ### Code 12 | 13 | I lost a lot of time drawing the checkerboard. My first version used shaky code to compute the projections from checkerboard space to screen space. 14 | While it looked good enough, it was impossible to reverse the projection for computing mouse input, so I had to do it again with a more mathematical approach. 15 | 16 | For the pieces, I decided to use SVG in order to have them scale nicely. It was my first contact with SVG and it was quite interesting. 17 | I probably would have saved some time by writing the SVG elements directly in the HTML instead of using JS to generate everything. 18 | 19 | As for the rest of the code, it is a little messy, as usual. I should probably have taken some time to write things more cleanly, especially the intro & dialogs part. 20 | 21 | 22 | ### External code 23 | 24 | I relied on Mr Doob stats.js lib during debug: 25 | https://github.com/mrdoob/stats.js/ 26 | 27 | And on jsfx for the sound: 28 | https://github.com/mneubrand/jsfxr 29 | 30 | Using code provided by Jack Rugile for the integration of jsfxr: 31 | http://codepen.io/jackrugile/blog/arcade-audio-for-js13k-games 32 | -------------------------------------------------------------------------------- /bin-debug/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 46 | 47 | 48 |
49 | Use arrow keys or mouse to move around.
50 | You can use the 'return' key to pause the game (but that's cheating !).
51 | You don't need to really know the chess rules to play, but that could definitely help! 52 |
53 | 54 | -------------------------------------------------------------------------------- /bin-release/bin-release.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Saturnyn/ChessPursuit/58ca8fc5d8f847eba884917d45dfa05c017bf6ae/bin-release/bin-release.zip -------------------------------------------------------------------------------- /bin-release/game.js: -------------------------------------------------------------------------------- 1 | function J(){this.B=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)}}var W=new function(){this.A=new J;var a,b,c,d,e,f,g,h,i,j,k,l;this.reset=function(){var a=this.A;d=100/(a.f*a.f+.001),e=100/(a.g*a.g+.001),f=1-a.h*a.h*a.h*.01,g=-a.i*a.i*a.i*1e-6,a.a||(k=.5-a.n/2,l=5e-5*-a.o),h=1+a.l*a.l*(0o.q?-1020:1020),A=o.p?((1-o.p)*(1-o.p)*2e4|0)+32:0,B=o.d,C=o.j/2,D=o.k*o.k*.01,E=o.a,F=a,G=1/a,H=1/b,I=1/c,o=5/(1+o.u*o.u*20)*(.01+s);o>.8&&(o=.8);for(var J,K,L,M,N,o=1-o,O=!1,P=0,Q=0,R=0,S=0,T=0,U=0,V=0,W=0,X=0,Y=0,Z=Array(1024),$=Array(32),_=Z.length;_--;)Z[_]=0;for(_=$.length;_--;)$[_]=2*Math.random()-1;for(_=0;n>_;_++){if(O)return _;if(A&&++X>=A&&(X=0,this.reset()),j&&++i>=j&&(j=0,d*=h),f+=g,d*=f,d>e&&(d=e,w>0&&(O=!0)),K=d,C>0&&(Y+=D,K*=1+Math.sin(Y)*C),K|=0,8>K&&(K=8),E||(k+=l,0>k?k=0:k>.5&&(k=.5)),++Q>F)switch(Q=0,++P){case 1:F=b;break;case 2:F=c}switch(P){case 0:R=Q*G;break;case 1:R=1+2*(1-Q*H)*B;break;case 2:R=1-Q*I;break;case 3:R=0,O=!0}x&&(z+=y,L=0|z,0>L?L=-L:L>1023&&(L=1023)),p&&r&&(q*=r,1e-5>q?q=1e-5:q>.1&&(q=.1)),N=0;for(var ab=8;ab--;){if(V++,V>=K&&(V%=K,3==E))for(J=$.length;J--;)$[J]=2*Math.random()-1;switch(E){case 0:M=k>V/K?.5:-.5;break;case 1:M=1-V/K*2;break;case 2:M=V/K,M=6.28318531*(M>.5?M-1:M),M=1.27323954*M+.405284735*M*M*(0>M?1:-1),M=.225*((0>M?-1:1)*M*M-M)+M;break;case 3:M=$[Math.abs(32*V/K|0)]}p&&(J=U,s*=t,0>s?s=0:s>.1&&(s=.1),u?(T+=(M-U)*s,T*=o):(U=M,T=0),U+=T,S+=U-J,M=S*=1-q),x&&(Z[W%1024]=M,M+=Z[(W-L+1024)%1024],W++),N+=M}N=.125*N*R*v,m[_]=N>=1?32767:-1>=N?-32768:32767*N|0}return n}};window.jsfxr=function(a){W.A.B(a);var b=W.D();a=new Uint8Array(4*((b+1)/2|0)+44);var b=2*W.C(new Uint16Array(a.buffer,44),b),c=new Uint32Array(a.buffer,0,44);c[0]=1179011410,c[1]=b+36,c[2]=1163280727,c[3]=544501094,c[4]=16,c[5]=65537,c[6]=44100,c[7]=88200,c[8]=1048578,c[9]=1635017060,c[10]=b;for(var b=b+44,c=0,d="data:audio/wav;base64,";b>c;c+=3)var e=a[c]<<16|a[c+1]<<8|a[c+2],d=d+("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[e>>18]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[e>>12&63]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[e>>6&63]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[63&e]);return d};var aa;!function(){function a(){this.sounds={}}a.prototype.add=function(a,b,c){this.sounds[a]=[],c.forEach(function(c,d){this.sounds[a].push({tick:0,count:b,pool:[]});for(var e=0;b>e;e++){var f=new Audio;f.src=jsfxr(c),this.sounds[a][d].pool.push(f)}},this)},a.prototype.play=function(a){var b=this.sounds[a],c=b.length>1?b[Math.floor(Math.random()*b.length)]:b[0];c.pool[c.tick].play(),c.tick0&&(a=b-1);break}c(a),P=null,Bb=-1,rb++}function c(a){function b(){var a=_.ceil(f/2);Ab.push(h+a);for(var b=0;f>b;b++){if(zb[h]=[],b==a)for(var c=0;eb>c;c++)zb[h][c]={checkPoint:!0};h++}}function c(){var b,c=Array.prototype.slice.call(arguments);b="object"==typeof c[0]?c.shift():{};var d,f,i,j,k=a=0;i--){if(f=c[i],f.length>eb)throw new Error;if(zb[h]=[],k&&""!==f)for(j=0;ji;i++)for(f=zb[i],j=0;eb>j;j++){var p=g(i,j);p&&p.piece==d.piece&&(zb[i][j]||(zb[i][j]={}),zb[i][j].showThreat=!0)}}zb&&d(),zb=[],Ab=[0];var f=5,h=0;c({intro:!0},"","","",Kb?" reqr":"",Kb?" kk":"","","","","","","","kkkkkkkk"),c({showThreat:"p"},"",""," p","","","","",""),c(" pppp"," pppp",""," p",""," p",""," p"),c("","pppppp",""," pppppp","","pppppp","",""),c("","pp ppp"," p p"," p","","","",""),c(""," pp","p p p p"," p p","","","",""),c(""," ppp"," p","ppp"," p ppp"," p","",""),b(),c({showThreat:"r"},""," p",""," r",""," p","",""),c("","","r"," r"," r"," r"," r"," r","pppppp"),c("","",""," p"," rp r","","","pp","r p p","p"),c("","p r","r p","r p",""," p"," p","p ppppp",""),b(),c({showThreat:"b"},"","","","","...b","","","",""),c(""," p p p","b b b b","","","","","",""),c("","r.p..p.r","p......p","","b b","pp....pp","",""),c("","r"," r p..b"," ..r","....p","...p","..p","pp.....p"),b(),c({showThreat:"k"},"",""," k","","","","",""),c("p","r.....p","","","......kk","","pkk",""),c("p pppp","r b r"," p b p","","",""," k","","",""),b(),c({showThreat:"l"},"","",""," l","","","",""),c("l.llllll","l.l....l","..l.llll",".ll.l ","l...l l ","l.lll l ","l. l ","lllllll "),c(" p pll"," l p l","lpl p p "," lp","ll ll l"," l l","",""),c(""," r"," llk"," ."," l .."," p pb"," . . l"," p l","","","","",""),b(),c("","c","","","","",""),Q=h,0===a?(Eb=e(Ob,5,4).piece,xb=2):(xb=Ab[a],Eb=e(Ob,xb,3).piece,xb-=4)}function d(){for(var a in zb)if(zb[a])for(var b in zb[a]){var c=zb[a][b];c&&c.piece&&G(c.piece)}}function e(a,b,c){var d={shape:null,type:a,row:b,col:c,showThreat:!1};if(zb[b]||(zb[b]=[]),zb[b][c]||(zb[b][c]={}),zb[b][c].piece=d,a==Wb)for(var e=0;eb>e;e++)3!=e&&4!=e&&(zb[b][e]||(zb[b][e]={}),zb[b][e].wall=!0);return zb[b][c]}function f(a,b){var c=Eb.col,d=Eb.row;0>b?b=0:b>=eb&&(b=eb-1);var e=_.floor(xb),f=Bb-2;if(e>a?a=e:a>=f&&(a=f),b!=c||a!=d){var h=zb[d][c];zb[a]||(zb[a]=[]);var j=zb[a][b];if(j||(j={},zb[a][b]=j),!j.wall){var l=g(a,b);l?(aa.play("check"),Eb.invalid=!0,Eb.invalidCol=b,Eb.invalidRow=a,Eb.threateningPiece=l.piece,b=c,a=d,sb++):(Eb.invalid=!1,j.piece?(i(j.piece),qb++,aa.play("capture")):aa.play("move"),h.piece=null,j.piece=Eb),Eb.oldCol=c,Eb.oldRow=d,Eb.anim=!0,Eb.animStartTime=ob,Eb.col=b,Eb.row=a,a>=Q&&k(!0,!0)}}}function g(a,b){var c;if(c=h(a,b,Ub))return c;if(c=h(a+1,b-1,Qb)||h(a+1,b+1,Qb))return c;var d,e,f;for(e=b-1;e>=0;e--)if(f=h(a,e),f&&f.piece){if(f.piece.type==Rb)return f;break}for(e=b+1;eb>=e;e++)if(f=h(a,e),f&&f.piece){if(f.piece.type==Rb)return f;break}for(d=a+1;a+eb>=d;d++)if(f=h(d,b),f&&f.piece){if(f.piece.type==Rb)return f;break}for(d=a-1;d>=a-eb;d--)if(f=h(d,b),f&&f.piece){if(f.piece.type==Rb)return f;break}for(e=b-1,d=a-1;e>=0;e--,d--)if(f=h(d,e),f&&f.piece){if(f.piece.type==Sb)return f;break}for(e=b+1,d=a-1;eb>e;e++,d--)if(f=h(d,e),f&&f.piece){if(f.piece.type==Sb)return f;break}for(e=b-1,d=a+1;e>=0;e--,d++)if(f=h(d,e),f&&f.piece){if(f.piece.type==Sb)return f;break}for(e=b+1,d=a+1;eb>e;e++,d++)if(f=h(d,e),f&&f.piece){if(f.piece.type==Sb)return f;break}return c=h(a+2,b-1,Tb)||h(a-2,b-1,Tb)||h(a+2,b+1,Tb)||h(a-2,b+1,Tb)||h(a+1,b-2,Tb)||h(a-1,b-2,Tb)||h(a+1,b+2,Tb)||h(a-1,b+2,Tb)}function h(a,b,c){var d=zb[a];if(d){var e=d[b];if(e&&e.piece&&(!c||e.piece.type==c))return e}}function i(a){if(Db.push(a),a.removedTime=ob,a.justRemoved=!0,a.showThreat)for(var b=a.row-eb;b=0&&(0===Dc.space||bc.click)&&(Dc.space=-1,o()),n()):(l(),m()),t()),bc.click=!1,window.ste&&ste(),Cb&&requestAnimationFrame(j)}function k(a,c){c?(Cb=!1,vb=!0,tb.style.display="block"):a!=vb&&(vb=a,vb?ub.style.display="block":(ub.style.display="none",b()))}function l(){if(!(0>=Bb||Eb.invalid||Kb)){var a=0,b=0;ac||((Dc.down==$b||0===Dc.down)&&(b=-1),(Dc.up==$b||0===Dc.up)&&(b=1),(Dc.left==$b||0===Dc.left)&&(a=-1),(Dc.right==$b||0===Dc.right)&&(a=1),(a||b)&&(a?(Dc.up<=$b&&Dc.up>0&&(b=1),Dc.down<=$b&&Dc.down>0&&(b=-1)):(Dc.left<=$b&&Dc.left>0&&(a=-1),Dc.right<=$b&&Dc.right>0&&(a=1)),f(Eb.row+b,Eb.col+a),ac=!0));var c;if(ac){var d=!0;for(c in Cc)if(Cc[c]){d=!1;break}ac=!d}for(c in Dc)Dc[c]>=1?Dc[c]++:Dc[c]>_b&&Dc[c]--;if(!Eb.anim&&bc.x>0&&bc.xcb&&bc.y1?a=1:-1>a&&(a=-1),b>1?b=1:-1>b&&(b=-1),cc=Eb.row+b,dc=Eb.col+a,bc.click&&(a||b)&&f(cc,dc)}else dc=-1,cc=-1}}function m(){if(ob=Date.now(),P&&(xb+=yb*(ob-P)*pb),Eb.rowBb){for(var b,c,d,e=a-gb-5;e>Bb-gb-5;e--)if(b=zb[e])for(d=!0,c=0;eb>c;c++)b[c]&&b[c].piece&&G(b[c].piece);for(e=Bb+1;a>=e;e++)if(b=zb[e])for(d=!0,c=0;eb>c;c++){var h=eb/2;c%2===0?h+=c/2:h-=(c+1)/2,b[h]&&b[h].piece&&F(b[h].piece)}Bb=a}P=ob}function n(){var a,b,c,d,e=zb[8][3].piece,f=Eb;ob=Date.now();var g=!R;if(g&&(R=ob,r(),e&&(e.talking=!1,e.talkingStarTime=ob),f.talking=!1,f.talkingStarTime=ob),-1==Nb)kb.style.opacity=0,mb.style.opacity=0,Lb=0,m(),P=null,Nb=0;else if(0===Nb);else if(1==Nb)g&&(I(!0,["Surrender Black King !","Your army is defeated, and your Queen is mine !"]),e.talking=!0);else if(2==Nb){if(g)for(J(),b=0;eb>b;b++)c=zb[0][b],p(c.piece,{row:2,col:b+(b%2===0?1:-1)},.1*b,.5)}else if(3==Nb)g&&(I(!0,["You thought I'd only bring two knights to battle ?","You are surrounded,","admit defeat now and I shall be merciful."]),e.talking=!0);else if(4==Nb)g&&(I(!1,["Never !"]),f.talking=!0);else if(5==Nb)g&&(I(!0,["As you wish...","I am taking the prisoner back to the castle.","Knights, capture him, I want him alive."]),e.talking=!0);else if(6==Nb){if(g){for(J(),b=2;5>=b;b++)c=zb[8][b],c&&(p(c.piece,{row:9},0,.5),p(c.piece,{row:10},1,.5));p(zb[7][3].piece,{row:8,col:1},.5,.5),p(zb[7][3].piece,{row:10,col:2},1.5,.5),p(zb[7][4].piece,{row:8,col:6},.5,.5),p(zb[7][4].piece,{row:10,col:5},1.5,.5)}}else if(7==Nb){if(g){var h='';for(I(!1,['It looks like our roles are REVERSED'+h+" my Queen.","Today, it is my turn to protect you !"]),f.talking=!0,a=1;2*eb>a;a++)if(d=zb[a])for(b=0;eb>b;b++)c=d[b],c&&c.piece&&c.piece.intro&&(G(c.piece),d[b]={})}}else 8==Nb&&(g&&(J(),Hb.style.display="none"),Mb=(ob-R)/4e3,Mb>1?Nb=9:(Mb=_.sin(Mb*ab/2),Lb=(Mb-.2)/.6,0>Lb?Lb=0:Lb>1&&(Lb=1),kb.style.opacity=Lb,Mb>.8&&(Jb.style.opacity=(1-Mb)/.2,mb.style.opacity=1-Jb.style.opacity)));if(g)if(W.length){Hb.style.opacity=0;var i=W[0].e==S&&W[0].to._y==wc,j=W[W.length-1],k=j.du+j.de;i?p(Hb.style,{opacity:1},k+2,.1):Nb>1&&8>Nb&&q(o,k)}else Hb.style.opacity=1;s(),9==Nb&&(Kb=!1,Lb=1,Jb.style.display="none",kb.style.opacity=1,mb.style.opacity=1,Hb.style.display="none",J(),Eb.talking=!1),P=ob}function o(){Nb++,R=null}function p(a,b,c,d){W.push({e:a,to:b,de:c||0,du:d})}function q(a,b){W.push({cb:a,de:0,du:b})}function r(){if(W)for(var a=0;a=d.de+d.du)d.cb&&(d.cb(),d.cb=null);else if(d.e){if(!d.from){d.from={};for(a in d.to)d.from[a]=d.e[a]}var e=(b-d.de)/d.du;e=_.sin(e*ab/2);for(a in d.to)d.e[a]=d.from[a]*(1-e)+d.to[a]*e}}S.setAttributeNS(null,"y",S._y)}function t(){for(var a,b,c=Bb-gb-5;Bb>=c;c++)if(b=zb[c])for(var d=0;eb>d;d++)if(b[d]&&b[d].piece){var e=b[d].piece;if(u(e.row,e.col,e),e.talking){var f=-_.abs(_.sin((ob-e.talkingStarTime)*ab/800))*fb*.2;e.y+=f}v(e),e.y>Eb.y&&(!a||a.y>e.y)&&(a=e)}var g;if(Eb.anim)if(g=Eb.invalid?pb*(ob-Eb.animStartTime)/Gb:pb*(ob-Eb.animStartTime)/Fb,0>g||g>=1)Eb.anim=!1,Eb.invalid&&Eb.threateningPiece&&(Eb.threateningPiece.shape.style.filter="none"),Eb.invalid=!1;else if(g=_.sin(g*ab*.5),Eb.invalid){u(Eb.row,Eb.col);var h=.4*(.5>g?g:1-g)*fb,i=_.sin(6*g*ab)*h;Eb.x+=i,v(Eb),Eb.threateningPiece.shape.style.filter="url(#"+Yb+")"}else u(Eb.oldRow,Eb.oldCol),Eb.opacity=g*Eb.opacity+(1-g)*u.res.opacity,Eb.scale=g*Eb.scale+(1-g)*u.res.scale,Eb.x=g*Eb.x+(1-g)*u.res.x,Eb.y=g*Eb.y+(1-g)*u.res.y,v(Eb);for(l=0,n=Db.length;n>l;l++){var j=Db[l],k=(ob-j.removedTime)/1e3;k>1?(G(j),Db[l]=Db[n-1],n--,l--,Db=Db.slice(0,n)):(j.justRemoved&&(j.justRemoved=!1,j.removedX=j.x,j.removedY=j.y),j.x=j.x<.5*db?j.removedX-k*db:j.removedX+k*db,j.y=j.removedY-_.sin(k*ab)*db*.4,v(j))}Eb.anim&&Eb.invalid?(V||(V={onTop:!0,type:Zb}),V.shape||(F(V),V.row=Eb.invalidRow,V.col=Eb.invalidCol),u(Eb.invalidRow,Eb.invalidCol,V),V.scale=1,V.y-=g*V.scale*fb*.2,V.opacity=.8>g?1:1-(g-.8)/(1-.8),v(V)):V&&V.shape&&G(V),a&&a.shape&&Eb.shape&&Eb.shape.nextSibling!=a.shape&&yc.insertBefore(Eb.shape,a.shape),jb.save(),jb.translate(0,cb),jb.fillStyle=ec,jb.beginPath(),jb.rect(0,0,db,db),jb.fill(),jb.clip();var l,m,n,o=_.floor(xb),p=-(xb-_.floor(xb)),q={},r={},s={},t={};for(l=-1;gb>l;l++)for(m=0;eb>m;m++){if(w(m/eb,(l+p)/eb,q),w(m/eb,(l+1+p)/eb,t),w((m+1)/eb,(l+1+p)/eb,s),w((m+1)/eb,(l+p)/eb,r),jb.beginPath(),jb.moveTo(q.x*db,q.y*db),jb.lineTo(r.x*db,r.y*db),jb.lineTo(s.x*db,s.y*db),jb.lineTo(t.x*db,t.y*db),jb.closePath(),jb.lineWidth=1,jb.fillStyle=-1!=cc&&-1!=dc&&l+o==cc&&m==dc?hc:(l+m+o)%2===0?fc:gc,jb.fill(),b=zb[l+o],b&&b[m]){var x=b[m];x.showThreat&&(jb.fillStyle="rgba("+kc+",0.5)",jb.fill()),x.checkPoint&&(jb.fillStyle=lc,jb.fill())}if(Eb.invalid&&Eb.invalidCol==m&&Eb.invalidRow==l+o){var y=1.5*(.5>g?g:1-g);jb.fillStyle="rgba("+kc+","+y+")",jb.fill()}}jb.restore()}function u(a,b,c){c=c||u.res,w((b+.5)/eb,(a-xb+.5)/eb);var d=.02,e=1;return w.res.y0&&a.shape.setAttributeNS(null,"transform","scale("+a.scale+") translate("+a.x/a.scale+","+a.y/a.scale+")"))}function w(a,b,c){return c=c||w.res,c.y=y(b,oc,pc,qc),c.scaleX=y(b,rc,sc,tc),c.scaleY=c.scaleX,c.x=(1-c.scaleX)/2+a*c.scaleX,Kb&&(c.x=Lb*c.x+(1-Lb)*a,c.y=Lb*c.y+(1-Lb)*(1-b),c.scaleX=Lb*c.scaleX+1*(1-Lb),c.scaleY=c.scaleX),c}function x(a,b,c){c=c||x.res,c.y=z(b,oc,pc,qc,!1);var d=y(c.y,rc,sc,tc);return c.x=(a-(1-d)/2)/d,c}function y(a,b,c,d){return b*a*a+c*a+d}function z(a,b,c,d,e){return e?(-c+_.sqrt(c*c-4*b*(d-a)))/(2*b):(-c-_.sqrt(c*c-4*b*(d-a)))/(2*b)}function A(){$.appendChild(ib),ib.width=db,ib.height=db+cb}function B(){var a=lb,b=a.createLinearGradient(0,0,0,db),c="rgba(10,20,25,",d=")";b.addColorStop(0,c+0+d),b.addColorStop(.2,c+0+d),b.addColorStop(1,c+.5+d),a.fillStyle=b,a.fillRect(0,0,db,db),a.restore(),kb.style.top=cb+"px",kb.style.pointerEvents="none",$.appendChild(kb)}function C(){var a=.02*db;mb.width=db,mb.height=cb+a;var b=nb;b.clearRect(0,0,db,db),b.save(),b.fillStyle="#FF8601",b.beginPath(),b.rect(0,0,db,cb),b.fill(),b.clip(),b.fillStyle="#FFE7CA";var c=db/4;b.beginPath(),b.arc(db/2,cb+.3*c,c,0,ab,!0),b.fill(),b.restore(),b.beginPath(),b.fillStyle="rgb(10,20,25)";for(var d=40,e=[0,.7,.1,.3,.2,1,.3,.5,.35,.8,.42,.5,.55,.9,.7,.45,.8,1.1,.88,.4,1,.8],f=0;f'),v.appendChild(b),b}function d(){var a=f(5,8,3.1,1.8);return M(a,"rgba(0,0,0,0.2)","none"),a}function e(a,b,c){var d=Y.createElementNS(r,"circle");return L(d,{cx:j(a-w),cy:j(b-x),r:j(c)}),d}function f(a,b,c,d){var e=Y.createElementNS(r,"ellipse");return L(e,{cx:j(a-w),cy:j(b-x),rx:j(c),ry:j(d)}),e}function g(a,b){var c=Y.createElementNS(r,"path");return c.setAttributeNS(null,"d",h(a)),b&&L(c,b),c}function h(a){for(var b="",c=0;cPress SPACE or CLICKto restart from the last checkpoint.'),ub.appendChild(b)}function m(){Jb=Y.createElementNS(r,"g"),vc.appendChild(Jb);var a=Y.createElementNS(r,"rect");L(a,{x:0,y:0,width:"100%",height:cb,fill:ec,stroke:"#000"}),Jb.appendChild(a);var b=Y.createElementNS(r,"text");L(b,{x:"50%",y:"60","font-size":"48px",fill:"orange",stroke:"red","stroke-width":"2px","text-anchor":"middle","font-family":"Impact"}),K(b,'CHESSPURSUIT'),Jb.appendChild(b)}function n(){Hb=Y.createElementNS(r,"text"),L(Hb,{x:"50%",y:cb+db-10,"font-size":"22px",fill:"white",stroke:"black","stroke-width":"1px","text-anchor":"middle","font-family":"Impact"}),K(Hb,'Press SPACE or CLICK'),vc.appendChild(Hb)}function o(){Ib=Y.createElementNS(r,"text"),L(Ib,{x:"50%",y:"50%","font-size":"32px",fill:"orange",stroke:"black","stroke-width":"1px","text-anchor":"middle","font-family":"Impact"}),K(Ib,"PAUSED"),Ib.style.display="none",vc.appendChild(Ib)}function p(){var a=db-2*hb,b=.3*db-2*hb;xc=db+cb,wc=cb+db-b-hb,S=Y.createElementNS(r,"svg"),S._y=xc,L(S,{x:hb,y:xc,width:a,height:b}),vc.appendChild(S);var c=Y.createElementNS(r,"rect");L(c,{width:"100%",height:"100%",fill:"rgba(0,0,0,0.8)",stroke:"#fff","stroke-width":2}),S.appendChild(c),T=Y.createElementNS(r,"text"),L(T,{x:10,y:20,"font-size":"18px",fill:"#fff","text-anchor":"left","font-family":"Impact"}),S.appendChild(T),U=Y.createElementNS(r,"text"),L(U,{x:10,y:40,"font-size":"16px",fill:"#fff","text-anchor":"left","font-family":"sans-serif"}),S.appendChild(U)}function q(){tb=Y.createElementNS(r,"g"),tb.style.display="none",vc.appendChild(tb);var a=Y.createElementNS(r,"rect");L(a,{x:0,y:0,width:"100%",height:"100%",fill:"rgba(0,0,0,0.5)"}),tb.appendChild(a);var b=Y.createElementNS(r,"text");L(b,{x:"50%",y:"50%","font-size":"48px",fill:"#5f7",stroke:"black","stroke-width":"2px","text-anchor":"middle","font-family":"Impact"}),K(b,"YOU WIN !"),tb.appendChild(b),b=Y.createElementNS(r,"text"),L(b,{x:"50%",y:"60%","font-size":"22px",fill:"white",stroke:"black","stroke-width":"1px","text-anchor":"middle","font-family":"Impact"}),K(b,"Alas, your Queen is in another castle..."),tb.appendChild(b)}uc=i;var r="http://www.w3.org/2000/svg",s="http://www.w3.org/1999/xlink",t=db,u=db+cb;vc=Y.createElementNS(r,"svg"),vc.setAttribute("xmlns",r),vc.setAttributeNS(null,"viewBox","0 0 "+t+" "+u),vc.setAttributeNS(null,"width",t),vc.setAttributeNS(null,"height",u),$.appendChild(vc);var v=Y.createElementNS(r,"defs");vc.appendChild(v),yc=Y.createElementNS(r,"g"),vc.appendChild(yc),m(),c(Yb),k(Zb,"CHECK"),l(),p(),n(),o(),q();var w=5,x=8;M(b(Qb,[g(["M",[2,8],"Q",[5,10],[8,8],"L",[5,3],"L",[2,8]]),e(5,3,2)]),ic,jc,0),M(b(Pb,[g(["M",[4.6,1.4],"L",[5.4,1.4],"L",[5.4,-.6],"L",[6.4,-.6],"L",[6.4,-1.4],"L",[5.4,-1.4],"L",[5.4,-2.4],"L",[4.6,-2.4],"L",[4.6,-1.4],"L",[3.6,-1.4],"L",[3.6,-.6],"L",[4.6,-.6],"L",[4.6,1.4]]),g(["M",[2,8],"Q",[5,10],[8,8],"L",[6,3],"L",[7,1],"Q",[5,0],[3,1],"L",[4,3],"L",[2,8]])]),ic,jc,0),M(b(Tb,[g(["M",[2,8],"Q",[5,10],[8,8],"L",[7,6],"Q",[8,3],[7,0],"L",[6,1],"L",[5,1],"L",[2,4],"L",[3,5],"L",[5,4],"L",[2,8]])]),ic,jc,0),M(b(Rb,[g(["M",[2,8],"Q",[5,10],[8,8],"L",[6.5,3],"L",[8,2],"L",[8,0],"L",[7,0],"L",[7,1],"L",[6,1],"L",[6,0],"L",[4,0],"L",[4,1],"L",[3,1],"L",[3,0],"L",[2,0],"L",[2,2],"L",[3.5,3],"L",[2,8]])]),ic,jc,0),M(b(Sb,[g(["M",[2,8],"Q",[5,10],[8,8],"L",[6,4],"Q",[8,1.1],[5,0],"Q",[2,1.1],[4,4],"L",[2,8]]),e(5,0,.7),g(["M",[3.8,.8],"L",[4.4,2.5]],{"stroke-width":2})]),ic,jc,0),M(b(Vb,[g(["M",[2,8],"Q",[5,10],[8,8],"L",[8.5,6],"L",[7,7],"L",[7,5],"L",[6,6],"L",[5,4.5],"L",[4,6],"L",[3,5],"L",[3,7],"L",[1.5,6],"L",[2,8]])]),ic,jc,0),M(b(Ub,[a(2,9.5),a(5,9),a(8,10),a(7,8),a(4,7.5),a(1,8.5)],!0),ic,jc,0),M(b(Ob,[g(["M",[4.6,1.4],"L",[5.4,1.4],"L",[5.4,-.6],"L",[6.4,-.6],"L",[6.4,-1.4],"L",[5.4,-1.4],"L",[5.4,-2.4],"L",[4.6,-2.4],"L",[4.6,-1.4],"L",[3.6,-1.4],"L",[3.6,-.6],"L",[4.6,-.6],"L",[4.6,1.4]]),g(["M",[2,8],"Q",[5,10],[8,8],"L",[6,3],"L",[7,1],"Q",[5,0],[3,1],"L",[4,3],"L",[2,8]])]),"#002","#333",0),M(b(Xb,[e(5,.4,.6),g(["M",[2,8],"Q",[5,10],[8,8],"L",[6,3],"L",[7.5,1],"L",[5.8,1.5],"L",[5,.8],"L",[4.2,1.5],"L",[2.5,1],"L",[4,3],"L",[2,8]])]),"#002","#333",0),w=2.5,x=50,M(b(Wb,[g(["M",[25,30],"L",[50,30],"L",[50,5],"L",[45,5],"L",[45,10],"L",[40,10],"L",[40,5],"L",[35,5],"L",[35,10],"L",[30,10],"L",[30,5],"L",[25,5],"L",[25,30]]),g(["M",[0,50],"L",[0,50],"L",[27.5,50],"L",[27.5,35],"L",[47.5,35],"L",[47.5,50],"L",[75,50],"L",[75,20],"L",[70,20],"L",[70,25],"L",[65,25],"L",[65,20],"L",[60,20],"L",[60,25],"L",[55,25],"L",[55,20],"L",[50,20],"L",[50,25],"L",[45,25],"L",[45,20],"L",[40,20],"L",[40,25],"L",[35,25],"L",[35,20],"L",[30,20],"L",[30,25],"L",[25,25],"L",[25,20],"L",[20,20],"L",[20,25],"L",[15,25],"L",[15,20],"L",[10,20],"L",[10,25],"L",[5,25],"L",[5,20],"L",[0,20],"L",[0,50]])],!0),ic,jc,0)}function I(a,b){a?K(T,"White King :"):K(T,"Black King :");for(var c="",d=0;d"+b[d]+"";K(U,c),p(S,{_y:wc},0,.5)}function J(){p(S,{_y:xc},0,.5)}function K(a,b){for(;a.firstChild;)a.removeChild(a.firstChild);var c=""+b+"";Ac.innerHTML=c;var d=Array.prototype.slice.call(Ac.childNodes[0].childNodes);d.forEach(function(b){a.appendChild(b)})}function L(a,b){if(b)for(var c in b)a.setAttributeNS(null,c,b[c]);return a}function M(a,b,c,d){var e={};return"undefined"!=typeof b&&(e.fill=b),"undefined"!=typeof c&&(e.stroke=c),"undefined"!=typeof d&&(e.strokeWidth=d),L(a,e),a}function N(a,b){b||(b=window.e);var c=b.keyCode;b.charCode&&!c&&(c=b.charCode);var d=Bc[c];d&&Cc[d]!==a&&(Cc[d]=a,"undefined"==typeof Dc[d]&&(Dc[d]=-1),a?Dc[d]<1&&(Dc[d]=1):Dc[d]>0&&(Dc[d]=0))}function O(a,b){bc.click=a,Y.onmousemove(b)}var P,Q,R,S,T,U,V,W,X=window,Y=X.document,Z=Y.body,$=null,_=X.Math,ab=_.PI,bb=(_.sqrt,_.random,1),cb=100*bb,db=400*bb,eb=8,fb=db/eb,gb=2*eb+3,hb=6,ib=D(db,db,"bg"),jb=E(ib),kb=D(db,db,"shadow"),lb=E(kb),mb=D(db,2*db,"sky"),nb=E(mb),ob=0,pb=.001,qb=0,rb=0,sb=0,tb=null,ub=null,vb=!1,wb=!1,xb=0,yb=1,zb=null,Ab=null,Bb=-1,Cb=!0,Db=[],Eb=null,Fb=.05,Gb=.5,Hb=null,Ib=null,Jb=null,Kb=!0,Lb=0,Mb=0,Nb=-1,Ob="h",Pb="e",Qb="p",Rb="r",Sb="b",Tb="k",Ub="l",Vb="w",Wb="c",Xb="q",Yb="ef",Zb="ct",$b=6,_b=-1,ac=!1,bc={},cc=-1,dc=-1,ec="#193441",fc="#D1DBBD",gc="#3E606F",hc="#794",ic="#eee",jc="#555",kc="255,0,0",lc="rgba(93, 255, 182, 0.56)",mc=-.02,nc=.02;u.res={};var oc=3/16,pc=-0.875,qc=1,rc=-2.5/16,sc=0,tc=1;w.res={},x.res={};var uc,vc,wc,xc,yc=null,zc={},Ac=Y.createElement("div"),Bc={37:"left",65:"left",81:"left",38:"up",90:"up",87:"up",83:"down",40:"down",39:"right",68:"right",32:"space",27:"esc",13:"enter"},Cc={},Dc={};Y.onkeyup=function(a){Kb||!Cc.enter||vb||(Cb=!Cb,Ib.style.display=Cb?"none":"block",console.log("debug toggle anim: ",Cb),Cb&&(P=Date.now(),j())),N(!1,a)},Y.onkeydown=function(a){N(!0,a)},Y.onmousedown=function(a){O(!0,a)},Y.onmousemove=function(a){bc.x=a.clientX-$.offsetLeft,bc.y=a.clientY},a()}; -------------------------------------------------------------------------------- /bin-release/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 46 | 47 | 48 |
49 | Use arrow keys or mouse to move around.
50 | You can use the 'return' key to pause the game (but that's cheating !).
51 | You don't need to really know the chess rules to play, but that could definitely help! 52 |
53 | 54 | -------------------------------------------------------------------------------- /chessPursuit.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Saturnyn/ChessPursuit/58ca8fc5d8f847eba884917d45dfa05c017bf6ae/chessPursuit.zip -------------------------------------------------------------------------------- /older-versions/01-render/game.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 | function J(){this.B=function(e){for(var f=0;24>f;f++)this[String.fromCharCode(97+f)]=e[f]||0;.01>this.c&&(this.c=.01);e=this.b+this.c+this.e;.18>e&&(e=.18/e,this.b*=e,this.c*=e,this.e*=e)}} 21 | var W=new function(){this.A=new J;var e,f,d,h,l,z,K,L,M,A,m,N;this.reset=function(){var b=this.A;h=100/(b.f*b.f+.001);l=100/(b.g*b.g+.001);z=1-b.h*b.h*b.h*.01;K=-b.i*b.i*b.i*1E-6;b.a||(m=.5-b.n/2,N=5E-5*-b.o);L=1+b.l*b.l*(0a.q?-1020:1020),S=a.p?((1-a.p)*(1-a.p)*2E4|0)+32:0,ba=a.d,T=a.j/2,ca=a.k*a.k*.01,E=a.a,F=e,da=1/e,ea=1/f,fa=1/d,a=5/(1+a.u*a.u*20)*(.01+n);.8=S&&(V=0,this.reset());A&&++M>=A&&(A=0,h*=L);z+=K;h*=z;h>l&&(h=l,0<$&&(G=!0));g=h;0g&&(g=8);E||(m+=N,0>m?m=0:.5F)switch(v=0,++U){case 1:F=f;break;case 2:F=d}switch(U){case 0:w=v*da;break;case 1:w=1+2*(1-v*ea)*ba;break;case 2:w=1-v*fa;break;case 3:w=0,G=!0}R&&(D+=aa,s=D|0,0>s?s=-s:1023r?r=1E-5:.1=g&&(p%=g,3==E))for(x=y.length;x--;)y[x]=2*Math.random()-1;switch(E){case 0:c=p/gc?1:-1);c=.225*((0>c?-1:1)*c*c-c)+c;break;case 3:c=y[Math.abs(32*p/g|0)]}P&&(x=u,n*=X,0>n?n=0:.1=q?-32768:32767*q|0}return O}}; 25 | window.jsfxr=function(e){W.A.B(e);var f=W.D();e=new Uint8Array(4*((f+1)/2|0)+44);var f=2*W.C(new Uint16Array(e.buffer,44),f),d=new Uint32Array(e.buffer,0,44);d[0]=1179011410;d[1]=f+36;d[2]=1163280727;d[3]=544501094;d[4]=16;d[5]=65537;d[6]=44100;d[7]=88200;d[8]=1048578;d[9]=1635017060;d[10]=f;for(var f=f+44,d=0,h="data:audio/wav;base64,";d>18]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[l>> 26 | 12&63]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[l>>6&63]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[l&63]);return h};;/* 27 | 28 | Code adapted from: 29 | http://codepen.io/jackrugile/blog/arcade-audio-for-js13k-games 30 | 31 | */ 32 | 33 | var aa; 34 | (function(){ 35 | function ArcadeAudio() { 36 | this.sounds = {}; 37 | } 38 | 39 | ArcadeAudio.prototype.add = function( key, count, settings ) { 40 | this.sounds[ key ] = []; 41 | settings.forEach( function( elem, index ) { 42 | this.sounds[ key ].push( { 43 | tick: 0, 44 | count: count, 45 | pool: [] 46 | } ); 47 | for( var i = 0; i < count; i++ ) { 48 | var audio = new Audio(); 49 | audio.src = jsfxr( elem ); 50 | this.sounds[ key ][ index ].pool.push( audio ); 51 | } 52 | }, this ); 53 | }; 54 | 55 | ArcadeAudio.prototype.play = function( key ) { 56 | var sound = this.sounds[ key ]; 57 | var soundData = sound.length > 1 ? sound[ Math.floor( Math.random() * sound.length ) ] : sound[ 0 ]; 58 | soundData.pool[ soundData.tick ].play(); 59 | soundData.tick < soundData.count - 1 ? soundData.tick++ : soundData.tick = 0; 60 | }; 61 | 62 | aa = new ArcadeAudio(); 63 | 64 | aa.add( 'start', 1, 65 | [ 66 | [0,0.2,0.13,0.01,,0.3328,,,,,,,,0.0994,,,,,1,,,0.1,,0.5] 67 | ] 68 | ); 69 | 70 | aa.add( 'right', 5, 71 | [ 72 | [3,1,0.3197,0.7823,0.2124,0.1117,,0.091,,,,,,,,0.3901,0.1061,-0.0873,1,,,,,0.5] 73 | ] 74 | ); 75 | 76 | aa.add( 'pad', 1, 77 | [ 78 | [0,,0.0945,,0.1582,0.4416,,-0.535,,,,,,0.3856,,,,,1,,,0.0896,,0.5] 79 | ] 80 | ); 81 | aa.add( 'boost', 1, 82 | [ 83 | [0,,0.2833,,0.1918,0.4914,,0.2408,,,,,,0.0182,,,,,1,,,,,0.5] 84 | ] 85 | ); 86 | aa.add( 'ring', 5, 87 | [ 88 | [0,,0.0177,0.565,0.2893,0.4147,,,,,,0.4159,0.6958,,,,,,1,,,,,0.5] 89 | ] 90 | ); 91 | aa.add( 'bumper', 1, 92 | [ 93 | [0,,0.2389,,0.2437,0.5057,,0.164,,,,,,0.1032,,,,,1,,,,,0.5] 94 | ] 95 | ); 96 | aa.add( 'hurt', 1, 97 | [ 98 | [0,,0.2056,,0.2341,0.2899,,0.3315,,,,,,0.1817,,0.6354,,,1,,,,,0.5] 99 | ] 100 | ); 101 | aa.add( 'eat', 1, 102 | [ 103 | [0,,0.3402,,0.2772,0.4978,,0.1736,,0.2589,0.3885,,,0.1153,,,,,1,,,,,0.5] 104 | ] 105 | ); 106 | aa.add( 'die', 1, 107 | [ 108 | [3,,0.296,0.6924,0.1761,0.2478,,0.0895,,,,-0.6933,0.8848,,,,,,1,,,,,0.5] 109 | ] 110 | ); 111 | })();;window.onload = function(){ 112 | "use strict"; 113 | 114 | var win = window; 115 | var document = win.document; 116 | var body = document.body; 117 | var Math = win.Math; 118 | 119 | var PI = Math.PI; 120 | var sqrt = Math.sqrt; 121 | var rand = Math.random; 122 | 123 | 124 | var YES = true; 125 | var NO = false; 126 | 127 | 128 | //------------------------------------------------------------------------------------------------------------------ 129 | // sizes and DOM 130 | //------------------------------------------------------------------------------------------------------------------ 131 | 132 | var DRAW_OFFSET_Y = 100; 133 | var SIZE = 400; 134 | var NUM_CELLS = 8; 135 | var CELL_SIZE = SIZE/NUM_CELLS; 136 | var NUM_CELLS_DISPLAYED = NUM_CELLS*2+1; 137 | 138 | var screenWidth; 139 | var screenHeight; 140 | var screenMinSize; 141 | 142 | var bgCanvas = makeCanvas(SIZE, SIZE); 143 | var bgCtx = getContext(bgCanvas); 144 | var textureCanvas = makeCanvas(SIZE, 2*SIZE); 145 | var textureCtx = getContext(textureCanvas); 146 | 147 | window.onresize = onResize(); 148 | function onResize(){ 149 | screenWidth = win.innerWidth; 150 | screenHeight = win.innerHeight; 151 | bgCanvas.width = SIZE; 152 | bgCanvas.height = SIZE+DRAW_OFFSET_Y; 153 | } 154 | 155 | body.appendChild(bgCanvas); 156 | 157 | //------------------------------------------------------------------------------------------------------------------ 158 | // game logic 159 | //------------------------------------------------------------------------------------------------------------------ 160 | 161 | var gameIsOver = false; 162 | var homeScreen = false; 163 | var progress = 0; //expressed as a number of rows 164 | var progressPerSec = 2; 165 | var checkBoard = {}; 166 | var topRowDisplayed = 0; 167 | var raf = true; 168 | 169 | //pieces 170 | var PLAYER = 'kp'; 171 | var PAWN = 'p'; 172 | var KING = 'k'; 173 | 174 | 175 | //input 176 | var keys = {}; 177 | var mouse = {}; 178 | 179 | //------------------------------------------------------------------------------------------------------------------ 180 | // main loop 181 | //------------------------------------------------------------------------------------------------------------------ 182 | 183 | function init(){ 184 | initSvg(); 185 | drawTexture(); 186 | initCheckBoard(); 187 | 188 | onResize(); 189 | tic(); 190 | 191 | document.onmousedown = function(){ 192 | raf = !raf; 193 | console.log('debug toggle anim: ',raf); 194 | }; 195 | } 196 | 197 | function initCheckBoard(){ 198 | var p = makePiece; 199 | 200 | p(PAWN, 5, 5); 201 | for(var i=0; itopRowDisplayed - NUM_CELLS_DISPLAYED - 5; i--){ 267 | row = checkBoard[i]; 268 | if(row){ 269 | changes = true; 270 | console.log('removing row',i); 271 | for(colIndex=0; colIndex[0,1] 380 | var width = ellipseEq(t + 0.2, 1.4, 1.0); 381 | //for y, interpolate between y=x and y=0.75 + 0.125*x [0,2]=>[0,1] 382 | y = (1-t)*t + t*(0.75+0.25*t); 383 | 384 | res.x = (1-width)/2 + x*width; 385 | res.y = 1-y; 386 | res.scaleX = width; 387 | res.scaleY = width; 388 | 389 | return res; 390 | } 391 | 392 | function ellipseEq(x, a, b){ 393 | // x²/a² + y²/b² = 1 394 | // y = sqrt( (1-x²/a²)*b² ) 395 | return Math.sqrt( (1-((x*x)/(a*a)))*b*b ); 396 | } 397 | 398 | function drawTexture(){ 399 | var ctx = textureCtx; 400 | ctx.clearRect(0,0,SIZE,SIZE); 401 | ctx.save(); 402 | 403 | ctx.save(); 404 | //Draw sky 405 | ctx.fillStyle = '#FF8601'; 406 | ctx.beginPath(); 407 | ctx.rect(0, 0, SIZE, DRAW_OFFSET_Y); 408 | ctx.fill(); 409 | ctx.clip(); 410 | //Draw sun 411 | ctx.fillStyle = '#FFE7CA'; 412 | var sunRadius = SIZE/6; 413 | ctx.beginPath(); 414 | ctx.arc(SIZE/2, DRAW_OFFSET_Y + 0.3*sunRadius, sunRadius, 0, PI, true); 415 | ctx.fill(); 416 | ctx.restore(); 417 | //Draw Mountains 418 | ctx.beginPath(); 419 | ctx.fillStyle = 'rgb(10,20,25)'; 420 | var mountainWidth = 40; 421 | var mountainHeight = 10; 422 | var mountainX = 0; 423 | ctx.moveTo(-mountainWidth,DRAW_OFFSET_Y); 424 | var up = true; 425 | while(mountainX < SIZE){ 426 | mountainX += (rand() + 0.5) * mountainWidth; 427 | var plotY = (up ? (0.8 + rand() * 0.2) : (0.2 + rand() * 0.2)) * mountainHeight; 428 | ctx.lineTo(mountainX, DRAW_OFFSET_Y - plotY); 429 | up = !up; 430 | } 431 | ctx.lineTo(SIZE,DRAW_OFFSET_Y); 432 | ctx.lineTo(0,DRAW_OFFSET_Y); 433 | ctx.fill(); 434 | ctx.restore(); 435 | 436 | /* 437 | textureCtx.beginPath(); 438 | textureCtx.lineWidth = 1; 439 | textureCtx.strokeStyle = STROKE_COLOR; 440 | var randOffset = 4; 441 | var y1, y2; 442 | for(var i=-randOffset; i10){ 784 | //debug break here 785 | console.log("FRAAAAAAME",t); 786 | } 787 | stats.end(); 788 | }; 789 | stats.setMode(1); // 0: fps, 1: ms 790 | 791 | // Align top-left 792 | stats.domElement.style.position = 'absolute'; 793 | stats.domElement.style.bottom = '0px'; 794 | stats.domElement.style.right = '0px'; 795 | 796 | document.body.appendChild( stats.domElement ); 797 | 798 | clearInterval(loadInterval); 799 | } 800 | }, 200 ); 801 | })();;/** 802 | * @author mrdoob / http://mrdoob.com/ 803 | */ 804 | 805 | var Stats = function () { 806 | var startTime = Date.now(), prevTime = startTime; 807 | var ms = 0, msMin = Infinity, msMax = 0; 808 | var fps = 0, fpsMin = Infinity, fpsMax = 0; 809 | var frames = 0, mode = 0; 810 | 811 | var container = document.createElement( 'div' ); 812 | container.id = 'stats'; 813 | container.addEventListener( 'mousedown', function ( event ) { event.preventDefault(); setMode( ++ mode % 2 ) }, false ); 814 | container.style.cssText = 'width:80px;opacity:0.9;cursor:pointer'; 815 | 816 | var fpsDiv = document.createElement( 'div' ); 817 | fpsDiv.id = 'fps'; 818 | fpsDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#002'; 819 | container.appendChild( fpsDiv ); 820 | 821 | var fpsText = document.createElement( 'div' ); 822 | fpsText.id = 'fpsText'; 823 | fpsText.style.cssText = 'color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; 824 | fpsText.innerHTML = 'FPS'; 825 | fpsDiv.appendChild( fpsText ); 826 | 827 | var fpsGraph = document.createElement( 'div' ); 828 | fpsGraph.id = 'fpsGraph'; 829 | fpsGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0ff'; 830 | fpsDiv.appendChild( fpsGraph ); 831 | 832 | while ( fpsGraph.children.length < 74 ) { 833 | 834 | var bar = document.createElement( 'span' ); 835 | bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#113'; 836 | fpsGraph.appendChild( bar ); 837 | 838 | } 839 | 840 | var msDiv = document.createElement( 'div' ); 841 | msDiv.id = 'ms'; 842 | msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;display:none'; 843 | container.appendChild( msDiv ); 844 | 845 | var msText = document.createElement( 'div' ); 846 | msText.id = 'msText'; 847 | msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; 848 | msText.innerHTML = 'MS'; 849 | msDiv.appendChild( msText ); 850 | 851 | var msGraph = document.createElement( 'div' ); 852 | msGraph.id = 'msGraph'; 853 | msGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0f0'; 854 | msDiv.appendChild( msGraph ); 855 | 856 | while ( msGraph.children.length < 74 ) { 857 | 858 | var bar = document.createElement( 'span' ); 859 | bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#131'; 860 | msGraph.appendChild( bar ); 861 | 862 | } 863 | 864 | var setMode = function ( value ) { 865 | 866 | mode = value; 867 | 868 | switch ( mode ) { 869 | 870 | case 0: 871 | fpsDiv.style.display = 'block'; 872 | msDiv.style.display = 'none'; 873 | break; 874 | case 1: 875 | fpsDiv.style.display = 'none'; 876 | msDiv.style.display = 'block'; 877 | break; 878 | } 879 | 880 | }; 881 | 882 | var updateGraph = function ( dom, value ) { 883 | 884 | var child = dom.appendChild( dom.firstChild ); 885 | child.style.height = value + 'px'; 886 | 887 | }; 888 | 889 | return { 890 | 891 | REVISION: 12, 892 | 893 | domElement: container, 894 | 895 | setMode: setMode, 896 | 897 | begin: function () { 898 | 899 | startTime = Date.now(); 900 | 901 | }, 902 | 903 | end: function () { 904 | 905 | var time = Date.now(); 906 | 907 | ms = time - startTime; 908 | msMin = Math.min( msMin, ms ); 909 | msMax = Math.max( msMax, ms ); 910 | 911 | msText.textContent = ms + ' MS (' + msMin + '-' + msMax + ')'; 912 | updateGraph( msGraph, Math.min( 30, 30 - ( ms / 200 ) * 30 ) ); 913 | 914 | frames ++; 915 | 916 | if ( time > prevTime + 1000 ) { 917 | 918 | fps = Math.round( ( frames * 1000 ) / ( time - prevTime ) ); 919 | fpsMin = Math.min( fpsMin, fps ); 920 | fpsMax = Math.max( fpsMax, fps ); 921 | 922 | fpsText.textContent = fps + ' FPS (' + fpsMin + '-' + fpsMax + ')'; 923 | updateGraph( fpsGraph, Math.min( 30, 30 - ( fps / 100 ) * 30 ) ); 924 | 925 | prevTime = time; 926 | frames = 0; 927 | 928 | } 929 | 930 | return time; 931 | 932 | }, 933 | 934 | update: function () { 935 | 936 | startTime = this.end(); 937 | 938 | } 939 | 940 | } 941 | 942 | }; 943 | 944 | if ( typeof module === 'object' ) { 945 | module.exports = Stats; 946 | } -------------------------------------------------------------------------------- /older-versions/01-render/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chess-pursuit", 3 | "version": "0.1.0", 4 | "devDependencies": { 5 | "grunt": "~0.4.5", 6 | "grunt-contrib-concat": "^0.5.0", 7 | "grunt-contrib-copy": "^0.5.0", 8 | "grunt-contrib-jshint": "~0.10.0", 9 | "grunt-contrib-nodeunit": "~0.4.1", 10 | "grunt-contrib-qunit": "^0.5.2", 11 | "grunt-contrib-uglify": "~0.5.0", 12 | "grunt-contrib-watch": "^0.6.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /photos/1-pawned.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Saturnyn/ChessPursuit/58ca8fc5d8f847eba884917d45dfa05c017bf6ae/photos/1-pawned.png -------------------------------------------------------------------------------- /screens/screen-160x160.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Saturnyn/ChessPursuit/58ca8fc5d8f847eba884917d45dfa05c017bf6ae/screens/screen-160x160.png -------------------------------------------------------------------------------- /screens/screen-400x250.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Saturnyn/ChessPursuit/58ca8fc5d8f847eba884917d45dfa05c017bf6ae/screens/screen-400x250.png -------------------------------------------------------------------------------- /src/game-audio.js: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Code adapted from: 4 | http://codepen.io/jackrugile/blog/arcade-audio-for-js13k-games 5 | 6 | */ 7 | 8 | var aa; 9 | (function(){ 10 | function ArcadeAudio() { 11 | this.sounds = {}; 12 | } 13 | 14 | ArcadeAudio.prototype.add = function( key, count, settings ) { 15 | this.sounds[ key ] = []; 16 | settings.forEach( function( elem, index ) { 17 | this.sounds[ key ].push( { 18 | tick: 0, 19 | count: count, 20 | pool: [] 21 | } ); 22 | for( var i = 0; i < count; i++ ) { 23 | var audio = new Audio(); 24 | audio.src = jsfxr( elem ); 25 | this.sounds[ key ][ index ].pool.push( audio ); 26 | } 27 | }, this ); 28 | }; 29 | 30 | ArcadeAudio.prototype.play = function( key ) { 31 | var sound = this.sounds[ key ]; 32 | var soundData = sound.length > 1 ? sound[ Math.floor( Math.random() * sound.length ) ] : sound[ 0 ]; 33 | soundData.pool[ soundData.tick ].play(); 34 | soundData.tick < soundData.count - 1 ? soundData.tick++ : soundData.tick = 0; 35 | }; 36 | 37 | aa = new ArcadeAudio(); 38 | 39 | aa.add( 'check', 1, 40 | [ 41 | [2,,0.1747,,0.1291,0.6731,0.2,-0.2999,,,,,,0.4368,0.1862,,0.28,,1,,,0.1596,,0.5] 42 | ] 43 | ); 44 | aa.add( 'move', 5, 45 | [ 46 | [3,,0.0316,,0.1483,0.5871,,-0.6609,,,,,,,,,,,1,,,0.0227,,0.25] 47 | ] 48 | ); 49 | aa.add( 'capture', 5, 50 | [ 51 | [0,,0.3065,,0.2516,0.36,,0.1584,,,,,,0.1149,,,,,1,,,0.2188,,0.25] 52 | ] 53 | ); 54 | aa.add( 'checkmate', 1, 55 | [ 56 | [1,,0.2402,,0.3917,0.2242,,0.1535,,,,,,,,0.5609,,,1,,,,,0.5] 57 | ] 58 | ); 59 | 60 | })(); -------------------------------------------------------------------------------- /src/game.js: -------------------------------------------------------------------------------- 1 | window.onload = function(){ 2 | "use strict"; 3 | 4 | var win = window; 5 | var document = win.document; 6 | var body = document.body; 7 | var root = null; 8 | var Math = win.Math; 9 | 10 | var PI = Math.PI; 11 | var sqrt = Math.sqrt; 12 | var rand = Math.random; 13 | 14 | 15 | var YES = true; 16 | var NO = false; 17 | 18 | 19 | //------------------------------------------------------------------------------------------------------------------ 20 | // sizes and DOM 21 | //------------------------------------------------------------------------------------------------------------------ 22 | 23 | var SCALE = 1; 24 | var HORIZON_Y = 100 * SCALE; 25 | var SIZE = 400 * SCALE; 26 | var NUM_CELLS = 8; 27 | var CELL_SIZE = SIZE/NUM_CELLS; 28 | var NUM_CELLS_DISPLAYED = NUM_CELLS*2+3; 29 | 30 | var DIALOG_MARGIN = 6; 31 | 32 | var screenWidth; 33 | var screenHeight; 34 | var screenMinSize; 35 | 36 | var bgCanvas = makeCanvas(SIZE, SIZE, 'bg'); 37 | var bgCtx = getContext(bgCanvas); 38 | 39 | var shadowCanvas = makeCanvas(SIZE, SIZE, 'shadow'); 40 | var shadowCtx = getContext(shadowCanvas); 41 | 42 | var skyCanvas = makeCanvas(SIZE, 2*SIZE,'sky'); 43 | var skyCtx = getContext(skyCanvas); 44 | 45 | 46 | 47 | //------------------------------------------------------------------------------------------------------------------ 48 | // game logic 49 | //------------------------------------------------------------------------------------------------------------------ 50 | 51 | var now = 0; //Date.now(), set on update 52 | var lastTime; 53 | var MS_TO_S = 1/1000; 54 | 55 | var killCount = 0; 56 | var checkMateCount = 0; 57 | var checkCount = 0; 58 | 59 | var winScreen = null; 60 | var gameOverScreen = null; 61 | var gameIsOver = false; 62 | var autoMove = false; 63 | var progress = 0; //expressed as a number of rows 64 | var progressPerSec = 1; //1 65 | var checkBoard = null; 66 | var checkPoints = null; 67 | var topRowDisplayed = -1; 68 | var raf = true; 69 | var removedPieces = []; 70 | var lastRowIndex; 71 | 72 | var player = null; 73 | var playerAnimDuration = 0.05; 74 | var playerInvalidDuration = 0.5; 75 | 76 | var pressSpaceText = null; 77 | var pauseText = null; 78 | var introScreen = null; 79 | var intro = true; 80 | var introStartTime; 81 | var introDurationSec = 5; 82 | var perspectiveProgress = 0; 83 | var introProgress = 0; 84 | var introStep = -1; 85 | 86 | var dialogBox; 87 | var dialogSpeakerText; 88 | var dialogText; 89 | var dialogHint; 90 | 91 | var checkText; 92 | 93 | //pieces id (used in SVG and checkboard) 94 | var HERO_KING = 'h'; 95 | var ENEMY_KING = 'e'; 96 | var PAWN = 'p'; 97 | var ROOK = 'r'; 98 | var BISHOP = 'b'; 99 | var KNIGHT = 'k'; 100 | var LAND_MINE = 'l'; 101 | var WAR_BEAR = 'w'; 102 | var CASTLE = 'c'; 103 | var QUEEN = 'q'; 104 | 105 | //other SCG ids 106 | var ENEMY_FILTER = 'ef'; 107 | var CHECK_TEXT = 'ct'; 108 | var CHECK_GRADIENT = 'cg'; 109 | 110 | 111 | 112 | var DANGER = '*'; 113 | var CHECK_POINT = '#'; 114 | 115 | //------------------------------------------------------------------------------------------------------------------ 116 | // initialization 117 | //------------------------------------------------------------------------------------------------------------------ 118 | 119 | function init(){ 120 | root = document.createElement('div'); 121 | root.id = 'root'; 122 | body.appendChild(root); 123 | var style = root.style; 124 | style.position = 'absolute'; 125 | style.left = '50%'; 126 | style.top = '0'; 127 | style.marginLeft = (-SIZE/2)+'px '; 128 | 129 | initCheckBoardCanvas(); 130 | initSkyCanvas(); 131 | initSvg(); 132 | initShadowCanvas(); 133 | 134 | //intro = false; 135 | initCheckBoard(0); 136 | 137 | tic(); 138 | } 139 | 140 | 141 | //------------------------------------------------------------------------------------------------------------------ 142 | // checkboard management 143 | //------------------------------------------------------------------------------------------------------------------ 144 | 145 | function restart(){ 146 | //find we checkpoint we were at 147 | var checkPointIndex = checkPoints.length-1; 148 | for(var i=0; i0){ 151 | checkPointIndex = i-1; 152 | } 153 | break; 154 | } 155 | } 156 | //reinitialize game 157 | initCheckBoard(checkPointIndex); 158 | //prepare next update 159 | lastTime = null; 160 | topRowDisplayed = -1; 161 | checkMateCount++; 162 | } 163 | 164 | function initCheckBoard(startCheckPointIndex){ 165 | if(checkBoard){ 166 | destroyCheckBoard(); 167 | } 168 | checkBoard = []; 169 | checkPoints = [0]; 170 | var CHECK_POINT_HEIGHT = 5; 171 | var currentRowIndex = 0; 172 | 173 | //0 TODO: intro: first pawn 174 | block( 175 | {intro:true}, 176 | '','','', 177 | intro ? ' reqr' : '', 178 | intro ? ' kk' : '', 179 | '', 180 | '', 181 | '', 182 | '', 183 | '', 184 | '', 185 | 'kkkkkkkk' 186 | ); 187 | 188 | // first pawn 189 | block( 190 | {showThreat:'p'}, 191 | '', 192 | '', 193 | ' p', 194 | '', 195 | '', 196 | '', 197 | '', 198 | '' 199 | ); 200 | 201 | // scattered pawns 202 | block( 203 | ' pppp', 204 | ' pppp', 205 | '', 206 | ' p', 207 | '', 208 | ' p', 209 | '', 210 | ' p' 211 | ); 212 | 213 | // pawn rows 214 | block( 215 | '', 216 | 'pppppp', 217 | '', 218 | ' pppppp', 219 | '', 220 | 'pppppp', 221 | '', 222 | '' 223 | ); 224 | 225 | // triangle 226 | block( 227 | '', 228 | 'pp ppp', 229 | ' p p', 230 | ' p', 231 | '', 232 | '', 233 | '', 234 | '' 235 | ); 236 | 237 | // sawtooth 238 | block( 239 | '', 240 | ' pp', 241 | 'p p p p', 242 | ' p p', 243 | '', 244 | '', 245 | '', 246 | '' 247 | ); 248 | 249 | // wedges 250 | block( 251 | '', 252 | ' ppp', 253 | ' p', 254 | 'ppp', 255 | ' p ppp', 256 | ' p', 257 | '', 258 | '' 259 | ); 260 | 261 | //CHECK POINT 1 262 | checkPoint(); 263 | 264 | // first rook 265 | block( 266 | {showThreat:'r'}, 267 | '', 268 | ' p', 269 | '', 270 | ' r', 271 | '', 272 | ' p', 273 | '', 274 | '' 275 | ); 276 | 277 | // rook diag 278 | block( 279 | '', 280 | '', 281 | 'r', 282 | ' r', 283 | ' r', 284 | ' r', 285 | ' r', 286 | ' r', 287 | 'pppppp' 288 | ); 289 | 290 | // rook rows 291 | block( 292 | '', 293 | '', 294 | '', 295 | ' p', 296 | ' rp r', 297 | '', 298 | '', 299 | 'pp', 300 | 'r p p', 301 | 'p' 302 | ); 303 | 304 | // rook labyrinth 305 | block( 306 | '', 307 | 'p r', 308 | 'r p', 309 | 'r p', 310 | '', 311 | ' p', 312 | ' p', 313 | 'p ppppp', 314 | '' 315 | ); 316 | 317 | //CHECK POINT 2 318 | checkPoint(); 319 | 320 | //10 first bishop 321 | block( 322 | {showThreat:'b'}, 323 | '', 324 | '', 325 | '', 326 | '', 327 | '...b', 328 | '', 329 | '', 330 | '', 331 | '' 332 | ); 333 | 334 | //11 bishop field 335 | block( 336 | '', 337 | ' p p p', 338 | 'b b b b', 339 | '', 340 | '', 341 | '', 342 | '', 343 | '', 344 | '' 345 | ); 346 | 347 | //12 rooks & bishops simple 348 | block( 349 | '', 350 | 'r.p..p.r', 351 | 'p......p', 352 | '', 353 | 'b b', 354 | 'pp....pp', 355 | '', 356 | '' 357 | ); 358 | 359 | 360 | //13 pawns, bishops and rooks 361 | block( 362 | '', 363 | 'r', 364 | ' r p..b', 365 | ' ..r', 366 | '....p', 367 | '...p', 368 | '..p', 369 | 'pp.....p' 370 | ); 371 | 372 | //CHECK POINT 3 373 | checkPoint(); 374 | 375 | //14 first knight 376 | block( 377 | {showThreat:'k'}, 378 | '', 379 | '', 380 | ' k', 381 | '', 382 | '', 383 | '', 384 | '', 385 | '' 386 | ); 387 | 388 | //15 knight rows 389 | block( 390 | 'p', 391 | 'r.....p', 392 | '', 393 | '', 394 | '......kk', 395 | '', 396 | 'pkk', 397 | '' 398 | ); 399 | 400 | //rooks bishop and knight 401 | block( 402 | 'p pppp', 403 | 'r b r', 404 | ' p b p', 405 | '', 406 | '', 407 | '', 408 | ' k', 409 | '', 410 | '', 411 | '' 412 | ); 413 | 414 | //CHECK POINT 4 415 | checkPoint(); 416 | 417 | //first land mine 418 | block( 419 | {showThreat:'l'}, 420 | '', 421 | '', 422 | '', 423 | ' l', 424 | '', 425 | '', 426 | '', 427 | '' 428 | ); 429 | 430 | //land mines 431 | block( 432 | 'l.llllll', 433 | 'l.l....l', 434 | '..l.llll', 435 | '.ll.l ', 436 | 'l...l l ', 437 | 'l.lll l ', 438 | 'l. l ', 439 | 'lllllll ' 440 | ); 441 | 442 | //land mines and pawns 443 | block( 444 | ' p pll', 445 | ' l p l', 446 | 'lpl p p ', 447 | ' lp', 448 | 'll ll l', 449 | ' l l', 450 | '', 451 | '' 452 | ); 453 | 454 | //k,r,b,p,l 455 | block( 456 | '', 457 | ' r', 458 | ' llk', 459 | ' .', 460 | ' l ..', 461 | ' p pb', 462 | ' . . l', 463 | ' p l', 464 | '', 465 | '', 466 | '', 467 | '', 468 | '' 469 | ); 470 | 471 | //CHECK POINT 5 472 | checkPoint(); 473 | 474 | block( 475 | '', 476 | 'c', 477 | '', 478 | '', 479 | '', 480 | '', 481 | '' 482 | ); 483 | lastRowIndex = currentRowIndex; 484 | 485 | if(startCheckPointIndex === 0){ 486 | //Add player 487 | player = addPieceAt(HERO_KING, 5, 4).piece; 488 | progress = 2; //hide starting pawn row 489 | }else{ 490 | progress = checkPoints[startCheckPointIndex]; 491 | player = addPieceAt(HERO_KING, progress, 3).piece; 492 | progress -= 4; 493 | } 494 | 495 | function checkPoint(){ 496 | var center = Math.ceil(CHECK_POINT_HEIGHT/2); 497 | checkPoints.push(currentRowIndex + center); 498 | for(var i=0; i=0; i--){ 525 | row = args[i]; 526 | if(row.length > NUM_CELLS) throw new Error(); 527 | 528 | checkBoard[currentRowIndex] = []; 529 | if(doAdd){ 530 | if(row !== ''){ 531 | for(j=0; j= NUM_CELLS){ 621 | col = NUM_CELLS-1; 622 | } 623 | var rowMin = Math.floor(progress); 624 | var rowMax = topRowDisplayed-2; 625 | if(row < rowMin){ 626 | row = rowMin; 627 | }else if(row >= rowMax){ 628 | row = rowMax; 629 | } 630 | if(col != oldCol || row != oldRow){ 631 | var oldCell = checkBoard[oldRow][oldCol]; 632 | if(!checkBoard[row]){ 633 | checkBoard[row] = []; 634 | } 635 | var cell = checkBoard[row][col]; 636 | if(!cell){ 637 | cell = {}; 638 | checkBoard[row][col] = cell; 639 | } 640 | if(!cell.wall){ 641 | //can cell be taken ? 642 | var threateningCell = getThreateningCell(row,col); 643 | if(threateningCell){ 644 | aa.play('check'); 645 | //invalid position 646 | player.invalid = true; 647 | player.invalidCol = col; 648 | player.invalidRow = row; 649 | player.threateningPiece = threateningCell.piece; 650 | col = oldCol; 651 | row = oldRow; 652 | checkCount++; 653 | }else{ 654 | 655 | player.invalid = false; 656 | if(cell.piece){ 657 | //take the piece 658 | destroyPiece(cell.piece); 659 | killCount++; 660 | aa.play('capture'); 661 | }else{ 662 | aa.play('move'); 663 | } 664 | //move piece on check board 665 | oldCell.piece = null; 666 | cell.piece = player; 667 | } 668 | player.oldCol = oldCol; 669 | player.oldRow = oldRow; 670 | player.anim = true; 671 | player.animStartTime = now; 672 | player.col = col; 673 | player.row = row; 674 | 675 | if(row >= lastRowIndex){ 676 | setGameIsOver(true, true); 677 | } 678 | } 679 | } 680 | } 681 | 682 | function getThreateningCell(row,col){ 683 | var threateningCell; 684 | //land mine 685 | threateningCell = getCellWithPieceAt(row,col,LAND_MINE); 686 | if(threateningCell){ 687 | return threateningCell; 688 | } 689 | //pawns 690 | threateningCell = 691 | getCellWithPieceAt(row+1,col-1,PAWN) || 692 | getCellWithPieceAt(row+1,col+1,PAWN); 693 | if(threateningCell){ 694 | return threateningCell; 695 | } 696 | //ROOK 697 | var i,j,cell; 698 | //Check left 699 | for(j=col-1; j>=0 ; j--){ 700 | cell = getCellWithPieceAt(row,j); 701 | if(cell && cell.piece){ 702 | if(cell.piece.type == ROOK){ 703 | return cell; 704 | }else{ 705 | break; 706 | } 707 | } 708 | } 709 | //Check right 710 | for(j=col+1; j<=NUM_CELLS ; j++){ 711 | cell = getCellWithPieceAt(row,j); 712 | if(cell && cell.piece){ 713 | if(cell.piece.type == ROOK){ 714 | return cell; 715 | }else{ 716 | break; 717 | } 718 | } 719 | } 720 | //Check up 721 | for(i=row+1; i<=row+NUM_CELLS ; i++){ 722 | cell = getCellWithPieceAt(i,col); 723 | if(cell && cell.piece){ 724 | if(cell.piece.type == ROOK){ 725 | return cell; 726 | }else{ 727 | break; 728 | } 729 | } 730 | } 731 | //Check down 732 | for(i=row-1; i>=row-NUM_CELLS ; i--){ 733 | cell = getCellWithPieceAt(i,col); 734 | if(cell && cell.piece){ 735 | if(cell.piece.type == ROOK){ 736 | return cell; 737 | }else{ 738 | break; 739 | } 740 | } 741 | } 742 | 743 | //BISHOP 744 | //diag bottom left 745 | for(j=col-1,i=row-1; j>=0; j--,i--){ 746 | cell = getCellWithPieceAt(i,j); 747 | if(cell && cell.piece){ 748 | if(cell.piece.type == BISHOP){ 749 | return cell; 750 | }else{ 751 | break; 752 | } 753 | } 754 | } 755 | //diag bottom right 756 | for(j=col+1,i=row-1; j=0; j--,i++){ 768 | cell = getCellWithPieceAt(i,j); 769 | if(cell && cell.piece){ 770 | if(cell.piece.type == BISHOP){ 771 | return cell; 772 | }else{ 773 | break; 774 | } 775 | } 776 | } 777 | //diag top right 778 | for(j=col+1,i=row+1; j= 0){ 853 | if(keys.space === 0 || mouse.click){ 854 | keys.space = -1; 855 | nextIntroStep(); 856 | } 857 | } 858 | updateIntro(); 859 | } 860 | //var t = now; 861 | render(); 862 | //console.log('renderTime', now-t); 863 | 864 | } 865 | mouse.click = false; 866 | 867 | if(window.ste) ste(); 868 | 869 | if(raf){ 870 | requestAnimationFrame(tic); 871 | } 872 | } 873 | 874 | function setGameIsOver(val, win){ 875 | if(win){ 876 | raf = false; 877 | gameIsOver = true; 878 | winScreen.style.display = 'block'; 879 | }else{ 880 | if(val != gameIsOver){ 881 | gameIsOver = val; 882 | 883 | if(gameIsOver){ 884 | gameOverScreen.style.display = 'block'; 885 | }else{ 886 | gameOverScreen.style.display = 'none'; 887 | restart(); 888 | } 889 | } 890 | } 891 | } 892 | 893 | //------------------------------------------------------------------------------------------------------------------ 894 | // game update 895 | //------------------------------------------------------------------------------------------------------------------ 896 | 897 | //input 898 | var KEY_LATENCY = 6; 899 | var KEY_DONE = -1; 900 | var keysBlockedUntilAllUp = false; 901 | var mouse = {}; 902 | var mouseRow = -1; 903 | var mouseCol = -1; 904 | function processInput(){ 905 | if(topRowDisplayed <= 0){ 906 | return; 907 | } 908 | if(player.invalid || intro){ 909 | //can't during animation 910 | return; 911 | } 912 | 913 | var dx = 0; 914 | var dy = 0; 915 | if(!keysBlockedUntilAllUp){ 916 | 917 | //read keys that were just released or that were pressed just a few frames ago 918 | //this leaves some time for a combination (ie: up+right) 919 | if(keys.down == KEY_LATENCY || keys.down === 0){ 920 | dy = -1; 921 | } 922 | if(keys.up == KEY_LATENCY || keys.up === 0){ 923 | dy = 1; 924 | } 925 | if(keys.left == KEY_LATENCY || keys.left === 0){ 926 | dx = -1; 927 | } 928 | if(keys.right == KEY_LATENCY || keys.right === 0){ 929 | dx = 1; 930 | } 931 | 932 | if(dx || dy){ 933 | //look for a combo: another key that was pressed during the latency 934 | if(!dx){ 935 | if(keys.left <= KEY_LATENCY && keys.left > 0){ 936 | dx = -1; 937 | } 938 | if(keys.right <= KEY_LATENCY && keys.right > 0){ 939 | dx = 1; 940 | } 941 | }else{ 942 | if(keys.up <= KEY_LATENCY && keys.up > 0){ 943 | dy = 1; 944 | } 945 | if(keys.down <= KEY_LATENCY && keys.down > 0){ 946 | dy = -1; 947 | } 948 | } 949 | //console.log(keys,dx,dy); 950 | movePlayer(player.row+dy, player.col+dx); 951 | 952 | keysBlockedUntilAllUp = true; 953 | } 954 | } 955 | 956 | var keyName; 957 | if(keysBlockedUntilAllUp){ 958 | var allUp = true; 959 | for(keyName in keyBoolMap){ 960 | if(keyBoolMap[keyName]){ 961 | allUp = false; 962 | break; 963 | } 964 | } 965 | keysBlockedUntilAllUp = !allUp; 966 | } 967 | 968 | //update key pressed counters 969 | for(keyName in keys){ 970 | if(keys[keyName] >= 1){ 971 | keys[keyName]++; 972 | }else if(keys[keyName]>KEY_DONE){ 973 | keys[keyName]--; 974 | } 975 | } 976 | 977 | // no keyboard input => check mouse 978 | if(!player.anim && mouse.x > 0 && mouse.x HORIZON_Y && mouse.y < SIZE + HORIZON_Y ){ 979 | 980 | var mouseX = mouse.x/SIZE; 981 | var mouseY = (mouse.y-HORIZON_Y)/SIZE; 982 | reverseProject(mouseX, mouseY); 983 | mouseCol = Math.floor(reverseProject.res.x * NUM_CELLS); 984 | mouseRow = Math.floor(reverseProject.res.y * NUM_CELLS + progress); 985 | dx = mouseCol - player.col; 986 | dy = mouseRow - player.row; 987 | if(dx > 1){ 988 | dx = 1; 989 | }else if(dx < -1){ 990 | dx = -1; 991 | } 992 | if(dy > 1){ 993 | dy = 1; 994 | }else if(dy < -1){ 995 | dy = -1; 996 | } 997 | mouseRow = player.row+dy; 998 | mouseCol = player.col+dx; 999 | if(mouse.click && (dx || dy)){ 1000 | movePlayer(mouseRow,mouseCol); 1001 | } 1002 | 1003 | //console.log(mouseX,mouseY,reverseProject.res,mouseCol,mouseRow); 1004 | 1005 | }else{ 1006 | mouseCol = -1; 1007 | mouseRow = -1; 1008 | } 1009 | } 1010 | 1011 | function update(){ 1012 | now = Date.now(); 1013 | if(lastTime){ 1014 | progress += progressPerSec * (now-lastTime) * MS_TO_S; 1015 | } 1016 | 1017 | if(player.row < progress - 0.9){ 1018 | //player out of view 1019 | if(autoMove){ 1020 | // auto move if possible 1021 | if(!getThreateningCell(player.row+1, player.col)){ 1022 | movePlayer(player.row+1, player.col); 1023 | }else if(!getThreateningCell(player.row+1, player.col+1)){ 1024 | movePlayer(player.row+1, player.col+1); 1025 | }else if(!getThreateningCell(player.row+1, player.col-1)){ 1026 | movePlayer(player.row+1, player.col+1); 1027 | }else{ 1028 | setGameIsOver(true); 1029 | return; 1030 | } 1031 | }else{ 1032 | setGameIsOver(true); 1033 | } 1034 | aa.play('checkmate'); 1035 | } 1036 | 1037 | //update checkboard based on progress 1038 | var topRow = Math.floor(progress) + NUM_CELLS_DISPLAYED; 1039 | if(!lastTime || topRowDisplayed < topRow){ 1040 | var row, colIndex, changes; 1041 | //Destroy out of view rows 1042 | for(var i=topRow - NUM_CELLS_DISPLAYED - 5; i>topRowDisplayed - NUM_CELLS_DISPLAYED - 5; i--){ 1043 | row = checkBoard[i]; 1044 | if(row){ 1045 | changes = true; 1046 | //console.log('removing row',i); 1047 | for(colIndex=0; colIndexREVERSED'+anim+' my Queen.',"Today, it is my turn to protect you !"]); 1170 | blackKing.talking = true; 1171 | 1172 | //remove intro pieces 1173 | for(i=1; i<2*NUM_CELLS;i++){ 1174 | row = checkBoard[i]; 1175 | if(row){ 1176 | for(j=0; j 1){ 1194 | introStep = 9; 1195 | }else{ 1196 | introProgress = Math.sin(introProgress*PI/2); 1197 | 1198 | perspectiveProgress = (introProgress - 0.2) / 0.6; 1199 | if(perspectiveProgress < 0){ 1200 | perspectiveProgress = 0; 1201 | }else if(perspectiveProgress > 1){ 1202 | perspectiveProgress = 1; 1203 | } 1204 | shadowCanvas.style.opacity = perspectiveProgress; 1205 | if(introProgress>0.8){ 1206 | introScreen.style.opacity = (1-introProgress)/0.2; 1207 | skyCanvas.style.opacity = 1 - introScreen.style.opacity; 1208 | } 1209 | } 1210 | } 1211 | 1212 | if(init){ 1213 | if(!introTweens.length){ 1214 | pressSpaceText.style.opacity = 1; 1215 | }else{ 1216 | pressSpaceText.style.opacity = 0; 1217 | var hasText = introTweens[0].e == dialogBox && introTweens[0].to._y == dialogOpenY; 1218 | var lastTween = introTweens[introTweens.length-1]; 1219 | var delay = lastTween.du+lastTween.de; 1220 | if(hasText){ 1221 | addIntroTween(pressSpaceText.style, {opacity:1}, delay + 2, 0.1); 1222 | }else if(introStep > 1 && introStep < 8){ 1223 | addIntroCallback(nextIntroStep, delay); 1224 | } 1225 | } 1226 | } 1227 | updateIntroTweens(); 1228 | 1229 | if(introStep == 9){ 1230 | intro = false; 1231 | perspectiveProgress = 1; 1232 | introScreen.style.display = 'none'; 1233 | shadowCanvas.style.opacity = 1; 1234 | skyCanvas.style.opacity = 1; 1235 | pressSpaceText.style.display = 'none'; 1236 | hideDialog(); 1237 | player.talking = false; 1238 | } 1239 | 1240 | lastTime = now; 1241 | } 1242 | 1243 | function nextIntroStep(){ 1244 | introStep ++; 1245 | introStartTime = null; 1246 | } 1247 | 1248 | function addIntroTween(element, props, delay, duration){ 1249 | introTweens.push({e:element, to:props, de:delay || 0, du:duration}); 1250 | } 1251 | 1252 | function addIntroCallback(callback, duration){ 1253 | introTweens.push({cb:callback, de:0, du:duration}); 1254 | } 1255 | 1256 | function skipIntroTweens(){ 1257 | if(introTweens){ 1258 | for(var i=0; i= tween.de +tween.du){ 1278 | //Done 1279 | if(tween.cb){ 1280 | tween.cb(); 1281 | tween.cb = null; 1282 | } 1283 | }else{ 1284 | if(tween.e){ 1285 | if(!tween.from){ 1286 | tween.from = {}; 1287 | for(key in tween.to){ 1288 | tween.from[key] = tween.e[key]; 1289 | } 1290 | } 1291 | var p = (time - tween.de)/tween.du; 1292 | //Ease 1293 | p = Math.sin(p*PI/2); 1294 | for(key in tween.to){ 1295 | tween.e[key] = tween.from[key] * (1-p) + tween.to[key] * p; 1296 | } 1297 | } 1298 | } 1299 | } 1300 | dialogBox.setAttributeNS(null,'y',dialogBox._y); 1301 | } 1302 | 1303 | 1304 | //------------------------------------------------------------------------------------------------------------------ 1305 | // RENDER 1306 | //------------------------------------------------------------------------------------------------------------------ 1307 | 1308 | var BG_COLOR = '#193441'; 1309 | var CELL_COLOR_1 = '#D1DBBD'; 1310 | var CELL_COLOR_2 = '#3E606F'; 1311 | var STROKE_COLOR = '#D1DBBD'; 1312 | var ROLLOVER_COLOR = '#794'; 1313 | var PIECE_FILL_COLOR = '#eee'; 1314 | var PIECE_STROKE_COLOR = '#555'; 1315 | var INVALID_CELL_COLOR_RGB = '255,0,0'; 1316 | var CHECK_POINT_COLOR = 'rgba(93, 255, 182, 0.56)'; 1317 | 1318 | function render(){ 1319 | //console.log('render',perspectiveProgress,intro); 1320 | // SVG ------------------------------------------------------------------------------------------------------ 1321 | 1322 | //update pieces 1323 | var pieceAfterPlayer; 1324 | var row; 1325 | for(var rowIndex=topRowDisplayed-NUM_CELLS_DISPLAYED-5; rowIndex<=topRowDisplayed; rowIndex++){ 1326 | row = checkBoard[rowIndex]; 1327 | if(row){ 1328 | for(var colIndex=0; colIndex player.y && (!pieceAfterPlayer || pieceAfterPlayer.y > piece.y)){ 1340 | pieceAfterPlayer = piece; 1341 | } 1342 | } 1343 | } 1344 | } 1345 | } 1346 | 1347 | 1348 | //update player anim 1349 | var playerAnimProgress; 1350 | if(player.anim){ 1351 | if(!player.invalid){ 1352 | playerAnimProgress = (MS_TO_S*(now - player.animStartTime))/playerAnimDuration; 1353 | }else{ 1354 | playerAnimProgress = (MS_TO_S*(now - player.animStartTime))/playerInvalidDuration; 1355 | } 1356 | if(playerAnimProgress<0 || playerAnimProgress>=1){ 1357 | player.anim = false; 1358 | if(player.invalid && player.threateningPiece){ 1359 | player.threateningPiece.shape.style.filter = 'none'; 1360 | } 1361 | player.invalid = false; 1362 | }else{ 1363 | playerAnimProgress = Math.sin(playerAnimProgress * PI * 0.5); //Ease out 1364 | if(player.invalid){ 1365 | computeCellPos(player.row, player.col); 1366 | var shakeAmplitude = 0.4 * (playerAnimProgress < 0.5 ? playerAnimProgress : 1-playerAnimProgress)*CELL_SIZE; 1367 | var shake = Math.sin(6*playerAnimProgress*PI) * shakeAmplitude; 1368 | player.x += shake; 1369 | updatePieceStyle(player); 1370 | //color enemy piece 1371 | player.threateningPiece.shape.style.filter = 'url(#'+ENEMY_FILTER+')'; 1372 | }else{ 1373 | //compute old pos 1374 | computeCellPos(player.oldRow, player.oldCol); 1375 | //interpolate 1376 | player.opacity = playerAnimProgress * player.opacity + (1-playerAnimProgress) * computeCellPos.res.opacity; 1377 | player.scale = playerAnimProgress * player.scale + (1-playerAnimProgress) * computeCellPos.res.scale; 1378 | player.x = playerAnimProgress * player.x + (1-playerAnimProgress) * computeCellPos.res.x; 1379 | player.y = playerAnimProgress * player.y + (1-playerAnimProgress) * computeCellPos.res.y; 1380 | updatePieceStyle(player); 1381 | } 1382 | } 1383 | } 1384 | 1385 | //update removedPieces 1386 | for(i=0,len=removedPieces.length; i 1){ 1391 | //console.log('removedPieces',removedPieces); 1392 | removeSvgShape(removedPiece); 1393 | removedPieces[i] = removedPieces[len-1]; 1394 | len--; 1395 | i--; 1396 | removedPieces = removedPieces.slice(0,len); 1397 | //console.log('============>',removedPieces); 1398 | }else{ 1399 | if(removedPiece.justRemoved){ 1400 | removedPiece.justRemoved = false; 1401 | removedPiece.removedX = removedPiece.x; 1402 | removedPiece.removedY = removedPiece.y; 1403 | } 1404 | if(removedPiece.x < SIZE*0.5){ 1405 | removedPiece.x = removedPiece.removedX - removedPieceProgress * SIZE; 1406 | }else{ 1407 | removedPiece.x = removedPiece.removedX + removedPieceProgress * SIZE; 1408 | } 1409 | removedPiece.y = removedPiece.removedY - Math.sin(removedPieceProgress*PI) * SIZE * 0.4; 1410 | updatePieceStyle(removedPiece); 1411 | 1412 | //console.log(removedPiece.x, removedPiece.y, removedPieceProgress); 1413 | } 1414 | } 1415 | 1416 | //Check text 1417 | if(player.anim && player.invalid){ 1418 | if(!checkText){ 1419 | checkText = { 1420 | onTop: true, 1421 | type: CHECK_TEXT 1422 | }; 1423 | } 1424 | if(!checkText.shape){ 1425 | addSvgShape(checkText); 1426 | checkText.row = player.invalidRow; 1427 | checkText.col = player.invalidCol; 1428 | } 1429 | computeCellPos(player.invalidRow, player.invalidCol, checkText); 1430 | checkText.scale = 1; 1431 | checkText.y -= playerAnimProgress * checkText.scale * CELL_SIZE * 0.2; 1432 | checkText.opacity = playerAnimProgress < 0.8 ? 1 : (1-(playerAnimProgress-0.8)/(1-0.8)); 1433 | updatePieceStyle(checkText); 1434 | }else if(checkText && checkText.shape){ 1435 | removeSvgShape(checkText); 1436 | } 1437 | 1438 | if(pieceAfterPlayer && pieceAfterPlayer.shape && player.shape && player.shape.nextSibling != pieceAfterPlayer.shape){ 1439 | //adjust player z-index 1440 | svgPiecesLayer.insertBefore(player.shape, pieceAfterPlayer.shape); 1441 | } 1442 | 1443 | // CANVAS ------------------------------------------------------------------------------------------------------ 1444 | 1445 | //clear & fill 1446 | bgCtx.save(); 1447 | bgCtx.translate(0,HORIZON_Y); 1448 | bgCtx.fillStyle = BG_COLOR; 1449 | bgCtx.beginPath(); 1450 | bgCtx.rect(0,0,SIZE,SIZE); 1451 | bgCtx.fill(); 1452 | bgCtx.clip(); 1453 | 1454 | var progressIndex = Math.floor(progress); 1455 | var di = -(progress - Math.floor(progress)); 1456 | var p1 = {}, p2 = {}, p3 = {}, p4 = {}; 1457 | var i,j,len; 1458 | for(i=-1; i 0){ 1535 | //Note: svg transform origin is the root SVG element origin 1536 | piece.shape.setAttributeNS(null,'transform', 'scale('+piece.scale+') translate('+(piece.x / piece.scale)+','+(piece.y / piece.scale)+')'); 1537 | } 1538 | } 1539 | } 1540 | 1541 | //------------------------------------------------------------------------------------------------------------------ 1542 | // projections 1543 | //------------------------------------------------------------------------------------------------------------------ 1544 | 1545 | // For y, projection of a [0,2] position in logical chessboard into a [1,0] position inside the canvas 1546 | // we want: 0->1 and 2->1. You don't want to know how I got those coeff. Don't ask. 1547 | // http://fooplot.com/#W3sidHlwZSI6MCwiZXEiOiItKDMvMTYpKngqeCsoMC8xNikqeCsxIiwiY29sb3IiOiIjMDAwMDAwIn0seyJ0eXBlIjoxMDAwLCJ3aW5kb3ciOlsiLTYuNiIsIjYuNCIsIi0zLjkyIiwiNC4wOCJdfV0- 1548 | var A_Y = 3/16; 1549 | var B_Y = -14/16; 1550 | var C_Y = 1; 1551 | //For scale, we roughly inverse the curve 1552 | var A_S = -2.5/16; 1553 | var B_S = 0/16; 1554 | var C_S = 1; 1555 | 1556 | // [0,2] in logical chessboard => [0,1] in canvas coordinates (y reversed) 1557 | project.res = {}; 1558 | function project(x, y, res){ 1559 | res = res || project.res; 1560 | 1561 | res.y = quadraticEq(y, A_Y, B_Y, C_Y); 1562 | res.scaleX = quadraticEq(y, A_S, B_S, C_S); 1563 | res.scaleY = res.scaleX; 1564 | res.x = (1-res.scaleX)/2 + x*res.scaleX; 1565 | 1566 | if(intro){ 1567 | res.x = perspectiveProgress * res.x + (1-perspectiveProgress) * x; 1568 | res.y = perspectiveProgress * res.y + (1-perspectiveProgress) * (1-y); 1569 | res.scaleX = perspectiveProgress * res.scaleX + (1-perspectiveProgress) * 1; 1570 | res.scaleY = res.scaleX; 1571 | } 1572 | 1573 | return res; 1574 | } 1575 | 1576 | function ellipseEq(x, a, b){ 1577 | // x²/a² + y²/b² = 1 1578 | // y = sqrt( (1-x²/a²)*b² ) 1579 | return Math.sqrt( (1-((x*x)/(a*a)))*b*b ); 1580 | } 1581 | 1582 | reverseProject.res = {}; 1583 | function reverseProject(x, y, res){ 1584 | res = res || reverseProject.res; 1585 | res.y = reverseQuadraticEq(y, A_Y, B_Y, C_Y, false); 1586 | var scale = quadraticEq(res.y, A_S, B_S, C_S); 1587 | res.x = (x - (1-scale)/2)/scale; 1588 | return res; 1589 | } 1590 | 1591 | function quadraticEq(x, a, b, c){ 1592 | return a*x*x + b*x + c; 1593 | } 1594 | 1595 | function reverseQuadraticEq(y, a, b, c, pos){ 1596 | if(pos){ 1597 | return (-b + Math.sqrt(b*b - 4*a*(c-y)))/(2*a); 1598 | }else{ 1599 | return (-b - Math.sqrt(b*b - 4*a*(c-y)))/(2*a); 1600 | } 1601 | } 1602 | 1603 | //------------------------------------------------------------------------------------------------------------------ 1604 | // canvases 1605 | //------------------------------------------------------------------------------------------------------------------ 1606 | 1607 | function initCheckBoardCanvas(){ 1608 | root.appendChild(bgCanvas); 1609 | bgCanvas.width = SIZE; 1610 | bgCanvas.height = SIZE + HORIZON_Y; 1611 | } 1612 | 1613 | function initShadowCanvas(){ 1614 | var ctx = shadowCtx; 1615 | //Top down shadow */ 1616 | var grd = ctx.createLinearGradient(0,0,0,SIZE); 1617 | var c = 'rgba(10,20,25,'; 1618 | var c2 = ')'; 1619 | grd.addColorStop(0, c + 0 + c2); 1620 | grd.addColorStop(0.2, c + 0 + c2); 1621 | //grd.addColorStop(0.9,"rgba(0,0,0,0.3)"); 1622 | grd.addColorStop(1, c + 0.5 + c2); 1623 | 1624 | ctx.fillStyle = grd; 1625 | ctx.fillRect(0, 0, SIZE, SIZE); 1626 | ctx.restore(); 1627 | 1628 | shadowCanvas.style.top = HORIZON_Y+'px'; 1629 | shadowCanvas.style.pointerEvents = 'none'; 1630 | root.appendChild(shadowCanvas); 1631 | } 1632 | 1633 | function initSkyCanvas(){ 1634 | var shadowSize = SIZE * 0.02; 1635 | skyCanvas.width = SIZE; 1636 | skyCanvas.height = HORIZON_Y + shadowSize; 1637 | 1638 | var ctx = skyCtx; 1639 | ctx.clearRect(0,0,SIZE,SIZE); 1640 | 1641 | ctx.save(); 1642 | //Draw sky 1643 | ctx.fillStyle = '#FF8601'; 1644 | ctx.beginPath(); 1645 | ctx.rect(0, 0, SIZE, HORIZON_Y); 1646 | ctx.fill(); 1647 | ctx.clip(); 1648 | //Draw sun 1649 | ctx.fillStyle = '#FFE7CA'; 1650 | var sunRadius = SIZE/4; 1651 | ctx.beginPath(); 1652 | ctx.arc(SIZE/2, HORIZON_Y + 0.3*sunRadius, sunRadius, 0, PI, true); 1653 | ctx.fill(); 1654 | ctx.restore(); 1655 | //Draw Mountains 1656 | ctx.beginPath(); 1657 | ctx.fillStyle = 'rgb(10,20,25)'; 1658 | 1659 | var mountainMaxHeight = 40; 1660 | var points = [ 1661 | 0, 0.7, 1662 | 0.1,0.3, 1663 | 0.2, 1, 1664 | 0.3, 0.5, 1665 | 0.35, 0.8, 1666 | 0.42, 0.5, 1667 | 0.55, 0.9, 1668 | 0.7, 0.45, 1669 | 0.8, 1.1, 1670 | 0.88, 0.4, 1671 | 1,0.8 1672 | ]; 1673 | var mountainX = 0; 1674 | for(var i=0; i'+ 1926 | ''); 1927 | 1928 | defs.appendChild (def); 1929 | return def; 1930 | } 1931 | 1932 | function makeShadow(){ 1933 | var shadow = makeEllipse(5, 8, 3.1, 1.8); 1934 | svgStyle(shadow,'rgba(0,0,0,0.2)','none'); 1935 | return shadow; 1936 | } 1937 | 1938 | function makeCircle(cx, cy, r){ 1939 | var circle = document.createElementNS (xmlns, "circle"); 1940 | svgAttrs(circle, { 1941 | cx: svgFloat(cx - OX), 1942 | cy: svgFloat(cy - OY), 1943 | r: svgFloat(r) 1944 | }); 1945 | return circle; 1946 | } 1947 | 1948 | function makeRect(x, y, w, h){ 1949 | var rect = document.createElementNS (xmlns, "rect"); 1950 | svgAttrs(rect, { 1951 | x: svgFloat(x - OX), 1952 | y: svgFloat(y - OY), 1953 | width: svgFloat(w), 1954 | height: svgFloat(h) 1955 | }); 1956 | return rect; 1957 | } 1958 | 1959 | function makeEllipse(cx, cy, rx, ry){ 1960 | var ellipse = document.createElementNS (xmlns, "ellipse"); 1961 | svgAttrs(ellipse, { 1962 | cx: svgFloat(cx - OX), 1963 | cy: svgFloat(cy - OY), 1964 | rx: svgFloat(rx), 1965 | ry: svgFloat(ry) 1966 | }); 1967 | return ellipse; 1968 | } 1969 | 1970 | function makePath(list, style){ 1971 | var path = document.createElementNS (xmlns, "path"); 1972 | path.setAttributeNS (null, "d", makePathString(list)); 1973 | if(style){ 1974 | svgAttrs(path,style); 1975 | } 1976 | return path; 1977 | } 1978 | 1979 | function makePathString(list){ 1980 | var path = ''; 1981 | for(var i=0; i' + 2011 | '' + 2012 | ''; 2013 | defs.appendChild(def); 2014 | */ 2015 | 2016 | var def = document.createElementNS (xmlns, 'text'); 2017 | def.setAttributeNS (null, 'id', id); 2018 | svgAttrs(def,{ 2019 | 'x':'-40', 2020 | 'font-size':'28', 2021 | 'fill':'red', 2022 | 'stroke':'black', 2023 | 'stroke-width':'1', 2024 | 'font-family':'Impact' 2025 | }); 2026 | svgInnerHtml(def, text); 2027 | defs.appendChild(def); 2028 | } 2029 | 2030 | function makeGameOverScreen(){ 2031 | gameOverScreen = document.createElementNS(xmlns, "g"); 2032 | gameOverScreen.style.display = 'none'; 2033 | svgElem.appendChild(gameOverScreen); 2034 | 2035 | var rect = document.createElementNS(xmlns,'rect'); 2036 | svgAttrs(rect, {x:0, y:0, width:'100%',height:'100%',fill:'rgba(0,0,0,0.5)'}); 2037 | gameOverScreen.appendChild(rect); 2038 | 2039 | var text = document.createElementNS (xmlns, 'text'); 2040 | svgAttrs(text,{ 2041 | 'x': '50%', 2042 | 'y': '50%', 2043 | 'font-size':'48px', 2044 | 'fill': 'orange', 2045 | 'stroke': 'red', 2046 | 'stroke-width':'2px', 2047 | 'text-anchor': 'middle', 2048 | 'font-family':'Impact' 2049 | }); 2050 | svgInnerHtml(text, 'CHECKMATE !'); 2051 | gameOverScreen.appendChild(text); 2052 | 2053 | text = document.createElementNS (xmlns, 'text'); 2054 | svgAttrs(text,{ 2055 | 'x': '50%', 2056 | 'y': '60%', 2057 | 'font-size':'22px', 2058 | 'fill': 'white', 2059 | 'stroke': 'black', 2060 | 'stroke-width':'1px', 2061 | 'text-anchor': 'middle', 2062 | 'font-family':'Impact' 2063 | }); 2064 | svgInnerHtml(text, 2065 | 'Press SPACE or CLICK' + 2066 | 'to restart from the last checkpoint.' 2067 | ); 2068 | gameOverScreen.appendChild(text); 2069 | } 2070 | 2071 | function makeIntroScreen(){ 2072 | introScreen = document.createElementNS(xmlns, "g"); 2073 | svgElem.appendChild(introScreen); 2074 | 2075 | var rect = document.createElementNS(xmlns,'rect'); 2076 | svgAttrs(rect, {x:0, y:0, width:'100%',height:HORIZON_Y,fill:BG_COLOR,stroke:'#000'}); 2077 | introScreen.appendChild(rect); 2078 | 2079 | var text = document.createElementNS (xmlns, 'text'); 2080 | svgAttrs(text,{ 2081 | 'x': '50%', 2082 | 'y': '60', 2083 | 'font-size':'48px', 2084 | 'fill': 'orange', 2085 | 'stroke': 'red', 2086 | 'stroke-width':'2px', 2087 | 'text-anchor': 'middle', 2088 | 'font-family':'Impact' 2089 | }); 2090 | svgInnerHtml(text, 'CHESSPURSUIT'); 2091 | introScreen.appendChild(text); 2092 | } 2093 | 2094 | function makePressSpaceText(){ 2095 | pressSpaceText = document.createElementNS (xmlns, 'text'); 2096 | svgAttrs(pressSpaceText,{ 2097 | 'x': '50%', 2098 | 'y': HORIZON_Y+SIZE-10, 2099 | 'font-size':'22px', 2100 | 'fill': 'white', 2101 | 'stroke': 'black', 2102 | 'stroke-width':'1px', 2103 | 'text-anchor': 'middle', 2104 | 'font-family':'Impact' 2105 | }); 2106 | svgInnerHtml(pressSpaceText, 'Press SPACE or CLICK'); 2107 | svgElem.appendChild(pressSpaceText); 2108 | } 2109 | 2110 | function makePauseText(){ 2111 | pauseText = document.createElementNS (xmlns, 'text'); 2112 | svgAttrs(pauseText,{ 2113 | 'x': '50%', 2114 | 'y': '50%', 2115 | 'font-size':'32px', 2116 | 'fill': 'orange', 2117 | 'stroke': 'black', 2118 | 'stroke-width':'1px', 2119 | 'text-anchor': 'middle', 2120 | 'font-family':'Impact' 2121 | }); 2122 | svgInnerHtml(pauseText, 'PAUSED'); 2123 | pauseText.style.display = 'none'; 2124 | svgElem.appendChild(pauseText); 2125 | } 2126 | 2127 | function makeDialogBox(){ 2128 | 2129 | var width = SIZE - 2* DIALOG_MARGIN; 2130 | var height = 0.3 * SIZE - 2* DIALOG_MARGIN; 2131 | dialogCloseY = SIZE+HORIZON_Y; 2132 | dialogOpenY = (HORIZON_Y+SIZE-height-DIALOG_MARGIN); 2133 | dialogBox = document.createElementNS(xmlns, "svg"); 2134 | dialogBox._y = dialogCloseY; //used for tweening 2135 | svgAttrs(dialogBox, {x:DIALOG_MARGIN, y:dialogCloseY,width:width,height:height}); 2136 | svgElem.appendChild(dialogBox); 2137 | 2138 | var rect = document.createElementNS(xmlns,'rect'); 2139 | svgAttrs(rect, {width:'100%',height:'100%',fill:'rgba(0,0,0,0.8)',stroke:'#fff','stroke-width':2}); 2140 | dialogBox.appendChild(rect); 2141 | 2142 | dialogSpeakerText = document.createElementNS (xmlns, 'text'); 2143 | svgAttrs(dialogSpeakerText,{ 2144 | 'x': 10, 2145 | 'y': 20, 2146 | 'font-size':'18px', 2147 | 'fill': '#fff', 2148 | 'text-anchor': 'left', 2149 | 'font-family':'Impact' 2150 | }); 2151 | dialogBox.appendChild(dialogSpeakerText); 2152 | 2153 | dialogText = document.createElementNS (xmlns, 'text'); 2154 | svgAttrs(dialogText,{ 2155 | 'x': 10, 2156 | 'y': 40, 2157 | 'font-size':'16px', 2158 | 'fill': '#fff', 2159 | 'text-anchor': 'left', 2160 | 'font-family':'sans-serif' 2161 | }); 2162 | dialogBox.appendChild(dialogText); 2163 | } 2164 | 2165 | function makeWinScreen(){ 2166 | winScreen = document.createElementNS(xmlns, "g"); 2167 | winScreen.style.display = 'none'; 2168 | svgElem.appendChild(winScreen); 2169 | 2170 | var rect = document.createElementNS(xmlns,'rect'); 2171 | svgAttrs(rect, {x:0, y:0, width:'100%',height:'100%',fill:'rgba(0,0,0,0.5)'}); 2172 | winScreen.appendChild(rect); 2173 | 2174 | var text = document.createElementNS (xmlns, 'text'); 2175 | svgAttrs(text,{ 2176 | 'x': '50%', 2177 | 'y': '50%', 2178 | 'font-size':'48px', 2179 | 'fill': '#5f7', 2180 | 'stroke': 'black', 2181 | 'stroke-width':'2px', 2182 | 'text-anchor': 'middle', 2183 | 'font-family':'Impact' 2184 | }); 2185 | svgInnerHtml(text, 'YOU WIN !'); 2186 | winScreen.appendChild(text); 2187 | 2188 | text = document.createElementNS (xmlns, 'text'); 2189 | svgAttrs(text,{ 2190 | 'x': '50%', 2191 | 'y': '60%', 2192 | 'font-size':'22px', 2193 | 'fill': 'white', 2194 | 'stroke': 'black', 2195 | 'stroke-width':'1px', 2196 | 'text-anchor': 'middle', 2197 | 'font-family':'Impact' 2198 | }); 2199 | svgInnerHtml(text, 'Alas, your Queen is in another castle...'); 2200 | winScreen.appendChild(text); 2201 | } 2202 | } 2203 | 2204 | var dialogOpenY; 2205 | var dialogCloseY; 2206 | function showDialog(whiteKing,texts){ 2207 | if(whiteKing){ 2208 | svgInnerHtml(dialogSpeakerText, 'White King :'); 2209 | }else{ 2210 | svgInnerHtml(dialogSpeakerText, 'Black King :'); 2211 | } 2212 | var txt = ''; 2213 | for(var i=0; i'+texts[i]+''; 2215 | } 2216 | svgInnerHtml(dialogText, txt); 2217 | 2218 | addIntroTween(dialogBox,{_y:dialogOpenY},0,0.5); 2219 | } 2220 | 2221 | function hideDialog(){ 2222 | addIntroTween(dialogBox,{_y:dialogCloseY},0,0.5); 2223 | } 2224 | 2225 | var svgInnerHtmlElement = document.createElement('div'); 2226 | function svgInnerHtml(svg, html){ 2227 | while (svg.firstChild) { 2228 | svg.removeChild(svg.firstChild); 2229 | } 2230 | var svgText=''+html+''; 2231 | svgInnerHtmlElement.innerHTML = svgText; 2232 | var nodes = Array.prototype.slice.call(svgInnerHtmlElement.childNodes[0].childNodes); 2233 | nodes.forEach(function(el){ 2234 | svg.appendChild(el); 2235 | }); 2236 | } 2237 | 2238 | function svgAttrs(el, attrs){ 2239 | if(attrs){ 2240 | for(var key in attrs){ 2241 | el.setAttributeNS (null, key, attrs[key]); 2242 | } 2243 | } 2244 | return el; 2245 | } 2246 | 2247 | function svgStyle(svgElem, fill, stroke, strokeWidth){ 2248 | var attrs = {}; 2249 | if(typeof fill != 'undefined'){ 2250 | attrs.fill = fill; 2251 | } 2252 | if(typeof stroke != 'undefined'){ 2253 | attrs.stroke = stroke; 2254 | } 2255 | if(typeof strokeWidth != 'undefined'){ 2256 | attrs.strokeWidth = strokeWidth; 2257 | } 2258 | svgAttrs(svgElem, attrs); 2259 | return svgElem; 2260 | } 2261 | 2262 | //----------------------------------------------------------- 2263 | // Input 2264 | //----------------------------------------------------------- 2265 | 2266 | 2267 | var keyMap = { 2268 | 37: "left", // left arrow 2269 | 65: "left", // a 2270 | 81: "left", // q 2271 | 38: "up", // up arrow 2272 | 90: "up", // z 2273 | 87: "up", // w 2274 | 83: "down", // d 2275 | 40: "down", 2276 | 39: "right",// right arrow 2277 | 68: "right",//d 2278 | 32: "space", 2279 | 27: "esc", 2280 | 13: "enter" 2281 | }; 2282 | // keyName => isDown bool 2283 | var keyBoolMap = {}; 2284 | // keyName => int 2285 | var keys = {}; 2286 | //Set up key listener 2287 | function onkey(isDown, e) { 2288 | if (!e) e = window.e; 2289 | var c = e.keyCode; 2290 | if (e.charCode && !c) c = e.charCode; 2291 | 2292 | var keyName = keyMap[c]; 2293 | if(keyName){ 2294 | //only take events that represent an actual change 2295 | if(keyBoolMap[keyName] !== isDown){ 2296 | keyBoolMap[keyName] = isDown; 2297 | if(typeof keys[keyName] == 'undefined'){ 2298 | keys[keyName] = -1; 2299 | } 2300 | if(isDown){ 2301 | if(keys[keyName]<1){ 2302 | //console.log('keyDown',keyName); 2303 | keys[keyName] = 1; 2304 | } 2305 | }else{ 2306 | if(keys[keyName] > 0){ 2307 | //console.log('keyUp',keyName); 2308 | keys[keyName] = 0; 2309 | } 2310 | } 2311 | } 2312 | } 2313 | } 2314 | document.onkeyup = function(e){ 2315 | //DEBUG/CHEAT: pause 2316 | if(!intro && keyBoolMap.enter && !gameIsOver){ 2317 | raf = !raf; 2318 | pauseText.style.display = raf ? 'none' : 'block'; 2319 | console.log('debug toggle anim: ',raf); 2320 | if(raf){ 2321 | lastTime = Date.now(); 2322 | tic(); 2323 | } 2324 | } 2325 | 2326 | onkey(false, e); 2327 | }; 2328 | document.onkeydown = function(e){ 2329 | onkey(true, e); 2330 | }; 2331 | 2332 | function onmouse(isClick,e){ 2333 | mouse.click = isClick; 2334 | document.onmousemove(e); 2335 | } 2336 | document.onmousedown = function(e){ 2337 | onmouse(true,e); 2338 | }; 2339 | document.onmousemove = function(e){ 2340 | mouse.x = e.clientX - root.offsetLeft; 2341 | mouse.y = e.clientY; 2342 | }; 2343 | 2344 | /* 2345 | document.oncontextmenu = function(e){ 2346 | return false; 2347 | }; 2348 | */ 2349 | 2350 | init(); 2351 | }; -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 46 | 47 | 48 |
49 | Use arrow keys or mouse to move around.
50 | You can use the 'return' key to pause the game (but that's cheating !).
51 | You don't need to really know the chess rules to play, but that could definitely help! 52 |
53 | 54 | -------------------------------------------------------------------------------- /src/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 | function J(){this.B=function(e){for(var f=0;24>f;f++)this[String.fromCharCode(97+f)]=e[f]||0;.01>this.c&&(this.c=.01);e=this.b+this.c+this.e;.18>e&&(e=.18/e,this.b*=e,this.c*=e,this.e*=e)}} 21 | var W=new function(){this.A=new J;var e,f,d,h,l,z,K,L,M,A,m,N;this.reset=function(){var b=this.A;h=100/(b.f*b.f+.001);l=100/(b.g*b.g+.001);z=1-b.h*b.h*b.h*.01;K=-b.i*b.i*b.i*1E-6;b.a||(m=.5-b.n/2,N=5E-5*-b.o);L=1+b.l*b.l*(0a.q?-1020:1020),S=a.p?((1-a.p)*(1-a.p)*2E4|0)+32:0,ba=a.d,T=a.j/2,ca=a.k*a.k*.01,E=a.a,F=e,da=1/e,ea=1/f,fa=1/d,a=5/(1+a.u*a.u*20)*(.01+n);.8=S&&(V=0,this.reset());A&&++M>=A&&(A=0,h*=L);z+=K;h*=z;h>l&&(h=l,0<$&&(G=!0));g=h;0g&&(g=8);E||(m+=N,0>m?m=0:.5F)switch(v=0,++U){case 1:F=f;break;case 2:F=d}switch(U){case 0:w=v*da;break;case 1:w=1+2*(1-v*ea)*ba;break;case 2:w=1-v*fa;break;case 3:w=0,G=!0}R&&(D+=aa,s=D|0,0>s?s=-s:1023r?r=1E-5:.1=g&&(p%=g,3==E))for(x=y.length;x--;)y[x]=2*Math.random()-1;switch(E){case 0:c=p/gc?1:-1);c=.225*((0>c?-1:1)*c*c-c)+c;break;case 3:c=y[Math.abs(32*p/g|0)]}P&&(x=u,n*=X,0>n?n=0:.1=q?-32768:32767*q|0}return O}}; 25 | window.jsfxr=function(e){W.A.B(e);var f=W.D();e=new Uint8Array(4*((f+1)/2|0)+44);var f=2*W.C(new Uint16Array(e.buffer,44),f),d=new Uint32Array(e.buffer,0,44);d[0]=1179011410;d[1]=f+36;d[2]=1163280727;d[3]=544501094;d[4]=16;d[5]=65537;d[6]=44100;d[7]=88200;d[8]=1048578;d[9]=1635017060;d[10]=f;for(var f=f+44,d=0,h="data:audio/wav;base64,";d>18]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[l>> 26 | 12&63]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[l>>6&63]+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"[l&63]);return h}; -------------------------------------------------------------------------------- /src/requestAnimationFame.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var lastTime = 0; 3 | var vendors = ['webkit', 'moz']; 4 | for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { 5 | window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame']; 6 | window.cancelAnimationFrame = 7 | window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame']; 8 | } 9 | 10 | if (!window.requestAnimationFrame) 11 | window.requestAnimationFrame = function(callback, element) { 12 | var currTime = new Date().getTime(); 13 | var timeToCall = Math.max(0, 16 - (currTime - lastTime)); 14 | var id = window.setTimeout(function() { callback(currTime + timeToCall); }, 15 | timeToCall); 16 | lastTime = currTime + timeToCall; 17 | return id; 18 | }; 19 | 20 | if (!window.cancelAnimationFrame) 21 | window.cancelAnimationFrame = function(id) { 22 | clearTimeout(id); 23 | }; 24 | }()); -------------------------------------------------------------------------------- /src/stats-wrapper.js: -------------------------------------------------------------------------------- 1 | 2 | var stb; 3 | var ste; 4 | 5 | (function(){ 6 | var loadInterval = setInterval( function () { 7 | if (document.readyState === "complete") { 8 | var stats = new Stats(); 9 | 10 | var t; 11 | stb = function(){ 12 | stats.begin(); 13 | t = Date.now(); 14 | }; 15 | ste = function(){ 16 | t = Date.now()-t; 17 | if(t>10){ 18 | //debug break here 19 | console.log("FRAAAAAAME",t); 20 | } 21 | stats.end(); 22 | }; 23 | stats.setMode(1); // 0: fps, 1: ms 24 | 25 | // Align top-left 26 | stats.domElement.style.position = 'absolute'; 27 | stats.domElement.style.bottom = '0px'; 28 | stats.domElement.style.right = '0px'; 29 | 30 | document.body.appendChild( stats.domElement ); 31 | 32 | clearInterval(loadInterval); 33 | } 34 | }, 200 ); 35 | })(); -------------------------------------------------------------------------------- /src/stats.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | var Stats = function () { 6 | var startTime = Date.now(), prevTime = startTime; 7 | var ms = 0, msMin = Infinity, msMax = 0; 8 | var fps = 0, fpsMin = Infinity, fpsMax = 0; 9 | var frames = 0, mode = 0; 10 | 11 | var container = document.createElement( 'div' ); 12 | container.id = 'stats'; 13 | container.addEventListener( 'mousedown', function ( event ) { event.preventDefault(); setMode( ++ mode % 2 ) }, false ); 14 | container.style.cssText = 'width:80px;opacity:0.9;cursor:pointer'; 15 | 16 | var fpsDiv = document.createElement( 'div' ); 17 | fpsDiv.id = 'fps'; 18 | fpsDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#002'; 19 | container.appendChild( fpsDiv ); 20 | 21 | var fpsText = document.createElement( 'div' ); 22 | fpsText.id = 'fpsText'; 23 | fpsText.style.cssText = 'color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; 24 | fpsText.innerHTML = 'FPS'; 25 | fpsDiv.appendChild( fpsText ); 26 | 27 | var fpsGraph = document.createElement( 'div' ); 28 | fpsGraph.id = 'fpsGraph'; 29 | fpsGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0ff'; 30 | fpsDiv.appendChild( fpsGraph ); 31 | 32 | while ( fpsGraph.children.length < 74 ) { 33 | 34 | var bar = document.createElement( 'span' ); 35 | bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#113'; 36 | fpsGraph.appendChild( bar ); 37 | 38 | } 39 | 40 | var msDiv = document.createElement( 'div' ); 41 | msDiv.id = 'ms'; 42 | msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;display:none'; 43 | container.appendChild( msDiv ); 44 | 45 | var msText = document.createElement( 'div' ); 46 | msText.id = 'msText'; 47 | msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; 48 | msText.innerHTML = 'MS'; 49 | msDiv.appendChild( msText ); 50 | 51 | var msGraph = document.createElement( 'div' ); 52 | msGraph.id = 'msGraph'; 53 | msGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0f0'; 54 | msDiv.appendChild( msGraph ); 55 | 56 | while ( msGraph.children.length < 74 ) { 57 | 58 | var bar = document.createElement( 'span' ); 59 | bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#131'; 60 | msGraph.appendChild( bar ); 61 | 62 | } 63 | 64 | var setMode = function ( value ) { 65 | 66 | mode = value; 67 | 68 | switch ( mode ) { 69 | 70 | case 0: 71 | fpsDiv.style.display = 'block'; 72 | msDiv.style.display = 'none'; 73 | break; 74 | case 1: 75 | fpsDiv.style.display = 'none'; 76 | msDiv.style.display = 'block'; 77 | break; 78 | } 79 | 80 | }; 81 | 82 | var updateGraph = function ( dom, value ) { 83 | 84 | var child = dom.appendChild( dom.firstChild ); 85 | child.style.height = value + 'px'; 86 | 87 | }; 88 | 89 | return { 90 | 91 | REVISION: 12, 92 | 93 | domElement: container, 94 | 95 | setMode: setMode, 96 | 97 | begin: function () { 98 | 99 | startTime = Date.now(); 100 | 101 | }, 102 | 103 | end: function () { 104 | 105 | var time = Date.now(); 106 | 107 | ms = time - startTime; 108 | msMin = Math.min( msMin, ms ); 109 | msMax = Math.max( msMax, ms ); 110 | 111 | msText.textContent = ms + ' MS (' + msMin + '-' + msMax + ')'; 112 | updateGraph( msGraph, Math.min( 30, 30 - ( ms / 200 ) * 30 ) ); 113 | 114 | frames ++; 115 | 116 | if ( time > prevTime + 1000 ) { 117 | 118 | fps = Math.round( ( frames * 1000 ) / ( time - prevTime ) ); 119 | fpsMin = Math.min( fpsMin, fps ); 120 | fpsMax = Math.max( fpsMax, fps ); 121 | 122 | fpsText.textContent = fps + ' FPS (' + fpsMin + '-' + fpsMax + ')'; 123 | updateGraph( fpsGraph, Math.min( 30, 30 - ( fps / 100 ) * 30 ) ); 124 | 125 | prevTime = time; 126 | frames = 0; 127 | 128 | } 129 | 130 | return time; 131 | 132 | }, 133 | 134 | update: function () { 135 | 136 | startTime = this.end(); 137 | 138 | } 139 | 140 | } 141 | 142 | }; 143 | 144 | if ( typeof module === 'object' ) { 145 | module.exports = Stats; 146 | } --------------------------------------------------------------------------------