Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
User prompt
Fix Bug: 'ReferenceError: enter is not defined' in this line: 'self.enter = enter;' Line Number: 434
Code edit (5 edits merged)
Please save this source code
User prompt
Fix Bug: 'ReferenceError: columnIndex is not defined' in this line: 'if (!player.invulnerability && player.y > min && player.y < max && playerColumn >= column.index - 1 && playerColumn <= columnIndex + 1) {' Line Number: 387
Code edit (9 edits merged)
Please save this source code
User prompt
Fix Bug: 'TypeError: newColumn is undefined' in this line: 'var sideBox = newColumn.boxes[rowIndex];' Line Number: 580
Code edit (1 edits merged)
Please save this source code
var BorderedText = Container.expand(function (string, settings) { var self = Container.call(this); var textList = []; var defaultFill = '#ffffff'; var defaultBorder = '#000000'; var defaultFont = 'Arial'; var defaultSize = 80; var weight = 6; var r3by2 = Math.sqrt(3) / 2; var offsets = [[0, 1], [r3by2, 0.5], [r3by2, -0.5], [0, -1], [-r3by2, -0.5], [-r3by2, 0.5], [0, 0]]; var borderSettings = { fill: settings.border || defaultBorder, font: settings.font || defaultFont, size: settings.size || defaultSize }; var textSettings = { fill: settings.fill || defaultFill, font: settings.font || defaultFont, size: settings.size || defaultSize }; for (var i = 0; i < offsets.length; i++) { var localSettings = i === offsets.length - 1 ? textSettings : borderSettings; var text = self.addChild(new Text2(string, localSettings)); text.x += offsets[i][0] * weight; text.y += offsets[i][1] * weight; if (settings.anchor) { text.anchor.set(settings.anchor.x || 0, settings.anchor.y || 0); } textList.push(text); } ; self.x = settings.x; self.y = settings.y; self.setText = setText; self.setFill = setFill; ; function setText(string) { for (var i = 0; i < textList.length; i++) { textList[i].setText(string); } } function setFill(newFill) { textList[textList.length - 1].fill = newFill; } }); var Interface = Container.expand(function () { var self = Container.call(this); var score = 0; var shown = true; var scoreTxt = self.addChild(new BorderedText(score.toString(), { size: 150, anchor: { x: .5 } })); var moveInstructions = self.addChild(new BorderedText('Swipe left or right to move', { y: scoreTxt.height + 20, anchor: { x: .5 } })); var jumpInstructions = self.addChild(new BorderedText('Swipe up or tap to jump', { y: moveInstructions.y + moveInstructions.height + 10, anchor: { x: .5 } })); ; self.isPaused = false; self.isGameOver = false; self.deathReason = ''; self.increment = increment; self.gameOver = gameOver; self.hideInstructions = hideInstructions; ; function increment(value) { score += value; scoreTxt.setText(score.toString()); LK.setScore(score); } function gameOver() { LK.effects.flashScreen(0xFF0000, 1000); var deathReasonTxt = self.addChild(new BorderedText(self.deathReason, { y: scoreTxt.y + scoreTxt.height + 20, anchor: { x: .5 } })); hideInstructions(); LK.showGameOver(); } function hideInstructions() { if (shown) { shown = false; moveInstructions.destroy(); jumpInstructions.destroy(); } } }); var ShiftableContainer = Container.expand(function () { var self = Container.call(this); ; self.shift = shift; ; function shift(amount) { self.y += amount; } ; return self; }); var Background = ShiftableContainer.expand(function () { var self = ShiftableContainer.call(this); var baseShift = self.shift; var backgroundBotLGraphics = self.createAsset('backgroundLeft', 'Background bottom left image', 0, 0); var backgroundBotRGraphics = self.createAsset('backgroundRight', 'Background bottom right image', 1, 0); var backgroundTopLGraphics = self.createAsset('backgroundLeft', 'Background top left image', 0, 0); var backgroundTopRGraphics = self.createAsset('backgroundRight', 'Background top right image', 1, 0); backgroundBotLGraphics.width = STAGE_WIDTH / 2; backgroundBotRGraphics.width = STAGE_WIDTH / 2; backgroundBotLGraphics.height = STAGE_HEIGHT; backgroundBotRGraphics.height = STAGE_HEIGHT; backgroundBotRGraphics.x = STAGE_WIDTH; backgroundTopLGraphics.width = STAGE_WIDTH / 2; backgroundTopRGraphics.width = STAGE_WIDTH / 2; backgroundTopLGraphics.height = STAGE_HEIGHT; backgroundTopRGraphics.height = STAGE_HEIGHT; backgroundTopRGraphics.x = STAGE_WIDTH; backgroundTopLGraphics.scale.y = -1; backgroundTopRGraphics.scale.y = -1; ; self.shift = shift; ; function shift(amount) { baseShift(amount); ; if (self.y > STAGE_HEIGHT) { self.y -= STAGE_HEIGHT; } return false; } }); var Floor = ShiftableContainer.expand(function (x, y) { var self = ShiftableContainer.call(this); var shiftCount = 0; var floorGraphics = self.createAsset('floor', 'Floor image', 0.5, 0.5); ; self.x = x; self.y = y; self.shift = shift; ; function shift(amount) { shiftCount += amount; floorGraphics.y += amount; return shiftCount > floorGraphics.height / 2; } }); var ExplosionEffect = ShiftableContainer.expand(function (x, y) { var self = ShiftableContainer.call(this); var alphaMax = 0.8; var durationMax = 10; var duration = durationMax; var explosionGraphics = self.createAsset('explosion', 'Explosion graphics', 0.5, 0.5); ; self.x = x; self.y = y; self.update = update; ; function update() { var progress = duration / durationMax; explosionGraphics.alpha = alphaMax * progress; explosionGraphics.scale.set(2 - progress); return !duration--; } }); var Box = ShiftableContainer.expand(function (x, y, args) { var self = ShiftableContainer.call(this); var baseShift = self.shift; var {index, column} = args; var speed = BOX_SPEED; ; self.x = x; self.y = y; self.alive = true; self.active = false; self.falling = true; self.gravity = BOX_GRAVITY; self.index = index; self.build = build; self.update = update; self.activation = activation; self.collide = collide; self.touch = touch; self.shift = shift; self.deathReason = DEATH_CRUSH_BOX; ; function build(name, xAnchor, yAnchor, variations) { var boxGraphics; if (name) { var nameVariant = !variations || variations <= 1 ? '' : 1 + Math.floor(Math.random() * variations); boxGraphics = self.createAsset(name + nameVariant + 'Box', 'Box graphics', xAnchor, yAnchor); } var shadowGraphics = self.createAsset('shadow', 'Shadow image', 0.5, 0.5); var hitboxGraphics = self.createAsset('hitbox', 'Hitbox graphics', .5, .25); shadowGraphics.alpha = 0.5; shadowGraphics.y = -BOX_HEIGHT * 0.5; hitboxGraphics.width = BOX_WIDTH * 0.9; hitboxGraphics.height = BOX_HEIGHT * 0.6; hitboxGraphics.alpha = 0; self.graphic = boxGraphics; self.hitbox = hitboxGraphics; self.shadow = shadowGraphics; } function update(args) { var destroyNextTick = false; var upperBox = column.boxes[self.index + 1]; var shadowVisible = upperBox && upperBox.falling; self.shadow.visible = shadowVisible; if (shadowVisible) { var scaleFraction = 1 - (self.y - upperBox.y) / STAGE_HEIGHT; var scale = SHADOW_MIN_SCALE + (1 - SHADOW_MIN_SCALE) * scaleFraction; self.shadow.scale.set(scale); } if (self.alive && self.falling) { var {player} = args; speed += self.gravity; self.y += speed; var targetHeight = BOX_LINE - self.index * BOX_HEIGHT; if (self.y >= targetHeight) { speed = 0; self.x = 0; self.y = targetHeight; self.falling = false; column.count++; column.touch(self, self.index - 1); } else if (player.hitbox.intersects(self.hitbox)) { destroyNextTick = self.collide(args); } } if (!destroyNextTick && self.active) { self.activation(args); } if (!self.alive) { column.remove(self); return true; } if (destroyNextTick) { self.alive = false; } } function activation(args) {} function collide(args) { var {player, interface} = args; if (!player.invulnerability) { interface.isGameOver = true; interface.deathReason = self.deathReason; } return false; } function touch(target) {} function shift(amount) { baseShift(amount); ; if (self.y > BOX_LINE) { column.remove(self); return true; } return false; } ; return self; }); var InvisBox = Box.expand(function (x, y, args) { var self = Box.call(this, x, y, args); ; self.build(); }); var BasicBox = Box.expand(function (x, y, args) { var self = Box.call(this, x, y, args); ; self.build('basic', .5, .5, 3); }); var StoneBox = Box.expand(function (x, y, args) { var self = Box.call(this, x, y, args); ; self.build('stone', .5, .5); self.deathReason = DEATH_CRUSH_STONE; }); var PointsBox = Box.expand(function (x, y, args) { var self = Box.call(this, x, y, args); ; self.build('points', .5, .55, 2); self.activation = activation; self.collide = collide; self.touch = touch; ; function activation(args) { var {interface} = args; interface.increment(POINTS_GAIN_PICKUP); } function collide(args) { self.active = true; return true; } function touch(target) { if (target instanceof Player) { self.active = true; self.alive = false; } else if (target instanceof StoneBox) { self.alive = false; } } }); var GoldBox = Box.expand(function (x, y, args) { var self = Box.call(this, x, y, args); ; self.build('gold', .5, .5); self.activation = activation; self.collide = collide; self.touch = touch; ; function activation(args) { var {player} = args; player.invulnerability += INVULNERABILITY_TIME; } function collide(args) { self.active = true; return true; } function touch(target) { if (target instanceof Player) { self.active = true; self.alive = false; } else if (target instanceof StoneBox) { self.alive = false; } } }); var TntBox = Box.expand(function (x, y, args) { var self = Box.call(this, x, y, args); var baseCollide = self.collide; var {column} = args; var countdown = 0; ; self.build('tnt', .5, .55); self.activation = activation; self.collide = collide; self.touch = touch; self.deathReason = DEATH_CRUSH_TNT; ; function activation(args) { if (countdown <= 0) { explode(args); } else if (countdown % (countdown >= STAGE_TICKS ? 30 : 15) === 0) { LK.effects.flashObject(self.graphic, 0xff0000, 500); } countdown--; } function collide(args) { baseCollide(args); ; if (!self.active) { self.active = true; countdown = TNT_COUNTDOWN; } return false; } function touch(target) { if (target instanceof Player) { if (!self.active) { self.active = true; self.deathReason = DEATH_TNT_BASIC; countdown = TNT_COUNTDOWN; } } else if (target instanceof StoneBox) { self.active = true; self.deathReason = DEATH_TNT_INSTA; countdown = 0; } } function explode(args) { var {game, player, interface, columnList, effectList} = args; effectList.push(game.addChild(new ExplosionEffect(column.x, self.y))); self.alive = false; var min = self.y - BOX_HEIGHT * 1.25; var max = self.y + BOX_HEIGHT * 1.25; if (!player.invulnerability && player.y > min && player.y < max && player.x > column.x - BOX_WIDTH * 1.5 && player.x < column.x + BOX_WIDTH * 1.5) { interface.isGameOver = true; interface.deathReason = self.deathReason; } for (var i = column.index - 1; i <= column.index + 1; i++) { var col = columnList[i]; if (col) { for (var j = 0; j < col.boxes.length; j++) { var box = col.boxes[j]; if (box.y < max) { if (box.y > min) { if (box !== self) { if (box instanceof TntBox) { box.active = true; box.deathReason = DEATH_TNT_CHAIN; } else if (!(box instanceof StoneBox || box instanceof InvisBox)) { box.alive = false; } } } else { break; } } } } } } }); var Column = Container.expand(function (x, y, args) { var self = Container.call(this); var {index, boxList} = args; var boxes = []; var countdown = SPAWN_INITIAL + getCountdown(); var defaultBox = self.addChild(new InvisBox(0, BOX_LINE, { column: self, index: 0 })); boxList.push(defaultBox); boxes.push(defaultBox); ; self.x = x; self.y = y; self.boxes = boxes; self.index = index; self.touch = touch; self.remove = remove; self.update = update; self.enter = enter; self.count = 0; ; function getCountdown() { return SPAWN_CONST + Math.floor(Math.random() * (SPAWN_VARIANCE + SPAWN_COUNT_VARIANCE * boxes.length)); } function touch(target, index) { var box = self.boxes[index]; if (box) { box.touch(target); } } function remove(box) { var index = box.index; boxes.splice(index, 1); if (!box.falling) { self.count--; } for (var i = index; i < boxes.length; i++) { var box = boxes[i]; box.index--; if (!box.falling) { self.count--; box.falling = true; box.gravity = PLAYER_GRAVITY; } } } function update(args) { if (--countdown <= 0) { countdown = getCountdown(); if (self.count < COLUMN_VOLUME) { var {boxList} = args; var typeValue = Math.random(); var typeInstance = BasicBox; if ((typeValue -= SPAWN_GOLD_CHANCE) < 0) { typeInstance = GoldBox; } else if ((typeValue -= SPAWN_TNT_CHANCE) < 0) { typeInstance = TntBox; } else if ((typeValue -= SPAWN_POINTS_CHANCE) < 0) { typeInstance = PointsBox; } else if ((typeValue -= SPAWN_STONE_CHANCE) < 0) { typeInstance = StoneBox; } var box = self.addChild(new typeInstance(0, SPAWN_OFFSET, { column: self, index: boxes.length })); boxList.push(box); boxes.push(box); } } } function enter(player) { self.addChild(player); self.children.sort(function (a, b) { return a.y - b.y; }); } }); var Player = ShiftableContainer.expand(function (x, y) { var self = ShiftableContainer.call(this); var baseShift = self.shift; var isJumping = false; var verticalSpeed = 0; var targetDirection = 0; var targetX = 0; var playerGraphics = self.createAsset('player', 'Player character', .5, .5); var hitboxGraphics = self.createAsset('hitbox', 'Player hitbox', .5, .5); var invR = 0xFF; var invG = 0xD7; var invB = 0x00; playerGraphics.width = PLAYER_SIZE; playerGraphics.height = PLAYER_SIZE; hitboxGraphics.width = PLAYER_SIZE * 0.6; hitboxGraphics.height = PLAYER_SIZE * 0.8; hitboxGraphics.alpha = 0; ; self.x = x; self.y = y; self.heightClimbed = BOX_HEIGHT; self.invulnerability = 0; self.airborne = false; self.hitbox = hitboxGraphics; self.move = move; self.jump = jump; self.update = update; self.shift = shift; ; function getRowIndex() { return Math.floor((BOX_LINE - self.y) / BOX_HEIGHT + 0.5); } function move(direction) { if (!targetDirection) { targetDirection = direction; targetX += direction * BOX_WIDTH; } } function jump() { if (!self.airborne) { self.airborne = true; verticalSpeed = PLAYER_JUMP_SPEED; } } function update(args) { var {interface, columnList} = args; var column = self.parent; var targetY = BOX_LINE - column.count * BOX_HEIGHT; if (self.invulnerability > 0) { self.invulnerability--; var factor = 1 - Math.min(self.invulnerability, INVULNERABILITY_WARNING) / INVULNERABILITY_WARNING; var newR = (invR + Math.floor((0xFF - invR) * factor)) * Math.pow(16, 4); var newG = (invG + Math.floor((0xFF - invG) * factor)) * Math.pow(16, 2); var newB = (invB + Math.floor((0xFF - invB) * factor)) * Math.pow(16, 0); playerGraphics.tint = newR + newG + newB; } if (targetDirection) { var moveSpeed = Math.min(targetX + self.x * targetDirection, PLAYER_MOVE_SPEED); self.x += moveSpeed * targetDirection; playerGraphics.rotation = Math.PI * (column.index + self.x / BOX_WIDTH); } if (self.airborne) { verticalSpeed -= PLAYER_GRAVITY; self.y -= verticalSpeed; if (self.y >= targetY) { self.y = targetY; self.airborne = false; verticalSpeed = 0; if (self.y >= BOX_LINE) { interface.isGameOver = true; interface.deathReason = DEATH_FALLDOWN; } } } else { if (self.y < targetY) { self.airborne = true; } else { self.y = targetY; } } var overhang = Math.max(0, Math.abs(self.x) - BOX_WIDTH / 2); if (overhang > 0) { var newColumn = columnList[column.index + targetDirection]; var newRowIndex = getRowIndex(); var sideBox = newColumn.boxes[newRowIndex]; var moveDist = BOX_WIDTH * targetDirection; if (sideBox && !sideBox.falling) { sideBox.touch(self); self.x -= overhang * 2 * targetDirection; targetX -= moveDist; targetDirection = -targetDirection; playerGraphics.rotation = Math.PI * (column.index + self.x / BOX_WIDTH); } else { newColumn.enter(self); targetX += moveDist; self.x += moveDist; var underBox = newColumn.boxes[newRowIndex - 1]; if (!underBox || underBox.falling) { self.airborne = true; } } } if (!self.airborne) { var prevHeightIndex = Math.round(self.heightClimbed / BOX_HEIGHT); columnList[newColIndex].touch(self, newRowIndex - 1); if (newRowIndex > prevHeightIndex) { interface.increment((newRowIndex - prevHeightIndex) * POINTS_GAIN_CLIMBING); self.heightClimbed = newRowIndex * BOX_HEIGHT; if (newRowIndex >= SHIFT_THRESHOLD) { return true; } } } return false; } function shift(amount) { baseShift(amount); ; self.heightClimbed -= amount; } }); ; var STAGE_WIDTH = 2048; var STAGE_HEIGHT = 2732; var STAGE_TICKS = 60; var CONTROL_SWIPE_DIST = 100; var CONTROL_TAP_TICKS = STAGE_TICKS / 3; var NUM_COLUMNS = 9; var BOX_LINE = STAGE_HEIGHT; var BOX_WIDTH = STAGE_WIDTH / NUM_COLUMNS; var BOX_HEIGHT = 0.75 * BOX_WIDTH; var BOX_SPEED = 5; var BOX_GRAVITY = 0.2; var TNT_COUNTDOWN = 2 * STAGE_TICKS; var INVULNERABILITY_TIME = 5 * STAGE_TICKS; var INVULNERABILITY_WARNING = STAGE_TICKS; var COLUMN_VOLUME = Math.ceil(BOX_LINE / BOX_HEIGHT); var PLAYER_SIZE = 0.8 * BOX_WIDTH; var PLAYER_GRAVITY = 0.5; var PLAYER_JUMP_SPEED = 20; var PLAYER_MOVE_SPEED = 25; var SPAWN_OFFSET = -100; var SPAWN_INITIAL = 3 * STAGE_TICKS; var SPAWN_CONST = STAGE_TICKS / 2; var SPAWN_VARIANCE = 10 * STAGE_TICKS - SPAWN_CONST; var SPAWN_COUNT_VARIANCE = STAGE_TICKS / 4; var SHADOW_MIN_SCALE = 0.4; var SPAWN_TNT_CHANCE = 0.05; var SPAWN_GOLD_CHANCE = 0.02; var SPAWN_POINTS_CHANCE = 0.08; var SPAWN_STONE_CHANCE = 0.1; var POINTS_GAIN_CLIMBING = 1; var POINTS_GAIN_PICKUP = 5; var SHIFT_THRESHOLD = 6; var SHIFT_COUNT = 3; var SHIFT_DURATION = STAGE_TICKS / 3; var SHIFT_AMOUNT = SHIFT_COUNT * BOX_HEIGHT / SHIFT_DURATION; var INSTRUCTION_DURATION = 5 * STAGE_TICKS; var DEATH_FALLDOWN = 'Fell into the factory!'; var DEATH_CRUSH_BOX = 'Crushed by a packing crate!'; var DEATH_CRUSH_TNT = 'Crushed by a freshly armed TNT!'; var DEATH_CRUSH_STONE = 'Crushed by the naughty list!'; var DEATH_TNT_BASIC = 'Blew up after arming a TNT!'; var DEATH_TNT_INSTA = 'Coal detonated a TNT... And you!'; var DEATH_TNT_CHAIN = 'Blew up in a chain reaction!'; ; var Game = Container.expand(function () { var self = Container.call(this); var lastTouchX = null; var lastTouchY = null; var touchTime = null; var boxList = []; var effectList = []; var columnList = []; var shiftTicks = 0; var background = self.addChild(new Background(self)); var floor = self.addChild(new Floor(STAGE_WIDTH / 2, STAGE_HEIGHT)); for (var i = 0; i < NUM_COLUMNS; i++) { columnList.push(self.addChild(new Column((i + 0.5) * BOX_WIDTH, 0, { index: i, boxList }))); } var midColumn = columnList[Math.floor(NUM_COLUMNS / 2)]; var player = midColumn.addChild(new Player(0, BOX_LINE - PLAYER_SIZE / 2)); var interface = LK.gui.topCenter.addChild(new Interface()); var shiftContainers = [[player, background, floor], boxList, effectList]; ; stage.on('down', function (obj) { var event = obj.event; lastTouchX = event.global.x; lastTouchY = event.global.y; touchTime = LK.ticks; }); stage.on('up', function (obj) { if (touchTime !== null && LK.ticks - touchTime < CONTROL_TAP_TICKS) { player.jump(); } lastTouchX = null; lastTouchY = null; touchTime = null; }); stage.on('move', function (obj) { if (touchTime !== null) { var reset = false; var event = obj.event; var deltaX = lastTouchX - event.global.x; var deltaY = lastTouchY - event.global.y; if (Math.abs(deltaX) > CONTROL_SWIPE_DIST) { var direction = deltaX > 0 ? -1 : 1; player.move(direction); reset = true; } else if (deltaY > CONTROL_SWIPE_DIST) { player.jump(); reset = true; } if (reset) { touchTime = null; lastTouchX = null; lastTouchY = null; } } }); LK.on('tick', function () { if (interface.isGameOver) { interface.gameOver(); } else if (interface.isPaused = shiftTicks > 0) { shiftTicks--; shiftContainers.forEach(function (containers) { for (var i = containers.length - 1; i >= 0; i--) { var container = containers[i]; if (container.shift(SHIFT_AMOUNT)) { container.destroy(); containers.splice(i, 1); } } }); } else { var playerArgs = { interface, columnList }; if (player.update(playerArgs)) { shiftTicks += SHIFT_DURATION; } ; var columnArgs = { game: self, boxList }; for (var i = 0; i < columnList.length; i++) { columnList[i].update(columnArgs); } ; var boxArgs = { game: self, columnList, effectList, interface, player }; for (var i = 0; i < boxList.length; i++) { var box = boxList[i]; if (box.update(boxArgs)) { box.destroy(); boxList.splice(i, 1); i--; } } ; for (var i = 0; i < effectList.length; i++) { var effect = effectList[i]; if (effect.update()) { effect.destroy(); effectList.splice(i, 1); i--; } } } if (LK.ticks === INSTRUCTION_DURATION) { interface.hideInstructions(); } }); });
var BorderedText = Container.expand(function (string, settings) {
var self = Container.call(this);
var textList = [];
var defaultFill = '#ffffff';
var defaultBorder = '#000000';
var defaultFont = 'Arial';
var defaultSize = 80;
var weight = 6;
var r3by2 = Math.sqrt(3) / 2;
var offsets = [[0, 1], [r3by2, 0.5], [r3by2, -0.5], [0, -1], [-r3by2, -0.5], [-r3by2, 0.5], [0, 0]];
var borderSettings = {
fill: settings.border || defaultBorder,
font: settings.font || defaultFont,
size: settings.size || defaultSize
};
var textSettings = {
fill: settings.fill || defaultFill,
font: settings.font || defaultFont,
size: settings.size || defaultSize
};
for (var i = 0; i < offsets.length; i++) {
var localSettings = i === offsets.length - 1 ? textSettings : borderSettings;
var text = self.addChild(new Text2(string, localSettings));
text.x += offsets[i][0] * weight;
text.y += offsets[i][1] * weight;
if (settings.anchor) {
text.anchor.set(settings.anchor.x || 0, settings.anchor.y || 0);
}
textList.push(text);
}
;
self.x = settings.x;
self.y = settings.y;
self.setText = setText;
self.setFill = setFill;
;
function setText(string) {
for (var i = 0; i < textList.length; i++) {
textList[i].setText(string);
}
}
function setFill(newFill) {
textList[textList.length - 1].fill = newFill;
}
});
var Interface = Container.expand(function () {
var self = Container.call(this);
var score = 0;
var shown = true;
var scoreTxt = self.addChild(new BorderedText(score.toString(), {
size: 150,
anchor: {
x: .5
}
}));
var moveInstructions = self.addChild(new BorderedText('Swipe left or right to move', {
y: scoreTxt.height + 20,
anchor: {
x: .5
}
}));
var jumpInstructions = self.addChild(new BorderedText('Swipe up or tap to jump', {
y: moveInstructions.y + moveInstructions.height + 10,
anchor: {
x: .5
}
}));
;
self.isPaused = false;
self.isGameOver = false;
self.deathReason = '';
self.increment = increment;
self.gameOver = gameOver;
self.hideInstructions = hideInstructions;
;
function increment(value) {
score += value;
scoreTxt.setText(score.toString());
LK.setScore(score);
}
function gameOver() {
LK.effects.flashScreen(0xFF0000, 1000);
var deathReasonTxt = self.addChild(new BorderedText(self.deathReason, {
y: scoreTxt.y + scoreTxt.height + 20,
anchor: {
x: .5
}
}));
hideInstructions();
LK.showGameOver();
}
function hideInstructions() {
if (shown) {
shown = false;
moveInstructions.destroy();
jumpInstructions.destroy();
}
}
});
var ShiftableContainer = Container.expand(function () {
var self = Container.call(this);
;
self.shift = shift;
;
function shift(amount) {
self.y += amount;
}
;
return self;
});
var Background = ShiftableContainer.expand(function () {
var self = ShiftableContainer.call(this);
var baseShift = self.shift;
var backgroundBotLGraphics = self.createAsset('backgroundLeft', 'Background bottom left image', 0, 0);
var backgroundBotRGraphics = self.createAsset('backgroundRight', 'Background bottom right image', 1, 0);
var backgroundTopLGraphics = self.createAsset('backgroundLeft', 'Background top left image', 0, 0);
var backgroundTopRGraphics = self.createAsset('backgroundRight', 'Background top right image', 1, 0);
backgroundBotLGraphics.width = STAGE_WIDTH / 2;
backgroundBotRGraphics.width = STAGE_WIDTH / 2;
backgroundBotLGraphics.height = STAGE_HEIGHT;
backgroundBotRGraphics.height = STAGE_HEIGHT;
backgroundBotRGraphics.x = STAGE_WIDTH;
backgroundTopLGraphics.width = STAGE_WIDTH / 2;
backgroundTopRGraphics.width = STAGE_WIDTH / 2;
backgroundTopLGraphics.height = STAGE_HEIGHT;
backgroundTopRGraphics.height = STAGE_HEIGHT;
backgroundTopRGraphics.x = STAGE_WIDTH;
backgroundTopLGraphics.scale.y = -1;
backgroundTopRGraphics.scale.y = -1;
;
self.shift = shift;
;
function shift(amount) {
baseShift(amount);
;
if (self.y > STAGE_HEIGHT) {
self.y -= STAGE_HEIGHT;
}
return false;
}
});
var Floor = ShiftableContainer.expand(function (x, y) {
var self = ShiftableContainer.call(this);
var shiftCount = 0;
var floorGraphics = self.createAsset('floor', 'Floor image', 0.5, 0.5);
;
self.x = x;
self.y = y;
self.shift = shift;
;
function shift(amount) {
shiftCount += amount;
floorGraphics.y += amount;
return shiftCount > floorGraphics.height / 2;
}
});
var ExplosionEffect = ShiftableContainer.expand(function (x, y) {
var self = ShiftableContainer.call(this);
var alphaMax = 0.8;
var durationMax = 10;
var duration = durationMax;
var explosionGraphics = self.createAsset('explosion', 'Explosion graphics', 0.5, 0.5);
;
self.x = x;
self.y = y;
self.update = update;
;
function update() {
var progress = duration / durationMax;
explosionGraphics.alpha = alphaMax * progress;
explosionGraphics.scale.set(2 - progress);
return !duration--;
}
});
var Box = ShiftableContainer.expand(function (x, y, args) {
var self = ShiftableContainer.call(this);
var baseShift = self.shift;
var {index, column} = args;
var speed = BOX_SPEED;
;
self.x = x;
self.y = y;
self.alive = true;
self.active = false;
self.falling = true;
self.gravity = BOX_GRAVITY;
self.index = index;
self.build = build;
self.update = update;
self.activation = activation;
self.collide = collide;
self.touch = touch;
self.shift = shift;
self.deathReason = DEATH_CRUSH_BOX;
;
function build(name, xAnchor, yAnchor, variations) {
var boxGraphics;
if (name) {
var nameVariant = !variations || variations <= 1 ? '' : 1 + Math.floor(Math.random() * variations);
boxGraphics = self.createAsset(name + nameVariant + 'Box', 'Box graphics', xAnchor, yAnchor);
}
var shadowGraphics = self.createAsset('shadow', 'Shadow image', 0.5, 0.5);
var hitboxGraphics = self.createAsset('hitbox', 'Hitbox graphics', .5, .25);
shadowGraphics.alpha = 0.5;
shadowGraphics.y = -BOX_HEIGHT * 0.5;
hitboxGraphics.width = BOX_WIDTH * 0.9;
hitboxGraphics.height = BOX_HEIGHT * 0.6;
hitboxGraphics.alpha = 0;
self.graphic = boxGraphics;
self.hitbox = hitboxGraphics;
self.shadow = shadowGraphics;
}
function update(args) {
var destroyNextTick = false;
var upperBox = column.boxes[self.index + 1];
var shadowVisible = upperBox && upperBox.falling;
self.shadow.visible = shadowVisible;
if (shadowVisible) {
var scaleFraction = 1 - (self.y - upperBox.y) / STAGE_HEIGHT;
var scale = SHADOW_MIN_SCALE + (1 - SHADOW_MIN_SCALE) * scaleFraction;
self.shadow.scale.set(scale);
}
if (self.alive && self.falling) {
var {player} = args;
speed += self.gravity;
self.y += speed;
var targetHeight = BOX_LINE - self.index * BOX_HEIGHT;
if (self.y >= targetHeight) {
speed = 0;
self.x = 0;
self.y = targetHeight;
self.falling = false;
column.count++;
column.touch(self, self.index - 1);
} else if (player.hitbox.intersects(self.hitbox)) {
destroyNextTick = self.collide(args);
}
}
if (!destroyNextTick && self.active) {
self.activation(args);
}
if (!self.alive) {
column.remove(self);
return true;
}
if (destroyNextTick) {
self.alive = false;
}
}
function activation(args) {}
function collide(args) {
var {player, interface} = args;
if (!player.invulnerability) {
interface.isGameOver = true;
interface.deathReason = self.deathReason;
}
return false;
}
function touch(target) {}
function shift(amount) {
baseShift(amount);
;
if (self.y > BOX_LINE) {
column.remove(self);
return true;
}
return false;
}
;
return self;
});
var InvisBox = Box.expand(function (x, y, args) {
var self = Box.call(this, x, y, args);
;
self.build();
});
var BasicBox = Box.expand(function (x, y, args) {
var self = Box.call(this, x, y, args);
;
self.build('basic', .5, .5, 3);
});
var StoneBox = Box.expand(function (x, y, args) {
var self = Box.call(this, x, y, args);
;
self.build('stone', .5, .5);
self.deathReason = DEATH_CRUSH_STONE;
});
var PointsBox = Box.expand(function (x, y, args) {
var self = Box.call(this, x, y, args);
;
self.build('points', .5, .55, 2);
self.activation = activation;
self.collide = collide;
self.touch = touch;
;
function activation(args) {
var {interface} = args;
interface.increment(POINTS_GAIN_PICKUP);
}
function collide(args) {
self.active = true;
return true;
}
function touch(target) {
if (target instanceof Player) {
self.active = true;
self.alive = false;
} else if (target instanceof StoneBox) {
self.alive = false;
}
}
});
var GoldBox = Box.expand(function (x, y, args) {
var self = Box.call(this, x, y, args);
;
self.build('gold', .5, .5);
self.activation = activation;
self.collide = collide;
self.touch = touch;
;
function activation(args) {
var {player} = args;
player.invulnerability += INVULNERABILITY_TIME;
}
function collide(args) {
self.active = true;
return true;
}
function touch(target) {
if (target instanceof Player) {
self.active = true;
self.alive = false;
} else if (target instanceof StoneBox) {
self.alive = false;
}
}
});
var TntBox = Box.expand(function (x, y, args) {
var self = Box.call(this, x, y, args);
var baseCollide = self.collide;
var {column} = args;
var countdown = 0;
;
self.build('tnt', .5, .55);
self.activation = activation;
self.collide = collide;
self.touch = touch;
self.deathReason = DEATH_CRUSH_TNT;
;
function activation(args) {
if (countdown <= 0) {
explode(args);
} else if (countdown % (countdown >= STAGE_TICKS ? 30 : 15) === 0) {
LK.effects.flashObject(self.graphic, 0xff0000, 500);
}
countdown--;
}
function collide(args) {
baseCollide(args);
;
if (!self.active) {
self.active = true;
countdown = TNT_COUNTDOWN;
}
return false;
}
function touch(target) {
if (target instanceof Player) {
if (!self.active) {
self.active = true;
self.deathReason = DEATH_TNT_BASIC;
countdown = TNT_COUNTDOWN;
}
} else if (target instanceof StoneBox) {
self.active = true;
self.deathReason = DEATH_TNT_INSTA;
countdown = 0;
}
}
function explode(args) {
var {game, player, interface, columnList, effectList} = args;
effectList.push(game.addChild(new ExplosionEffect(column.x, self.y)));
self.alive = false;
var min = self.y - BOX_HEIGHT * 1.25;
var max = self.y + BOX_HEIGHT * 1.25;
if (!player.invulnerability && player.y > min && player.y < max && player.x > column.x - BOX_WIDTH * 1.5 && player.x < column.x + BOX_WIDTH * 1.5) {
interface.isGameOver = true;
interface.deathReason = self.deathReason;
}
for (var i = column.index - 1; i <= column.index + 1; i++) {
var col = columnList[i];
if (col) {
for (var j = 0; j < col.boxes.length; j++) {
var box = col.boxes[j];
if (box.y < max) {
if (box.y > min) {
if (box !== self) {
if (box instanceof TntBox) {
box.active = true;
box.deathReason = DEATH_TNT_CHAIN;
} else if (!(box instanceof StoneBox || box instanceof InvisBox)) {
box.alive = false;
}
}
} else {
break;
}
}
}
}
}
}
});
var Column = Container.expand(function (x, y, args) {
var self = Container.call(this);
var {index, boxList} = args;
var boxes = [];
var countdown = SPAWN_INITIAL + getCountdown();
var defaultBox = self.addChild(new InvisBox(0, BOX_LINE, {
column: self,
index: 0
}));
boxList.push(defaultBox);
boxes.push(defaultBox);
;
self.x = x;
self.y = y;
self.boxes = boxes;
self.index = index;
self.touch = touch;
self.remove = remove;
self.update = update;
self.enter = enter;
self.count = 0;
;
function getCountdown() {
return SPAWN_CONST + Math.floor(Math.random() * (SPAWN_VARIANCE + SPAWN_COUNT_VARIANCE * boxes.length));
}
function touch(target, index) {
var box = self.boxes[index];
if (box) {
box.touch(target);
}
}
function remove(box) {
var index = box.index;
boxes.splice(index, 1);
if (!box.falling) {
self.count--;
}
for (var i = index; i < boxes.length; i++) {
var box = boxes[i];
box.index--;
if (!box.falling) {
self.count--;
box.falling = true;
box.gravity = PLAYER_GRAVITY;
}
}
}
function update(args) {
if (--countdown <= 0) {
countdown = getCountdown();
if (self.count < COLUMN_VOLUME) {
var {boxList} = args;
var typeValue = Math.random();
var typeInstance = BasicBox;
if ((typeValue -= SPAWN_GOLD_CHANCE) < 0) {
typeInstance = GoldBox;
} else if ((typeValue -= SPAWN_TNT_CHANCE) < 0) {
typeInstance = TntBox;
} else if ((typeValue -= SPAWN_POINTS_CHANCE) < 0) {
typeInstance = PointsBox;
} else if ((typeValue -= SPAWN_STONE_CHANCE) < 0) {
typeInstance = StoneBox;
}
var box = self.addChild(new typeInstance(0, SPAWN_OFFSET, {
column: self,
index: boxes.length
}));
boxList.push(box);
boxes.push(box);
}
}
}
function enter(player) {
self.addChild(player);
self.children.sort(function (a, b) {
return a.y - b.y;
});
}
});
var Player = ShiftableContainer.expand(function (x, y) {
var self = ShiftableContainer.call(this);
var baseShift = self.shift;
var isJumping = false;
var verticalSpeed = 0;
var targetDirection = 0;
var targetX = 0;
var playerGraphics = self.createAsset('player', 'Player character', .5, .5);
var hitboxGraphics = self.createAsset('hitbox', 'Player hitbox', .5, .5);
var invR = 0xFF;
var invG = 0xD7;
var invB = 0x00;
playerGraphics.width = PLAYER_SIZE;
playerGraphics.height = PLAYER_SIZE;
hitboxGraphics.width = PLAYER_SIZE * 0.6;
hitboxGraphics.height = PLAYER_SIZE * 0.8;
hitboxGraphics.alpha = 0;
;
self.x = x;
self.y = y;
self.heightClimbed = BOX_HEIGHT;
self.invulnerability = 0;
self.airborne = false;
self.hitbox = hitboxGraphics;
self.move = move;
self.jump = jump;
self.update = update;
self.shift = shift;
;
function getRowIndex() {
return Math.floor((BOX_LINE - self.y) / BOX_HEIGHT + 0.5);
}
function move(direction) {
if (!targetDirection) {
targetDirection = direction;
targetX += direction * BOX_WIDTH;
}
}
function jump() {
if (!self.airborne) {
self.airborne = true;
verticalSpeed = PLAYER_JUMP_SPEED;
}
}
function update(args) {
var {interface, columnList} = args;
var column = self.parent;
var targetY = BOX_LINE - column.count * BOX_HEIGHT;
if (self.invulnerability > 0) {
self.invulnerability--;
var factor = 1 - Math.min(self.invulnerability, INVULNERABILITY_WARNING) / INVULNERABILITY_WARNING;
var newR = (invR + Math.floor((0xFF - invR) * factor)) * Math.pow(16, 4);
var newG = (invG + Math.floor((0xFF - invG) * factor)) * Math.pow(16, 2);
var newB = (invB + Math.floor((0xFF - invB) * factor)) * Math.pow(16, 0);
playerGraphics.tint = newR + newG + newB;
}
if (targetDirection) {
var moveSpeed = Math.min(targetX + self.x * targetDirection, PLAYER_MOVE_SPEED);
self.x += moveSpeed * targetDirection;
playerGraphics.rotation = Math.PI * (column.index + self.x / BOX_WIDTH);
}
if (self.airborne) {
verticalSpeed -= PLAYER_GRAVITY;
self.y -= verticalSpeed;
if (self.y >= targetY) {
self.y = targetY;
self.airborne = false;
verticalSpeed = 0;
if (self.y >= BOX_LINE) {
interface.isGameOver = true;
interface.deathReason = DEATH_FALLDOWN;
}
}
} else {
if (self.y < targetY) {
self.airborne = true;
} else {
self.y = targetY;
}
}
var overhang = Math.max(0, Math.abs(self.x) - BOX_WIDTH / 2);
if (overhang > 0) {
var newColumn = columnList[column.index + targetDirection];
var newRowIndex = getRowIndex();
var sideBox = newColumn.boxes[newRowIndex];
var moveDist = BOX_WIDTH * targetDirection;
if (sideBox && !sideBox.falling) {
sideBox.touch(self);
self.x -= overhang * 2 * targetDirection;
targetX -= moveDist;
targetDirection = -targetDirection;
playerGraphics.rotation = Math.PI * (column.index + self.x / BOX_WIDTH);
} else {
newColumn.enter(self);
targetX += moveDist;
self.x += moveDist;
var underBox = newColumn.boxes[newRowIndex - 1];
if (!underBox || underBox.falling) {
self.airborne = true;
}
}
}
if (!self.airborne) {
var prevHeightIndex = Math.round(self.heightClimbed / BOX_HEIGHT);
columnList[newColIndex].touch(self, newRowIndex - 1);
if (newRowIndex > prevHeightIndex) {
interface.increment((newRowIndex - prevHeightIndex) * POINTS_GAIN_CLIMBING);
self.heightClimbed = newRowIndex * BOX_HEIGHT;
if (newRowIndex >= SHIFT_THRESHOLD) {
return true;
}
}
}
return false;
}
function shift(amount) {
baseShift(amount);
;
self.heightClimbed -= amount;
}
});
;
var STAGE_WIDTH = 2048;
var STAGE_HEIGHT = 2732;
var STAGE_TICKS = 60;
var CONTROL_SWIPE_DIST = 100;
var CONTROL_TAP_TICKS = STAGE_TICKS / 3;
var NUM_COLUMNS = 9;
var BOX_LINE = STAGE_HEIGHT;
var BOX_WIDTH = STAGE_WIDTH / NUM_COLUMNS;
var BOX_HEIGHT = 0.75 * BOX_WIDTH;
var BOX_SPEED = 5;
var BOX_GRAVITY = 0.2;
var TNT_COUNTDOWN = 2 * STAGE_TICKS;
var INVULNERABILITY_TIME = 5 * STAGE_TICKS;
var INVULNERABILITY_WARNING = STAGE_TICKS;
var COLUMN_VOLUME = Math.ceil(BOX_LINE / BOX_HEIGHT);
var PLAYER_SIZE = 0.8 * BOX_WIDTH;
var PLAYER_GRAVITY = 0.5;
var PLAYER_JUMP_SPEED = 20;
var PLAYER_MOVE_SPEED = 25;
var SPAWN_OFFSET = -100;
var SPAWN_INITIAL = 3 * STAGE_TICKS;
var SPAWN_CONST = STAGE_TICKS / 2;
var SPAWN_VARIANCE = 10 * STAGE_TICKS - SPAWN_CONST;
var SPAWN_COUNT_VARIANCE = STAGE_TICKS / 4;
var SHADOW_MIN_SCALE = 0.4;
var SPAWN_TNT_CHANCE = 0.05;
var SPAWN_GOLD_CHANCE = 0.02;
var SPAWN_POINTS_CHANCE = 0.08;
var SPAWN_STONE_CHANCE = 0.1;
var POINTS_GAIN_CLIMBING = 1;
var POINTS_GAIN_PICKUP = 5;
var SHIFT_THRESHOLD = 6;
var SHIFT_COUNT = 3;
var SHIFT_DURATION = STAGE_TICKS / 3;
var SHIFT_AMOUNT = SHIFT_COUNT * BOX_HEIGHT / SHIFT_DURATION;
var INSTRUCTION_DURATION = 5 * STAGE_TICKS;
var DEATH_FALLDOWN = 'Fell into the factory!';
var DEATH_CRUSH_BOX = 'Crushed by a packing crate!';
var DEATH_CRUSH_TNT = 'Crushed by a freshly armed TNT!';
var DEATH_CRUSH_STONE = 'Crushed by the naughty list!';
var DEATH_TNT_BASIC = 'Blew up after arming a TNT!';
var DEATH_TNT_INSTA = 'Coal detonated a TNT... And you!';
var DEATH_TNT_CHAIN = 'Blew up in a chain reaction!';
;
var Game = Container.expand(function () {
var self = Container.call(this);
var lastTouchX = null;
var lastTouchY = null;
var touchTime = null;
var boxList = [];
var effectList = [];
var columnList = [];
var shiftTicks = 0;
var background = self.addChild(new Background(self));
var floor = self.addChild(new Floor(STAGE_WIDTH / 2, STAGE_HEIGHT));
for (var i = 0; i < NUM_COLUMNS; i++) {
columnList.push(self.addChild(new Column((i + 0.5) * BOX_WIDTH, 0, {
index: i,
boxList
})));
}
var midColumn = columnList[Math.floor(NUM_COLUMNS / 2)];
var player = midColumn.addChild(new Player(0, BOX_LINE - PLAYER_SIZE / 2));
var interface = LK.gui.topCenter.addChild(new Interface());
var shiftContainers = [[player, background, floor], boxList, effectList];
;
stage.on('down', function (obj) {
var event = obj.event;
lastTouchX = event.global.x;
lastTouchY = event.global.y;
touchTime = LK.ticks;
});
stage.on('up', function (obj) {
if (touchTime !== null && LK.ticks - touchTime < CONTROL_TAP_TICKS) {
player.jump();
}
lastTouchX = null;
lastTouchY = null;
touchTime = null;
});
stage.on('move', function (obj) {
if (touchTime !== null) {
var reset = false;
var event = obj.event;
var deltaX = lastTouchX - event.global.x;
var deltaY = lastTouchY - event.global.y;
if (Math.abs(deltaX) > CONTROL_SWIPE_DIST) {
var direction = deltaX > 0 ? -1 : 1;
player.move(direction);
reset = true;
} else if (deltaY > CONTROL_SWIPE_DIST) {
player.jump();
reset = true;
}
if (reset) {
touchTime = null;
lastTouchX = null;
lastTouchY = null;
}
}
});
LK.on('tick', function () {
if (interface.isGameOver) {
interface.gameOver();
} else if (interface.isPaused = shiftTicks > 0) {
shiftTicks--;
shiftContainers.forEach(function (containers) {
for (var i = containers.length - 1; i >= 0; i--) {
var container = containers[i];
if (container.shift(SHIFT_AMOUNT)) {
container.destroy();
containers.splice(i, 1);
}
}
});
} else {
var playerArgs = {
interface,
columnList
};
if (player.update(playerArgs)) {
shiftTicks += SHIFT_DURATION;
}
;
var columnArgs = {
game: self,
boxList
};
for (var i = 0; i < columnList.length; i++) {
columnList[i].update(columnArgs);
}
;
var boxArgs = {
game: self,
columnList,
effectList,
interface,
player
};
for (var i = 0; i < boxList.length; i++) {
var box = boxList[i];
if (box.update(boxArgs)) {
box.destroy();
boxList.splice(i, 1);
i--;
}
}
;
for (var i = 0; i < effectList.length; i++) {
var effect = effectList[i];
if (effect.update()) {
effect.destroy();
effectList.splice(i, 1);
i--;
}
}
}
if (LK.ticks === INSTRUCTION_DURATION) {
interface.hideInstructions();
}
});
});
Pixel art, side view of a concrete factory floor . Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
Pixel art, square with cute eyes . Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
Pixel art, square with the texture of a tnt . Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
pixel art of a crate, side view . Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
pixel art of a crate, flat side view . Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
pixel art of a crate, flat side view . Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
Pixel art of a golden christmas present. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
Pixel art of a green christmas present with red ribbons. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
Pixel art of an elaborate green christmas present with red ribbons. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
pixel art of a metal background.
pixel art of a crate made of stone with a label of coal on the side, flat side view. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
pixel art of a square tnt explosion. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.