/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var BlockManager = Container.expand(function () { var self = Container.call(this); var blockRows = []; var index = 0; for (var i = 0; i < ROW_COUNT; i++) { addRow({ x: game.width / 2, y: LAVA_LINE - i * (BLOCK_SIZE * BLOCK_SCALE + BLOCK_MARGIN), index: index++ }); } blockRows[0].blocks[Math.floor(ROW_SPAN / 2)].onLit(true); self.update = function () { if (blockRows[0].y > game.height) { popRow(); } }; function addRow(settings) { var prevRow = blockRows[blockRows.length - 1]; var newRow = self.addChild(new BlockRow(settings)); blockRows.push(newRow); if (prevRow) { newRow.setDown(prevRow); prevRow.setUp(newRow); } } function popRow() { blockRows.shift().callDestroy(); blockRows[0].blocks.forEach(function (block) { if (block) { block.downBlock = undefined; } }); addRow({ x: game.width / 2, y: blockRows[blockRows.length - 1].y - BLOCK_SIZE * BLOCK_SCALE - BLOCK_MARGIN, index: index++, disabled: true }); adjustRowSpeed(); } return self; }); var ConfigContainer = Container.expand(function (config) { var self = Container.call(this); config = config || {}; self.destroyed = false; self.tags = {}; self.x = config.x || 0; self.y = config.y || 0; self.rotation = config.rotation || 0; self.alpha = config.alpha !== undefined ? config.alpha : 1.0; if (config.scale !== undefined || config.scaleX !== undefined || config.scaleY !== undefined) { var scaleX = config.scaleX !== undefined ? config.scaleX : config.scale !== undefined ? config.scale : 1; var scaleY = config.scaleY !== undefined ? config.scaleY : config.scale !== undefined ? config.scale : 1; self.scale.set(scaleX, scaleY); } self.callDestroy = function () { if (!self.destroyed) { self.destroyed = true; self.onDestroy(); self.destroy(); } }; self.onDestroy = function () {}; return self; }); var PointText = ConfigContainer.expand(function (config) { var self = ConfigContainer.call(this, config); var text = self.addChild(new BorderedText('+1', { anchorX: 0.5, anchorY: 0.5, size: 100 })); tween(self, { y: self.y - 50 }, { duration: 2000 }); LK.setTimeout(function () { tween(self, { alpha: 0 }, { duration: 1000, onFinish: function onFinish() { self.callDestroy(); } }); }, 1000); return self; }); var Light = ConfigContainer.expand(function (config) { var self = ConfigContainer.call(this, config); var lightShape = self.addChild(new Glow({ y: -LIGHT_OFFSET, width: BLOCK_SIZE - 2 * LIGHT_OFFSET - 10, height: BLOCK_SIZE - 2 * LIGHT_OFFSET - 10, anchorX: 0.5, anchorY: 1, layers: 5, rangeX: 20, rangeY: 10, tint: 0xFFFFFF, scaleX: 0, scaleY: 0, asset: 'shapeEllipse', solid: true })); tween(lightShape, { scaleX: 1, scaleY: 1 }, { duration: LIGHT_GROW_SPEED + Math.random() * LIGHT_GROW_VARIANCE, onFinish: function onFinish() { if (self.parent) { config.callback(); } } }); return self; }); var LavaSlice = ConfigContainer.expand(function (config) { var self = ConfigContainer.call(this, config); self.attachAsset('lava' + config.index, { anchorX: 0.5, anchorY: 0, width: LAVA_SLICE_WIDTH, tint: 0xFFA500 }); self.attachAsset('shapeBox', { width: LAVA_SLICE_WIDTH, height: 10, anchorX: 0.5, anchorY: 0, tint: 0xFF4D00 }); self.addChild(new Glow({ anchorX: 0.5, anchorY: 0.75, height: 20, rangeY: 150, width: 3 * LAVA_SLICE_WIDTH, alpha: 0.35, tint: 0xFF4D00 })); self.update = function () { self.y = Math.sin(LK.ticks * LAVA_BOB_PERIOD + config.index * LAVA_BOB_OFFSET) * LAVA_BOB_HEIGHT; if (Math.random() < 0.0025) { self.parent.particleContainer.addChild(new LavaParticle({ x: self.x + Math.random() * LAVA_SLICE_WIDTH - LAVA_SLICE_WIDTH / 2, y: self.y, scale: 1 + Math.random() * 0.5 // Random scale between 1 and 1.5 })); } }; return self; }); var LavaParticle = ConfigContainer.expand(function (config) { var self = ConfigContainer.call(this, config); // Attach a glow asset with specified properties self.rotation = Math.random() * Math.PI; // Random rotation between 0 and pi self.attachAsset('shapeBox', { anchorX: 0.5, anchorY: 0.5, width: 20, height: 20, tint: 0xFF4D00, alpha: 0.5 }); // Attach a smaller shapeBox with black tint and 0.5 alpha self.attachAsset('shapeBox', { anchorX: 0.5, anchorY: 0.5, width: 10, height: 10, tint: 0x000000, alpha: 0.25 }); // Set initial random velocities increased by a factor of 10 self.vx = (1 - 2 * Math.random()) * 10; // Random x velocity between 10 and -10 self.vy = (1 - 0.5 * Math.random()) * -8; // Random y velocity between -8 and -4 // Update function to apply gravity and movement self.update = function () { self.vy += 0.1; // Gravity effect self.vx *= 0.975; self.x += self.vx; self.y += self.vy; // Destroy the particle if it goes below the game height if (self.y > LAVA_BOB_HEIGHT) { self.callDestroy(); } }; if (Math.random() < 0.25) { var bubbleSounds = ['bubble1', 'bubble2', 'bubble3', 'bubble4', 'bubble5']; var randomBubbleSound = bubbleSounds[Math.floor(Math.random() * bubbleSounds.length)]; LK.getSound(randomBubbleSound).play(); } return self; }); var Lava = ConfigContainer.expand(function (config) { var self = ConfigContainer.call(this, config); self.particleContainer = self.addChild(new Container()); var position = Math.round((-0.5 - LAVA_SLICE_COUNT / 2) * LAVA_SLICE_WIDTH); for (var i = 1; i <= LAVA_SLICE_COUNT; i++) { var lavaSlice = self.addChild(new LavaSlice({ x: position += LAVA_SLICE_WIDTH, index: i })); } // Function to play the 'flow' sound at random intervals between 5s to 10s function playFlowSound() { LK.getSound('flow').play(); var nextInterval = 5000 + Math.random() * 5000; // Random interval between 5000ms (5s) and 10000ms (10s) LK.setTimeout(playFlowSound, nextInterval); } // Start playing the 'flow' sound immediately playFlowSound(); return self; }); var Glow = ConfigContainer.expand(function (config) { var self = ConfigContainer.call(this, config); var layers = Math.max(2, config.layers || 10); var layerAlpha = 1 / layers; var widthStep = (config.rangeX || 0) * layerAlpha; var heightStep = (config.rangeY || 0) * layerAlpha; for (var i = 0; i < layers; i++) { self.attachAsset(config.asset || 'shapeBox', { width: config.width + widthStep * i, height: config.height + heightStep * i, anchorX: config.anchorX, anchorY: config.anchorY, tint: config.tint, alpha: i === 0 && config.solid ? 1 : layerAlpha }); } return self; }); /** * var config = { * x : Number || 0, // See: ConfigContainer * y : Number || 0, // See: ConfigContainer * rotation : Number || 0, // See: ConfigContainer * anchorX : Number || 0, * anchorY : Number || 1, * size : Number || TEXT_DEFAULT_SIZE, * font : String || TEXT_DEFAULT_FONT, * fill : String || TEXT_DEFAULT_FILL, * border : String || TEXT_DEFAULT_BORDER, * } **/ var BorderedText = ConfigContainer.expand(function (text, config) { var self = ConfigContainer.call(this, config); config = config || {}; var anchorX = config.anchorX !== undefined ? config.anchorX : 0; var anchorY = config.anchorY !== undefined ? config.anchorY : 1; var size = config.size !== undefined ? config.size : TEXT_DEFAULT_SIZE; var font = config.font !== undefined ? config.font : TEXT_DEFAULT_FONT; var textFill = config.fill !== undefined ? config.fill : TEXT_DEFAULT_FILL; var borderFill = config.border !== undefined ? config.border : TEXT_DEFAULT_BORDER; var mainAsset; var textAssets = []; ; self.setText = setText; self.setFill = setFill; ; function setText(newText) { for (var i = 0; i < textAssets.length; i++) { textAssets[i].setText(newText); } } function setFill(newFill) { textFill = newFill; mainAsset.fill = newFill; } function buildTextAssets(newText) { for (var i = 0; i < TEXT_OFFSETS.length; i++) { var main = i === TEXT_OFFSETS.length - 1; var textAsset = textAssets[i]; if (textAsset) { textAsset.destroy(); } textAsset = self.addChild(new Text2(newText, { fill: main ? textFill : borderFill, font: font, size: size })); textAsset.anchor = { x: anchorX, y: anchorY }; // NOTE: Cannot be set in config textAsset.x = TEXT_OFFSETS[i][0] * TEXT_BORDER_WEIGHT; // NOTE: Cannot be set in config textAsset.y = TEXT_OFFSETS[i][1] * TEXT_BORDER_WEIGHT; // NOTE: Cannot be set in config textAssets[i] = textAsset; } mainAsset = textAssets[TEXT_OFFSETS.length - 1]; } ; buildTextAssets(text); return self; }); /** * var config = { * x : Number || 0, // See: ConfigContainer * y : Number || 0, // See: ConfigContainer * rotation : Number || 0, // See: ConfigContainer * anchorX : Number || .5, * anchorY : Number || .5, * scale : Number || undefined, * scaleX : Number || scale, * scaleY : Number || scale, * width : Number || undefined, // Auto-calculated if left undefined and height is set * height : Number || undefined, // Auto-calculated if left undefined and width is set * tint : String || 0xFFFFFF, // Not tinted by default * border : String || TEXT_DEFAULT_BORDER, * } **/ var BorderedSymbol = ConfigContainer.expand(function (symbol, config) { var self = ConfigContainer.call(this, config); config = config || {}; var width = config.width !== undefined ? config.width : undefined; var height = config.height !== undefined ? config.height : undefined; var scale = config.scale !== undefined ? config.scale : undefined; var scaleX = config.scaleX !== undefined ? config.scaleX : scale; var scaleY = config.scaleY !== undefined ? config.scaleX : scale; var anchorX = config.anchorX !== undefined ? config.anchorX : .5; var anchorY = config.anchorY !== undefined ? config.anchorY : .5; var symbolTint = config.tint !== undefined ? config.tint : 0xFFFFFF; var borderTint = config.border !== undefined ? config.border : TEXT_DEFAULT_BORDER; var mainSymbol; var symbolAssets = []; ; self.setSymbol = buildSymbolAssets; self.setTint = setTint; ; function setTint(newTint) { // NOTE: Tinting is currently broken (cannot use string) // mainSymbol.tint = newTint; // symbolConfig.tint = newTint; } function buildSymbolAssets(newSymbol) { for (var i = 0; i < TEXT_OFFSETS.length; i++) { var main = i === TEXT_OFFSETS.length - 1; var symbolAsset = symbolAssets[i]; if (symbolAsset) { symbolAsset.destroy(); } symbolAsset = self.attachAsset(newSymbol, { // tint: main ? symbolTint : borderTint, tint: main ? 0xFFFFFF : 0x000000, // NOTE: Tinting is currently broken (cannot use string) x: TEXT_OFFSETS[i][0] * TEXT_BORDER_WEIGHT, y: TEXT_OFFSETS[i][1] * TEXT_BORDER_WEIGHT, anchorX: anchorX, anchorY: anchorY }); if (width !== undefined && height === undefined) { height = symbolAsset.height * (width / symbolAsset.width); } else if (height !== undefined && width === undefined) { width = symbolAsset.width * (height / symbolAsset.height); } symbolAsset.width = width; symbolAsset.height = height; if (scaleX !== undefined && scaleY !== undefined) { symbolAsset.scale.set(scaleX, scaleY); } symbolAssets[i] = symbolAsset; } mainSymbol = symbolAssets[TEXT_OFFSETS.length - 1]; } ; buildSymbolAssets(symbol); return self; }); var Border = ConfigContainer.expand(function (config) { var self = ConfigContainer.call(this, config); var radius = 70; var totalHeight = 0; var borderAssets = ['border1', 'border2']; while (totalHeight < game.height) { var randomAsset = borderAssets[Math.floor(Math.random() * borderAssets.length)]; var borderSegment = self.attachAsset(randomAsset, { anchorX: 0.5, anchorY: 0, y: totalHeight }); totalHeight += borderSegment.height; } var borderGear = self.attachAsset('borderGear', { width: 2 * radius, height: 2 * radius, anchorX: 0.5, anchorY: 0.5, y: LIGHT_LEVEL, rotation: Math.random() * Math.PI * 2 // Initialize with random rotation }); var borderLevel = self.attachAsset('borderLevel', { anchorX: 0.15, anchorY: 0.85, y: LIGHT_LEVEL, rotation: Math.PI / 4 // 45 degree rotation }); self.update = function () { borderGear.rotation += rowSpeed / radius; }; return self; }); var BlockSparkParticle = ConfigContainer.expand(function (config) { var self = ConfigContainer.call(this, config); var shapeBox = self.attachAsset('shapeBox', { width: 10, height: 10, anchorX: 0.5, anchorY: 0.5, rotation: Math.random() * Math.PI // Random rotation between 0 and pi }); // Set initial random direction and speed var angle = Math.random() * 2 * Math.PI; var offset = Math.random() * 50; // Random offset between 0 and 50 self.x += Math.cos(angle) * offset; self.y += Math.sin(angle) * offset; self.vx = Math.cos(angle) * 10; self.vy = Math.sin(angle) * 10; // Update function to move the particle self.update = function () { self.vy += 0.5; // Apply gravity effect self.x += self.vx; self.y += self.vy; }; // Tween to fade out the particle tween(self, { alpha: 0 }, { duration: 350, onFinish: function onFinish() { self.callDestroy(); } }); return self; }); var BlockRow = ConfigContainer.expand(function (config) { var self = ConfigContainer.call(this, config); var blockClasses = generateBlockClasses(config.index); self.index = config.index; self.disabled = !!config.disabled; self.blocks = []; for (var i = 0; i < ROW_SPAN; i++) { var blockClass = blockClasses[i]; self.blocks[i] = self.addChild(new blockClass({ x: (0.5 + i - ROW_SPAN / 2) * (BLOCK_SIZE * BLOCK_SCALE + BLOCK_MARGIN), scale: BLOCK_SCALE, disabled: self.disabled, index: i, row: self })); } for (var i = 0; i < ROW_SPAN; i++) { var block = self.blocks[i]; if (i > 0) { block.leftBlock = self.blocks[i - 1]; } if (i < ROW_SPAN - 1) { block.rightBlock = self.blocks[i + 1]; } } self.update = function () { self.y += rowSpeed; if (self.disabled && self.y > LIGHT_LEVEL - BLOCK_SIZE * BLOCK_SCALE / 2) { self.disabled = false; self.blocks.forEach(function (block) { block.disabled = false; }); self.blocks.forEach(function (block) { if (!block.isRotating) { block.onSettle(); } }); } }; self.setUp = function (upRow) { for (var i = 0; i < ROW_SPAN; i++) { var selfBlock = self.blocks[i]; var upBlock = upRow.blocks[i]; upBlock.downBlock = selfBlock; selfBlock.upBlock = upBlock; } }; self.setDown = function (downRow) { for (var i = 0; i < ROW_SPAN; i++) { var selfBlock = self.blocks[i]; var downBlock = downRow.blocks[i]; downBlock.upBlock = selfBlock; selfBlock.downBlock = downBlock; } }; self.onDestroy = function () { self.blocks.forEach(function (block) { block.callDestroy(); }); }; return self; }); var BlockLitParticle = ConfigContainer.expand(function (config) { var self = ConfigContainer.call(this, config); var shapeBox = self.attachAsset('particleBlockLit', { scaleX: 1.0, scaleY: 1.0, anchorX: 0.5, anchorY: 0.5, alpha: 0.1 }); tween(shapeBox, { scaleX: 2.0, scaleY: 2.0, alpha: 0.0 }, { duration: 350, easing: tween.linear, onFinish: function onFinish() { self.callDestroy(); } }); return self; }); var BlockBreakParticle = ConfigContainer.expand(function (config) { var self = ConfigContainer.call(this, config); var blockAssets = ['blockX1', 'blockX2', 'blockX3', 'blockX4']; var randomAsset = blockAssets[Math.floor(Math.random() * blockAssets.length)]; var shapeBox = self.attachAsset(randomAsset, { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5, rotation: Math.random() * Math.PI * 2 }); // Set initial random direction and speed var angle = Math.random() * 2 * Math.PI; var offset = 50 + Math.random() * 50; // Random offset between 0 and 50 self.x += Math.cos(angle) * offset; self.y += Math.sin(angle) * offset; self.vx = Math.cos(angle) * 10; self.vy = Math.sin(angle) * 10; // Update function to move the particle self.update = function () { self.vy += 0.5; // Apply gravity effect self.x += self.vx; self.y += self.vy; }; // Tween to fade out the particle tween(self, { alpha: 0 }, { duration: 350, onFinish: function onFinish() { self.callDestroy(); } }); return self; }); var Block = ConfigContainer.expand(function (config) { var self = ConfigContainer.call(this, config); self.isRotating = false; self.disabled = config.disabled; self.row = config.row; self.index = config.index; self.orientation = config.orientation !== undefined ? config.orientation : Math.floor(4 * Math.random()); self.toLight = false; self.upPath = false; self.rightPath = false; self.downPath = false; self.leftPath = false; self.upBlock; self.rightBlock; self.downBlock; self.leftBlock; self.attachAsset('shapeBox', { width: BLOCK_SIZE, height: BLOCK_SIZE, anchorX: 0.5, anchorY: 0.5, alpha: 0 }); self.lightContainer = lightManager.addChild(new ConfigContainer({ scale: BLOCK_SCALE })); self.blockContainer = self.addChild(new ConfigContainer({ rotation: self.orientation * Math.PI / 2 })); self.rotate = function () { if (self.lit) { self.showLock(); } else if (!self.isRotating) { self.toLight = false; self.isRotating = true; LK.getSound('rotate').play(); self.lightContainer.removeChildren(); tween(self.blockContainer, { rotation: self.blockContainer.rotation + Math.PI / 2 }, { duration: BLOCK_ROTATE_SPEED, onFinish: function onFinish() { if (!self.destroyed) { self.isRotating = false; self.orientation = (self.orientation + 1) % 4; self.onSettle(); } } }); } }; self.update = function () { if (self.parent && !self.destroyed) { self.y = columns[self.index].y; self.lightContainer.x = self.x + self.parent.x; self.lightContainer.y = self.y + self.parent.y; } if (self.toLight && self.row.y >= LIGHT_LEVEL + BLOCK_SCALE * BLOCK_HALFSIZE) { self.toLight = false; self.onLit(); } }; self.down = function () { self.rotate(); tween(self.scale, { x: 0.9 * BLOCK_SCALE, y: 0.9 * BLOCK_SCALE }, { duration: BLOCK_ROTATE_SPEED / 2, onFinish: function onFinish() { tween(self.scale, { x: BLOCK_SCALE, y: BLOCK_SCALE }, { duration: BLOCK_ROTATE_SPEED / 2 }); } }); }; self.onLit = function (pointless) { if (self.row.y < LIGHT_LEVEL + BLOCK_SCALE * BLOCK_HALFSIZE) { self.toLight = true; } else if (!self.lit && !self.destroyed && (self.upPath || self.downPath || self.leftPath || self.rightPath)) { self.lit = true; self.lightContainer.removeChildren(); self.lightContainer.attachAsset('shapeEllipse', { width: BLOCK_SIZE - 2 * LIGHT_OFFSET, height: BLOCK_SIZE - 2 * LIGHT_OFFSET, anchorX: 0.5, anchorY: 0.5 }); if (self.upPath && (!self.upBlock || !self.upBlock.lit || !self.upBlock.downPath)) { makeGlow(0, -BLOCK_SIZE / 2 - BLOCK_MARGIN / 4); } if (self.rightPath && (!self.rightBlock || !self.rightBlock.lit || !self.rightBlock.leftPath)) { makeGlow(BLOCK_SIZE / 2 + BLOCK_MARGIN / 4, 0); } if (self.downPath && (!self.downBlock || !self.downBlock.lit || !self.downBlock.upPath)) { makeGlow(0, BLOCK_SIZE / 2 + BLOCK_MARGIN / 4); } if (self.leftPath && (!self.leftBlock || !self.leftBlock.lit || !self.leftBlock.rightPath)) { makeGlow(-BLOCK_SIZE / 2 - BLOCK_MARGIN / 4, 0); } if (!pointless) { LK.setScore(LK.getScore() + 1); scoreText.setText(LK.getScore()); game.addChild(new BlockLitParticle({ // Create a BlockLitParticle x: self.x + self.parent.x, y: self.y + self.parent.y, scale: BLOCK_SCALE })); game.addChild(new PointText({ x: self.x + self.parent.x, y: self.y + self.parent.y })); } lightSources += 1; LK.getSound('light').play(); self.onSettle(); } }; self.onSettle = function () { self.updatePaths(); if (!self.disabled) { var litRotationFactor = self.lit ? 0 : 1; var litPositionFactor = self.lit ? 1 : -1; [{ direction: 'up', rotation: 0, y: 1 }, { direction: 'right', rotation: 0.5, x: -1 }, { direction: 'down', rotation: 1, y: -1 }, { direction: 'left', rotation: 1.5, x: 1 }].forEach(function (item) { if (self[item.direction + 'Path']) { var block = self[item.direction + 'Block']; if (block && !block.disabled && block.lit !== self.lit && block[COMPLIMENTS[item.direction] + 'Path']) { (self.lit ? block : self).lightContainer.addChild(new Light({ x: (item.x || 0) * litPositionFactor * BLOCK_HALFSIZE, y: (item.y || 0) * litPositionFactor * BLOCK_HALFSIZE, rotation: Math.PI * (item.rotation + litRotationFactor), callback: self.lit ? block.onLit : self.onLit })); } } }); } }; self.showLock = function () { // Check and destroy existing lockSymbol before creating a new one if (self.lockSymbol) { self.lockSymbol.destroy(); } var lockSymbol = self.addChild(LK.getAsset('iconLock', { anchorX: 0.5, anchorY: 0.5, scaleX: 1 / self.scale.x, scaleY: 1 / self.scale.y })); // Play error sound if not already playing if (!errorSoundPlaying) { errorSoundPlaying = true; LK.getSound('error').play(); LK.setTimeout(function () { errorSoundPlaying = false; }, 500); } self.lockSymbol = lockSymbol; // Set a timeout to fade out the lockSymbol after 500ms LK.setTimeout(function () { tween(lockSymbol, { alpha: 0 }, { duration: 500, onFinish: function onFinish() { lockSymbol.destroy(); self.lockSymbol = null; } }); }, 500); }; self.updatePaths = function () {}; function makeGlow(x, y) { var glow = self.lightContainer.addChild(new Glow({ x: x, y: y, width: LIGHT_SPILL_SIZE, height: LIGHT_SPILL_SIZE, anchorX: 0.5, anchorY: 0.5, rangeX: 20, rangeY: 20, layers: 4, solid: true, tint: 0xFFFFFF, alpha: 0 })); tween(glow, { alpha: 1 }, { duration: 250 }); } self.onDestroy = function () { if (self.lit) { lightSources -= 1; if (lightSources === 0) { LK.getSound('gong').play(); // Play the gong sound var gameOverText = game.addChild(new BorderedText("- Your Light is Dead -", { size: 150, anchorX: 0.5, anchorY: 0.5, x: game.width / 2, y: game.height / 4 })); LK.stopMusic(); // Stop the background music when the game ends LK.setTimeout(function () { // Delay the game over widget by 1 second LK.showGameOver(); }, 1000); } } if (self.lightContainer) { self.lightContainer.destroy(); } }; return self; }); var BlockX = Block.expand(function (config) { var self = Block.call(this, config); var shiftY = 2; self.upPath = true; self.rightPath = true; self.downPath = true; self.leftPath = true; self.blockContainer.attachAsset('blockX1', { x: -BLOCK_HALFSIZE, y: -BLOCK_HALFSIZE + shiftY, anchorX: 0, anchorY: 0 }); self.blockContainer.attachAsset('blockX2', { x: BLOCK_HALFSIZE, y: -BLOCK_HALFSIZE + shiftY, anchorX: 1, anchorY: 0 }); self.blockContainer.attachAsset('blockX3', { x: -BLOCK_HALFSIZE, y: BLOCK_HALFSIZE + shiftY, anchorX: 0, anchorY: 1 }); self.blockContainer.attachAsset('blockX4', { x: BLOCK_HALFSIZE, y: BLOCK_HALFSIZE + shiftY, anchorX: 1, anchorY: 1 }); return self; }); var BlockT = Block.expand(function (config) { var self = Block.call(this, config); self.blockContainer.attachAsset('blockT1', { x: -BLOCK_HALFSIZE, y: -BLOCK_HALFSIZE, anchorX: 0, anchorY: 0 }); self.blockContainer.attachAsset('blockT2', { x: BLOCK_HALFSIZE, y: -BLOCK_HALFSIZE, anchorX: 1, anchorY: 0 }); self.blockContainer.attachAsset('blockT3', { y: BLOCK_HALFSIZE, anchorX: 0.5, anchorY: 1 }); self.updatePaths = function () { self.downPath = self.orientation !== 0; self.leftPath = self.orientation !== 1; self.upPath = self.orientation !== 2; self.rightPath = self.orientation !== 3; }; self.updatePaths(); return self; }); var BlockLine = Block.expand(function (config) { var self = Block.call(this, config); self.blockContainer.attachAsset('blockLine1', { x: -BLOCK_HALFSIZE, anchorX: 0, anchorY: 0.5, height: BLOCK_SIZE }); self.blockContainer.attachAsset('blockLine2', { x: BLOCK_HALFSIZE, anchorX: 1, anchorY: 0.5, height: BLOCK_SIZE }); self.updatePaths = function () { var vertical = !(self.orientation % 2); self.upPath = vertical; self.downPath = vertical; self.leftPath = !vertical; self.rightPath = !vertical; }; self.updatePaths(); return self; }); var BlockL = Block.expand(function (config) { var self = Block.call(this, config); self.blockContainer.attachAsset('blockL1', { anchorX: 0.5, anchorY: 0.5, height: BLOCK_SIZE }); self.blockContainer.attachAsset('blockL2', { x: -BLOCK_HALFSIZE, y: BLOCK_HALFSIZE, anchorX: 0, anchorY: 1 }); self.updatePaths = function () { self.leftPath = self.orientation === 0 || self.orientation === 1; self.upPath = self.orientation === 1 || self.orientation === 2; self.rightPath = self.orientation === 2 || self.orientation === 3; self.downPath = self.orientation === 3 || self.orientation === 0; }; self.updatePaths(); return self; }); var BlockBlank = Block.expand(function (config) { var self = Block.call(this, config); var crackAsset; var crackLevel = 0; var blockBlankAssets = ['blockBlank1', 'blockBlank2', 'blockBlank3']; var randomAsset = blockBlankAssets[Math.floor(Math.random() * blockBlankAssets.length)]; self.blockContainer.attachAsset(randomAsset, { anchorX: 0.5, anchorY: 0.5, width: BLOCK_SIZE, height: BLOCK_SIZE }); self.rotate = function (force) { if (force || crackLevel > 0) { if (++crackLevel <= 4) { if (crackAsset) { crackAsset.destroy(); } if (!force) { LK.getSound('crack').play(); } crackAsset = self.blockContainer.attachAsset('crack' + crackLevel, { anchorX: 0.5, anchorY: 0.5, rotation: crackLevel === 1 ? Math.random() * Math.PI * 2 : 0 // Random rotation if crackLevel is 1 }); // Create BlockSparkParticles if (self.parent) { for (var i = 0; i < 2 * crackLevel; i++) { game.addChild(new BlockSparkParticle({ x: self.x + self.parent.x, y: self.y + self.parent.y })); } } } else { LK.getSound('break').play(); var blockClass = randomizeBlockClass({ index: self.row.index, blanks: ROW_SPAN // Prevent spawning blocks }); var newBlock = self.parent.addChild(new blockClass({ x: self.x, y: self.y, scale: BLOCK_SCALE, index: self.index, row: self.row })); self.row.blocks[self.index] = newBlock; // Update block references if (self.upBlock) { self.upBlock.downBlock = newBlock; newBlock.upBlock = self.upBlock; } if (self.downBlock) { self.downBlock.upBlock = newBlock; newBlock.downBlock = self.downBlock; } if (self.leftBlock) { self.leftBlock.rightBlock = newBlock; newBlock.leftBlock = self.leftBlock; } if (self.rightBlock) { self.rightBlock.leftBlock = newBlock; newBlock.rightBlock = self.rightBlock; } if (!self.disabled) { newBlock.onSettle(); } // Create BlockBreakParticles for (var i = 0; i < 6; i++) { game.addChild(new BlockBreakParticle({ x: self.x + self.parent.x, y: self.y + self.parent.y })); } self.callDestroy(); } } else { self.showLock(); } }; if (Math.random() < BLOCK_CRACK_CHANCE) { self.rotate(true); } return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // Math constants / pre-calculations var MATH_HALF_ROOT_3 = Math.sqrt(3) / 2; // Required by: TEXT_OFFSETS, BorderedText, BorderedSymbol, SymbolText ; // Text settings var TEXT_OFFSETS = [[0, 1], [MATH_HALF_ROOT_3, 0.5], [MATH_HALF_ROOT_3, -0.5], [0, -1], [-MATH_HALF_ROOT_3, -0.5], [-MATH_HALF_ROOT_3, 0.5], [0, 0]]; // Required by: BorderedText, BorderedSymbol, SymbolText var TEXT_BORDER_WEIGHT = 5; // Required by: BorderedText, BorderedSymbol, SymbolText var TEXT_DEFAULT_BORDER = '#000000'; // Required by: BorderedText, BorderedSymbol, SymbolText var TEXT_DEFAULT_FILL = '#FFFFFF'; // Required by: BorderedText, SymbolText var TEXT_DEFAULT_FONT = 'Arial'; // Required by: BorderedText, SymbolText var TEXT_DEFAULT_SIZE = 50; // Required by: BorderedText, SymbolText ; // Other settings var LAVA_LINE = game.height - 200; var LAVA_SLICE_COUNT = 18; var LAVA_SLICE_WIDTH = 128; var LAVA_BOB_HEIGHT = 10; var LAVA_BOB_OFFSET = Math.PI / 5; var LAVA_BOB_PERIOD = Math.PI / 60; var BLOCK_SCALE = 2.5; var BLOCK_SIZE = 100; var BLOCK_MARGIN = 20; var BLOCK_HALFSIZE = BLOCK_SIZE * 0.5; var BLOCK_CRACK_CHANCE = 0.45; var BLOCK_ROTATE_SPEED = 100; var LIGHT_LEVEL = 800; var LIGHT_GROW_SPEED = 4500; var LIGHT_GROW_VARIANCE = 1000; var LIGHT_SPILL_SIZE = 35; var LIGHT_OFFSET = 5; var ROW_SPAN = 7; var ROW_COUNT = 11; var ROW_SPEED_BASE = 0.2; var ROW_SPEED_INCREASE = 0.04; var ROW_SPEED_INCREASE_MIN = 0.01; var ROW_SPEED_INCREASE_FACTOR = 0.9; var COL_SLIP_MIN = 10; var COL_SLIP_MAX = 30; var COL_SLIP_TIME = 750; var COL_TIMEOUT_MIN = 1000; var COL_TIMEOUT_MAX = 10000; var COL_RISE_FACTOR = 0.2; var COMPLIMENTS = { up: 'down', right: 'left', down: 'up', left: 'right' }; var lightSources = 0; var rowSpeed = ROW_SPEED_BASE; var rowSpeedIncrease = ROW_SPEED_INCREASE; var errorSoundPlaying = false; var columns = []; for (var i = 0; i < ROW_SPAN; i++) { columns.push({ y: 0, timer: false, index: i }); } // Play background music on repeat when the game starts LK.playMusic('background', { loop: true }); game.update = function () { for (var i = 1; i < columns.length - 1; i++) { var column = columns[i]; if (!column.timer) { if (column.y > 0) { column.y = Math.max(0, column.y - rowSpeed * COL_RISE_FACTOR); } else { startColumnTimer(i); } } } }; var scoreText = new BorderedText('0', { size: 100, anchorX: 0.5, anchorY: 0 }); LK.gui.top.addChild(scoreText); var rowSpeedText = LK.gui.topRight.addChild(new Text2((rowSpeed * 60).toFixed(1), { size: 50, fill: 0xFFFFFF, stroke: 0x000000, strokeThickness: 5 })); rowSpeedText.anchor.set(1.0, -0.5); var speedIcon = LK.gui.topRight.addChild(LK.getAsset('iconGear', { anchorX: 0.5, anchorY: 0.5, x: rowSpeedText.x - rowSpeedText.width - 40, y: rowSpeedText.height })); var iconRotate = LK.gui.topRight.addChild(new BorderedSymbol('iconRotate', { anchorX: 0.2, anchorY: 1.0, x: speedIcon.x, y: speedIcon.y })); var lightManager = game.addChild(new Container()); game.addChild(new Glow({ width: game.width, height: LIGHT_LEVEL, rangeY: 20, layers: 5, solid: true })); var blockManager = game.addChild(new BlockManager()); var leftBorder = game.addChild(new Border({ x: 30 })); var rightBorder = game.addChild(new Border({ x: game.width - 30, scaleX: -1 })); var lava = game.addChild(new Lava({ x: game.width / 2, y: LAVA_LINE })); game.addChild(new Glow({ x: game.width / 2, width: game.width, height: 200, anchorX: 0.5, anchorY: 0, rangeY: 200, tint: 0x000000, alpha: 2.0 })); var iconLight = game.addChild(LK.getAsset('iconLight', { x: game.width / 2, y: 100, anchorX: 0.5, anchorY: 0.5, tint: 0xFF4D00 })); function shuffle(array) { var currentIndex = array.length; while (currentIndex != 0) { var randomIndex = Math.floor(Math.random() * currentIndex); currentIndex--; var ref = [array[randomIndex], array[currentIndex]]; array[currentIndex] = ref[0]; array[randomIndex] = ref[1]; } return array; } function generateBlockClasses(index) { var settings = { index: index, blanks: 0, xs: 0 }; switch (index) { case 0: return [BlockBlank, BlockBlank, BlockBlank, BlockX, BlockBlank, BlockBlank, BlockBlank]; case 1: settings.blanks = ROW_SPAN; settings.xs = 1; return [BlockBlank, BlockBlank, randomizeBlockClass(settings), BlockX, randomizeBlockClass(settings), BlockBlank, BlockBlank]; case 2: settings.blanks = ROW_SPAN; settings.xs = 1; return [BlockBlank, randomizeBlockClass(settings), randomizeBlockClass(settings), BlockX, randomizeBlockClass(settings), randomizeBlockClass(settings), BlockBlank]; default: var blockClasses = []; for (var i = 0; i < ROW_SPAN; i++) { blockClasses[i] = randomizeBlockClass(settings); } return shuffle(blockClasses); } } function adjustRowSpeed() { rowSpeed += rowSpeedIncrease; rowSpeedText.setText((rowSpeed * 60).toFixed(1)); rowSpeedIncrease = Math.max(ROW_SPEED_INCREASE_MIN, rowSpeedIncrease * ROW_SPEED_INCREASE_FACTOR); } function startColumnTimer(index) { var column = columns[index]; column.timer = true; var timeoutDuration = COL_TIMEOUT_MIN + Math.random() * (COL_TIMEOUT_MAX - COL_TIMEOUT_MIN); LK.setTimeout(function () { var targetY = COL_SLIP_MIN + Math.random() * (COL_SLIP_MAX - COL_SLIP_MIN); tween(column, { y: targetY }, { duration: COL_SLIP_TIME, easing: tween.easeIn, // Apply ease in for the tween onFinish: function onFinish() { column.timer = false; // Reset the timer to false after tween completes } }); }, timeoutDuration); } function randomizeBlockClass(settings) { var classes = [{ blockClass: BlockBlank, chance: Math.max(0, 1.5 - 0.5 * (settings.blanks || 0)) }, { blockClass: BlockT, chance: 1 }, { blockClass: BlockX, chance: Math.max(0, 1 - 1 * (settings.xs || 0)) }, { blockClass: BlockLine, chance: 1 }, { blockClass: BlockL, chance: 1 }]; var totalChance = classes.reduce(function (sum, item) { return sum + item.chance; }, 0); var randomValue = Math.random() * totalChance; var accumulatedChance = 0; var blockClass = BlockBlank; for (var i = 0; i < classes.length; i++) { accumulatedChance += classes[i].chance; if (randomValue < accumulatedChance) { blockClass = classes[i].blockClass; break; } } switch (blockClass) { case BlockBlank: settings.blanks = (settings.blanks || 0) + 1; break; case BlockX: settings.xs = (settings.xs || 0) + 1; break; } return blockClass; }
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
var BlockManager = Container.expand(function () {
var self = Container.call(this);
var blockRows = [];
var index = 0;
for (var i = 0; i < ROW_COUNT; i++) {
addRow({
x: game.width / 2,
y: LAVA_LINE - i * (BLOCK_SIZE * BLOCK_SCALE + BLOCK_MARGIN),
index: index++
});
}
blockRows[0].blocks[Math.floor(ROW_SPAN / 2)].onLit(true);
self.update = function () {
if (blockRows[0].y > game.height) {
popRow();
}
};
function addRow(settings) {
var prevRow = blockRows[blockRows.length - 1];
var newRow = self.addChild(new BlockRow(settings));
blockRows.push(newRow);
if (prevRow) {
newRow.setDown(prevRow);
prevRow.setUp(newRow);
}
}
function popRow() {
blockRows.shift().callDestroy();
blockRows[0].blocks.forEach(function (block) {
if (block) {
block.downBlock = undefined;
}
});
addRow({
x: game.width / 2,
y: blockRows[blockRows.length - 1].y - BLOCK_SIZE * BLOCK_SCALE - BLOCK_MARGIN,
index: index++,
disabled: true
});
adjustRowSpeed();
}
return self;
});
var ConfigContainer = Container.expand(function (config) {
var self = Container.call(this);
config = config || {};
self.destroyed = false;
self.tags = {};
self.x = config.x || 0;
self.y = config.y || 0;
self.rotation = config.rotation || 0;
self.alpha = config.alpha !== undefined ? config.alpha : 1.0;
if (config.scale !== undefined || config.scaleX !== undefined || config.scaleY !== undefined) {
var scaleX = config.scaleX !== undefined ? config.scaleX : config.scale !== undefined ? config.scale : 1;
var scaleY = config.scaleY !== undefined ? config.scaleY : config.scale !== undefined ? config.scale : 1;
self.scale.set(scaleX, scaleY);
}
self.callDestroy = function () {
if (!self.destroyed) {
self.destroyed = true;
self.onDestroy();
self.destroy();
}
};
self.onDestroy = function () {};
return self;
});
var PointText = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var text = self.addChild(new BorderedText('+1', {
anchorX: 0.5,
anchorY: 0.5,
size: 100
}));
tween(self, {
y: self.y - 50
}, {
duration: 2000
});
LK.setTimeout(function () {
tween(self, {
alpha: 0
}, {
duration: 1000,
onFinish: function onFinish() {
self.callDestroy();
}
});
}, 1000);
return self;
});
var Light = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var lightShape = self.addChild(new Glow({
y: -LIGHT_OFFSET,
width: BLOCK_SIZE - 2 * LIGHT_OFFSET - 10,
height: BLOCK_SIZE - 2 * LIGHT_OFFSET - 10,
anchorX: 0.5,
anchorY: 1,
layers: 5,
rangeX: 20,
rangeY: 10,
tint: 0xFFFFFF,
scaleX: 0,
scaleY: 0,
asset: 'shapeEllipse',
solid: true
}));
tween(lightShape, {
scaleX: 1,
scaleY: 1
}, {
duration: LIGHT_GROW_SPEED + Math.random() * LIGHT_GROW_VARIANCE,
onFinish: function onFinish() {
if (self.parent) {
config.callback();
}
}
});
return self;
});
var LavaSlice = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
self.attachAsset('lava' + config.index, {
anchorX: 0.5,
anchorY: 0,
width: LAVA_SLICE_WIDTH,
tint: 0xFFA500
});
self.attachAsset('shapeBox', {
width: LAVA_SLICE_WIDTH,
height: 10,
anchorX: 0.5,
anchorY: 0,
tint: 0xFF4D00
});
self.addChild(new Glow({
anchorX: 0.5,
anchorY: 0.75,
height: 20,
rangeY: 150,
width: 3 * LAVA_SLICE_WIDTH,
alpha: 0.35,
tint: 0xFF4D00
}));
self.update = function () {
self.y = Math.sin(LK.ticks * LAVA_BOB_PERIOD + config.index * LAVA_BOB_OFFSET) * LAVA_BOB_HEIGHT;
if (Math.random() < 0.0025) {
self.parent.particleContainer.addChild(new LavaParticle({
x: self.x + Math.random() * LAVA_SLICE_WIDTH - LAVA_SLICE_WIDTH / 2,
y: self.y,
scale: 1 + Math.random() * 0.5 // Random scale between 1 and 1.5
}));
}
};
return self;
});
var LavaParticle = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
// Attach a glow asset with specified properties
self.rotation = Math.random() * Math.PI; // Random rotation between 0 and pi
self.attachAsset('shapeBox', {
anchorX: 0.5,
anchorY: 0.5,
width: 20,
height: 20,
tint: 0xFF4D00,
alpha: 0.5
});
// Attach a smaller shapeBox with black tint and 0.5 alpha
self.attachAsset('shapeBox', {
anchorX: 0.5,
anchorY: 0.5,
width: 10,
height: 10,
tint: 0x000000,
alpha: 0.25
});
// Set initial random velocities increased by a factor of 10
self.vx = (1 - 2 * Math.random()) * 10; // Random x velocity between 10 and -10
self.vy = (1 - 0.5 * Math.random()) * -8; // Random y velocity between -8 and -4
// Update function to apply gravity and movement
self.update = function () {
self.vy += 0.1; // Gravity effect
self.vx *= 0.975;
self.x += self.vx;
self.y += self.vy;
// Destroy the particle if it goes below the game height
if (self.y > LAVA_BOB_HEIGHT) {
self.callDestroy();
}
};
if (Math.random() < 0.25) {
var bubbleSounds = ['bubble1', 'bubble2', 'bubble3', 'bubble4', 'bubble5'];
var randomBubbleSound = bubbleSounds[Math.floor(Math.random() * bubbleSounds.length)];
LK.getSound(randomBubbleSound).play();
}
return self;
});
var Lava = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
self.particleContainer = self.addChild(new Container());
var position = Math.round((-0.5 - LAVA_SLICE_COUNT / 2) * LAVA_SLICE_WIDTH);
for (var i = 1; i <= LAVA_SLICE_COUNT; i++) {
var lavaSlice = self.addChild(new LavaSlice({
x: position += LAVA_SLICE_WIDTH,
index: i
}));
}
// Function to play the 'flow' sound at random intervals between 5s to 10s
function playFlowSound() {
LK.getSound('flow').play();
var nextInterval = 5000 + Math.random() * 5000; // Random interval between 5000ms (5s) and 10000ms (10s)
LK.setTimeout(playFlowSound, nextInterval);
}
// Start playing the 'flow' sound immediately
playFlowSound();
return self;
});
var Glow = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var layers = Math.max(2, config.layers || 10);
var layerAlpha = 1 / layers;
var widthStep = (config.rangeX || 0) * layerAlpha;
var heightStep = (config.rangeY || 0) * layerAlpha;
for (var i = 0; i < layers; i++) {
self.attachAsset(config.asset || 'shapeBox', {
width: config.width + widthStep * i,
height: config.height + heightStep * i,
anchorX: config.anchorX,
anchorY: config.anchorY,
tint: config.tint,
alpha: i === 0 && config.solid ? 1 : layerAlpha
});
}
return self;
});
/**
* var config = {
* x : Number || 0, // See: ConfigContainer
* y : Number || 0, // See: ConfigContainer
* rotation : Number || 0, // See: ConfigContainer
* anchorX : Number || 0,
* anchorY : Number || 1,
* size : Number || TEXT_DEFAULT_SIZE,
* font : String || TEXT_DEFAULT_FONT,
* fill : String || TEXT_DEFAULT_FILL,
* border : String || TEXT_DEFAULT_BORDER,
* }
**/
var BorderedText = ConfigContainer.expand(function (text, config) {
var self = ConfigContainer.call(this, config);
config = config || {};
var anchorX = config.anchorX !== undefined ? config.anchorX : 0;
var anchorY = config.anchorY !== undefined ? config.anchorY : 1;
var size = config.size !== undefined ? config.size : TEXT_DEFAULT_SIZE;
var font = config.font !== undefined ? config.font : TEXT_DEFAULT_FONT;
var textFill = config.fill !== undefined ? config.fill : TEXT_DEFAULT_FILL;
var borderFill = config.border !== undefined ? config.border : TEXT_DEFAULT_BORDER;
var mainAsset;
var textAssets = [];
;
self.setText = setText;
self.setFill = setFill;
;
function setText(newText) {
for (var i = 0; i < textAssets.length; i++) {
textAssets[i].setText(newText);
}
}
function setFill(newFill) {
textFill = newFill;
mainAsset.fill = newFill;
}
function buildTextAssets(newText) {
for (var i = 0; i < TEXT_OFFSETS.length; i++) {
var main = i === TEXT_OFFSETS.length - 1;
var textAsset = textAssets[i];
if (textAsset) {
textAsset.destroy();
}
textAsset = self.addChild(new Text2(newText, {
fill: main ? textFill : borderFill,
font: font,
size: size
}));
textAsset.anchor = {
x: anchorX,
y: anchorY
}; // NOTE: Cannot be set in config
textAsset.x = TEXT_OFFSETS[i][0] * TEXT_BORDER_WEIGHT; // NOTE: Cannot be set in config
textAsset.y = TEXT_OFFSETS[i][1] * TEXT_BORDER_WEIGHT; // NOTE: Cannot be set in config
textAssets[i] = textAsset;
}
mainAsset = textAssets[TEXT_OFFSETS.length - 1];
}
;
buildTextAssets(text);
return self;
});
/**
* var config = {
* x : Number || 0, // See: ConfigContainer
* y : Number || 0, // See: ConfigContainer
* rotation : Number || 0, // See: ConfigContainer
* anchorX : Number || .5,
* anchorY : Number || .5,
* scale : Number || undefined,
* scaleX : Number || scale,
* scaleY : Number || scale,
* width : Number || undefined, // Auto-calculated if left undefined and height is set
* height : Number || undefined, // Auto-calculated if left undefined and width is set
* tint : String || 0xFFFFFF, // Not tinted by default
* border : String || TEXT_DEFAULT_BORDER,
* }
**/
var BorderedSymbol = ConfigContainer.expand(function (symbol, config) {
var self = ConfigContainer.call(this, config);
config = config || {};
var width = config.width !== undefined ? config.width : undefined;
var height = config.height !== undefined ? config.height : undefined;
var scale = config.scale !== undefined ? config.scale : undefined;
var scaleX = config.scaleX !== undefined ? config.scaleX : scale;
var scaleY = config.scaleY !== undefined ? config.scaleX : scale;
var anchorX = config.anchorX !== undefined ? config.anchorX : .5;
var anchorY = config.anchorY !== undefined ? config.anchorY : .5;
var symbolTint = config.tint !== undefined ? config.tint : 0xFFFFFF;
var borderTint = config.border !== undefined ? config.border : TEXT_DEFAULT_BORDER;
var mainSymbol;
var symbolAssets = [];
;
self.setSymbol = buildSymbolAssets;
self.setTint = setTint;
;
function setTint(newTint) {
// NOTE: Tinting is currently broken (cannot use string)
// mainSymbol.tint = newTint;
// symbolConfig.tint = newTint;
}
function buildSymbolAssets(newSymbol) {
for (var i = 0; i < TEXT_OFFSETS.length; i++) {
var main = i === TEXT_OFFSETS.length - 1;
var symbolAsset = symbolAssets[i];
if (symbolAsset) {
symbolAsset.destroy();
}
symbolAsset = self.attachAsset(newSymbol, {
// tint: main ? symbolTint : borderTint,
tint: main ? 0xFFFFFF : 0x000000,
// NOTE: Tinting is currently broken (cannot use string)
x: TEXT_OFFSETS[i][0] * TEXT_BORDER_WEIGHT,
y: TEXT_OFFSETS[i][1] * TEXT_BORDER_WEIGHT,
anchorX: anchorX,
anchorY: anchorY
});
if (width !== undefined && height === undefined) {
height = symbolAsset.height * (width / symbolAsset.width);
} else if (height !== undefined && width === undefined) {
width = symbolAsset.width * (height / symbolAsset.height);
}
symbolAsset.width = width;
symbolAsset.height = height;
if (scaleX !== undefined && scaleY !== undefined) {
symbolAsset.scale.set(scaleX, scaleY);
}
symbolAssets[i] = symbolAsset;
}
mainSymbol = symbolAssets[TEXT_OFFSETS.length - 1];
}
;
buildSymbolAssets(symbol);
return self;
});
var Border = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var radius = 70;
var totalHeight = 0;
var borderAssets = ['border1', 'border2'];
while (totalHeight < game.height) {
var randomAsset = borderAssets[Math.floor(Math.random() * borderAssets.length)];
var borderSegment = self.attachAsset(randomAsset, {
anchorX: 0.5,
anchorY: 0,
y: totalHeight
});
totalHeight += borderSegment.height;
}
var borderGear = self.attachAsset('borderGear', {
width: 2 * radius,
height: 2 * radius,
anchorX: 0.5,
anchorY: 0.5,
y: LIGHT_LEVEL,
rotation: Math.random() * Math.PI * 2 // Initialize with random rotation
});
var borderLevel = self.attachAsset('borderLevel', {
anchorX: 0.15,
anchorY: 0.85,
y: LIGHT_LEVEL,
rotation: Math.PI / 4 // 45 degree rotation
});
self.update = function () {
borderGear.rotation += rowSpeed / radius;
};
return self;
});
var BlockSparkParticle = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var shapeBox = self.attachAsset('shapeBox', {
width: 10,
height: 10,
anchorX: 0.5,
anchorY: 0.5,
rotation: Math.random() * Math.PI // Random rotation between 0 and pi
});
// Set initial random direction and speed
var angle = Math.random() * 2 * Math.PI;
var offset = Math.random() * 50; // Random offset between 0 and 50
self.x += Math.cos(angle) * offset;
self.y += Math.sin(angle) * offset;
self.vx = Math.cos(angle) * 10;
self.vy = Math.sin(angle) * 10;
// Update function to move the particle
self.update = function () {
self.vy += 0.5; // Apply gravity effect
self.x += self.vx;
self.y += self.vy;
};
// Tween to fade out the particle
tween(self, {
alpha: 0
}, {
duration: 350,
onFinish: function onFinish() {
self.callDestroy();
}
});
return self;
});
var BlockRow = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var blockClasses = generateBlockClasses(config.index);
self.index = config.index;
self.disabled = !!config.disabled;
self.blocks = [];
for (var i = 0; i < ROW_SPAN; i++) {
var blockClass = blockClasses[i];
self.blocks[i] = self.addChild(new blockClass({
x: (0.5 + i - ROW_SPAN / 2) * (BLOCK_SIZE * BLOCK_SCALE + BLOCK_MARGIN),
scale: BLOCK_SCALE,
disabled: self.disabled,
index: i,
row: self
}));
}
for (var i = 0; i < ROW_SPAN; i++) {
var block = self.blocks[i];
if (i > 0) {
block.leftBlock = self.blocks[i - 1];
}
if (i < ROW_SPAN - 1) {
block.rightBlock = self.blocks[i + 1];
}
}
self.update = function () {
self.y += rowSpeed;
if (self.disabled && self.y > LIGHT_LEVEL - BLOCK_SIZE * BLOCK_SCALE / 2) {
self.disabled = false;
self.blocks.forEach(function (block) {
block.disabled = false;
});
self.blocks.forEach(function (block) {
if (!block.isRotating) {
block.onSettle();
}
});
}
};
self.setUp = function (upRow) {
for (var i = 0; i < ROW_SPAN; i++) {
var selfBlock = self.blocks[i];
var upBlock = upRow.blocks[i];
upBlock.downBlock = selfBlock;
selfBlock.upBlock = upBlock;
}
};
self.setDown = function (downRow) {
for (var i = 0; i < ROW_SPAN; i++) {
var selfBlock = self.blocks[i];
var downBlock = downRow.blocks[i];
downBlock.upBlock = selfBlock;
selfBlock.downBlock = downBlock;
}
};
self.onDestroy = function () {
self.blocks.forEach(function (block) {
block.callDestroy();
});
};
return self;
});
var BlockLitParticle = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var shapeBox = self.attachAsset('particleBlockLit', {
scaleX: 1.0,
scaleY: 1.0,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.1
});
tween(shapeBox, {
scaleX: 2.0,
scaleY: 2.0,
alpha: 0.0
}, {
duration: 350,
easing: tween.linear,
onFinish: function onFinish() {
self.callDestroy();
}
});
return self;
});
var BlockBreakParticle = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var blockAssets = ['blockX1', 'blockX2', 'blockX3', 'blockX4'];
var randomAsset = blockAssets[Math.floor(Math.random() * blockAssets.length)];
var shapeBox = self.attachAsset(randomAsset, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1.5,
scaleY: 1.5,
rotation: Math.random() * Math.PI * 2
});
// Set initial random direction and speed
var angle = Math.random() * 2 * Math.PI;
var offset = 50 + Math.random() * 50; // Random offset between 0 and 50
self.x += Math.cos(angle) * offset;
self.y += Math.sin(angle) * offset;
self.vx = Math.cos(angle) * 10;
self.vy = Math.sin(angle) * 10;
// Update function to move the particle
self.update = function () {
self.vy += 0.5; // Apply gravity effect
self.x += self.vx;
self.y += self.vy;
};
// Tween to fade out the particle
tween(self, {
alpha: 0
}, {
duration: 350,
onFinish: function onFinish() {
self.callDestroy();
}
});
return self;
});
var Block = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
self.isRotating = false;
self.disabled = config.disabled;
self.row = config.row;
self.index = config.index;
self.orientation = config.orientation !== undefined ? config.orientation : Math.floor(4 * Math.random());
self.toLight = false;
self.upPath = false;
self.rightPath = false;
self.downPath = false;
self.leftPath = false;
self.upBlock;
self.rightBlock;
self.downBlock;
self.leftBlock;
self.attachAsset('shapeBox', {
width: BLOCK_SIZE,
height: BLOCK_SIZE,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0
});
self.lightContainer = lightManager.addChild(new ConfigContainer({
scale: BLOCK_SCALE
}));
self.blockContainer = self.addChild(new ConfigContainer({
rotation: self.orientation * Math.PI / 2
}));
self.rotate = function () {
if (self.lit) {
self.showLock();
} else if (!self.isRotating) {
self.toLight = false;
self.isRotating = true;
LK.getSound('rotate').play();
self.lightContainer.removeChildren();
tween(self.blockContainer, {
rotation: self.blockContainer.rotation + Math.PI / 2
}, {
duration: BLOCK_ROTATE_SPEED,
onFinish: function onFinish() {
if (!self.destroyed) {
self.isRotating = false;
self.orientation = (self.orientation + 1) % 4;
self.onSettle();
}
}
});
}
};
self.update = function () {
if (self.parent && !self.destroyed) {
self.y = columns[self.index].y;
self.lightContainer.x = self.x + self.parent.x;
self.lightContainer.y = self.y + self.parent.y;
}
if (self.toLight && self.row.y >= LIGHT_LEVEL + BLOCK_SCALE * BLOCK_HALFSIZE) {
self.toLight = false;
self.onLit();
}
};
self.down = function () {
self.rotate();
tween(self.scale, {
x: 0.9 * BLOCK_SCALE,
y: 0.9 * BLOCK_SCALE
}, {
duration: BLOCK_ROTATE_SPEED / 2,
onFinish: function onFinish() {
tween(self.scale, {
x: BLOCK_SCALE,
y: BLOCK_SCALE
}, {
duration: BLOCK_ROTATE_SPEED / 2
});
}
});
};
self.onLit = function (pointless) {
if (self.row.y < LIGHT_LEVEL + BLOCK_SCALE * BLOCK_HALFSIZE) {
self.toLight = true;
} else if (!self.lit && !self.destroyed && (self.upPath || self.downPath || self.leftPath || self.rightPath)) {
self.lit = true;
self.lightContainer.removeChildren();
self.lightContainer.attachAsset('shapeEllipse', {
width: BLOCK_SIZE - 2 * LIGHT_OFFSET,
height: BLOCK_SIZE - 2 * LIGHT_OFFSET,
anchorX: 0.5,
anchorY: 0.5
});
if (self.upPath && (!self.upBlock || !self.upBlock.lit || !self.upBlock.downPath)) {
makeGlow(0, -BLOCK_SIZE / 2 - BLOCK_MARGIN / 4);
}
if (self.rightPath && (!self.rightBlock || !self.rightBlock.lit || !self.rightBlock.leftPath)) {
makeGlow(BLOCK_SIZE / 2 + BLOCK_MARGIN / 4, 0);
}
if (self.downPath && (!self.downBlock || !self.downBlock.lit || !self.downBlock.upPath)) {
makeGlow(0, BLOCK_SIZE / 2 + BLOCK_MARGIN / 4);
}
if (self.leftPath && (!self.leftBlock || !self.leftBlock.lit || !self.leftBlock.rightPath)) {
makeGlow(-BLOCK_SIZE / 2 - BLOCK_MARGIN / 4, 0);
}
if (!pointless) {
LK.setScore(LK.getScore() + 1);
scoreText.setText(LK.getScore());
game.addChild(new BlockLitParticle({
// Create a BlockLitParticle
x: self.x + self.parent.x,
y: self.y + self.parent.y,
scale: BLOCK_SCALE
}));
game.addChild(new PointText({
x: self.x + self.parent.x,
y: self.y + self.parent.y
}));
}
lightSources += 1;
LK.getSound('light').play();
self.onSettle();
}
};
self.onSettle = function () {
self.updatePaths();
if (!self.disabled) {
var litRotationFactor = self.lit ? 0 : 1;
var litPositionFactor = self.lit ? 1 : -1;
[{
direction: 'up',
rotation: 0,
y: 1
}, {
direction: 'right',
rotation: 0.5,
x: -1
}, {
direction: 'down',
rotation: 1,
y: -1
}, {
direction: 'left',
rotation: 1.5,
x: 1
}].forEach(function (item) {
if (self[item.direction + 'Path']) {
var block = self[item.direction + 'Block'];
if (block && !block.disabled && block.lit !== self.lit && block[COMPLIMENTS[item.direction] + 'Path']) {
(self.lit ? block : self).lightContainer.addChild(new Light({
x: (item.x || 0) * litPositionFactor * BLOCK_HALFSIZE,
y: (item.y || 0) * litPositionFactor * BLOCK_HALFSIZE,
rotation: Math.PI * (item.rotation + litRotationFactor),
callback: self.lit ? block.onLit : self.onLit
}));
}
}
});
}
};
self.showLock = function () {
// Check and destroy existing lockSymbol before creating a new one
if (self.lockSymbol) {
self.lockSymbol.destroy();
}
var lockSymbol = self.addChild(LK.getAsset('iconLock', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1 / self.scale.x,
scaleY: 1 / self.scale.y
}));
// Play error sound if not already playing
if (!errorSoundPlaying) {
errorSoundPlaying = true;
LK.getSound('error').play();
LK.setTimeout(function () {
errorSoundPlaying = false;
}, 500);
}
self.lockSymbol = lockSymbol;
// Set a timeout to fade out the lockSymbol after 500ms
LK.setTimeout(function () {
tween(lockSymbol, {
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
lockSymbol.destroy();
self.lockSymbol = null;
}
});
}, 500);
};
self.updatePaths = function () {};
function makeGlow(x, y) {
var glow = self.lightContainer.addChild(new Glow({
x: x,
y: y,
width: LIGHT_SPILL_SIZE,
height: LIGHT_SPILL_SIZE,
anchorX: 0.5,
anchorY: 0.5,
rangeX: 20,
rangeY: 20,
layers: 4,
solid: true,
tint: 0xFFFFFF,
alpha: 0
}));
tween(glow, {
alpha: 1
}, {
duration: 250
});
}
self.onDestroy = function () {
if (self.lit) {
lightSources -= 1;
if (lightSources === 0) {
LK.getSound('gong').play(); // Play the gong sound
var gameOverText = game.addChild(new BorderedText("- Your Light is Dead -", {
size: 150,
anchorX: 0.5,
anchorY: 0.5,
x: game.width / 2,
y: game.height / 4
}));
LK.stopMusic(); // Stop the background music when the game ends
LK.setTimeout(function () {
// Delay the game over widget by 1 second
LK.showGameOver();
}, 1000);
}
}
if (self.lightContainer) {
self.lightContainer.destroy();
}
};
return self;
});
var BlockX = Block.expand(function (config) {
var self = Block.call(this, config);
var shiftY = 2;
self.upPath = true;
self.rightPath = true;
self.downPath = true;
self.leftPath = true;
self.blockContainer.attachAsset('blockX1', {
x: -BLOCK_HALFSIZE,
y: -BLOCK_HALFSIZE + shiftY,
anchorX: 0,
anchorY: 0
});
self.blockContainer.attachAsset('blockX2', {
x: BLOCK_HALFSIZE,
y: -BLOCK_HALFSIZE + shiftY,
anchorX: 1,
anchorY: 0
});
self.blockContainer.attachAsset('blockX3', {
x: -BLOCK_HALFSIZE,
y: BLOCK_HALFSIZE + shiftY,
anchorX: 0,
anchorY: 1
});
self.blockContainer.attachAsset('blockX4', {
x: BLOCK_HALFSIZE,
y: BLOCK_HALFSIZE + shiftY,
anchorX: 1,
anchorY: 1
});
return self;
});
var BlockT = Block.expand(function (config) {
var self = Block.call(this, config);
self.blockContainer.attachAsset('blockT1', {
x: -BLOCK_HALFSIZE,
y: -BLOCK_HALFSIZE,
anchorX: 0,
anchorY: 0
});
self.blockContainer.attachAsset('blockT2', {
x: BLOCK_HALFSIZE,
y: -BLOCK_HALFSIZE,
anchorX: 1,
anchorY: 0
});
self.blockContainer.attachAsset('blockT3', {
y: BLOCK_HALFSIZE,
anchorX: 0.5,
anchorY: 1
});
self.updatePaths = function () {
self.downPath = self.orientation !== 0;
self.leftPath = self.orientation !== 1;
self.upPath = self.orientation !== 2;
self.rightPath = self.orientation !== 3;
};
self.updatePaths();
return self;
});
var BlockLine = Block.expand(function (config) {
var self = Block.call(this, config);
self.blockContainer.attachAsset('blockLine1', {
x: -BLOCK_HALFSIZE,
anchorX: 0,
anchorY: 0.5,
height: BLOCK_SIZE
});
self.blockContainer.attachAsset('blockLine2', {
x: BLOCK_HALFSIZE,
anchorX: 1,
anchorY: 0.5,
height: BLOCK_SIZE
});
self.updatePaths = function () {
var vertical = !(self.orientation % 2);
self.upPath = vertical;
self.downPath = vertical;
self.leftPath = !vertical;
self.rightPath = !vertical;
};
self.updatePaths();
return self;
});
var BlockL = Block.expand(function (config) {
var self = Block.call(this, config);
self.blockContainer.attachAsset('blockL1', {
anchorX: 0.5,
anchorY: 0.5,
height: BLOCK_SIZE
});
self.blockContainer.attachAsset('blockL2', {
x: -BLOCK_HALFSIZE,
y: BLOCK_HALFSIZE,
anchorX: 0,
anchorY: 1
});
self.updatePaths = function () {
self.leftPath = self.orientation === 0 || self.orientation === 1;
self.upPath = self.orientation === 1 || self.orientation === 2;
self.rightPath = self.orientation === 2 || self.orientation === 3;
self.downPath = self.orientation === 3 || self.orientation === 0;
};
self.updatePaths();
return self;
});
var BlockBlank = Block.expand(function (config) {
var self = Block.call(this, config);
var crackAsset;
var crackLevel = 0;
var blockBlankAssets = ['blockBlank1', 'blockBlank2', 'blockBlank3'];
var randomAsset = blockBlankAssets[Math.floor(Math.random() * blockBlankAssets.length)];
self.blockContainer.attachAsset(randomAsset, {
anchorX: 0.5,
anchorY: 0.5,
width: BLOCK_SIZE,
height: BLOCK_SIZE
});
self.rotate = function (force) {
if (force || crackLevel > 0) {
if (++crackLevel <= 4) {
if (crackAsset) {
crackAsset.destroy();
}
if (!force) {
LK.getSound('crack').play();
}
crackAsset = self.blockContainer.attachAsset('crack' + crackLevel, {
anchorX: 0.5,
anchorY: 0.5,
rotation: crackLevel === 1 ? Math.random() * Math.PI * 2 : 0 // Random rotation if crackLevel is 1
});
// Create BlockSparkParticles
if (self.parent) {
for (var i = 0; i < 2 * crackLevel; i++) {
game.addChild(new BlockSparkParticle({
x: self.x + self.parent.x,
y: self.y + self.parent.y
}));
}
}
} else {
LK.getSound('break').play();
var blockClass = randomizeBlockClass({
index: self.row.index,
blanks: ROW_SPAN // Prevent spawning blocks
});
var newBlock = self.parent.addChild(new blockClass({
x: self.x,
y: self.y,
scale: BLOCK_SCALE,
index: self.index,
row: self.row
}));
self.row.blocks[self.index] = newBlock;
// Update block references
if (self.upBlock) {
self.upBlock.downBlock = newBlock;
newBlock.upBlock = self.upBlock;
}
if (self.downBlock) {
self.downBlock.upBlock = newBlock;
newBlock.downBlock = self.downBlock;
}
if (self.leftBlock) {
self.leftBlock.rightBlock = newBlock;
newBlock.leftBlock = self.leftBlock;
}
if (self.rightBlock) {
self.rightBlock.leftBlock = newBlock;
newBlock.rightBlock = self.rightBlock;
}
if (!self.disabled) {
newBlock.onSettle();
}
// Create BlockBreakParticles
for (var i = 0; i < 6; i++) {
game.addChild(new BlockBreakParticle({
x: self.x + self.parent.x,
y: self.y + self.parent.y
}));
}
self.callDestroy();
}
} else {
self.showLock();
}
};
if (Math.random() < BLOCK_CRACK_CHANCE) {
self.rotate(true);
}
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
// Math constants / pre-calculations
var MATH_HALF_ROOT_3 = Math.sqrt(3) / 2; // Required by: TEXT_OFFSETS, BorderedText, BorderedSymbol, SymbolText
;
// Text settings
var TEXT_OFFSETS = [[0, 1], [MATH_HALF_ROOT_3, 0.5], [MATH_HALF_ROOT_3, -0.5], [0, -1], [-MATH_HALF_ROOT_3, -0.5], [-MATH_HALF_ROOT_3, 0.5], [0, 0]]; // Required by: BorderedText, BorderedSymbol, SymbolText
var TEXT_BORDER_WEIGHT = 5; // Required by: BorderedText, BorderedSymbol, SymbolText
var TEXT_DEFAULT_BORDER = '#000000'; // Required by: BorderedText, BorderedSymbol, SymbolText
var TEXT_DEFAULT_FILL = '#FFFFFF'; // Required by: BorderedText, SymbolText
var TEXT_DEFAULT_FONT = 'Arial'; // Required by: BorderedText, SymbolText
var TEXT_DEFAULT_SIZE = 50; // Required by: BorderedText, SymbolText
;
// Other settings
var LAVA_LINE = game.height - 200;
var LAVA_SLICE_COUNT = 18;
var LAVA_SLICE_WIDTH = 128;
var LAVA_BOB_HEIGHT = 10;
var LAVA_BOB_OFFSET = Math.PI / 5;
var LAVA_BOB_PERIOD = Math.PI / 60;
var BLOCK_SCALE = 2.5;
var BLOCK_SIZE = 100;
var BLOCK_MARGIN = 20;
var BLOCK_HALFSIZE = BLOCK_SIZE * 0.5;
var BLOCK_CRACK_CHANCE = 0.45;
var BLOCK_ROTATE_SPEED = 100;
var LIGHT_LEVEL = 800;
var LIGHT_GROW_SPEED = 4500;
var LIGHT_GROW_VARIANCE = 1000;
var LIGHT_SPILL_SIZE = 35;
var LIGHT_OFFSET = 5;
var ROW_SPAN = 7;
var ROW_COUNT = 11;
var ROW_SPEED_BASE = 0.2;
var ROW_SPEED_INCREASE = 0.04;
var ROW_SPEED_INCREASE_MIN = 0.01;
var ROW_SPEED_INCREASE_FACTOR = 0.9;
var COL_SLIP_MIN = 10;
var COL_SLIP_MAX = 30;
var COL_SLIP_TIME = 750;
var COL_TIMEOUT_MIN = 1000;
var COL_TIMEOUT_MAX = 10000;
var COL_RISE_FACTOR = 0.2;
var COMPLIMENTS = {
up: 'down',
right: 'left',
down: 'up',
left: 'right'
};
var lightSources = 0;
var rowSpeed = ROW_SPEED_BASE;
var rowSpeedIncrease = ROW_SPEED_INCREASE;
var errorSoundPlaying = false;
var columns = [];
for (var i = 0; i < ROW_SPAN; i++) {
columns.push({
y: 0,
timer: false,
index: i
});
}
// Play background music on repeat when the game starts
LK.playMusic('background', {
loop: true
});
game.update = function () {
for (var i = 1; i < columns.length - 1; i++) {
var column = columns[i];
if (!column.timer) {
if (column.y > 0) {
column.y = Math.max(0, column.y - rowSpeed * COL_RISE_FACTOR);
} else {
startColumnTimer(i);
}
}
}
};
var scoreText = new BorderedText('0', {
size: 100,
anchorX: 0.5,
anchorY: 0
});
LK.gui.top.addChild(scoreText);
var rowSpeedText = LK.gui.topRight.addChild(new Text2((rowSpeed * 60).toFixed(1), {
size: 50,
fill: 0xFFFFFF,
stroke: 0x000000,
strokeThickness: 5
}));
rowSpeedText.anchor.set(1.0, -0.5);
var speedIcon = LK.gui.topRight.addChild(LK.getAsset('iconGear', {
anchorX: 0.5,
anchorY: 0.5,
x: rowSpeedText.x - rowSpeedText.width - 40,
y: rowSpeedText.height
}));
var iconRotate = LK.gui.topRight.addChild(new BorderedSymbol('iconRotate', {
anchorX: 0.2,
anchorY: 1.0,
x: speedIcon.x,
y: speedIcon.y
}));
var lightManager = game.addChild(new Container());
game.addChild(new Glow({
width: game.width,
height: LIGHT_LEVEL,
rangeY: 20,
layers: 5,
solid: true
}));
var blockManager = game.addChild(new BlockManager());
var leftBorder = game.addChild(new Border({
x: 30
}));
var rightBorder = game.addChild(new Border({
x: game.width - 30,
scaleX: -1
}));
var lava = game.addChild(new Lava({
x: game.width / 2,
y: LAVA_LINE
}));
game.addChild(new Glow({
x: game.width / 2,
width: game.width,
height: 200,
anchorX: 0.5,
anchorY: 0,
rangeY: 200,
tint: 0x000000,
alpha: 2.0
}));
var iconLight = game.addChild(LK.getAsset('iconLight', {
x: game.width / 2,
y: 100,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xFF4D00
}));
function shuffle(array) {
var currentIndex = array.length;
while (currentIndex != 0) {
var randomIndex = Math.floor(Math.random() * currentIndex);
currentIndex--;
var ref = [array[randomIndex], array[currentIndex]];
array[currentIndex] = ref[0];
array[randomIndex] = ref[1];
}
return array;
}
function generateBlockClasses(index) {
var settings = {
index: index,
blanks: 0,
xs: 0
};
switch (index) {
case 0:
return [BlockBlank, BlockBlank, BlockBlank, BlockX, BlockBlank, BlockBlank, BlockBlank];
case 1:
settings.blanks = ROW_SPAN;
settings.xs = 1;
return [BlockBlank, BlockBlank, randomizeBlockClass(settings), BlockX, randomizeBlockClass(settings), BlockBlank, BlockBlank];
case 2:
settings.blanks = ROW_SPAN;
settings.xs = 1;
return [BlockBlank, randomizeBlockClass(settings), randomizeBlockClass(settings), BlockX, randomizeBlockClass(settings), randomizeBlockClass(settings), BlockBlank];
default:
var blockClasses = [];
for (var i = 0; i < ROW_SPAN; i++) {
blockClasses[i] = randomizeBlockClass(settings);
}
return shuffle(blockClasses);
}
}
function adjustRowSpeed() {
rowSpeed += rowSpeedIncrease;
rowSpeedText.setText((rowSpeed * 60).toFixed(1));
rowSpeedIncrease = Math.max(ROW_SPEED_INCREASE_MIN, rowSpeedIncrease * ROW_SPEED_INCREASE_FACTOR);
}
function startColumnTimer(index) {
var column = columns[index];
column.timer = true;
var timeoutDuration = COL_TIMEOUT_MIN + Math.random() * (COL_TIMEOUT_MAX - COL_TIMEOUT_MIN);
LK.setTimeout(function () {
var targetY = COL_SLIP_MIN + Math.random() * (COL_SLIP_MAX - COL_SLIP_MIN);
tween(column, {
y: targetY
}, {
duration: COL_SLIP_TIME,
easing: tween.easeIn,
// Apply ease in for the tween
onFinish: function onFinish() {
column.timer = false; // Reset the timer to false after tween completes
}
});
}, timeoutDuration);
}
function randomizeBlockClass(settings) {
var classes = [{
blockClass: BlockBlank,
chance: Math.max(0, 1.5 - 0.5 * (settings.blanks || 0))
}, {
blockClass: BlockT,
chance: 1
}, {
blockClass: BlockX,
chance: Math.max(0, 1 - 1 * (settings.xs || 0))
}, {
blockClass: BlockLine,
chance: 1
}, {
blockClass: BlockL,
chance: 1
}];
var totalChance = classes.reduce(function (sum, item) {
return sum + item.chance;
}, 0);
var randomValue = Math.random() * totalChance;
var accumulatedChance = 0;
var blockClass = BlockBlank;
for (var i = 0; i < classes.length; i++) {
accumulatedChance += classes[i].chance;
if (randomValue < accumulatedChance) {
blockClass = classes[i].blockClass;
break;
}
}
switch (blockClass) {
case BlockBlank:
settings.blanks = (settings.blanks || 0) + 1;
break;
case BlockX:
settings.xs = (settings.xs || 0) + 1;
break;
}
return blockClass;
}