Skip to content

Instantly share code, notes, and snippets.

@flox1an
Last active December 13, 2015 21:38
Show Gist options
  • Save flox1an/4978233 to your computer and use it in GitHub Desktop.
Save flox1an/4978233 to your computer and use it in GitHub Desktop.
Tank Duel - Scorched Earth clone for two players JS1K spring demo competition entry 2013-02
/**
Tank Duel
(c) Florian Maul (@fmaul)
Scorched Earth clone for two players.
Gameplay:
- Adjust your browser window size for terrain sizes
- Press F5 until you have a desired terrain and tank placement
- Use the left/right arrow keys to aim
- Use number keys 1 (slow speed) to 9 (high speed) to fire a shot
- Take turns until one tank gets hit
- F5 for a new game
Notes:
- Spring theme? Look at the green hills ;-)
- Built with custom variable replacement, uglifyjs and jscrush
- Works in Chrome, FF, Opera, IE9
**/
with(Math) {
b.style.margin =
bulletx= // set the bullet position to 0
explt=explx= // set the explosion timer to 0
n=0; // player n=0 for player 1 (red), n=4 for player 2 (green)
trail={}; // records the bullet trail, keys are x coord, values y coord
c.width = w = innerWidth;
c.height = h = innerHeight-5; // -5 needed to disable scrollbars
FindTerrain=function (l) {
i = a.getImageData(l, 0, 1, h);
for(y = 1; !i.data[y+=4];); // increase y as long as the green channel (byte 1) == 0
return y/4;
};
// fill background with blue
a.fillStyle="#003";
a.fillRect(0,0,w,h);
// Create a green gradient for the landscape
a.fillStyle=O=a.createLinearGradient(0,0,0,h);
O.addColorStop(0.5, "#490");
O.addColorStop(1, "#040");
// Generate random mountain landscape
(GenerateTerrain = function(l, terrainl, u, terrainu, diff) {
if (l + 1 < u) {
m = l+u >> 1; // get middle of the two bounds
// add the bounds, devide by 2 and floor to int
terrainm = abs((terrainl + terrainu) / 2 + (random() * diff) - diff / 2);
GenerateTerrain(l, terrainl, m, terrainm, diff / 2); // recurse into the left half
GenerateTerrain(m, terrainm, u, terrainu, diff / 2); // recurse into the right half
}
a.fillRect(l,h - terrainl,1,terrainl); // paint the land directly here, no additional for loop needed.
})(0, h/3, w, h/3, h);
// Initialize the player information
player=[
x=30+random()*w/3|0, // player 1 random x-position on the left
// needs to be at least 30 from the edge, or explosion code will fail
FindTerrain(x), // player 1 y position depending on the terrain
1, // player 1 angle of the turret
"#f00", // player 1 color red
x=w-30-random()*w/3|0, // player 2 random x-position on the right
// needs to be at least 30 from the edge, or explosion code will fail
FindTerrain(x), // player 2 y position depending on the terrain
-1, // player 2 angle of the turret
"#0f0" // player 2 color light green
// note: the player color are in channel R and G, whereas the background is on
// the B channel, which makes screen updates easier.
];
DrawTank = function(x,y,d,f) {
// tank hit detection
if (abs(x-explx) < 30 && abs(y-exply) < 30) {
// announce the looser, announcing the winner would cost to much code
a.fillStyle=f;
a.fillText("Game Over", x, y-50);
explt%=30; // reset explosion timer, so it never reaches 30 and terminates
}
// cut whole around tank with blue sky color
a.beginPath();
a.fillStyle="#003";
a.arc(x, y, 30, 3, 0);
a.fill();
// draw the tank with the players color
a.beginPath();
a.strokeStyle = a.fillStyle = f;
a.arc(x, y, 10, 3, 0);
a.fill();
a.moveTo(x,y);
a.lineTo(x + sin(d) * 20, y - cos(d) * 20);
a.stroke();
};
// Game loop and event handling in one function
setInterval(onkeydown=function(e) {
// if a key was pressed ----------------------------
if (e) {
// handle key events
// 48 is ascii of '0' -> keys '1' to '9' map to S=1 to 9
if (x = e.which % 48 < 10) { // key 1-9 -> fire shot
bulletx = player[n] + 4 * ( // calculate start position of bullet
// inlining the speed assignment saved 1 byte ;-)
bulletsx = sin(player[n+2]) * (4+x) // set bullet speed in x direction
);
bullety = player[n+1] + 4 * (
bulletsy = -cos(player[n+2]) * (4+x) // set bullet speed in y direction
);
// Reset the bullet trail currently on the screen
// this is duplicated below, but compresses best if duplicated
i = a.getImageData(0, 0, 1, 1);
for(x in trail) a.putImageData(i, x|0, trail[x]); // use x coord as integer to fix draw issue in IE/Opera
trail = {};
// advance to next player, alternate between 0 and 4 (XOR)
n^=4;
}
else player[n+2] += (e.which % 37 - 1) * 0.02; // for keyCode 37, reduce angle by 0.02
// for keyCode 39, increase angle by 0.02
}
// draw tanks -----------------------------------
DrawTank.apply(a, player); // player 1 data, array elements 0-3
DrawTank.apply(a, player.slice(4)); // player 2 data, array elements 4-7
// draw projectile -----------------------------------
if (bulletx) {
bulletx += bulletsx; // bullet position x,y changed by bullet speed bulletsx, bulletsy
bullety += bulletsy;
bulletsy+=0.06; // accelerate towards the ground, aka gravity
i = a.getImageData(bulletx, bullety, 1, 1); // read the pixel at the bullets position.
if (i.data[0]>60 || i.data[1]>60) { // hit if we hit anything >mid red or >mid green, blue channel is ignored
explx = bulletx; // start the explosion at the bullets position;
exply = bullety;
explt = // start the explostion at time 0
bulletx = 0; // stop the bullet flight;
}
else {
i.data[0] = i.data[1] = 200; // draw the bullet
// only uses red and green channel, i.e. blue background is not effected
a.putImageData(i, bulletx, trail[bulletx]=bullety|0); // draw and assign the y coordinate to the trail history
// save coords as integer to fix draw issue in IE/Opera
// but use float index because there can be multiple entries per x-coord
}
}
// draw explosion -----------------------------------
if (explx) {
// Reset the bullet trail currently on the screen
// this is duplicated above, but compresses best if duplicated
i = a.getImageData(0, 0, 1, 1);
for(x in trail) a.putImageData(i, x|0, trail[x]); // use x coord as integer to fix draw issue in IE/Opera
trail = {};
a.beginPath();
a.arc(explx, exply, explt, 0, 6); // 6 = approx. Math.PI*2 ;-)
a.fillStyle = explt++ < 30 ? // if explosion timer < 30, meanwhile advance explosion timer
"#ff0" : // draw with yellow
(explx = 0, "#003"); // else end explosion and draw with bg color
a.fill();
}
}, 10); // end of game loop, every 10ms
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment