This app is all about the javascript. You need to activate it by adding this site to the trusted sites in your browser or NoScript plugin. Thanks.
Here you can see the source code. Feel free to play with it and hit the Run button to restart.
Please ignore the <p>s and </p>s. It's an artifact of using tumblr to host this.
// Music by Rob Beschizza available at http://www.boingboing.net/2011/03/28/game-deaths-mp3.html String COPYRIGHT = 'Cave Hunter version 0,3 Copyright (C) 2011 by Bill Bereza'; SegmentFont font; Game game; var songs = 1; var[] sounds = new var[songs]; soundManager.onready(function(){ for(int i=0; i <= songs; i++) { sounds[i] = soundManager.createSound({ id: 'track'+i, url: 'http://www.concentric.net/~bereza/scripts/mp3/cavehunter'+i+'.mp3', autoPlay: false, autoLoad: false }); } }); function loopSound(i) { sounds[i].play({ onfinish: function() { loopSound(int(i+1) % songs); } }); } void setup() { size(); frameRate(30); font = new SegmentFont(); game = new CaveHunter(); reset(); background(255); loopSound(0); } void draw() { background(255); game.draw(); if(game.over()) { drawInstructions('CLICK MOUSE TO PLAY AGAIN'); drawFlasher('GAME OVER'); } else if(focused) { game.update(); } else if(!focused) { drawPauseScreen(); } colorMode(HSB); drawScore(); } void mouseClicked() { if(game.over()) { reset(); } } void keyPressed() { game.keyPressed(); } void keyReleased() { game.keyReleased(); } void reset() { game.reset(); } class Ship extends Sprite { int gun; int x, y; int newCount; int upDownMove; int leftRightMove; boolean crashed; int length = 40; Ship() { super(); reset(); } void reset() { gun = 0; x = int(floor(width/4)); y = int(floor(height/2)); newCount = 30; upDownMove = 0; leftRightMove = 0; crashed = false; } int upDownMotion() { return upDownMove; } int leftRightMotion() { return leftRightMove; } void setMotion(int leftRight, int upDown) { if(!crashed) { upDownMove = upDown; leftRightMove = leftRight; } } void update() { x += leftRightMove; if(x < length) { x = length; } if(x > width) { x = width; } y += upDownMove; if(y < 0) { y = 0; } if(y > height) { y = height; } } void crash() { newCount = 30; crashed = true; } void draw() { colorMode(RGB); stroke(0,0,255); strokeWeight(2); if(crashed) { if(newCount > 0) { newCount = newCount - 1; if(newCount % 2) { fill(255,127,0); ellipse(x, y, 40, 40); } } else { reset(); } } else { // not crashed if(newCount > 0) { newCount = newCount - 1; if(newCount % 2) { fill(0, 255, 0); } else { fill(255,255,255,1); } } else { fill(0,255,0); } if(upDownMotion() == 0) { triangle(x, y, x-40, y+20, x-40, y-20); } else if(upDownMotion() < 0) { triangle(x, y, x-45, y+15, x-35, y-15); } else if(upDownMotion() > 0) { triangle(x, y, x-35, y+15, x-45, y-15); } if(leftRightMotion() > 0) { fill(255,127,0); triangle(x-60, y, x-40, y+10, x-40, y-10); } else if(leftRightMotion() < 0) { fill(255, 127, 0); triangle(x, y, x-20, y+10, x+5, y+15); triangle(x, y, x-20, y-10, x+5, y-15); } fill(255,0,0); ellipse(x-15, y, 10, 10); } } boolean collisionWith(int ox, oy) { return (ox >= x-40 && ox <= x && oy >= y-20 && oy <= y+20); } boolean isCrashed() { return crashed; } boolean finished() { return false; } int xLeft() { return x-40; } int yTop() { return y-20; } int xRight() { return x; } int yBottom() { return y+20; } } class Dancer extends Bullet { int dist = 10; int count = 0; Dancer(int ix, int iy) { super(color(0,255,0), ix, iy, -8, 0); radius = dist * 2; ay = random(dist) - random(dist); } void update() { if((++count % dist) == 0) { ay = random(dist) - random(dist); } super.update(); } int kind() { return 2; } } class Bobber extends Bullet { int dist = 10; int count = 0; Bobber(int ix, int iy) { super(color(0,0,255), ix, iy, -8, 0); radius = dist * 2; ay = random(dist) - random(dist); ax = -8 - random(dist); } void update() { if((++count % dist) == 0) { ay = random(dist) - random(dist); ax = -8 - random(dist); } super.update(); } int kind() { return 3; } } class Bullet extends Sprite { color c; int ax, ay; int x, y; boolean crashed; int crashCount; int radius; Bullet(color ci, int ix, int iy, int vx, int vy) { c = ci; x = ix; y = iy; ax = vx; ay = vy; crashed = false; crashCount = 0; radius = 4; } int kind() { return 666; } void draw() { colorMode(RGB); stroke(c); strokeWeight(radius); point(x, y); if(crashed && (crashCount-- % 2 == 0)) { stroke(255,0,0); strokeWeight(radius * 2); point(x, y); } } void update() { x += ax; y += ay; if(x > width || x < 0 || y > height || y < 0) { crashed = true; crashCount = 0; } } void setMotion(int ix, int iy) { ax = ix; ay = iy; } void crash() { crashed = true; crashCount = 10; } boolean isCrashed() { return crashed; } boolean finished() { return (crashed && crashCount <= 0); } int xLeft() { return (x - (radius/2)); } int xRight() { return (x + (radius/2)); } int yTop() { return (y - (radius/2)); } int yBottom() { return (y + (radius/2)); } } class Sprite { Sprite() { } int kind() { return 0; } void setMotion(int ix, int iy) { } void draw() { } void update() { } void crash() { } boolean isCrashed() { return false; } boolean finished() { } int xLeft() { } int yTop() { } int xRight() { } int yBottom() { } boolean overlaps(Sprite o) { return (xLeft() < o.xRight() && xRight() > o.xLeft() && yTop() < o.yBottom() && yBottom() > o.yTop()); } } class CaveHunter extends Game { Landscape landscape; Landscape ceiling; Ship ship; ArrayList sprites; int shipSpeed = 5; int scrollSpeed = 8; int ships; int points; CaveHunter() { reset(); } void reset() { sprites = new ArrayList(); landscape = new Landscape(width, Landscape.FLOOR); ceiling = new Landscape(width, Landscape.CEILING); ship = new Ship(); ships = 5; sprites.add(ship); points = 0; } String name() { return 'Cave Hunter'; } String instructions() { return 'USE THE ARROW KEYS TO MOVE YOUR SHIP AROUND.\nPRESS CONTROL KEY TO FIRE YOUR CANNON.\nPICK UP THE POWERUPS TO ENHANCE YOUR FIREPOWER.'; } void keyPressed() { if(key == CODED) { if(keyCode == UP) { ship.setMotion(0, -shipSpeed); } else if(keyCode == DOWN) { ship.setMotion(0, shipSpeed); } else if(keyCode == LEFT) { ship.setMotion(-shipSpeed, 0); } else if(keyCode == RIGHT) { ship.setMotion(shipSpeed, 0); } else if(keyCode == CONTROL) { sprites.add(new Bullet(color(0,0,255), ship.x + 4, ship.y, 20, 0)); } } } void keyReleased() { ship.setMotion(0,0); } int lives() { return ships; } boolean over() { return lives() == 0; } void update() { landscape.scroll(Landscape.RIGHT, scrollSpeed); ceiling.scroll(Landscape.RIGHT, scrollSpeed); if(random(100) >= 97) { sprites.add(new Dancer(width-1, height/2)); } else if(random(100) >= 98) { Sprite d = new Bobber(width-1, height/2); sprites.add(d); } for(int i=sprites.size() - 1; i >= 0; i--) { Sprite sprite = (Sprite) sprites.get(i); if(sprite.finished()) { sprites.remove(i); } else { sprite.update(); } } checkCollisions(); } void draw() { landscape.draw(); ceiling.draw(); for(int i=0; i < sprites.size(); i++) { Sprite sprite = (Sprite) sprites.get(i); sprite.draw(); } } int score() { return points; } void checkCollisions() { for(int i=0; i < sprites.size(); i++) { Sprite a = (Sprite) sprites.get(i); if(!a.isCrashed()) { for(int j=i+1; j < sprites.size(); j++) { Sprite b = (Sprite) sprites.get(j); if(!b.isCrashed() && a.overlaps(b)) { a.crash(); b.crash(); if(a == ship || b == ship) { ships--; } else if(a.kind() == 666 || b.kind() == 666) { points-=1000; } } } } if(!a.isCrashed()) { if(a.yBottom() > landscape.verticalAt(a.xRight()) || a.yTop() < ceiling.verticalAt(a.xRight())) { a.setMotion(-scrollSpeed, 0); a.crash(); if(a == ship) { ships--; } } } } points++; if((points % 1000) == 0) { ships++; } } } float lastMillis = 0; void drawScore() { float nowMillis = millis(); float fps = 1000/(nowMillis - lastMillis); lastMillis = nowMillis; float textScale = 4; strokeWeight(0.75); colorMode(RGB); stroke(0, random(55) + 200, 0); font.text("SCORE> " + game.score() + " LIVES> " + game.lives() + " FPS> " + floor(fps), 8, (font.charHeight*textScale + 4), textScale); } float titleScale[] = { 2, 1.8, 1.6, 1.4, 1.2, 1.0, 1.2, 1.4, 1.6, 1.8, 2 }; void drawFlasher(String title) { float titleSize=16; colorMode(HSB, title.length(), 255, 255); strokeWeight(0.25); for(int i=0; i < title.length(); i++) { stroke(i, 255, random(55) + 200); font.character(title.charAt(i), (width - (title.length()*font.charWidth*titleSize)) / 2.0 + i*font.charWidth*titleSize, height/2.5, titleSize*sin(titleScale[i])); titleScale[i] += 0.2; } colorMode(RGB,255); } void drawInstructions(String instr) { float instSize = 5; stroke(255,0,0); strokeWeight(0.5 + random(0.25)); font.text(instr, (width - (instr.length()*font.charWidth*instSize))/2.0, height/2, instSize); } void drawCopyright(String copyr) { float copySize = 3; String instructions = "\n\n" + game.instructions(); stroke(0,255,0); strokeWeight(0.25 + random(0.25)); font.text(copyr + instructions, (width - (copyr.length()*font.charWidth*copySize))/2.0, height - height/2.5, copySize); } void drawPauseScreen() { String title = game.name(); String instr = 'CLICK IN THE WINDOW TO PLAY'; drawInstructions(instr); drawFlasher(title); drawCopyright(COPYRIGHT); } class Game { boolean over() { return false; } void update() { } void draw() { } void reset() { } void keyPressed() { } void keyReleased() { } int score() { return 0; } int level() { return 0; } int lives() { return 0; } String instructions() { return ""; } String name() { return "Game"; } } class SegmentFont { int charWidth = 4; int charHeight = 6; HashMap chars; SegmentFont() { chars = new HashMap(); // char pattern // sixteen segment display // _ _ // |\|/| // - - // |/|\| // - - // // 0xF000 = X // 0x0F00 = + // 0x00F0 = top half of O // 0x000F = bottom half of O chars.put("0", 0x50FF); chars.put("1", 0x0011); chars.put("2", 0x057E); chars.put("3", 0x0577); chars.put("4", 0x0591); chars.put("5", 0x21E6); chars.put("6", 0x05EF); chars.put("7", 0x0071); chars.put("8", 0x05FF); chars.put("9", 0x05F1); chars.put("A", 0x05F9); chars.put("B", 0x0E77); chars.put("C", 0x00EE); chars.put("D", 0x0A77); chars.put("E", 0x05EE); chars.put("F", 0x05E8); chars.put("G", 0x04EF); chars.put("H", 0x0599); chars.put("I", 0x0A66); chars.put("J", 0x001F); chars.put("K", 0x6188); chars.put("L", 0x008E); chars.put("M", 0xC099); chars.put("N", 0xA099); chars.put("O", 0x00FF); chars.put("P", 0x05F8); chars.put("Q", 0x20FF); chars.put("R", 0x25F8); chars.put("S", 0x05E7); chars.put("T", 0x0A60); chars.put("U", 0x009F); chars.put("V", 0x5088); chars.put("W", 0x3099); chars.put("X", 0xF000); chars.put("Y", 0xC200); chars.put("Z", 0x5066); chars.put("a", 0x030E); chars.put("b", 0x038C); chars.put("c", 0x010C); chars.put("d", 0x0B0C); chars.put("e", 0x110C); chars.put("f", 0x4600); chars.put("g", 0x2403); chars.put("h", 0x0388); chars.put("i", 0x0200); chars.put("j", 0x0204); chars.put("k", 0x2E00); chars.put("l", 0x0202); chars.put("m", 0x0709); chars.put("n", 0x2201); chars.put("o", 0x030C); chars.put("p", 0x1108); chars.put("q", 0x1308); chars.put("r", 0x0600); chars.put("s", 0x2402); chars.put("t", 0x0700); chars.put("u", 0x020C); chars.put("v", 0x1008); chars.put("w", 0x3009); chars.put("x", 0x3500); chars.put("y", 0x9800); chars.put("z", 0x1104); chars.put(" ", 0x0000); chars.put("-", 0x0500); chars.put("+", 0x0F00); chars.put("_", 0x0006); chars.put("/", 0x5000); chars.put("\\",0xA000); chars.put("*", 0xFF00); chars.put("%", 0x5FC3); chars.put("(", 0x0A22); chars.put(")", 0x0A44); chars.put("[", 0x00EE); chars.put("]", 0x0077); chars.put("<", 0x6000); chars.put(">", 0x9000); chars.put(".", 0x130C); chars.put(",", 0x1000); chars.put("\"",0x4000); chars.put("?", 0x0670); chars.put("!", 0x0800); chars.put("$", 0x0FE7); chars.put("`", 0x8000); chars.put("=", 0x0560); chars.put("{", 0x0B22); chars.put("}", 0x0E44); chars.put("|", 0x0A00); chars.put("'", 0x0090); chars.put(":", 0x0280); chars.put(";", 0x1800); chars.put("&", 0x2DCF); chars.put("#", 0x0A99); chars.put("@", 0x0DFE); chars.put("~", 0x0518); chars.put("^", 0x00F0); } void character(char c, float x, float y, float scalez) { int map; map = chars.get(c); if(map != null) { pushMatrix(); translate(x,y); scale(scalez); if(map & 0xF000) { if(map & 0x8000) { line(0,-4,1,-2); } if(map & 0x4000) { line(2,-4,1,-2); } if(map & 0x2000) { line(2,-0,1,-2); } if(map & 0x1000) { line(0,-0,1,-2); } } if(map & 0xF00) { if(map & 0x800) { line(1,-4,1,-2); } if(map & 0x400) { line(2,-2,1,-2); } if(map & 0x200) { line(1,-0,1,-2); } if(map & 0x100) { line(0,-2,1,-2); } } if(map & 0xF0) { if(map & 0x80) { line(0,-2,0,-4); } if(map & 0x40) { line(0,-4,1,-4); } if(map & 0x20) { line(1,-4,2,-4); } if(map & 0x10) { line(2,-4,2,-2); } } if(map & 0xF) { if(map & 0x8) { line(0,-0,0,-2); } if(map & 0x4) { line(0,-0,1,-0); } if(map & 0x2) { line(1,-0,2,-0); } if(map & 0x1) { line(2,-0,2,-2); } } popMatrix(); } } void text(String str, float x, float y, float scalez) { float ix = x; for(int i=0; i < str.length(); i++) { if(str.charAt(i) == "\n") { x = ix; y += scalez * charHeight; } else { character(str.charAt(i), x, y, scalez); x += (charWidth * scalez); } } } } class Landscape { static int FLOOR = 1; static int CEILING = 3; static float STEP = 0.005; static float RIDGE_STEP = 0.001; static float SCALE; static int LEFT = -1; static int RIGHT = 1; static float WSCALE = 1.0; int[] points; int position = 0; int index = 0; float ridges = 0; int orientation; Landscape(int length, int ori) { length = floor(length / WSCALE); orientation = ori; if(orientation == Landscape.CEILING) { position += length; } SCALE = height * .75; points = new int[length]; for(int i=0; i < length; i++) { points[i] = int(floor(noise(position * STEP) * SCALE * abs(sin(position * RIDGE_STEP)))); position++; } } void scroll(int direction, int amount) { for(int i=0; i < amount; i++) { if(direction == LEFT) { index--; if(index < 0) { index = points.length - 1; } position--; points[index] = int(floor(noise((position - points.length) * STEP) * SCALE * abs(sin((position - points.length) * RIDGE_STEP)))); } else { position++; points[index] = int(floor(noise(position * STEP) * SCALE * abs(sin(position * RIDGE_STEP)))); index = (index + 1) % points.length; } } } int verticalAt(int x) { int p; x = floor(x / WSCALE); if(x < (points.length - index)) { p = points[x+index]; } else { p = points[x - (points.length - index)]; } if(orientation == Landscape.FLOOR) { return height - p; } else { return p; } } void draw() { pushMatrix(); scale(WSCALE, 1.0); stroke(0); //strokeWeight(WSCALE); for(int i=index; i < points.length; i++) { if(orientation == Landscape.FLOOR) { line(i-index, height, i - index, height - points[i]); } else { line(i-index, 0, i - index, points[i]); } } for(int i=0; i < index; i++) { if(orientation == Landscape.FLOOR) { line((points.length - index) + i, height, (points.length - index) + i, height - points[i]); } else { line((points.length - index) + i, 0, (points.length - index) + i, points[i]); } } popMatrix(); } }