Advertisement
christophermcasey

HTML5 Tank Survival Game JS

Dec 1st, 2013
159
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // Author: Christopher M. Casey
  2. // http://www.christophermcasey.com/
  3.  
  4. // === CONSTANTS === //
  5. var RELEASE = true;
  6. var CANVAS_WIDTH = 480;
  7. var CANVAS_HEIGHT = 320;
  8. var HALF_CANVAS_WIDTH = Math.floor(CANVAS_WIDTH/2);
  9. var HALF_CANVAS_HEIGHT = Math.floor(CANVAS_HEIGHT/2);
  10. var TANK_XOFFSET = Math.floor(CANVAS_WIDTH/8);
  11. var CANVAS_BACKGROUND_COLOR = "#444444";
  12. var MOBILE_FLAG = (/iphone|ipad|ipod|android|blackberry|mini|windows\sce|palm/i.test(navigator.userAgent.toLowerCase()));
  13. var WAIT_TO_LOAD_TIMEOUT = MOBILE_FLAG ? 0 : 60000;
  14. // ================= //
  15.  
  16. var gInstructionText;
  17.  
  18. var gCanvas;
  19. var gContext;
  20. var gTimer;
  21.  
  22. var gCanvasLeft = 0;
  23. var gCanvasTop = 0;
  24.  
  25. var gButtonPressed = false;
  26.  
  27. var gGameOver = 0;
  28. var gGameOverShield = false;
  29.  
  30. var gMainMusic;
  31. var gMainMusicLoaded = false;
  32. var gExplosionSound;
  33. var gExplosionSoundLoaded = false;
  34.  
  35. var gTankBodyImg;
  36. var gTankGunImg;
  37. var gGameOverImg;
  38. var gGameWinImg;
  39.  
  40. var gPlayer;
  41. var gPlayerMissiles = [];
  42.  
  43. var gParticles = [];
  44. var gSmoke = [];
  45. var gWalls = [];
  46.  
  47. var gPlayerSmokeGradient;
  48. var gPlayerLightSmokeGradient;
  49.  
  50. function OnWindowResize()
  51. {
  52.     if (gCanvas)
  53.     {
  54.         if (MOBILE_FLAG)
  55.         {
  56.             gCanvasLeft = 0;
  57.             gCanvasTop = 0;
  58.             gCanvas.style.left = gCanvasLeft+"px";
  59.             gCanvas.style.top = gCanvasTop+"px";
  60.             gInstructionText.style.display = "none";
  61.             window.scrollBy(0,50);
  62.         }
  63.         else
  64.         {
  65.             gCanvasLeft = window.innerWidth/2 - HALF_CANVAS_WIDTH;
  66.             gCanvasTop = window.innerHeight/2 - HALF_CANVAS_HEIGHT;
  67.             gCanvas.style.left = gCanvasLeft+"px";
  68.             gCanvas.style.top = gCanvasTop+"px";
  69.            
  70.             gInstructionText.style.top = (gCanvasTop - 35) + "px";
  71.             gInstructionText.style.left = gCanvasLeft + "px";
  72.         }
  73.     }
  74.     return true;
  75. }
  76.  
  77.  
  78. function OnMouseDown(e)
  79. {
  80.     if (e.preventDefault)
  81.         e.preventDefault();
  82.    
  83.     if (gGameOver>0 || gGameOverShield) return;
  84.    
  85.     if (MOBILE_FLAG)
  86.     {
  87.         gButtonPressed = true;
  88.     }
  89.     else
  90.     {
  91.         if (e.button == 0)
  92.         {
  93.             gButtonPressed = true;
  94.         }
  95.     }
  96.    
  97.     return;
  98. }
  99.  
  100.  
  101. function OnMouseUp(e)
  102. {
  103.     if (e.preventDefault)
  104.         e.preventDefault();
  105.        
  106.     var wasReleased = false;
  107.    
  108.     if (MOBILE_FLAG)
  109.     {
  110.         gButtonPressed = false;
  111.         wasReleased = true;
  112.     }
  113.     else
  114.     {
  115.         if (e.button == 0)
  116.         {
  117.             gButtonPressed = false;
  118.             wasReleased = true;
  119.         }
  120.     }
  121.    
  122.     if (wasReleased)
  123.     {
  124.         if (gGameOver===0)
  125.             gPlayer.fire();
  126.         else if (!gGameOverShield)
  127.             resetGame();
  128.     }
  129.    
  130.     return;
  131. }
  132.  
  133.  
  134. var gLastUpdateTime;
  135. var gFracOfSec;
  136. var gCurTime;
  137. function OnUpdate()
  138. {
  139.     var i,j,tmp,tmpX,tmpW;
  140.    
  141.     gCurTime = new Date().getTime();
  142.     gFracOfSec = (gCurTime - gLastUpdateTime)/1000;
  143.    
  144.     for (i=0; i<gParticles.length; ++i)
  145.     {
  146.         tmp = gParticles[i];
  147.         tmp.move();
  148.         if (tmp.expired)
  149.         {
  150.             gParticles.splice(i,1);
  151.             --i;
  152.         }
  153.         tmp = null;
  154.     }
  155.    
  156.     for (i=0; i<gSmoke.length; ++i)
  157.     {
  158.         tmp = gSmoke[i];
  159.         tmp.move();
  160.         if (tmp.expired)
  161.         {
  162.             gSmoke.splice(i,1);
  163.             --i;
  164.         }
  165.         tmp = null;
  166.     }
  167.    
  168.     if (gGameOver===0)
  169.         gPlayer.move();
  170.    
  171.     for (i=0; i<gPlayerMissiles.length; ++i)
  172.     {
  173.         tmp = gPlayerMissiles[i];
  174.         tmp.move();
  175.         if (tmp.expired)
  176.         {
  177.             gPlayerMissiles.splice(i,1);
  178.             --i;
  179.         }
  180.         tmp = null;
  181.     }
  182.    
  183.     // background
  184.     drawBackground();
  185.    
  186.     // walls
  187.     gContext.strokeStyle = "#000000";
  188.     for (i=0; i<gWalls.length; ++i)
  189.     {
  190.         tmp = gWalls[i];
  191.         tmp.draw();
  192.         if (tmp.expired)
  193.         {
  194.             gWalls.splice(i,1);
  195.             --i;
  196.         }
  197.         tmp = null;
  198.     }
  199.    
  200.     // ground
  201.     for (i=0; i<16; ++i)
  202.     {
  203.         if (i<8)
  204.             setFillStyle("rgb(0,"+Math.round(64+(255/16)*i)+",255)");
  205.         else
  206.             setFillStyle("rgb(0,"+Math.round(64+(255/16)*(16-i))+",255)");
  207.    
  208.         tmpX = i*32;
  209.        
  210.         while (tmpX+32-gPlayer.x+TANK_XOFFSET < 0)
  211.         {
  212.             tmpX += 32*16;
  213.         }
  214.        
  215.         gContext.fillRect
  216.         (
  217.             //tmpX-gPlayer.x,
  218.             tmpX+TANK_XOFFSET-gPlayer.x,
  219.             CANVAS_HEIGHT-13,
  220.             33,13
  221.         );
  222.         gContext.strokeRect
  223.         (
  224.             //tmpX-gPlayer.x,
  225.             tmpX+TANK_XOFFSET-gPlayer.x,
  226.             CANVAS_HEIGHT-13,
  227.             32,15
  228.         );
  229.     }
  230.    
  231.     // smoke
  232.     for (i=0; i<gSmoke.length; ++i)
  233.     {
  234.         gSmoke[i].draw();
  235.     }
  236.    
  237.     // particles
  238.     for (i=0; i<gParticles.length; ++i)
  239.     {
  240.         gParticles[i].draw();
  241.     }
  242.    
  243.     // player missiles
  244.     for (i=0; i<gPlayerMissiles.length; ++i)
  245.     {
  246.         gPlayerMissiles[i].draw();
  247.     }
  248.    
  249.     // player
  250.     gPlayer.draw();
  251.    
  252.     if (gWalls.length===0)
  253.         gGameOver = 1;
  254.    
  255.     if (gGameOver===1)
  256.     {
  257.         gContext.drawImage
  258.         (
  259.             gGameWinImg,
  260.             Math.round(CANVAS_WIDTH/2-203),
  261.             Math.round(CANVAS_HEIGHT/2-49)
  262.         )
  263.     }
  264.     else
  265.     if (gGameOver===2)
  266.     {
  267.         gContext.drawImage
  268.         (
  269.             gGameOverImg,
  270.             Math.round(CANVAS_WIDTH/2-203),
  271.             Math.round(CANVAS_HEIGHT/2-49)
  272.         )
  273.     }
  274.    
  275.     //---
  276.     gLastUpdateTime = gCurTime;
  277.     /*if ((gCurTime%2000) > 1990)
  278.     {
  279.         log("Smoke: "+gSmoke.length+"; Particles: "+gParticles.length+"; Missiles: "+gPlayerMissiles.length);
  280.     }*/
  281. };
  282.  
  283.  
  284. window.onload = function()
  285. {
  286.     var i,tmp,interval,loadWaitTime;
  287.    
  288.     if (!Modernizr.canvas)
  289.     {
  290.         document.body.innerHTML =
  291.             "Your browser does not support the HTML5 canvas element;<br>"+
  292.             "consider upgrading to the latest version.";
  293.         return;
  294.     }
  295.    
  296.     //---
  297.     // init globals
  298.     gCanvas = document.createElement("canvas");
  299.     gCanvas.onselectstart = function() { return false; };
  300.     gContext = gCanvas.getContext("2d");
  301.     gInstructionText = document.createElement("div");
  302.     //---
  303.    
  304.     //---
  305.     // style up the page
  306.     document.body.style.backgroundColor = "#000000";
  307.     gCanvas.style.position = "absolute";
  308.     gCanvas.width = CANVAS_WIDTH;
  309.     gCanvas.height = CANVAS_HEIGHT;
  310.     gCanvas.style.cursor = "none";
  311.    
  312.     gInstructionText.style.position = "absolute";
  313.     gInstructionText.style.color = "#ffffff";
  314.     gInstructionText.font = "normal 20px verdana";
  315.     gInstructionText.innerHTML = "Loading audio, may take up to a minute...";
  316.     gInstructionText.style.width = CANVAS_WIDTH+"px";
  317.     gInstructionText.style.height = "30px";
  318.    
  319.     OnWindowResize();
  320.     document.body.appendChild(gCanvas);
  321.     document.body.appendChild(gInstructionText);
  322.     //---
  323.    
  324.     //---
  325.     // register events
  326.     window.onresize = OnWindowResize;
  327.     if (MOBILE_FLAG)
  328.     {
  329.         document.body.addEventListener("touchstart",OnMouseDown,false);
  330.         document.body.addEventListener("touchend",OnMouseUp,false);
  331.     }
  332.     else
  333.     {
  334.         document.onmousedown = OnMouseDown;
  335.         document.onmouseup = OnMouseUp;
  336.     }
  337.     // ---
  338.    
  339.     // ---
  340.     // fix issues with events not firing??
  341.     if (!MOBILE_FLAG)
  342.     {
  343.         document.onclick = function(){return false};
  344.         document.ondblclick = function(){return false};
  345.         document.oncontextmenu = function(){return false;};
  346.         gCanvas.onclick = function(){return false};
  347.         gCanvas.ondblclick = function(){return false};
  348.         gCanvas.oncontextmenu = function(){return false;};
  349.         gCanvas.onmousedown = function(){return false;};
  350.     }
  351.     //---
  352.    
  353.     //---
  354.     // one-time setup
  355.     gPlayerSmokeGradient = gContext.createLinearGradient(0,CANVAS_HEIGHT-35,0,CANVAS_HEIGHT-65);
  356.     //gPlayerSmokeGradient.addColorStop(0,"rgb(34,34,34)");
  357.     //gPlayerSmokeGradient.addColorStop(1,"rgba(34,34,34,0.0)");
  358.     gPlayerSmokeGradient.addColorStop(0,"rgb(20,20,20)");
  359.     gPlayerSmokeGradient.addColorStop(1,"rgba(20,20,20,0.0)");
  360.    
  361.     gPlayerLightSmokeGradient = gContext.createLinearGradient(0,CANVAS_HEIGHT-35,0,CANVAS_HEIGHT-65);
  362.     gPlayerLightSmokeGradient.addColorStop(0,"rgb(85,85,85)");
  363.     gPlayerLightSmokeGradient.addColorStop(1,"rgba(85,85,85,0.0)");
  364.    
  365.     gContext.lineWidth = 2;
  366.    
  367.     gPlayer = new Tank();
  368.     makeLevel();
  369.     //---
  370.    
  371.     // ---
  372.     // load resources and begin
  373.     if (!MOBILE_FLAG && (!Modernizr.audio || !(Modernizr.audio.ogg || Modernizr.audio.mp3 || Modernizr.audio.wav)))
  374.     {
  375.         alert("HTML5 audio not properly supported. If you'd like to hear this game's audio, try using a newer and/or different browser!");
  376.     }
  377.     else if (!MOBILE_FLAG)
  378.     {
  379.         tryLoadSound("explosion2",window,"gExplosionSound",10000);
  380.         tryLoadSound("song",window,"gMainMusic",60000,[false,false,true]);
  381.     }
  382.    
  383.     loadImage("tankbody.png",window,"gTankBodyImg");
  384.     loadImage("tankgun.png",window,"gTankGunImg");
  385.     loadImage("game_over.png",window,"gGameOverImg");
  386.     loadImage("game_win.png",window,"gGameWinImg");
  387.    
  388.     var firefoxReload = true;
  389.     loadWaitTime = 0;
  390.     interval = window.setInterval
  391.     (
  392.         function()
  393.         {
  394.             loadWaitTime += 500;
  395.            
  396.             if (MOBILE_FLAG)
  397.             {
  398.                 if (gTankBodyImg && gTankGunImg && gGameOverImg && gGameWinImg)
  399.                 {
  400.                     window.clearInterval(interval);
  401.                     gLastUpdateTime = new Date().getTime();
  402.                     gTimer = setInterval(OnUpdate,1000/60);
  403.                 }
  404.             }
  405.             else
  406.             {
  407.                 if ((gMainMusicLoaded && gExplosionSoundLoaded && gTankBodyImg && gTankGunImg && gGameOverImg && gGameWinImg)
  408.                 ||  (loadWaitTime > WAIT_TO_LOAD_TIMEOUT))
  409.                 {
  410.                    
  411.                     window.clearInterval(interval);
  412.                     if (gExplosionSoundLoaded) gExplosionSound.volume = 0.5;
  413.                     gInstructionText.style.display = "none";
  414.                     gLastUpdateTime = new Date().getTime();
  415.                     gTimer = setInterval(OnUpdate,1000/60);
  416.                     if (gMainMusicLoaded) window.setTimeout(function(){gMainMusic.play();},3000);
  417.                 }
  418.             }
  419.         },
  420.         500
  421.     );
  422.     //---
  423. };
  424.  
  425.  
  426. // ================================= //
  427. // ============== GAME ============= //
  428. // ================================= //
  429.  
  430.  
  431. function Smoke(x,y,xv,yv,rad,color,duration)
  432. {
  433.     var ctx;
  434.    
  435.     this.expired = false;
  436.    
  437.     ctx = gContext;
  438.    
  439.     this.move = function()
  440.     {
  441.         x += xv * gFracOfSec;
  442.         y -= yv * gFracOfSec;
  443.         duration -= gFracOfSec;
  444.         if (duration < 0)
  445.             this.expired = true;
  446.     };
  447.    
  448.     this.draw = function()
  449.     {
  450.         setFillStyle(color);
  451.         ctx.beginPath();
  452.         ctx.arc(x+TANK_XOFFSET-gPlayer.x,y,rad,0,Math.PI*2,true);
  453.         ctx.closePath();
  454.         ctx.fill();
  455.     };
  456. }
  457.  
  458.  
  459. function Particle(x,y,xv,yv,size,colorChangeSpeed,duration,gravity)
  460. {
  461.     var ctx,g;
  462.    
  463.     this.expired = false;
  464.    
  465.     ctx = gContext;
  466.     g = 255;
  467.    
  468.     this.move = function()
  469.     {
  470.         x += xv * gFracOfSec;
  471.         y -= yv * gFracOfSec;
  472.         if (gravity) { yv -= 400 * gFracOfSec; }
  473.         if (g!==0)
  474.         {
  475.             g -= Math.round(colorChangeSpeed*gFracOfSec);
  476.             if (g < 0) g = 0;
  477.         }
  478.         duration -= gFracOfSec;
  479.         if (duration < 0)
  480.             this.expired = true;
  481.     };
  482.    
  483.     this.draw = function()
  484.     {
  485.         setFillStyle("rgb(255,"+g+",0)");
  486.         ctx.fillRect(x+TANK_XOFFSET-gPlayer.x,y,size,size);
  487.     };
  488. }
  489.  
  490.  
  491. function TankMissile(x,y,xv,yv)
  492. {
  493.     var i,ctx,createParticles,g,colorCycle,
  494.         rand,mag,nx,ny,randScale;
  495.    
  496.     this.x = x;
  497.     this.y = y;
  498.     this.xv = xv;
  499.     this.yv = yv;
  500.     this.expired = false;
  501.    
  502.     ctx = gContext;
  503.     createParticles = 0;
  504.    
  505.     g = 255;
  506.     colorCycle = 0;
  507.    
  508.     this.explode = function()
  509.     {
  510.         rand = Math.floor(Math.random()*100+200);
  511.         for (i=0; i<rand; ++i)
  512.         {
  513.             nx = Math.random()-0.5;
  514.             ny = Math.random()-0.5;
  515.             mag = Math.sqrt(nx*nx + ny*ny);
  516.             nx /= mag;
  517.             ny /= mag;
  518.             randScale = Math.random()*125+100;
  519.            
  520.             gParticles[gParticles.length] = new Particle
  521.             (
  522.                 this.x-1,
  523.                 this.y-1,
  524.                 nx*randScale,
  525.                 ny*randScale,
  526.                 Math.floor(Math.random()*2+1),
  527.                 350,
  528.                 Math.random()*0.5+0.5,
  529.                 false
  530.             );
  531.         }
  532.        
  533.         if (gExplosionSoundLoaded)
  534.         {
  535.             gExplosionSound.currentTime = 0.0;
  536.             gExplosionSound.play();
  537.         }
  538.     };
  539.    
  540.     this.move = function()
  541.     {
  542.         /*
  543.         // real gravity
  544.         t = (gCurTime/1000)-startTime;
  545.         this.x += this.xv * gFracOfSec;
  546.         this.y = startY+(-this.yv*t + 0.5*-15*(t*t));
  547.         this.yv += -15*t;
  548.         */
  549.        
  550.         this.x += this.xv * gFracOfSec;
  551.         this.y -= this.yv * gFracOfSec;
  552.         this.yv -= 400 * gFracOfSec;
  553.        
  554.         if (gCurTime-createParticles > 100)
  555.         {
  556.             mag = Math.sqrt(this.xv*this.xv + this.yv*this.yv);
  557.             nx = (this.xv / mag);
  558.             ny = (this.yv / mag);
  559.            
  560.             rand = Math.floor(Math.random()*12+5);
  561.             for (i=0; i<rand; ++i)
  562.             {
  563.                 randScale = mag-(Math.random()*100+100);
  564.                 gParticles[gParticles.length] = new Particle
  565.                 (
  566.                     this.x-1,
  567.                     this.y-1,
  568.                     nx*randScale+(Math.random()*100-50),
  569.                     ny*randScale+(Math.random()*100-50),
  570.                     Math.floor(Math.random()*2+1),
  571.                     350,
  572.                     Math.random()*0.5+0.5,
  573.                     true
  574.                 );
  575.             }
  576.             createParticles = gCurTime;
  577.         }
  578.        
  579.         if (colorCycle === 0)
  580.         {
  581.             g -= Math.round(1020 * gFracOfSec);
  582.             if (g < 0)
  583.             {
  584.                 g = 0;
  585.                 colorCycle = 1;
  586.             }
  587.         }
  588.         else
  589.         {
  590.             g += Math.round(1020 * gFracOfSec);
  591.             if (g > 255)
  592.             {
  593.                 g = 255;
  594.                 colorCycle = 0;
  595.             }
  596.         }
  597.        
  598.         // collision...
  599.        
  600.         for (i=0; i<gWalls.length; ++i)
  601.         {
  602.             if (gWalls[i].checkCollision( new rect(this.x-3,this.y-3,6,6), false ))
  603.             {
  604.                 this.expired = true;
  605.                 break;
  606.             }
  607.         }
  608.        
  609.         if (this.y > CANVAS_HEIGHT-13)
  610.         {
  611.             this.expired = true;
  612.         }
  613.        
  614.         if (this.expired) this.explode();
  615.     };
  616.    
  617.     this.draw = function()
  618.     {
  619.         setFillStyle("rgb(255,"+g+",0)");
  620.         ctx.beginPath();
  621.         ctx.arc(this.x+TANK_XOFFSET-gPlayer.x, this.y, 3, 0, Math.PI*2, true);
  622.         ctx.closePath();
  623.         ctx.fill();
  624.     };
  625. }
  626.  
  627.  
  628. function Tank()
  629. {
  630.     var i,ctx,fireDelay,createSmoke,createSparks,rand,nx,ny,mag,randScale,explodeGrad;
  631.    
  632.     this.x = 2*32;
  633.     this.y = 0;
  634.     this.v = 65;
  635.     this.rot = 0;
  636.     this.rotDir = 0;
  637.     this.health = 4;
  638.    
  639.     ctx = gContext;
  640.     fireDelay = 0;
  641.     createSmoke = 0;
  642.     createSparks = 0;
  643.    
  644.     this.fire = function()
  645.     {
  646.         if (gCurTime-fireDelay > 750)
  647.         {
  648.             gPlayerMissiles[gPlayerMissiles.length] = new TankMissile
  649.             (
  650.                 this.x+Math.abs(15*Math.cos(this.rot)),
  651.                 CANVAS_HEIGHT-29-Math.abs(15*Math.sin(this.rot)),
  652.                 Math.abs(400*Math.cos(this.rot))+this.v,
  653.                 Math.abs(400*Math.sin(this.rot))
  654.             );
  655.             fireDelay = gCurTime;
  656.         }
  657.     };
  658.    
  659.     this.move = function()
  660.     {
  661.         this.x += this.v * gFracOfSec;
  662.        
  663.         if (gButtonPressed)
  664.         {
  665.             if (this.rotDir===0)
  666.             {
  667.                 this.rot -= gFracOfSec * (Math.PI/2);
  668.                 if (this.rot < -Math.PI/2)
  669.                 {
  670.                     this.rot = -Math.PI/2;
  671.                     this.rotDir = 1;
  672.                 }
  673.             }
  674.             else
  675.             {
  676.                 this.rot += gFracOfSec * (Math.PI/2);
  677.                 if (this.rot > 0)
  678.                 {
  679.                     this.rot = 0;
  680.                     this.rotDir = 0;
  681.                 }
  682.             }
  683.         }
  684.         else if (this.rot!==0)
  685.         {
  686.             this.rotDir = 1;
  687.             this.rot += gFracOfSec * (Math.PI/2);
  688.             if (this.rot > 0)
  689.             {
  690.                 this.rot = 0;
  691.                 this.rotDir = 0;
  692.             }
  693.         }
  694.        
  695.         // smoke
  696.         if (this.health < 4)
  697.         {
  698.             if (gCurTime-createSmoke > 100)
  699.             {
  700.                 if (this.health===3)
  701.                     rand = Math.floor(Math.random()*2+2);
  702.                 else
  703.                     rand = Math.floor(Math.random()*4+3);
  704.                 for (i=0; i<rand; ++i)
  705.                 {
  706.                     //Smoke(x,y,xv,yv,rad,color,duration)
  707.                     gSmoke[gSmoke.length] = new Smoke
  708.                     (
  709.                         this.x+(Math.random()*20-16),
  710.                         CANVAS_HEIGHT-30,
  711.                         0,
  712.                         Math.random()*50+25,
  713.                         Math.floor(Math.random()*4+2),
  714.                         (this.health===3 ? gPlayerLightSmokeGradient : gPlayerSmokeGradient),
  715.                         Math.random()*0.5+0.5
  716.                     );
  717.                 }
  718.                 createSmoke = gCurTime;
  719.             }
  720.         }
  721.        
  722.         // sparks
  723.         if (this.health===1)
  724.         {
  725.             if (gCurTime-createSparks > 25)
  726.             {
  727.                 rand = Math.floor(Math.random()*4+3);
  728.                 for (i=0; i<rand; ++i)
  729.                 {
  730.                     gParticles[gParticles.length] = new Particle
  731.                     (
  732.                         this.x+(Math.random()*23-20),
  733.                         CANVAS_HEIGHT-30,
  734.                         Math.random()*25+12,
  735.                         Math.random()*50+25,
  736.                         Math.floor(Math.random()*2+1),
  737.                         1000,
  738.                         Math.random()*0.1+0.1,
  739.                         false
  740.                     );
  741.                 }
  742.                 createSparks = gCurTime;
  743.             }
  744.         }
  745.        
  746.         // collisions...
  747.         for (i=0; i<gWalls.length; ++i)
  748.         {
  749.             if (gWalls[i].checkCollision( new rect(this.x-16,CANVAS_HEIGHT-40,29,32), true ))
  750.             {
  751.                 --(this.health);
  752.                 //this.health = 0;
  753.                 if (this.health===0)
  754.                 {
  755.                     setGameOver(2);
  756.                 }
  757.             }
  758.         }
  759.        
  760.         if (this.health <= 0)
  761.         {
  762.             // explode!!!
  763.             for (i=0; i<300; ++i)
  764.             {
  765.                 nx = Math.random()-0.5;
  766.                 ny = Math.random()-0.5;
  767.                 mag = Math.sqrt(nx*nx + ny*ny);
  768.                 nx /= mag;
  769.                 ny /= mag;
  770.                 randScale = Math.random()*125+100;
  771.                
  772.                 gParticles[gParticles.length] = new Particle
  773.                 (
  774.                     this.x,
  775.                     CANVAS_HEIGHT-24,
  776.                     nx*randScale,
  777.                     ny*randScale,
  778.                     Math.floor(Math.random()*2+1),
  779.                     350,
  780.                     Math.random()*0.5+0.5,
  781.                     false
  782.                 );
  783.             }
  784.            
  785.             explodeGrad = gContext.createRadialGradient(TANK_XOFFSET,CANVAS_HEIGHT-24,16,TANK_XOFFSET,CANVAS_HEIGHT-24,75);
  786.             explodeGrad.addColorStop(0,"rgb(127,127,127)");
  787.             explodeGrad.addColorStop(1,"rgba(225,225,225,0.0)");
  788.            
  789.             for (i=0; i<200; ++i)
  790.             {
  791.                 nx = Math.random()-0.5;
  792.                 ny = Math.random()-0.5;
  793.                 mag = Math.sqrt(nx*nx + ny*ny);
  794.                 nx /= mag;
  795.                 ny /= mag;
  796.                 randScale = Math.random()*60+50;
  797.                
  798.                 //Smoke(x,y,xv,yv,rad,color,duration)
  799.                 gSmoke[gSmoke.length] = new Smoke
  800.                 (
  801.                     this.x,
  802.                     CANVAS_HEIGHT-24,
  803.                     nx*randScale,
  804.                     ny*randScale,
  805.                     Math.floor(Math.random()*4+2),
  806.                     explodeGrad,
  807.                     Math.random()*0.5+0.5
  808.                 );
  809.             }
  810.            
  811.             if (gExplosionSoundLoaded)
  812.             {
  813.                 gExplosionSound.currentTime = 0.0;
  814.                 gExplosionSound.play();
  815.             }
  816.         }
  817.     };
  818.    
  819.     this.draw = function()
  820.     {
  821.         if (this.health <= 0) return;
  822.        
  823.         ctx.save();
  824.             ctx.translate(TANK_XOFFSET,CANVAS_HEIGHT-30);
  825.             ctx.rotate(this.rot);
  826.             ctx.drawImage(gTankGunImg,-16,-10);
  827.         ctx.restore();
  828.    
  829.         ctx.save();
  830.             ctx.translate(TANK_XOFFSET-16,CANVAS_HEIGHT-40);
  831.             ctx.drawImage(gTankBodyImg,0,0);
  832.         ctx.restore();
  833.        
  834.         /*setFillStyle("rgba(255,255,255,0.5)");
  835.         ctx.fillRect
  836.         (
  837.             TANK_XOFFSET-16,
  838.             CANVAS_HEIGHT-40,
  839.             29,32
  840.         );*/
  841.     };
  842. }
  843.  
  844.  
  845. function Wall(x,w,h)
  846. {
  847.     var tmp,tmpFill,row,col,ctx,bricks,redCol,redRow,
  848.         nx,ny,mag,randScale,explodeGrad,exploding,thisWall;
  849.    
  850.     this.expired = false;
  851.    
  852.     x *= 32;
  853.     ctx = gContext;
  854.     if (w>2)
  855.         redCol = Math.floor(Math.random()*(w-1));
  856.     else
  857.         redCol = Math.floor(Math.random()*w);
  858.     redRow = Math.floor(Math.random()*(h-1))+1;
  859.    
  860.     exploding = false;
  861.    
  862.     thisWall = this;
  863.    
  864.     bricks = [];
  865.     for (row=0; row<h; ++row)
  866.     {
  867.         bricks[row] = [];
  868.         for (col=0; col<w; ++col)
  869.         {
  870.             bricks[row][col] =
  871.             {
  872.                 bot : (row===0 ? true : false),
  873.                 red : (row===redRow && col===redCol ? true : false),
  874.                 health : 100,
  875.                 bounds : new rect
  876.                 (
  877.                     x+(col*32),
  878.                     CANVAS_HEIGHT-13-((row+1)*32),
  879.                     32,
  880.                     32
  881.                 )
  882.             };
  883.         }
  884.     }
  885.    
  886.     function explode(tmp)
  887.     {
  888.         function bigExplosion(x,y)
  889.         {
  890.             for (i=0; i<150; ++i)
  891.             {
  892.                 nx = Math.random()-0.5;
  893.                 ny = Math.random()-0.5;
  894.                 mag = Math.sqrt(nx*nx + ny*ny);
  895.                 nx /= mag;
  896.                 ny /= mag;
  897.                 randScale = Math.random()*125+100;
  898.                
  899.                 gParticles[gParticles.length] = new Particle
  900.                 (
  901.                     x,y,
  902.                     nx*randScale,
  903.                     ny*randScale,
  904.                     Math.floor(Math.random()*2+1),
  905.                     350,
  906.                     Math.random()*0.5+0.5,
  907.                     false
  908.                 );
  909.             }
  910.            
  911.             for (i=0; i<100; ++i)
  912.             {
  913.                 nx = Math.random()-0.5;
  914.                 ny = Math.random()-0.5;
  915.                 mag = Math.sqrt(nx*nx + ny*ny);
  916.                 nx /= mag;
  917.                 ny /= mag;
  918.                 randScale = Math.random()*60+50;
  919.                
  920.                 //Smoke(x,y,xv,yv,rad,color,duration)
  921.                 gSmoke[gSmoke.length] = new Smoke
  922.                 (
  923.                     x,y,
  924.                     nx*randScale,
  925.                     ny*randScale,
  926.                     Math.floor(Math.random()*4+2),
  927.                     "#7f7f7f",
  928.                     Math.random()*0.5+0.5
  929.                 );
  930.             }
  931.         }
  932.        
  933.         // EXPLODE!!!!!
  934.         exploding = true;
  935.         bigExplosion(tmp.bounds.x+16,tmp.bounds.y+16);
  936.         window.setTimeout
  937.         (
  938.             function()
  939.             {
  940.                 bigExplosion(x,CANVAS_HEIGHT-13-((h/2)*32));
  941.             },
  942.             500
  943.         );
  944.        
  945.         window.setTimeout
  946.         (
  947.             function()
  948.             {
  949.                 //bigExplosion(x+((w/2)*32),CANVAS_HEIGHT-13-((h/2)*32));
  950.                 bigExplosion(x+(w*32),CANVAS_HEIGHT-13-((h/2)*32));
  951.                 thisWall.expired = true;
  952.             },
  953.             1000
  954.         );
  955.        
  956.         /*window.setTimeout
  957.         (
  958.             function()
  959.             {
  960.                 bigExplosion(x+(w*32),CANVAS_HEIGHT-13-((h/2)*32));
  961.                 thisWall.expired = true;
  962.             },
  963.             1500
  964.         );*/
  965.     }
  966.    
  967.     this.checkCollision = function(missile,playerHit)
  968.     {
  969.         if (exploding) return false;
  970.        
  971.         for (row=0; row<h; ++row)
  972.         {
  973.             for (col=0; col<w; ++col)
  974.             {
  975.                 tmp = bricks[row][col];
  976.                 if (tmp.health > 0 && tmp.bounds.isCollidingWith(missile))
  977.                 {
  978.                     if (row!==0)
  979.                         tmp.health -= 100;
  980.                    
  981.                     if (row===0 && playerHit)
  982.                     {  
  983.                         explode(tmp);
  984.                         return true;
  985.                     }
  986.                    
  987.                     if (tmp.red) explode(tmp);
  988.                    
  989.                     if (row != h-1) // top
  990.                     {
  991.                         tmp = bricks[row+1][col];
  992.                         if (!tmp.bot) tmp.health -= (tmp.red ? 34 : 34);
  993.                         if (tmp.red && tmp.health<=0) explode(tmp);
  994.                     }
  995.                     if (col != w-1) // right
  996.                     {
  997.                         tmp = bricks[row][col+1];
  998.                         if (!tmp.bot) tmp.health -= (tmp.red ? 34 : 34);
  999.                         if (tmp.red && tmp.health<=0) explode(tmp);
  1000.                     }
  1001.                     if (row != 0) // down
  1002.                     {
  1003.                         tmp = bricks[row-1][col];
  1004.                         if (!tmp.bot) tmp.health -= (tmp.red ? 34 : 34);
  1005.                         if (tmp.red && tmp.health<=0) explode(tmp);
  1006.                     }
  1007.                     if (col != 0) // left
  1008.                     {
  1009.                         tmp = bricks[row][col-1];
  1010.                         if (!tmp.bot) tmp.health -= (tmp.red ? 34 : 34);
  1011.                         if (tmp.red && tmp.health<=0) explode(tmp);
  1012.                     }
  1013.                    
  1014.                     if (row != h-1 && col!= w-1) // top-right
  1015.                     {
  1016.                         tmp = bricks[row+1][col+1];
  1017.                         if (!tmp.bot) tmp.health -= (tmp.red ? 17 : 17);
  1018.                         if (tmp.red && tmp.health<=0) explode(tmp);
  1019.                     }
  1020.                     if (row != 0 && col!= w-1) // down-right
  1021.                     {
  1022.                         tmp = bricks[row-1][col+1];
  1023.                         if (!tmp.bot) tmp.health -= (tmp.red ? 17 : 17);
  1024.                         if (tmp.red && tmp.health<=0) explode(tmp);
  1025.                     }
  1026.                     if (row != 0 && col!= 0) // down-left
  1027.                     {
  1028.                         tmp = bricks[row-1][col-1];
  1029.                         if (!tmp.bot) tmp.health -= (tmp.red ? 17 : 17);
  1030.                         if (tmp.red && tmp.health<=0) explode(tmp);
  1031.                     }
  1032.                     if (row != h-1 && col!= 0) // top-left
  1033.                     {
  1034.                         tmp = bricks[row+1][col-1];
  1035.                         if (!tmp.bot) tmp.health -= (tmp.red ? 17 : 17);
  1036.                         if (tmp.red && tmp.health<=0) explode(tmp);
  1037.                     }
  1038.                    
  1039.                     return true;
  1040.                 }
  1041.             }
  1042.         }
  1043.         return false;
  1044.     };
  1045.    
  1046.     this.draw = function()
  1047.     {
  1048.         if (x-gPlayer.x > CANVAS_WIDTH)
  1049.             return;
  1050.  
  1051.         if (gPlayer.x-(x+w*32) > CANVAS_WIDTH/4)
  1052.         {
  1053.             this.expired = true;
  1054.             return;
  1055.         }
  1056.        
  1057.         for (row=h-1; row>=0; --row)
  1058.         {
  1059.             for (col=0; col<w; ++col)
  1060.             {
  1061.                 if (exploding && row===0) continue;
  1062.                
  1063.                 tmp = bricks[row][col];
  1064.                 if (tmp.health > 0)
  1065.                 {
  1066.                     if (exploding)
  1067.                     {
  1068.                         setFillStyle("#111111");
  1069.                     }
  1070.                     else if (tmp.red)
  1071.                     {
  1072.                         setFillStyle("rgb("+Math.round((tmp.health/100)*255)+",0,0)");
  1073.                     }
  1074.                     else if (tmp.bot)
  1075.                     {
  1076.                         setFillStyle("#aaaaaa");
  1077.                     }
  1078.                     else
  1079.                     {
  1080.                         tmpFill = Math.round((tmp.health/100)*119);
  1081.                         setFillStyle("rgb("+tmpFill+","+tmpFill+","+tmpFill+")");
  1082.                     }
  1083.                     ctx.fillRect
  1084.                     (
  1085.                         tmp.bounds.x+TANK_XOFFSET-gPlayer.x,
  1086.                         tmp.bounds.y,
  1087.                         33,33
  1088.                     );
  1089.                     ctx.strokeRect
  1090.                     (
  1091.                         tmp.bounds.x+TANK_XOFFSET-gPlayer.x,
  1092.                         tmp.bounds.y,
  1093.                         32,32
  1094.                     );
  1095.                 }
  1096.             }
  1097.         }
  1098.        
  1099.     };
  1100. }
  1101.  
  1102.  
  1103. function drawBackground()
  1104. {
  1105.     var tmpX,tmpW,off,div;
  1106.    
  1107.     off = CANVAS_WIDTH * (5/4);
  1108.     div = 15;
  1109.    
  1110.     // background
  1111.     setFillStyle(CANVAS_BACKGROUND_COLOR);
  1112.     gContext.fillRect(0,0,CANVAS_WIDTH,CANVAS_HEIGHT);
  1113.    
  1114.     setFillStyle("#222222");
  1115.    
  1116.     tmpX = -gPlayer.x/div;
  1117.     tmpW = CANVAS_WIDTH/8-10;
  1118.     while (tmpX+tmpW < 0) tmpX += off;
  1119.     gContext.fillRect(tmpX,CANVAS_HEIGHT/2,tmpW,CANVAS_HEIGHT/2);
  1120.     // ---
  1121.     tmpX = CANVAS_WIDTH/8 - gPlayer.x/div;
  1122.     tmpW = CANVAS_WIDTH/4-10;
  1123.     while (tmpX+tmpW < 0) tmpX += off;
  1124.     gContext.fillRect(tmpX,CANVAS_HEIGHT/6,tmpW,CANVAS_HEIGHT*(5/6));
  1125.     // ---
  1126.     tmpX = CANVAS_WIDTH/8 + CANVAS_WIDTH/4 - gPlayer.x/div;
  1127.     tmpW = CANVAS_WIDTH/4-10;
  1128.     while (tmpX+tmpW < 0) tmpX += off;
  1129.     gContext.fillRect(tmpX,CANVAS_HEIGHT/3,tmpW,CANVAS_HEIGHT*(2/3));
  1130.     // ---
  1131.     tmpX = CANVAS_WIDTH/8 + CANVAS_WIDTH/4 + CANVAS_WIDTH/4 - gPlayer.x/div;
  1132.     tmpW = CANVAS_WIDTH*(3/8)-10;
  1133.     while (tmpX < 0) tmpX += off;
  1134.     gContext.fillRect(tmpX,CANVAS_HEIGHT*(2/3),tmpW,CANVAS_HEIGHT/3);
  1135.     if (tmpX+tmpW > off)
  1136.         gContext.fillRect(tmpX-off,CANVAS_HEIGHT*(2/3),tmpW,CANVAS_HEIGHT/3);
  1137.     // ---
  1138.     tmpX = CANVAS_WIDTH - gPlayer.x/div;
  1139.     tmpW = CANVAS_WIDTH/4-10;
  1140.     while (tmpX+tmpW < 0) tmpX += off;
  1141.     gContext.fillRect(tmpX,CANVAS_HEIGHT/3,tmpW,CANVAS_HEIGHT*(2/3));
  1142. }
  1143.  
  1144.  
  1145. function makeLevel()
  1146. {
  1147.     var i,x,w,h,lastX,finalX;
  1148.    
  1149.     gWalls.splice(0,gWalls.length);
  1150.     // ---
  1151.    
  1152.     finalX = (225*gPlayer.v)/32;
  1153.     lastX = 10;
  1154.     while (true)
  1155.     {
  1156.         x = lastX + Math.floor(Math.random()*4)+4;
  1157.         w = Math.floor(Math.random()*2)+2;
  1158.         //w = 3;
  1159.         h = Math.floor(Math.random()*6)+2;
  1160.        
  1161.         gWalls[gWalls.length] = new Wall(x,w,h);
  1162.        
  1163.         lastX = x+w;
  1164.         if (lastX > finalX)
  1165.             break;
  1166.     }
  1167.     //log("Number of walls: "+gWalls.length);
  1168. }
  1169.  
  1170.  
  1171. function setGameOver(result)
  1172. {
  1173.     gGameOver = result;
  1174.    
  1175.     if (result===1)
  1176.     {
  1177.     }
  1178.     else
  1179.     if (result===2)
  1180.     {
  1181.     }
  1182.    
  1183.     gGameOverShield = true;
  1184.     window.setTimeout(function(){gGameOverShield=false;},1000);
  1185. }
  1186.  
  1187.  
  1188. function resetGame()
  1189. {
  1190.     gGameOver = 0;
  1191.     gPlayer = new Tank();
  1192.     makeLevel();
  1193.     if (gMainMusicLoaded)
  1194.     {
  1195.         gMainMusic.pause();
  1196.         window.setTimeout
  1197.         (
  1198.             function()
  1199.             {
  1200.                 gMainMusic.currentTime = 0.0;
  1201.                 gMainMusic.play();
  1202.             },
  1203.             3000
  1204.         );
  1205.     }
  1206. }
  1207.  
  1208. var gFillStyle = "";
  1209. function setFillStyle(style)
  1210. {
  1211.     if (style!==gFillStyle)
  1212.     {
  1213.         gContext.fillStyle = gFillStyle = style;
  1214.     }
  1215. }
  1216.  
  1217.  
  1218. // ================================= //
  1219. // =========== UTILITIES =========== //
  1220. // ================================= //
  1221.  
  1222.  
  1223. function rect(x,y,width,height)
  1224. {
  1225.     this.x = x;
  1226.     this.y = y;
  1227.     this.w = width;
  1228.     this.h = height;
  1229.    
  1230.     this.isCollidingWith = function(r)
  1231.     {
  1232.         return !(r.x > this.x+this.w
  1233.               || r.x+r.w < this.x
  1234.               || r.y > this.y+this.h
  1235.               || r.y+r.h < this.y);
  1236.     };
  1237. }
  1238.  
  1239.  
  1240. function dist(x1,y1,x2,y2)
  1241. {
  1242.     return Math.sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1));
  1243. }
  1244.  
  1245.  
  1246. function loadImage(name,obj,prop)
  1247. {
  1248.     var tmpImg;
  1249.     obj[prop] = undefined;
  1250.     tmpImg = document.createElement("img");
  1251.     tmpImg.onload = function() { obj[prop] = tmpImg; };
  1252.     tmpImg.src = name;
  1253. }
  1254.  
  1255.  
  1256. function tryLoadSound(name,obj,prop,timeout,excludeExts)
  1257. {
  1258.     var tmpSound,interval,loadWaitTime,ext;
  1259.    
  1260.     if (!Modernizr.audio) return false;
  1261.    
  1262.     if (!excludeExts)
  1263.         excludeExts = [false,false,false];
  1264.    
  1265.     if (Modernizr.audio.ogg == "probably" && !excludeExts[0])
  1266.         ext = ".ogg";
  1267.     else
  1268.     if (Modernizr.audio.mp3 == "probably" && !excludeExts[1])
  1269.         ext = ".mp3";
  1270.     else
  1271.     if (Modernizr.audio.wav == "probably" && !excludeExts[2])
  1272.         ext = ".wav";
  1273.     else
  1274.     if (Modernizr.audio.ogg == "maybe" && !excludeExts[0])
  1275.         ext = ".ogg";
  1276.     else
  1277.     if (Modernizr.audio.mp3 == "maybe" && !excludeExts[1])
  1278.         ext = ".mp3";
  1279.     else
  1280.     if (Modernizr.audio.wav == "maybe" && !excludeExts[2])
  1281.         ext = ".wav";
  1282.     else
  1283.         return false;
  1284.    
  1285.    
  1286.     log("Trying to load "+name+ext);
  1287.    
  1288.     obj[prop] = undefined;
  1289.     loadWaitTime = 0;
  1290.     obj[prop] = document.createElement("audio");
  1291.     obj[prop].onerror = function() { log("had an error loading audio "+name+ext); };
  1292.     obj[prop].src = ""+name+ext;
  1293.     interval = window.setInterval
  1294.     (
  1295.         function()
  1296.         {
  1297.             if (obj[prop].readyState === 4)
  1298.             {
  1299.                 // Firefox hack... probably helps
  1300.                 if (navigator.userAgent.indexOf("Firefox") !== -1)
  1301.                 {
  1302.                     if ( prop==="gMainMusic" && obj[prop].buffered && obj[prop].buffered.length>0 && obj[prop].buffered.end(obj[prop].buffered.length-1)  < 10)
  1303.                     {
  1304.                         log("Failed, only buffered to "+obj[prop].buffered.end(obj[prop].buffered.length-1));
  1305.                         obj[prop].load();
  1306.                         return;
  1307.                     }
  1308.                 }
  1309.                
  1310.                 window.clearInterval(interval);
  1311.                 obj[prop+"Loaded"] = true;
  1312.                 if (obj[prop].buffered && obj[prop].buffered.length>0)
  1313.                     log("Successfully loaded "+name+ext+" : buffered to "+obj[prop].buffered.end(obj[prop].buffered.length-1));
  1314.                 return;
  1315.             }
  1316.            
  1317.             loadWaitTime += 500;
  1318.             if (loadWaitTime > timeout)
  1319.             {
  1320.                 window.clearInterval(interval);
  1321.                 if (ext == ".ogg")
  1322.                     excludeExts[0] = true;
  1323.                 if (ext == ".mp3")
  1324.                     excludeExts[1] = true;
  1325.                 if (ext == ".wav")
  1326.                     excludeExts[2] = true;
  1327.                 tryLoadSound(name,obj,prop,timeout,excludeExts);
  1328.                 log("Timed out "+name+ext+" @state "+obj[prop].readyState);
  1329.             }
  1330.         },
  1331.         500
  1332.     );
  1333. }
  1334.  
  1335.  
  1336. function log(msg)
  1337. {
  1338.     if (RELEASE) return;
  1339.     if (MOBILE_FLAG)
  1340.     {
  1341.         var tmp;
  1342.         if (!(tmp=document.getElementById("mobile_log")))
  1343.         {
  1344.             tmp = document.createElement("div");
  1345.             tmp.id = "mobile_log";
  1346.             tmp.style.position = "absolute";
  1347.             tmp.style.left = "0px";
  1348.             tmp.style.top = "0px";
  1349.             tmp.style.color = "#ffffff";
  1350.             document.body.appendChild(tmp);
  1351.         }
  1352.         tmp.innerHTML = msg;
  1353.     }
  1354.     else
  1355.         window.console && console.log && console.log(msg);
  1356. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement