forked from sent/waves
1032 lines
35 KiB
JavaScript
1032 lines
35 KiB
JavaScript
var FORCE_STOP_ENGINE_TICK_FUNCTION = true;
|
|
var STOP_ON_NEXT_TICK = true;
|
|
var DRAW_TAS_INFO = true;
|
|
var replay = false;
|
|
var replay_line=0;
|
|
var stop_replay=false;
|
|
|
|
var tas_textarea_persist=false;
|
|
|
|
var lnzrl=0;
|
|
|
|
var GLOBAL_TICK_RO=0;
|
|
var tas_loadLevel_tick=0;
|
|
|
|
//var TEST = {};
|
|
|
|
var GLOBAL_KEYS_DOWN = {};
|
|
var NEW_KEYS_DOWN = {};
|
|
|
|
function tf(s){
|
|
if(s==="1"){
|
|
return true;
|
|
}else{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function cta(s){
|
|
if(s==="S"){
|
|
tas_textarea_persist=true;
|
|
|
|
replay=true;
|
|
FORCE_STOP_ENGINE_TICK_FUNCTION = false;
|
|
STOP_ON_NEXT_TICK = true;
|
|
}else if(s==="P"){
|
|
stop_replay=true;
|
|
}
|
|
}
|
|
|
|
function REPLACE_FROM_CONSOLE(orig){
|
|
if(replay_line!==0){
|
|
lnzrl=replay_line;
|
|
}
|
|
lines = document.getElementById("keyoutputid").value.split("\n");
|
|
var rfc=true;
|
|
while(rfc){
|
|
//console.log(lines);
|
|
if(replay_line < lines.length){
|
|
if(lines[replay_line].at(0)==="0"||lines[replay_line].at(0)==="1"){
|
|
rfc=false;
|
|
orig[KEY_LEFT] = tf(lines[replay_line].at(0));
|
|
orig[KEY_RIGHT] = tf(lines[replay_line].at(1));
|
|
orig[KEY_JUMP] = tf(lines[replay_line].at(2));
|
|
orig[KEY_SHOOT] = tf(lines[replay_line].at(3));
|
|
STOP_ON_NEXT_TICK=true;
|
|
FORCE_STOP_ENGINE_TICK_FUNCTION=false;
|
|
replay_line++;
|
|
replay=true;
|
|
}else if(replay_line >= lines.length){
|
|
STOP_ON_NEXT_TICK=true;
|
|
FORCE_STOP_ENGINE_TICK_FUNCTION=true;
|
|
rfc=false;
|
|
replay=false;
|
|
}else{
|
|
replay_line++;
|
|
}
|
|
}else{
|
|
replay=false;
|
|
rfc=false;
|
|
STOP_ON_NEXT_TICK=true;
|
|
FORCE_STOP_ENGINE_TICK_FUNCTION=true;
|
|
}
|
|
}
|
|
//console.log(replay_line);
|
|
return orig;
|
|
}
|
|
|
|
function GameStorage(a) {
|
|
this.works = !0;
|
|
this.tempStorage = {};
|
|
try {
|
|
if (!localStorage.getItem("version") && (localStorage.setItem("version", a), !localStorage.getItem("version"))) throw "grenade";
|
|
} catch (b) {
|
|
this.works = !1
|
|
}
|
|
this.works ? Number(localStorage.getItem("version")) !== a && (localStorage.clear(), console.log("Storage cleared because of game update to version " + a)) : this.setItem("version", a)
|
|
}
|
|
GameStorage.prototype.setItem = function(a, b) {
|
|
b = JSON.stringify(b);
|
|
this.works ? localStorage.setItem(a, b) : this.tempStorage[a] = b
|
|
};
|
|
GameStorage.prototype.removeItem = function(a) {
|
|
this.works ? localStorage.removeItem(a) : delete this.tempStorage[a]
|
|
};
|
|
GameStorage.prototype.getItem = function(a) {
|
|
return this.works ? JSON.parse(localStorage.getItem(a)) : this.tempStorage[a] ? JSON.parse(this.tempStorage[a]) : null
|
|
};
|
|
Object.deepcopy = function(a) {
|
|
function b(a, c, f, m) {
|
|
var h;
|
|
if ("object" == typeof a) {
|
|
if (null == a) return a;
|
|
for (h in d)
|
|
if (d[h] === a) return g.push({
|
|
resolveTo: h,
|
|
child: f,
|
|
i: m
|
|
}), null;
|
|
d[c] = a;
|
|
Object.isArray(a) ? f = [] : (f = {}, f.__proto__ = a.__proto__);
|
|
for (h in a) f[h] = b(a[h], c + "[" + h + "]", f, h);
|
|
e[c] = f
|
|
} else f = a;
|
|
return f
|
|
}
|
|
var c, d = {},
|
|
e = {},
|
|
g = [];
|
|
a = b(a, "*");
|
|
for (c in g) {
|
|
var f = g[c];
|
|
f && f.child && f.i in f.child && (f.child[f.i] = e[f.resolveTo])
|
|
}
|
|
return a
|
|
};
|
|
var DEBUG = !0,
|
|
RIGHT = 0,
|
|
LEFT = 1,
|
|
NOT_FALLING = 0,
|
|
IN_JUMP = 1,
|
|
FALLING = 2,
|
|
GAME_WIDTH = 800,
|
|
GAME_HEIGHT = 600,
|
|
FIRST_LEVEL = "level1.js",
|
|
LEVEL_DIR = "levels/",
|
|
STORAGE_NO_AUDIO = "no_audio",
|
|
STORAGE_LEVEL = "last_level",
|
|
STORAGE_STATE = "state";
|
|
window.addEventListener("load", function() {
|
|
var a = new GameEngine;
|
|
DEBUG && "localhost" === location.host && window.LevelEditor && new LevelEditor(a)
|
|
}, !1);
|
|
|
|
function GameEngine() {
|
|
this.version = .02;
|
|
this.width = GAME_WIDTH;
|
|
this.height = GAME_HEIGHT;
|
|
this.storage = new GameStorage(this.version);
|
|
this.storage.works || dbg_warn("localStorage not supported. Your game will not be saved.");
|
|
this.keyboard = new KeyboardManager(this);
|
|
this.renderer = new GameRenderer(this);
|
|
this.audio = new AudioManager(!this.storage.getItem(STORAGE_NO_AUDIO));
|
|
this.audio.works || dbg_warn("Your browser does not support HTML5 audio or ogg/vorbis");
|
|
this.posY = this.posX = this.viewportY = this.viewportX = null;
|
|
this.lastTick = Date.now();
|
|
this.totalDelta = 0;
|
|
this.running = this.drawableObjects = this.blockingObjects = this.triggeringObjects = this.objectMap = this.objects = this.charBitmap = this.vspeed = this.direction = this.canJump = this.fallingState = this.tickCount = null;
|
|
this.tickFunctionStopped = !0;
|
|
this.isMoving = null;
|
|
this.images = {};
|
|
this.drawHooks = this.gameData = null;
|
|
this.levelFile = this.storage.getItem(STORAGE_LEVEL);
|
|
var a = this;
|
|
document.getElementById("mute_button").addEventListener("click", function() {
|
|
a.toggleMute()
|
|
}, !1);
|
|
document.getElementById("reset_save").addEventListener("click",
|
|
function() {
|
|
a.nextLevel(FIRST_LEVEL)
|
|
}, !1);
|
|
this.levelFile || (this.levelFile = FIRST_LEVEL);
|
|
this.loadLevel(this.levelFile)
|
|
}
|
|
GameEngine.prototype.nextLevel = function(a) {
|
|
console.log(document.getElementById("keyoutputid").value);
|
|
tas_loadLevel_tick = GLOBAL_TICK_RO;
|
|
this.levelFile = a;
|
|
this.storage.setItem(STORAGE_LEVEL, a);
|
|
this.storage.removeItem(STORAGE_STATE);
|
|
this.loadLevel(a)
|
|
};
|
|
GameEngine.prototype.loadLevel = function(a) {
|
|
var b = this;
|
|
this.running = !1;
|
|
http_get(LEVEL_DIR + a + "?" + Math.random(), function(a) {
|
|
a = eval(a);
|
|
if (!a) throw "level not found";
|
|
b.level = a;
|
|
b.loadResources(a)
|
|
})
|
|
};
|
|
GameEngine.prototype.loadResources = function(a) {
|
|
function b() {
|
|
g++;
|
|
return function() {
|
|
f++;
|
|
h.renderer.drawLoadingScreen(f, g);
|
|
g === f && h.start()
|
|
}
|
|
}
|
|
|
|
function c() {
|
|
throw "loading a resource failed. Will not start";
|
|
}
|
|
var d = a.resourceDir,
|
|
e = Object.keys(a.images),
|
|
g = 0,
|
|
f = 0,
|
|
h = this;
|
|
this.audio.path = a.musicDir;
|
|
this.level = a;
|
|
this.audio.preload(this.level.jumpMusic1, b(), c);
|
|
this.audio.preload(this.level.jumpMusic2, b(), c);
|
|
e.forEach(function(e) {
|
|
var g = a.images[e],
|
|
f = new Image;
|
|
h.images[e] = f;
|
|
f.onload = b();
|
|
f.onerror = c;
|
|
f.src = d + g
|
|
});
|
|
this.renderer.drawLoadingScreen(0,
|
|
g)
|
|
};
|
|
GameEngine.prototype.start = function() {
|
|
if(!tas_textarea_persist){
|
|
document.getElementById("keyoutputid").value = "";
|
|
tas_textarea_persist=false;
|
|
}
|
|
this.charBitmap = Bitmap.fromImage(this.images.charHitmap);
|
|
this.dead = !1;
|
|
this.audio.play(this.level.backgroundMusic, !0, !0);
|
|
this.restart();
|
|
this.running = !0;
|
|
this.tickFunctionStopped && this.doTick(this)
|
|
};
|
|
GameEngine.prototype.restart = function() {
|
|
document.getElementById("keyoutputid").value = "";
|
|
this.viewportX = this.level.startViewport.x;
|
|
this.viewportY = this.level.startViewport.y;
|
|
this.posX = this.level.startPosition.x;
|
|
this.posY = this.level.startPosition.y;
|
|
this.fallingState = FALLING;
|
|
this.direction = RIGHT;
|
|
this.canJump = this.isMoving = this.dead = !1;
|
|
this.vspeed = 0;
|
|
this.audio.stop(this.level.deathMusic);
|
|
this.tickCount = 0;
|
|
this.gameData = {};
|
|
this.drawHooks = [];
|
|
this.loadObjects();
|
|
var a = this.storage.getItem(STORAGE_STATE);
|
|
a && this.level.loadState(this, a);
|
|
this.level.init(this)
|
|
};
|
|
GameEngine.prototype.saveState = function(a) {
|
|
this.storage.setItem(STORAGE_STATE, a)
|
|
};
|
|
GameEngine.prototype.loadObjects = function() {
|
|
var a = this,
|
|
b = Object.deepcopy(this.level.objects);
|
|
this.objects = [];
|
|
this.objectMap = {};
|
|
this.triggeringObjects = [];
|
|
this.blockingObjects = [];
|
|
this.drawableObjects = [];
|
|
b = b.concatMap(function(a) {
|
|
return cartesianProductOnObjects(a.position, ["x", "y"]).map(function(b) {
|
|
var c = Object.deepcopy(a);
|
|
c.x = b.x;
|
|
c.y = b.y;
|
|
return c
|
|
})
|
|
});
|
|
var c = {};
|
|
b.forEach(function(b) {
|
|
b = a.addObject(b, c);
|
|
b.init && b.init(a)
|
|
});
|
|
b = this.drawableObjects.partition(function(a) {
|
|
return a.dynamic
|
|
});
|
|
this.drawableObjects =
|
|
b[0];
|
|
this.renderer.loadBackground(b[1]);
|
|
this.renderer.loadForeground([])
|
|
};
|
|
GameEngine.prototype.addObject = function(a, b) {
|
|
var c = a.dynamic || !!a.id,
|
|
d = a.shape,
|
|
e = a.trigger,
|
|
g = void 0,
|
|
f = void 0,
|
|
h = void 0,
|
|
l = void 0,
|
|
k = "x y dynamic trigger image blocking killing id tickFunction zIndex position shape retrigger init".split(" ");
|
|
b = b || {};
|
|
Object.keys(a).deleteList(k).length && (console.log(Object.keys(a).deleteList(k)), dbg_assert(!1, "Unkown properties"));
|
|
a.image && (g = this.images[a.image], dbg_assert(g, "invalid image id"), f = g.width, h = g.height);
|
|
dbg_assert(!a.blocking || !e, "an object cannot block and have a trigger at the same time");
|
|
a.killing && (dbg_assert(!e, "an object cannot kill and have a trigger at the same time"), dbg_assert(!a.blocking, "an object cannot kill and block at the same time"), e = this.die.bind(this));
|
|
void 0 === d && g && (e || a.blocking || c) && (b[a.image] ? d = b[a.image] : b[a.image] = d = new AutoShape(g));
|
|
d && (f = d.width, h = d.height, l = d.getBitmap());
|
|
b = {
|
|
x: a.x,
|
|
y: a.y,
|
|
width: f,
|
|
height: h,
|
|
dynamic: c,
|
|
visible: !0,
|
|
image: g,
|
|
bitmap: l,
|
|
trigger: e,
|
|
zIndex: a.zIndex || 0,
|
|
retrigger: !!a.retrigger,
|
|
tickFunction: a.tickFunction,
|
|
init: a.init
|
|
};
|
|
e && (dbg_assert(e instanceof Function, "trigger has to be a function"), dbg_assert(d, "objects that kill or have a trigger require a shape"), this.triggeringObjects.push(b));
|
|
a.image && this.drawableObjects.push(b);
|
|
a.blocking && (dbg_assert(d, "objects that block require a shape"), this.blockingObjects.push(b));
|
|
this.objects.push(b);
|
|
a.id && (dbg_assert(!this.objectMap[a.id], "id used twice"), this.objectMap[a.id] = b);
|
|
return b
|
|
};
|
|
GameEngine.prototype.removeObject = function(a) {
|
|
this.objects = this.objects.delete(a);
|
|
this.blockingObjects = this.blockingObjects.delete(a);
|
|
this.drawableObjects = this.drawableObjects.delete(a);
|
|
this.triggeringObjects = this.triggeringObjects.delete(a);
|
|
a.id && delete this.objectMap[a.id]
|
|
};
|
|
GameEngine.prototype.removeObjectById = function(a) {
|
|
(a = this.objectMap[a]) && this.removeObject(a)
|
|
};
|
|
GameEngine.prototype.toggleMute = function() {
|
|
this.audio.toggleMute();
|
|
this.audio.muted ? this.storage.setItem(STORAGE_NO_AUDIO, 1) : this.storage.removeItem(STORAGE_NO_AUDIO)
|
|
};
|
|
GameEngine.prototype.die = function() {
|
|
this.dead || (this.audio.play(this.level.deathMusic, !1, !0, .3), this.dead = !0);
|
|
console.log("end");
|
|
//document.getElementById("keyoutputid").value = "";
|
|
};
|
|
GameEngine.prototype.crush = function() {
|
|
console.log("death by crushing");
|
|
this.die()
|
|
};
|
|
GameEngine.prototype.doTick = function doTick(a) {
|
|
a.tickFunctionStopped = !1;
|
|
if (a.running) {
|
|
var c = a.level,
|
|
d = Date.now();
|
|
a.totalDelta += d - a.lastTick;
|
|
a.lastTick = d;
|
|
500 <= a.totalDelta && (a.totalDelta = 0);
|
|
for (; a.totalDelta >= c.physics.timePerTick;) a.tick(a), a.totalDelta -= c.physics.timePerTick;
|
|
a.renderer.redraw();
|
|
a.drawHooks.forEach(function(c) {
|
|
c.call(a.level, a)
|
|
});
|
|
requestAnimationFrame(function() {
|
|
doTick(a)
|
|
})
|
|
} else a.tickFunctionStopped = !0
|
|
};
|
|
GameEngine.prototype.tick = function(a) {
|
|
var tas_info_text = "X: " + this.posX + ", Y: " + this.posY + ", canJump(2nd): " + this.canJump + ", tick: " + this.tickCount + ", replay_line: " + replay_line + ", last non-zero replay_line: " + lnzrl + ", loadLevel tick: " + tas_loadLevel_tick;
|
|
GLOBAL_TICK_RO = this.tickCount;
|
|
if(replay){
|
|
NEW_KEYS_DOWN = REPLACE_FROM_CONSOLE(a.keyboard.keyWasPressed);
|
|
}else{
|
|
NEW_KEYS_DOWN=a.keyboard.keyWasPressed;
|
|
replay_line=0;
|
|
}
|
|
if(stop_replay){
|
|
STOP_ON_NEXT_TICK=true;
|
|
FORCE_STOP_ENGINE_TICK_FUNCTION=true;
|
|
replay=false;
|
|
stop_replay=false;
|
|
}
|
|
if(FORCE_STOP_ENGINE_TICK_FUNCTION){
|
|
document.getElementById("tas_info").innerHTML = tas_info_text;
|
|
return;
|
|
}
|
|
var b = a.level.physics,
|
|
c = a.keyboard.keyWasPressed;
|
|
a.level.tickFunction(a);
|
|
a.objects.forEach(function(b) {
|
|
b.tickFunction && b.tickFunction.call(b, a);
|
|
if (b.bitmap && b.trigger) {
|
|
var c = a.characterCollision(b.bitmap, b.x, b.y);
|
|
!c || !b.retrigger && b.triggered || b.trigger.call(b, a);
|
|
b.triggered = c
|
|
}
|
|
});
|
|
a.tickCount++;
|
|
a.dead || (!c[KEY_LEFT] !== !c[KEY_RIGHT] ? (a.isMoving = !0, c[KEY_LEFT] ? (a.moveCharacterRight(-b.moveSpeed), a.direction = LEFT) : c[KEY_RIGHT] && (a.moveCharacterRight(b.moveSpeed),
|
|
a.direction = RIGHT)) : a.isMoving = !1, a.fallingState === NOT_FALLING ? (c[KEY_JUMP] && (a.audio.play(a.level.jumpMusic1, !1, !1, .6), a.fallingState = IN_JUMP, a.canJump = !0, a.vspeed = b.jumpInitialSpeed, a.jumpTicks = b.jumpTicks), a.moveCharacterDown(1) || (a.fallingState = FALLING, a.canJump = !0)) : (a.fallingState === IN_JUMP ? (a.vspeed += b.jumpGravity, a.jumpTicks--, c[KEY_JUMP] && 0 !== a.jumpTicks || (a.fallingState = FALLING)) : (a.canJump && c[KEY_JUMP] && (a.audio.play(a.level.jumpMusic2, !1, !1, .6), a.fallingState = IN_JUMP, a.canJump = !1, a.vspeed =
|
|
b.jumpInitialSpeed / 1.5, a.jumpTicks = b.jumpTicks), a.vspeed = a.vspeed < b.fallSpeedCap ? a.vspeed + b.fallGravity : b.fallSpeedCap), a.moveCharacterDown(Math.roundInfinity(a.vspeed)) && (0 < a.vspeed ? (a.fallingState = NOT_FALLING, a.vspeed = 0, c[KEY_JUMP] = !1) : a.vspeed = 0)))
|
|
if(STOP_ON_NEXT_TICK){
|
|
FORCE_STOP_ENGINE_TICK_FUNCTION = true;
|
|
}
|
|
document.getElementById("tas_info").innerHTML = tas_info_text;
|
|
};
|
|
GameEngine.prototype.addDrawHook = function(a) {
|
|
this.drawHooks.push(a)
|
|
};
|
|
GameEngine.prototype.moveCharacterRight = function(a) {
|
|
if (!this.charBitmap.compareMany(this.blockingObjects, this.posX + a, this.posY)) return this.posX += a, !1;
|
|
for (var b = Math.sign(a); a;)
|
|
if (this.posX += b, a -= b, this.charBitmap.compareMany(this.blockingObjects, this.posX, this.posY)) return this.posX -= b, !0;
|
|
return !1
|
|
};
|
|
GameEngine.prototype.moveCharacterDown = function(a) {
|
|
if (!this.charBitmap.compareMany(this.blockingObjects, this.posX, this.posY + a)) return this.posY += a, !1;
|
|
for (var b = Math.sign(a); a;)
|
|
if (this.posY += b, a -= b, this.charBitmap.compareMany(this.blockingObjects, this.posX, this.posY)) return this.posY -= b, !0;
|
|
return !1
|
|
};
|
|
GameEngine.prototype.characterCollision = function(a, b, c) {
|
|
return b > this.posX + this.level.characterWidth || c > this.posY + this.level.characterHeight || b + a.width < this.posX || c + a.height < this.posY ? !1 : this.charBitmap.compare(a, Math.round(b - this.posX), Math.round(c - this.posY))
|
|
};
|
|
GameEngine.prototype.moveObjectRight = function(a, b) {
|
|
var c = Math.sign(b),
|
|
d = !1;
|
|
for (this.characterCollision(a.bitmap, a.x, a.y - 1) && (d = !0); b;) b -= c, a.x += c, d ? this.moveCharacterRight(c) : this.characterCollision(a.bitmap, a.x, a.y) && this.moveCharacterRight(c) && (console.log("crushed"), this.die())
|
|
};
|
|
GameEngine.prototype.moveObjectDown = function(a, b) {
|
|
if (0 <= b) {
|
|
var c = !1;
|
|
for (this.characterCollision(a.bitmap, a.x, a.y - 1) && (c = !0); b;) a.y++, b--, c ? this.moveCharacterDown(1) : this.characterCollision(a.bitmap, a.x, a.y) && this.moveCharacterDown(1) && (console.log("crushed"), this.die())
|
|
} else
|
|
for (; b;) b++, a.y--, this.characterCollision(a.bitmap, a.x, a.y) && this.moveCharacterDown(-1) && (console.log("crushed"), this.die())
|
|
};
|
|
|
|
function AudioManager(a) {
|
|
this.muted = !a;
|
|
this.works = !0;
|
|
window.Audio ? (new Audio).canPlayType("audio/ogg; codecs=vorbis") || (this.works = !1) : this.works = !1;
|
|
this.playing = [];
|
|
this.playQueue = []
|
|
}
|
|
AudioManager.prototype.play = function(a, b, c, d, e) {
|
|
if (this.works)
|
|
if (this.muted) this.playQueue.push([new Date, Array.toArray(arguments)]);
|
|
else {
|
|
var g = this.playing.filter(Function.byIndex("file", a)).find(function(a) {
|
|
return (a.audio.ended || a.audio.paused) != !!c
|
|
});
|
|
if (g) var f = g.audio;
|
|
else f = new Audio(this.path + a), this.playing.push({
|
|
audio: f,
|
|
file: a
|
|
});
|
|
g = !1;
|
|
void 0 !== e ? f.duration && f.duration < e && (g = !0) : e = 0;
|
|
g || (void 0 !== d && (f.volume = d), 4 > f.readyState ? f.addEventListener("loadedmetadata", function() {
|
|
f.currentTime = e
|
|
}) :
|
|
f.currentTime = e, f.loop = b, f.play())
|
|
}
|
|
};
|
|
AudioManager.prototype.preload = function(a, b, c) {
|
|
if (this.works && !this.muted)
|
|
if (this.playing.find(Function.byIndex("file", a))) b && setTimeout(b, 0);
|
|
else {
|
|
var d = new Audio(this.path + a);
|
|
d.muted = this.muted;
|
|
b && (d.addEventListener("canplaythrough", b), c && d.addEventListener("error", c));
|
|
d.load();
|
|
this.playing.push({
|
|
audio: d,
|
|
file: a
|
|
});
|
|
return d
|
|
}
|
|
else setTimeout(b, 0)
|
|
};
|
|
AudioManager.prototype.stop = function(a) {
|
|
function b(a) {
|
|
a.audio.pause()
|
|
}
|
|
this.works && this.playing.filter(Function.byIndex("file", a)).forEach(b)
|
|
};
|
|
AudioManager.prototype.toggleMute = function() {
|
|
function a(a) {
|
|
a.audio.muted = b
|
|
}
|
|
if (this.works) {
|
|
var b = this.muted = !this.muted;
|
|
this.playing.forEach(a)
|
|
}
|
|
if (!this.muted) {
|
|
var c = this;
|
|
this.playQueue.forEach(function(a) {
|
|
a[1][4] = (Date.now() - a[0]) / 1E3;
|
|
c.play.apply(c, a[1])
|
|
});
|
|
this.playQueue = []
|
|
}
|
|
};
|
|
|
|
function Bitmap(a, b) {
|
|
this.width = a;
|
|
this.height = b;
|
|
this.count = a * b;
|
|
this.data = new Uint8Array(this.count)
|
|
}
|
|
Bitmap.fromImage = function(a) {
|
|
var b = a.width,
|
|
c = a.height,
|
|
d = new Bitmap(b, c),
|
|
e = document.createElement("canvas"),
|
|
g = e.getContext("2d");
|
|
e.width = b;
|
|
e.height = c;
|
|
g.clearRect(0, 0, b, c);
|
|
g.drawImage(a, 0, 0);
|
|
a = g.getImageData(0, 0, b, c).data;
|
|
for (b = 0; b < d.count; b++) d.data[b] = 0 < a[4 * b + 3] | 0;
|
|
return d
|
|
};
|
|
Bitmap.prototype.withOtherRect = function(a, b, c, d, e) {
|
|
b = this.getIntersection(a, b, c, d);
|
|
c = b.otherY * a + b.otherX;
|
|
d = b.thisY * this.width + b.thisX;
|
|
for (var g = 0; g < b.height; g++) {
|
|
for (var f = 0; f < b.width; f++) e(b.otherX + f, b.otherY + g, this.data[d]), c++, d++;
|
|
c += a - b.width;
|
|
d += this.width - b.width
|
|
}
|
|
};
|
|
Bitmap.prototype.stringify = function() {
|
|
for (var a = "Bitmap width=" + this.width + " height=" + this.height + "\n", b = 0; b < this.height; b++) {
|
|
for (var c = 0; c < this.width; c++) a += String(this.data[b * this.width + c]);
|
|
a += "\n"
|
|
}
|
|
return a
|
|
};
|
|
Bitmap.prototype.set = function(a, b, c) {
|
|
0 <= a && 0 <= b && a < this.width && b < this.height && (this.data[b * this.width + a] = c)
|
|
};
|
|
Bitmap.prototype.copy = function() {
|
|
return {
|
|
__proto__: Bitmap.prototype,
|
|
width: this.width,
|
|
height: this.height,
|
|
count: this.count,
|
|
data: new Uint8Array(this.data)
|
|
}
|
|
};
|
|
Bitmap.prototype.getIntersection = function(a, b, c, d) {
|
|
c = c || 0;
|
|
d = d || 0;
|
|
var e = Math.max(0, c),
|
|
g = Math.max(0, d);
|
|
c = Math.max(0, -c);
|
|
d = Math.max(0, -d);
|
|
return {
|
|
thisX: e,
|
|
thisY: g,
|
|
otherX: c,
|
|
otherY: d,
|
|
width: Math.max(0, Math.min(this.width - e, a - c)),
|
|
height: Math.max(0, Math.min(this.height - g, b - d))
|
|
}
|
|
};
|
|
Bitmap.prototype.slice = function(a, b, c, d) {
|
|
c = this.getIntersection(a, b, c, d);
|
|
b = new Bitmap(a, b);
|
|
d = c.thisY * this.width + c.thisX;
|
|
for (var e = c.otherY * a + c.otherX, g = 0; g < c.height; g++) {
|
|
for (var f = 0; f < c.width; f++) b.data[e] = this.data[d], d++, e++;
|
|
d += this.width - c.width;
|
|
e += a - c.width
|
|
}
|
|
return b
|
|
};
|
|
Bitmap.prototype.isZero = function() {
|
|
for (var a = 0; a < this.count; a++)
|
|
if (this.data[a]) return !1;
|
|
return !0
|
|
};
|
|
Bitmap.prototype.or = function(a, b, c) {
|
|
b = this.getIntersection(a.width, a.height, b, c);
|
|
c = b.otherY * a.width + b.otherX;
|
|
for (var d = b.thisY * this.width + b.thisX, e = 0; e < b.height; e++) {
|
|
for (var g = 0; g < b.width; g++) this.data[d] |= a.data[c], c++, d++;
|
|
c += a.width - b.width;
|
|
d += this.width - b.width
|
|
}
|
|
};
|
|
Bitmap.prototype.compare = function(a, b, c) {
|
|
b = this.getIntersection(a.width, a.height, b, c);
|
|
c = b.otherY * a.width + b.otherX;
|
|
for (var d = b.thisY * this.width + b.thisX, e = 0; e < b.height; e++) {
|
|
for (var g = 0; g < b.width; g++) {
|
|
if (this.data[d] && a.data[c]) return !0;
|
|
c++;
|
|
d++
|
|
}
|
|
c += a.width - b.width;
|
|
d += this.width - b.width
|
|
}
|
|
return !1
|
|
};
|
|
Bitmap.prototype.compareMany = function(a, b, c) {
|
|
var d = this;
|
|
return a.some(function(a) {
|
|
return a.x > b + d.width || a.y > c + d.height || a.x + a.bitmap.width < b || a.y + a.bitmap.height < c ? !1 : d.compare(a.bitmap, a.x - b, a.y - c)
|
|
})
|
|
};
|
|
|
|
function Line(a, b, c, d) {
|
|
this.p1 = {
|
|
x: a,
|
|
y: b
|
|
};
|
|
this.p2 = {
|
|
x: c,
|
|
y: d
|
|
};
|
|
this.width = Math.abs(this.p1.x - this.p2.x) + 1;
|
|
this.height = Math.abs(this.p1.y - this.p2.y) + 1;
|
|
this.bitmap = null
|
|
}
|
|
Line.prototype.getBitmap = function() {
|
|
if (this.bitmap) return this.bitmap;
|
|
var a = (this.height - 1) / (this.width - 1);
|
|
this.bitmap = new Bitmap(this.width, this.height);
|
|
if (1 < a)
|
|
for (var b = 0; b < this.height; b++) this.bitmap.set(Math.round(b / a), b, 1);
|
|
else
|
|
for (b = 0; b < this.width; b++) this.bitmap.set(b, Math.round(b * a), 1);
|
|
return this.bitmap
|
|
};
|
|
|
|
function Rectangle(a, b) {
|
|
this.width = a;
|
|
this.height = b;
|
|
this.bitmap = null
|
|
}
|
|
Rectangle.prototype.getBitmap = function() {
|
|
if (this.bitmap) return this.bitmap;
|
|
this.bitmap = new Bitmap(this.width, this.height);
|
|
for (var a = 0; a < this.bitmap.count; a++) this.bitmap.data[a] = 1;
|
|
return this.bitmap
|
|
};
|
|
|
|
function AutoShape(a) {
|
|
this.width = a.width;
|
|
this.height = a.height;
|
|
this.image = a;
|
|
this.bitmap = null
|
|
}
|
|
AutoShape.prototype.getBitmap = function() {
|
|
this.bitmap || (this.bitmap = Bitmap.fromImage(this.image));
|
|
return this.bitmap
|
|
};
|
|
var KEY_ADVANCE = 70,
|
|
KEY_UP = 38,
|
|
KEY_DOWN = 40,
|
|
KEY_LEFT = 37,
|
|
KEY_RIGHT = 39,
|
|
KEY_JUMP = 32,
|
|
KEY_RESTART = 82,
|
|
KEY_MUTE = 77,
|
|
KEY_SHOOT = 84,
|
|
KEY_SUICIDE = 81,
|
|
KBD_STORAGE_KEY = "keyboard_settings";
|
|
|
|
function KeyboardManager(a) {
|
|
this.keysPressed = {};
|
|
this.keyWasPressed = {};
|
|
this.game = a;
|
|
(a = a.storage.getItem(KBD_STORAGE_KEY)) ? this.gameKeys = a: this.resetKeys();
|
|
window.addEventListener("keydown", this.onkeydown.bind(this), !1);
|
|
window.addEventListener("keyup", this.onkeyup.bind(this), !1);
|
|
window.addEventListener("blur", this.onblur.bind(this), !1);
|
|
var b = this,
|
|
c = 0;
|
|
document.getElementById("reset_keys").addEventListener("click", function() {
|
|
this.textContent = "Done.;Keys resetted.;You keep failing at this, huh?;undefined;Just kidding.;Bored?;Okay ...;There once was a girl from Kentucky ...".split(";")[c++];
|
|
8 === c && (location.href = "https://www.youtube.com/watch?v=oHg5SJYRHA0");
|
|
b.resetKeys();
|
|
b.saveSettings()
|
|
}, !1);
|
|
[
|
|
["advance", KEY_ADVANCE],
|
|
["left", KEY_LEFT],
|
|
["right", KEY_RIGHT],
|
|
["shoot", KEY_SHOOT],
|
|
["jump", KEY_JUMP],
|
|
["mute", KEY_MUTE],
|
|
["restart", KEY_RESTART]
|
|
].forEach(function(a) {
|
|
var c = a[1];
|
|
a = document.getElementById("change_" + a[0]);
|
|
var d = document.getElementById("keys"),
|
|
f = document.getElementById("press_key_msg");
|
|
a.addEventListener("click", function() {
|
|
d.style.display = "none";
|
|
f.style.display = "block";
|
|
window.addEventListener("keydown", function k(a) {
|
|
27 !==
|
|
a.which && (Object.deleteByValue(b.gameKeys, c), b.gameKeys[a.which] = c, b.saveSettings());
|
|
window.removeEventListener("keydown", k, !1);
|
|
d.style.display = "block";
|
|
f.style.display = "none";
|
|
a.preventDefault()
|
|
}, !1)
|
|
}, !1)
|
|
})
|
|
GLOBAL_KEYS_DOWN = this.keyWasPressed;
|
|
//TEST = this.keysPressed;
|
|
/*if(replay){
|
|
this.keyWasPressed = REPLACE_FROM_CONSOLE(this.keyWasPressed);
|
|
}else{
|
|
replay_line=0;
|
|
}*/
|
|
}
|
|
KeyboardManager.prototype.resetKeys = function() {
|
|
this.gameKeys = {
|
|
70: KEY_ADVANCE,
|
|
38: KEY_UP,
|
|
40: KEY_DOWN,
|
|
37: KEY_LEFT,
|
|
39: KEY_RIGHT,
|
|
32: KEY_JUMP,
|
|
82: KEY_RESTART,
|
|
77: KEY_MUTE,
|
|
84: KEY_SHOOT,
|
|
81: KEY_SUICIDE
|
|
}
|
|
};
|
|
KeyboardManager.prototype.saveSettings = function() {
|
|
this.game.storage.setItem(KBD_STORAGE_KEY, this.gameKeys)
|
|
};
|
|
KeyboardManager.prototype.isValid = function(a) {
|
|
return !(a.ctrlKey || a.altKey || a.metaKey || a.target instanceof HTMLTextAreaElement || a.target instanceof HTMLInputElement || !this.gameKeys[a.which])
|
|
};
|
|
KeyboardManager.prototype.onkeydown = function(a) {
|
|
this.keysPressed[a.which] ? a.preventDefault() : this.isValid(a) && (this.keysPressed[a.which] = !0, this.handleKey(!1, a.which), a.preventDefault())
|
|
};
|
|
KeyboardManager.prototype.onkeyup = function(a) {
|
|
this.isValid(a) && (this.keysPressed[a.which] = !1, this.handleKey(!0, a.which), a.preventDefault())
|
|
};
|
|
KeyboardManager.prototype.onblur = function() {
|
|
for (var a = Object.keys(this.keysPressed), b = 0; b < a.length; b++) {
|
|
var c = a[b];
|
|
this.keysPressed[c] && this.handleKey(!0, Number(c))
|
|
}
|
|
this.keysPressed = {}
|
|
};
|
|
KeyboardManager.prototype.handleKey = function(a, b) {
|
|
b = this.gameKeys[b];
|
|
b === KEY_MUTE ? a || this.game.toggleMute() : b === KEY_RESTART && (a || this.game.restart());
|
|
b === KEY_SUICIDE ? a || this.game.die() : this.keyWasPressed[b] = !a;
|
|
b === KEY_ADVANCE ? a || advance_ft() : this.keyWasPressed[b] = !a
|
|
};
|
|
window.requestAnimationFrame || (window.requestAnimationFrame = window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame);
|
|
|
|
function btn(num){
|
|
if(num){
|
|
return 1;
|
|
}else{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
function advance_ft(){
|
|
console.log("advance_ft");
|
|
FORCE_STOP_ENGINE_TICK_FUNCTION = false;
|
|
STOP_ON_NEXT_TICK = true;
|
|
//console.log(GLOBAL_KEYS_DOWN);
|
|
document.getElementById("keyoutputid").value+='\n';
|
|
document.getElementById("keyoutputid").value+=btn(GLOBAL_KEYS_DOWN[KEY_LEFT]);
|
|
document.getElementById("keyoutputid").value+=btn(GLOBAL_KEYS_DOWN[KEY_RIGHT]);
|
|
document.getElementById("keyoutputid").value+=btn(GLOBAL_KEYS_DOWN[KEY_JUMP]);
|
|
document.getElementById("keyoutputid").value+=btn(GLOBAL_KEYS_DOWN[KEY_SHOOT]);
|
|
}
|
|
|
|
function GameRenderer(a) {
|
|
var b = document.getElementById("canvas");
|
|
this.context = b.getContext("2d");
|
|
b.width = a.width;
|
|
b.height = a.height;
|
|
if (1 < window.devicePixelRatio) {
|
|
var c = a.width / window.devicePixelRatio * 2,
|
|
d = a.height / window.devicePixelRatio * 2;
|
|
c < window.innerWidth && d < window.innerHeight && (b.style.width = c + "px", b.style.height = d + "px", b.style.imageRendering = "crisp-edges", b.style.imageRendering = "pixelated")
|
|
}
|
|
this.canvas = b;
|
|
this.backgroundCanvas = null;
|
|
this.animationTick = 0;
|
|
this.animations = {};
|
|
this.game = a
|
|
if(DRAW_TAS_INFO){
|
|
GameRenderer.prototype.drawTASInfo(this.context);
|
|
}
|
|
}
|
|
GameRenderer.prototype.loadBackground = function(a) {
|
|
this.backgroundCanvas = this.imagesToCanvas(a, this.game.level.width, this.game.level.height, this.game.level.backgroundColor)
|
|
};
|
|
GameRenderer.prototype.loadForeground = function(a) {
|
|
this.foregroundCanvas = this.imagesToCanvas(a, this.game.level.width, this.game.level.height)
|
|
};
|
|
GameRenderer.prototype.imagesToCanvas = function(a, b, c, d) {
|
|
var e = document.createElement("canvas"),
|
|
g = e.getContext("2d");
|
|
e.width = b;
|
|
e.height = c;
|
|
d && (g.fillStyle = d, g.fillRect(0, 0, b, c));
|
|
a.forEach(function(a) {
|
|
g.drawImage(a.image, a.x, a.y, a.width, a.height)
|
|
});
|
|
return e
|
|
};
|
|
GameRenderer.prototype.drawAnimation = function(a, b, c) {
|
|
a = this.game.level.animations[a];
|
|
this.context.drawImage(this.game.images[a.images[(this.animationTick / a.time | 0) % a.images.length]], b, c)
|
|
};
|
|
GameRenderer.prototype.drawImageOrAnimation = function(a, b, c) {
|
|
this.game.level.animations[a] ? this.drawAnimation(a, b, c) : this.context.drawImage(this.game.images[a], b, c)
|
|
};
|
|
GameRenderer.prototype.redraw = function() {
|
|
var a = this.context,
|
|
b = this.game,
|
|
c = b.images;
|
|
this.animationTick++;
|
|
a.drawImage(this.backgroundCanvas, b.viewportX, b.viewportY, b.width, b.height, 0, 0, b.width, b.height);
|
|
if (!b.dead) {
|
|
var d = "char";
|
|
b.fallingState === NOT_FALLING ? b.isMoving && (d += "Moving") : d = 0 < b.vspeed ? d + "Falling" : d + "Jumping";
|
|
d = b.direction == LEFT ? d + "Left" : d + "Right";
|
|
this.drawImageOrAnimation(d, b.posX - b.viewportX, b.posY - b.viewportY)
|
|
}
|
|
b.drawableObjects.forEach(function(c) {
|
|
c.visible && c.x + c.width >= b.viewportX && c.x <
|
|
b.viewportX + b.width && c.y + c.height >= b.viewportY && c.y < b.viewportY + b.height && a.drawImage(c.image, Math.round(c.x - b.viewportX), Math.round(c.y - b.viewportY), c.width, c.height)
|
|
});
|
|
b.dead && a.drawImage(c.gameOver, 0, 150)
|
|
if(DRAW_TAS_INFO){
|
|
GameRenderer.prototype.drawTASInfo(this.context);
|
|
}
|
|
};
|
|
GameRenderer.prototype.drawLoadingScreen = function(a, b) {
|
|
this.context.fillStyle = "#000";
|
|
this.context.fillRect(0, 0, this.game.width, this.game.height);
|
|
this.context.fillStyle = "#fff";
|
|
this.context.font = "20px monospace";
|
|
this.context.textAlign = "center";
|
|
this.context.fillText("Loading resources. Please wait ...", this.game.width >> 1, 100);
|
|
this.context.fillText(a + " out of " + b, this.game.width >> 1, 140)
|
|
};
|
|
GameRenderer.prototype.drawTASInfo = function(cont){
|
|
cont.fillStyle = "#0aff00";
|
|
cont.font = "20px monospace";
|
|
cont.textAlign = "right";
|
|
cont.fillText("X: " + "nye", 0, 0);
|
|
};
|
|
Math.sign = function(a) {
|
|
return (0 < a) - (0 > a)
|
|
};
|
|
Math.roundInfinity = function(a) {
|
|
return 0 < a ? Math.ceil(a) : Math.floor(a)
|
|
};
|
|
Math.floatToRandom = function(a) {
|
|
var b = Math.floor(a);
|
|
return b + (Math.random() > a - b)
|
|
};
|
|
Math.triangle = function(a) {
|
|
return function(b) {
|
|
b /= a;
|
|
return -1 + 4 * Math.abs(b + .25 - Math.floor(b + .75))
|
|
}
|
|
};
|
|
Math.rectangle = function(a) {
|
|
var b = Math.triangle(a);
|
|
return function(a) {
|
|
return Math.sign(b(a))
|
|
}
|
|
};
|
|
Math.compare = function(a, b) {
|
|
return Math.sign(a - b)
|
|
};
|
|
Array.prototype.findIndex = function(a) {
|
|
var b;
|
|
this.some(function(c, d) {
|
|
if (a(c)) return b = d, !0
|
|
});
|
|
return b
|
|
};
|
|
Array.prototype.find = function(a) {
|
|
a = this.findIndex(a);
|
|
if (void 0 !== a) return this[a]
|
|
};
|
|
Array.prototype.delete = function(a) {
|
|
a = this.indexOf(a);
|
|
return -1 !== a ? this.slice(0, a).concat(this.slice(a + 1)) : this
|
|
};
|
|
Array.prototype.concatMap = function(a) {
|
|
var b = [];
|
|
this.forEach(function(c) {
|
|
b = b.concat(a(c))
|
|
});
|
|
return b
|
|
};
|
|
Array.prototype.deleteList = function(a) {
|
|
return this.filter(function(b) {
|
|
return -1 === a.indexOf(b)
|
|
})
|
|
};
|
|
Array.replicate = function(a, b) {
|
|
for (var c = [], d = 0; d < a; d++) c[d] = b;
|
|
return c
|
|
};
|
|
Array.prototype.partition = function(a) {
|
|
return this.reduce(function(b, c) {
|
|
a(c) ? b[0].push(c) : b[1].push(c);
|
|
return b
|
|
}, [
|
|
[],
|
|
[]
|
|
])
|
|
};
|
|
Array.toArray = function(a) {
|
|
return [].slice.call(a)
|
|
};
|
|
Function.byIndex = function(a, b) {
|
|
return function(c) {
|
|
return c[a] === b
|
|
}
|
|
};
|
|
Object.deepcopy = function clone(a) {
|
|
if ("object" === typeof a) {
|
|
if (a instanceof Array) return a.map(clone);
|
|
if (a.__proto__)
|
|
for (var c = {
|
|
__proto__: a.__proto__
|
|
}, d = Object.keys(a), e = 0; e < d.length; e++) c[d[e]] = a[d[e]];
|
|
else
|
|
for (d in c = {}, a) c[d] = clone(a[d]);
|
|
return c
|
|
}
|
|
return a
|
|
};
|
|
Object.isArray = function(a) {
|
|
return a instanceof Array
|
|
};
|
|
Function.not = function(a) {
|
|
return function(b) {
|
|
return !a(b)
|
|
}
|
|
};
|
|
|
|
function range(a, b, c) {
|
|
var d = [];
|
|
c = c || 1;
|
|
dbg_assert(0 < c);
|
|
void 0 === b && (b = a, a = 0);
|
|
for (; a < b; a += c) d.push(a);
|
|
return d
|
|
}
|
|
Function.hook = function(a, b, c) {
|
|
var d = a[b];
|
|
a[b] = function() {
|
|
d.apply(this, arguments);
|
|
c.apply(this, arguments)
|
|
}
|
|
};
|
|
Object.extend = function(a, b) {
|
|
for (var c = Object.keys(b), d, e = 0; e < c.length; e++) d = c[e], a[d] = b[d];
|
|
return a
|
|
};
|
|
Object.deleteByValue = function(a, b) {
|
|
for (var c = Object.keys(a), d = 0; d < c.length; d++) a[c[d]] === b && delete a[c[d]]
|
|
};
|
|
Object.merge = function(a, b) {
|
|
return Object.extend(Object.extend({}, a), b)
|
|
};
|
|
Object.values = function(a) {
|
|
for (var b = Object.keys(a), c = [], d = 0; d < b.length; d++) c.push(a[b[d]]);
|
|
return c
|
|
};
|
|
|
|
function dbg_log(a) {
|
|
document.getElementById("debug").textContent = a
|
|
}
|
|
|
|
function dbg_warn(a) {
|
|
document.getElementById("warn").textContent += a + "\n"
|
|
}
|
|
|
|
function dbg_assert(a, b) {
|
|
if (!a) throw console.trace(), "Assert failed: " + b;
|
|
}
|
|
|
|
function http_get(a, b, c) {
|
|
var d = new XMLHttpRequest;
|
|
d.onreadystatechange = function() {
|
|
4 === d.readyState && (200 === d.status ? b(d.responseText, a) : c && c(d.responseText, d.status))
|
|
};
|
|
d.open("get", a, !0);
|
|
d.send("");
|
|
return {
|
|
cancel: function() {
|
|
d.abort()
|
|
}
|
|
}
|
|
}
|
|
|
|
function cartesianProductOnObjects(a, b) {
|
|
Object.isArray(a) || (a = [a]);
|
|
return b.reduce(function(a, b) {
|
|
return a.concatMap(function(a) {
|
|
var c = a[b];
|
|
return Object.isArray(c) ? c.map(function(c) {
|
|
var d = Object.deepcopy(a);
|
|
d[b] = c;
|
|
return d
|
|
}) : [a]
|
|
})
|
|
}, a)
|
|
}; + function() {
|
|
var a = "";
|
|
window.addEventListener("keyup", function(b) {
|
|
a = (a + b.which).substr(-14);
|
|
83677278798377 == a && (localStorage.last_level = '"2up.js"', localStorage.removeItem("state"), location.reload())
|
|
}, !1)
|
|
}(); |