conf.js |
var conf = { | |||||
assets: "nsfw" | |||||
}; |
</head> | </head> | ||||
<body> | <body> | ||||
<canvas id="canvas"></canvas> | <canvas id="canvas"></canvas> | ||||
<script src="conf.js"></script> | |||||
<script src="js/util.js"></script> | <script src="js/util.js"></script> | ||||
<script src="js/vec2.js"></script> | <script src="js/vec2.js"></script> | ||||
<script src="js/assets.js"></script> | <script src="js/assets.js"></script> |
var assetsPath = "assets/"+conf.assets; | |||||
var assets = { | var assets = { | ||||
imgs: { | |||||
player: new ImageSource("player"), | |||||
background: new ImageSource("background", null, ".jpg"), | |||||
wall: new ImageSource("wall") | |||||
}, | |||||
soundtracks: { | |||||
soundtrack1: new AudioSource("soundtracks/track1"), | |||||
soundtrack2: new AudioSource("soundtracks/track2"), | |||||
soundtrack3: new AudioSource("soundtracks/track3") | |||||
} | |||||
}; | }; | ||||
/* | |||||
* Audio source. | |||||
*/ | |||||
function AudioSource(src, ext) { | |||||
makeEventListener(this); | |||||
var ext = ext || ".mp3"; | |||||
this.audio = document.createElement("audio"); | |||||
this.ready = false; | |||||
this.audio.addEventListener("canplaythrough", function() { | |||||
this.ready = true; | |||||
this.emit("load"); | |||||
}.bind(this)); | |||||
this.audio.addEventListener("ended", function() { | |||||
this.emit("ended"); | |||||
}.bind(this)); | |||||
this.audio.src = assetsPath+"/"+src+ext; | |||||
} | |||||
AudioSource.prototype.play = function() { | |||||
this.audio.currentTime = 0; | |||||
this.audio.play(); | |||||
} | |||||
AudioSource.prototype.pause = function() { | |||||
this.audio.pause(); | |||||
} | |||||
/* | /* | ||||
* Image source. | * Image source. | ||||
* If animation, the images must be stacked on top of each other. | |||||
*/ | */ | ||||
function ImageSource(src, frameh) { | |||||
var ext = ".png"; | |||||
function ImageSource(src, frameh, ext) { | |||||
var ext = ext || ".png"; | |||||
this.img = document.createElement("img"); | this.img = document.createElement("img"); | ||||
this.ready = false; | this.ready = false; | ||||
this.frameh = frameh == null ? -1 : frameh; | this.frameh = frameh == null ? -1 : frameh; | ||||
this.steps = 1; | this.steps = 1; | ||||
console.log("image", assetsPath, assetsPath+"/images/"+src+ext); | |||||
this.img.onload = function() { | this.img.onload = function() { | ||||
console.log(this); | |||||
this.ready = true; | this.ready = true; | ||||
this.width = this.img.width; | this.width = this.img.width; | ||||
this.height = this.img.height; | this.height = this.img.height; | ||||
"Height isn't evenly divisible by frame height. "+ | "Height isn't evenly divisible by frame height. "+ | ||||
"Height: "+this.height+", frame height: "+this.frameh); | "Height: "+this.height+", frame height: "+this.frameh); | ||||
} | } | ||||
console.log(this); | |||||
}.bind(this); | }.bind(this); | ||||
this.img.src = "assets/"+src+ext; | |||||
this.img.src = assetsPath+"/images/"+src+ext; | |||||
} | } | ||||
ImageSource.prototype.draw = function(ctx, step) { | ImageSource.prototype.draw = function(ctx, step) { | ||||
if (!step) step = 0; | if (!step) step = 0; | ||||
if (!this.ready) | |||||
return; | |||||
ctx.drawImage(this.img, | ctx.drawImage(this.img, | ||||
0, this.frameh * step, | 0, this.frameh * step, |
/* | |||||
* Background | |||||
*/ | |||||
function Background(game) { | |||||
makeEnt(this, game, 100); | |||||
this.img = assets.imgs.background; | |||||
this.img1x = 0; | |||||
this.img1y = -300; | |||||
this.img2x = 0; | |||||
this.img2y = -100; | |||||
this.collude = false; | |||||
this.moves = true; | |||||
this.started = false; | |||||
this.pos.set({ x: -400, y: 0 }); | |||||
} | |||||
Background.prototype.update = function() { | |||||
if (!this.img.ready) | |||||
return; | |||||
if (this.img.ready && this.img2x === 0) | |||||
this.img2x = this.img1x + this.img.width * 2; | |||||
if (this.game.started) | |||||
this.force.add({ x: 0.09, y: 0 }); | |||||
if (this.img1x + this.img.width * 2 + this.pos.x < this.game.camera.x) { | |||||
this.img1x = this.img2x; | |||||
this.img2x += this.img.width * 2; | |||||
var tmpy = this.img1y; | |||||
this.img1y = this.img2y; | |||||
this.img2y = tmpy; | |||||
} | |||||
} | |||||
Background.prototype.draw = function(ctx){ | |||||
ctx.save(); | |||||
ctx.translate(this.img1x, this.img1y); | |||||
ctx.scale(2, 2); | |||||
this.img.draw(ctx); | |||||
ctx.restore(); | |||||
ctx.translate(this.img2x, this.img2y); | |||||
ctx.scale(2, 2); | |||||
this.img.draw(ctx); | |||||
} | |||||
/* | /* | ||||
* Player | * Player | ||||
*/ | */ | ||||
function Player(game) { | function Player(game) { | ||||
makeEnt(this, game, 100); | makeEnt(this, game, 100); | ||||
this.img = assets.imgs.player; | |||||
game.started = false; | |||||
this.moves = true; | this.moves = true; | ||||
this.inputListener = true; | this.inputListener = true; | ||||
this.layer = 1; | |||||
this.invincible = false; | this.invincible = false; | ||||
this.invincibleTimeout = null; | this.invincibleTimeout = null; | ||||
this.started = false; | this.started = false; | ||||
this.rotation = 0; | this.rotation = 0; | ||||
this.bigBox = new Box(60, 40, { x: -30, y: -20 }); | |||||
this.box = new Box(); | this.box = new Box(); | ||||
this.shape.push(this.box); | this.shape.push(this.box); | ||||
this.pos.set({ x: 0, y: game.canvas.height / 2 }); | this.pos.set({ x: 0, y: game.canvas.height / 2 }); | ||||
} | } | ||||
Player.prototype.setBox = function() { | Player.prototype.setBox = function() { | ||||
var w = 30 + ((this.erectLevel - 1) * 15); | |||||
var w = 25 + ((this.erectLevel - 1) * 10); | |||||
var h = 15 + ((this.erectLevel - 1) * 6); | var h = 15 + ((this.erectLevel - 1) * 6); | ||||
this.box.width = w; | this.box.width = w; | ||||
this.box.height = h; | this.box.height = h; | ||||
// Jump | // Jump | ||||
if (name === "jump" && down) { | if (name === "jump" && down) { | ||||
this.started = true; | |||||
this.game.started = true; | |||||
this.vel.y = -1.3; | this.vel.y = -1.3; | ||||
} | } | ||||
} | } | ||||
Player.prototype.update = function() { | Player.prototype.update = function() { | ||||
// Gravity and movement | // Gravity and movement | ||||
if (this.started) { | |||||
if (this.game.started) { | |||||
this.force.y = 0.4; | this.force.y = 0.4; | ||||
this.force.x = 0.13 + ((this.erectLevel - 1) * 0.02); | this.force.x = 0.13 + ((this.erectLevel - 1) * 0.02); | ||||
} | } | ||||
this.rotation = this.vel.angle(); | this.rotation = this.vel.angle(); | ||||
} | } | ||||
Player.prototype.lose = function() { | Player.prototype.lose = function() { | ||||
alert("You died!"); | |||||
this.game.stop(); | |||||
this.game.stop(this.pos.x / 10); | |||||
} | } | ||||
Player.prototype.draw = function(ctx) { | Player.prototype.draw = function(ctx) { | ||||
if (this.invincible) | |||||
ctx.fillStyle = "#d5d5bf"; | |||||
else | |||||
ctx.fillStyle = "#f5f5dc"; | |||||
ctx.rotate(this.rotation); | ctx.rotate(this.rotation); | ||||
this.shape.draw(ctx); | |||||
var scale = 0.4 + this.erectLevel / 8; | |||||
ctx.scale(scale, scale); | |||||
ctx.translate(70, -50); | |||||
ctx.rotate(Math.PI / 2); | |||||
this.img.draw(ctx); | |||||
} | } | ||||
/* | /* | ||||
function Obstacle(game, x, y) { | function Obstacle(game, x, y) { | ||||
makeEnt(this, game, 100); | makeEnt(this, game, 100); | ||||
this.pos.set({ x: x, y: y }); | |||||
this.img = assets.imgs.wall; | |||||
this.collude = false; | |||||
var w = 70; | var w = 70; | ||||
var h = game.canvas.height; | var h = game.canvas.height; | ||||
this.shape.push(new Box(w, h, { x: 0, y: -(h / 2) - (gap / 2) })); | this.shape.push(new Box(w, h, { x: 0, y: -(h / 2) - (gap / 2) })); | ||||
this.shape.push(new Box(w, h, { x: 0, y: (h / 2) + (gap / 2) })); | this.shape.push(new Box(w, h, { x: 0, y: (h / 2) + (gap / 2) })); | ||||
this.pos.set({ x: x, y: y }); | |||||
} | } | ||||
Obstacle.prototype.draw = function(ctx) { | Obstacle.prototype.draw = function(ctx) { | ||||
this.shape.draw(ctx); | |||||
ctx.translate(-100, this.game.canvas.height / 2 - 340); | |||||
ctx.scale(0.9, 1.1); | |||||
this.img.draw(ctx); | |||||
} | } | ||||
Obstacle.prototype.update = function() { | Obstacle.prototype.update = function() { | ||||
if (this.game.camera.x > this.pos.x + this.shape.width()) | |||||
if (this.game.camera.x > this.pos.x + 800) | |||||
this.dead = true; | this.dead = true; | ||||
} | } | ||||
function PowerUp(game, x, y, type) { | function PowerUp(game, x, y, type) { | ||||
makeEnt(this, game, 100); | makeEnt(this, game, 100); | ||||
this.pos.set({ x: x, y: y }); | |||||
this.layer = 1; | |||||
this.type = type; | this.type = type; | ||||
this.shape.push(new Box(30, 30)); | this.shape.push(new Box(30, 30)); | ||||
this.pos.set({ x: x, y: y }); | |||||
} | } | ||||
PowerUp.prototype.draw = function(ctx) { | PowerUp.prototype.draw = function(ctx) { | ||||
ctx.fillStyle = "#33ee33"; | ctx.fillStyle = "#33ee33"; |
obj.moves = false; | obj.moves = false; | ||||
obj.dead = false; | obj.dead = false; | ||||
obj.inputListener = false; | obj.inputListener = false; | ||||
obj.collude = true; | |||||
obj.layer = 0; | |||||
obj.mass = mass || 0; | obj.mass = mass || 0; | ||||
obj.forceScalar = 1 / obj.mass; | obj.forceScalar = 1 / obj.mass; | ||||
this.worldgen = null; | this.worldgen = null; | ||||
this.entities = []; | this.entities = []; | ||||
this.layers = []; | |||||
this.inputListeners = []; | this.inputListeners = []; | ||||
this.keys = {}; | this.keys = {}; | ||||
} | } | ||||
Game.prototype.spawn = function(ent) { | Game.prototype.spawn = function(ent) { | ||||
this.entities.push(ent); | this.entities.push(ent); | ||||
if (!this.layers[ent.layer]) | |||||
this.layers[ent.layer] = []; | |||||
this.layers[ent.layer].push(ent); | |||||
if (ent.inputListener) | if (ent.inputListener) | ||||
this.inputListeners.push(ent); | this.inputListeners.push(ent); | ||||
} | } | ||||
this.entities.pop(); | this.entities.pop(); | ||||
} else { | } else { | ||||
this.entities[i] = this.entities.pop(); | this.entities[i] = this.entities.pop(); | ||||
var ent = this.entities[i]; | |||||
ent = this.entities[i]; | |||||
} | } | ||||
} | } | ||||
// Go through and draw | // Go through and draw | ||||
this.canvas.width = this.canvas.width; | this.canvas.width = this.canvas.width; | ||||
for (var i = 0; i < this.entities.length; ++i) { | |||||
var ent = this.entities[i]; | |||||
if (ent.dead) | |||||
continue; | |||||
if (ent.pos.x + ent.shape.width() < this.camera.x) | |||||
continue; | |||||
if (ent.pos.x > this.camera.x + this.canvas.width) | |||||
continue; | |||||
for (var i = 0; i < this.layers.length; ++i) { | |||||
var layer = this.layers[i]; | |||||
for (var j = 0; j < layer.length; ++j) { | |||||
var ent = layer[j]; | |||||
// Remove dead entities, replace them with the last entity | |||||
if (ent.dead) { | |||||
if (i + 1 === this.entities.length) { | |||||
layer.pop(); | |||||
} else { | |||||
layer[j] = layer.pop(); | |||||
ent = layer[j]; | |||||
} | |||||
} | |||||
this.ctx.save(); | |||||
this.ctx.translate( | |||||
ent.pos.x - this.camera.x, | |||||
ent.pos.y - this.camera.y); | |||||
ent.draw(this.ctx); | |||||
this.ctx.restore(); | |||||
if (ent.collude && ent.pos.x + ent.shape.width() < this.camera.x) | |||||
continue; | |||||
if (ent.collude && ent.pos.x > this.camera.x + this.canvas.width) | |||||
continue; | |||||
this.ctx.save(); | |||||
this.ctx.translate( | |||||
ent.pos.x - this.camera.x, | |||||
ent.pos.y - this.camera.y); | |||||
ent.draw(this.ctx); | |||||
this.ctx.restore(); | |||||
} | |||||
} | } | ||||
// Clear presses | // Clear presses | ||||
if (!this.stopped) | if (!this.stopped) | ||||
this.raf = reqAnimFrame(this.update.bind(this)); | this.raf = reqAnimFrame(this.update.bind(this)); | ||||
} | } | ||||
Game.prototype.stop = function() { | |||||
Game.prototype.stop = function(score) { | |||||
this.stopped = true; | this.stopped = true; | ||||
cancelAnimFrame(this.raf); | cancelAnimFrame(this.raf); | ||||
window.removeEventListener("keyup", this.onkey); | window.removeEventListener("keyup", this.onkey); | ||||
window.removeEventListener("keydown", this.onkey); | window.removeEventListener("keydown", this.onkey); | ||||
if (this.onstop) | if (this.onstop) | ||||
this.onstop(); | |||||
this.onstop(Math.floor(score)); | |||||
} | } |
evt.preventDefault(); | evt.preventDefault(); | ||||
} | } | ||||
var currentSoundtrack = -1; | |||||
function playSoundtrack(tracks) { | |||||
console.log("Playing track..."); | |||||
var keys = Object.keys(tracks); | |||||
if (currentSoundtrack === -1) | |||||
currentSoundtrack = randInt(0, keys.length); | |||||
var key = keys[currentSoundtrack]; | |||||
var track = tracks[key]; | |||||
console.log("Playing track", key); | |||||
var trackId = currentSoundtrack; | |||||
while (currentSoundtrack === trackId) | |||||
currentSoundtrack = randInt(0, keys.length); | |||||
if (track.ready) { | |||||
track.play(); | |||||
track.once("ended", function() { playSoundtrack(tracks) }); | |||||
console.log("playing immediately, as it's loaded"); | |||||
} else { | |||||
console.log("loading it..."); | |||||
track.once("load", function() { | |||||
console.log("loaded."); | |||||
track.play(); | |||||
track.once("ended", function() { playSoundtrack(tracks) }); | |||||
}); | |||||
} | |||||
} | |||||
playSoundtrack(assets.soundtracks); | |||||
function run() { | function run() { | ||||
var game = new Game(document.getElementById("canvas")); | var game = new Game(document.getElementById("canvas")); | ||||
game.canvas.width = window.innerWidth; | game.canvas.width = window.innerWidth; | ||||
game.start(worldgen); | game.start(worldgen); | ||||
game.onstop = run; | |||||
game.onstop = function(score) { | |||||
alert("You lost! Score: "+score); | |||||
run(); | |||||
} | |||||
} | } | ||||
run(); | run(); |
window.cancelAnimFrame = window.cancelAnimationFrame || window.clearTimeout; | window.cancelAnimFrame = window.cancelAnimationFrame || window.clearTimeout; | ||||
// Random number from min (inclusive) to max (exclusive) | |||||
function randInt(min, max) { | function randInt(min, max) { | ||||
return Math.floor(Math.random() * (max - min)) + min; | return Math.floor(Math.random() * (max - min)) + min; | ||||
} | } | ||||
function makeEventListener(obj) { | |||||
obj.__callbacks = {}; | |||||
obj.on = makeEventListener.on; | |||||
obj.off = makeEventListener.off; | |||||
obj.once = makeEventListener.once; | |||||
obj.emit = makeEventListener.emit; | |||||
} | |||||
makeEventListener.on = function on(name, fn) { | |||||
if (!this.__callbacks[name]) | |||||
this.__callbacks[name] = []; | |||||
this.__callbacks[name].push(fn); | |||||
} | |||||
makeEventListener.once = function once(name, fn) { | |||||
var self = this; | |||||
this.on(name, function f() { | |||||
self.off(name, f); | |||||
fn.apply(arguments); | |||||
}); | |||||
} | |||||
makeEventListener.off = function off(name, fn) { | |||||
if (!this.__callbacks[name]) | |||||
return; | |||||
for (var i in this.__callbacks[name]) { | |||||
if (this.__callbacks[name] === fn) | |||||
delete this.__callbacks[name][i]; | |||||
} | |||||
} | |||||
makeEventListener.emit = function emit(name) { | |||||
if (!this.__callbacks[name]) | |||||
return; | |||||
var args = [].slice.apply(arguments); | |||||
args.splice(0, 1); | |||||
this.__callbacks[name].forEach(function(f) { | |||||
f.apply(args); | |||||
}); | |||||
} |
this.maxY = 400; | this.maxY = 400; | ||||
this.powerupCounter = randInt(WorldGen.range[0], WorldGen.range[1]); | this.powerupCounter = randInt(WorldGen.range[0], WorldGen.range[1]); | ||||
// Spawn player | |||||
// Spawn player and background | |||||
game.spawn(new Background(game)); | |||||
game.spawn(new Player(game)); | game.spawn(new Player(game)); | ||||
} | } | ||||
WorldGen.range = [5, 12]; | WorldGen.range = [5, 12]; | ||||
if (y > this.maxY) y = this.maxY; | if (y > this.maxY) y = this.maxY; | ||||
var obstacle = new Obstacle(this.game, x, y); | var obstacle = new Obstacle(this.game, x, y); | ||||
this.game.entities.push(obstacle); | |||||
this.game.spawn(obstacle); | |||||
this.prevX = x; | this.prevX = x; | ||||
x - 50, | x - 50, | ||||
y + 80 + (this.game.canvas.height / 2)); | y + 80 + (this.game.canvas.height / 2)); | ||||
this.game.entities.push(powerup); | |||||
this.game.spawn(powerup); | |||||
this.powerupCounter = randInt(WorldGen.range[0], WorldGen.range[1]); | this.powerupCounter = randInt(WorldGen.range[0], WorldGen.range[1]); | ||||
} | } |