/**** * Classes ****/ var BloodsplatterEffect = Container.expand(function (x, y) { var self = Container.call(this); var duration = 15; var remaining = duration; var maxScale = 2.0; var startAlpha = 0.5; var graphics = self.attachAsset('bloodsplatter', { anchorX: 0.5, anchorY: 0.5 }); graphics.alpha = startAlpha; graphics.y = -graphics.height / 2; ; self.x = x; self.y = y; self._update_migrated = _update_migrated; ; function _update_migrated(velocityX, velocityY) { var progress = 1 - remaining-- / duration; if (remaining <= 0) { return true; } graphics.scale.set(1 + (maxScale - 1) * progress); graphics.alpha = startAlpha * (1 - progress); self.x -= velocityX; self.y -= velocityY; } }); var DropEffect = Container.expand(function (x, y) { var self = Container.call(this); var angle = Math.random() * Math.PI; var speed = 10 + Math.random() * 5; var speedX = Math.cos(angle) * speed; var speedY = Math.sin(angle) * speed; var spinSpeed = Math.random() * 0.2 - 0.1; var scale = 1; var scaleDecrement = 1 / 60; var variant = Math.floor(Math.random() * 3); var graphics = self.createAsset('pickup' + variant, { anchorX: 0.5, anchorY: 0.95 }); graphics.rotation = Math.random() * Math.PI * 2; ; self.x = x; self.y = y; self._update_migrated = _update_migrated; ; function _update_migrated(velocityX, velocityY) { self.x += speedX - velocityX; self.y += speedY - velocityY; scale -= scaleDecrement; graphics.rotation += spinSpeed; graphics.scale.set(scale); return scale <= 0; } }); var Interface = Container.expand(function (x, y) { var self = Container.call(this); var score = 0; var lives = 3; var lifeIcons = []; var rainbowIcons = []; var scoreIcon = self.attachAsset('scoreIcon', { anchorX: 0.5 }); var scoreText = self.addChild(new Text2('| ' + formatScore(score) + ' |', { size: 70, fill: "#000000", align: 'left' })); var distanceText = self.addChild(new Text2('0m', { size: 70, fill: "#000000", align: 'right' })); for (var i = 0; i < lives; i++) { var lifeIcon = self.attachAsset('lifeIcon', { anchorX: 1 }); lifeIcon.x = -115 - i * 60; lifeIcon.y = scoreIcon.height + 20; lifeIcons.push(lifeIcon); } for (var i = 0; i < 3; i++) { var rainbowIcon = self.attachAsset('rainbowIcon', { anchorX: 1 }); rainbowIcon.x = 175 + i * 65; rainbowIcon.y = scoreIcon.height + 20; rainbowIcon.alpha = 0.5; rainbowIcons.push(rainbowIcon); } scoreText.x = -110; scoreText.y = scoreIcon.height; scoreText.anchor.set(0, 0); distanceText.x = STAGE_WIDTH / 2 - 350; distanceText.y = 30; distanceText.anchor.set(1, 0); ; self.x = x; self.y = y; self._update_migrated = _update_migrated; self.changeScore = changeScore; self.changeLives = changeLives; self.changeRainbows = changeRainbows; ; function _update_migrated(distance) { if (hasLost) { return; } distanceText.setText(Math.round(distance * SCREEN_METERAGE) + 'm'); } function changeLives(amount) { lives += amount; for (var i = 0; i < lifeIcons.length; i++) { lifeIcons[i].alpha = i < lives ? 1 : 0.5; } if (amount < 0) { LK.effects.flashScreen(0xaa0000, 300); if (lives <= 0) { hasLost = true; bgMusic.stop(); LK.getSound('wreck').play(); // Play wreck sound LK.setTimeout(function () { LK.getSound('gameoverSound').play(); }, 1000); LK.setTimeout(function () { LK.showGameOver(); }, 3000); return true; } } return false; } function changeRainbows(amount) { rainbows = Math.min(3, rainbows + amount); for (var i = 0; i < rainbowIcons.length; i++) { rainbowIcons[i].alpha = i < rainbows ? 1 : 0.5; } if (amount > 0) { LK.getSound('bigBonusSound').play(); LK.effects.flashScreen(0x00ff00, 300); } return false; } function changeScore(amount, variant) { var returnValue = amount < 0 && score > 0; score = Math.min(999, Math.max(0, score + amount)); LK.setScore(score); scoreText.setText('| ' + formatScore(score) + ' |'); if (amount > 0) { LK.getSound('bonusSound').play(); var tinyPickup = self.attachAsset('pickup' + variant, { // Use the variant parameter anchorX: 0.5, anchorY: 0.5, scaleX: 0.5, scaleY: 0.5 }); tinyPickup.x = (Math.random() - 0.5) * scoreIcon.width; tinyPickup.y = 20 + (Math.random() - 0.5) * scoreIcon.height; } return returnValue; } }); var LandscapeTile = Container.expand(function (x, y, _ref) { var self = Container.call(this); var lookup = _ref.lookup, xIndex = _ref.xIndex, yIndex = _ref.yIndex, density = _ref.density, pickups = _ref.pickups, monsters = _ref.monsters, player = _ref.player, key = _ref.key, landscapeTiles = _ref.landscapeTiles, layers = _ref.layers, rampList = _ref.rampList; var obstructions = []; var background = self.attachAsset('landscapeBackground', { anchorX: 0.5, anchorY: 0.5 }); background.width = TILE_SIZE; background.height = TILE_SIZE; background.alpha = 0.03; background.x = yIndex % 2 === 0 ? TILE_BACKGROUND_OFFSET : 0; ; self.x = x; self.y = y; self.activate = activate; self.active = false; self.key = key; self.checkCollision = checkCollision; ; function checkCollision(player) { if (!player.airborne) { var reach = 200; var dx = Math.abs(player.x - self.x); var dy = Math.abs(player.y - self.y); if (dx < TILE_SIZE / 2 + reach && dy < TILE_SIZE / 2 + reach) { for (var i = 0; i < obstructions.length; i++) { var obstruction = obstructions[i]; if (obstruction.active && player.hitbox.intersects(obstruction.hitbox)) { LK.getSound('bump').play(); obstruction.deactivate(); return true; } } } } return false; } function activate() { if (!self.active) { self.active = true; var densityChance = density * Math.random(); var obstructionTypes = ['largeTree', 'smallTree', 'smallTree', 'deadTree', 'smallRock', 'smallRock', 'largeRock', 'stump']; var clearanceDist = 300; var clearanceDistSqr = clearanceDist * clearanceDist; var spawnBorder = 25; var cellSize = 150; var cols = Math.ceil(TILE_SIZE / cellSize); var rows = Math.ceil(TILE_SIZE / cellSize); var cellWidth = TILE_SIZE / cols; var cellHeight = TILE_SIZE / rows; var landmark; if (Math.random() <= SPAWN_PICKUP_CHANCE) { var _randomPointInRect = randomPointInRect(self.x, self.y, TILE_SIZE, TILE_SIZE, clearanceDist), pointX = _randomPointInRect.pointX, pointY = _randomPointInRect.pointY; pickups.push(landmark = self.parent.addChild(new Pickup(pointX, pointY))); } else if (Math.random() <= SPAWN_RAMP_CHANCE) { var _randomPointInRect2 = randomPointInRect(self.x, self.y, TILE_SIZE, TILE_SIZE, clearanceDist), pointX = _randomPointInRect2.pointX, pointY = _randomPointInRect2.pointY; rampList.push(landmark = layers[LAYER_BACKGROUND].addChild(new Ramp(pointX, pointY))); } if (player.distanceTravelled > MONSTER_SPAWN_DIST && Math.random() <= SPAWN_MONSTER_CHANCE) { var _randomPointInRect3 = randomPointInRect(self.x, self.y, TILE_SIZE, TILE_SIZE, clearanceDist), pointX = _randomPointInRect3.pointX, pointY = _randomPointInRect3.pointY; monsters.push(self.parent.addChild(new Monster(self.x, self.y))); } for (i = 0; i < cols; i++) { for (j = 0; j < rows; j++) { if (Math.random() <= densityChance) { var canPlace = true; var _randomPointInRect4 = randomPointInRect(cellWidth * i, cellHeight * j, cellWidth, cellHeight, spawnBorder), pointX = _randomPointInRect4.pointX, pointY = _randomPointInRect4.pointY; if (landmark) { var dx = landmark.x - pointX; var dy = landmark.y - pointY; var distSqr = dx * dx + dy * dy; if (distSqr <= clearanceDistSqr) { canPlace = false; } } if (canPlace) { var type = obstructionTypes[Math.floor(Math.random() * obstructionTypes.length)]; obstructions.push(self.addChild(new Obstruction(pointX, pointY, type))); } } } } } var leftKey = xIndex - 1 + ':' + yIndex; var rightKey = xIndex + 1 + ':' + yIndex; var downKey = xIndex + ':' + (yIndex + 1); var newDensity = (1 + Math.pow(Math.log(player.distanceTravelled / DIFFICULTY_FACTOR_DIST + 2), 2)) / 100; if (!lookup[leftKey]) { landscapeTiles.push(lookup[leftKey] = self.parent.addChild(new LandscapeTile(self.x - TILE_SIZE, self.y, { key: leftKey, density: newDensity, xIndex: xIndex - 1, yIndex: yIndex, landscapeTiles: landscapeTiles, rampList: rampList, lookup: lookup, pickups: pickups, monsters: monsters, layers: layers, player: player }))); } if (!lookup[rightKey]) { landscapeTiles.push(lookup[rightKey] = self.parent.addChild(new LandscapeTile(self.x + TILE_SIZE, self.y, { key: rightKey, density: newDensity, xIndex: xIndex + 1, yIndex: yIndex, landscapeTiles: landscapeTiles, rampList: rampList, lookup: lookup, pickups: pickups, monsters: monsters, layers: layers, player: player }))); } if (!lookup[downKey]) { landscapeTiles.push(lookup[downKey] = self.parent.addChild(new LandscapeTile(self.x, self.y + TILE_SIZE, { key: downKey, density: newDensity, yIndex: yIndex + 1, xIndex: xIndex, landscapeTiles: landscapeTiles, rampList: rampList, lookup: lookup, pickups: pickups, monsters: monsters, layers: layers, player: player }))); } function randomPointInRect(centerX, centerY, width, height) { var border = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0; var startX = centerX - width / 2 + border; var startY = centerY - height / 2 + border; var innerWidth = width - border * 2; var innerHeight = height - border * 2; var pointX = startX + Math.random() * innerWidth; var pointY = startY + Math.random() * innerHeight; return { pointX: pointX, pointY: pointY }; } } }); var Monster = Container.expand(function (x, y) { var self = Container.call(this); var active = false; var updateTick = 0; var updatePeriod = 30; var jumpSpeedMax = 30; var jumpHeight = 100; var jumpVelocityX = 0; var jumpVelocityY = 0; var jumpDuration = updatePeriod * 2 / 3; var invulnerabilityBoost = 1.8; var jumpDistMax = jumpSpeedMax * jumpDuration; var jumpDistMaxSqr = jumpDistMax * jumpDistMax; var activationDist = 1000; var activationDistSqr = activationDist * activationDist; var despawnY = -2500; var shadowReduction = 0.1; var shadow = self.attachAsset('shadow', { anchorX: 0.45, anchorY: 1 }); var graphics = self.attachAsset('monster', { anchorX: 0.5, anchorY: 1 }); graphics.scale.x = Math.random() < 0.5 ? 1 : -1; shadow.width = graphics.width * 0.9; shadow.height = graphics.width * 0.45; shadow.tint = 0x000000; shadow.alpha = 0.0; ; self.x = x; self.y = y; self.jumping = false; self.hitbox = shadow; self._update_migrated = _update_migrated; ; function _update_migrated(velocityX, velocityY, player) { if (!active) { var dx = player.x - self.x; var dy = player.y - self.y; var distSqr = dx * dx + dy * dy; if (distSqr <= activationDistSqr) { active = true; if (rainbows > 0) { rainbowAnimation(); } } } if (active) { var updateCurve = (-Math.cos(++updateTick * Math.PI / (updatePeriod / 2)) + 0.5) / 1.5; if (self.jumping) { if (updateCurve <= 0) { self.jumping = false; jumpVelocityX = 0; jumpVelocityY = 0; } } else { if (updateCurve > 0) { var boost = player.invulnerable ? invulnerabilityBoost : 1; var targetPosX = player.x + velocityX * jumpDuration * boost; var targetPosY = player.y + velocityY * jumpDuration * boost; var ddx = targetPosX - self.x; var ddy = targetPosY - self.y; var angle = Math.atan2(ddy, ddx); var targetDistSqr = ddx * ddx + ddy * ddy; var jumpSpeed = jumpSpeedMax * (targetDistSqr >= jumpDistMaxSqr ? 1 : Math.sqrt(targetDistSqr) / jumpDistMax); jumpVelocityX = Math.cos(angle) * jumpSpeed; jumpVelocityY = Math.sin(angle) * jumpSpeed; self.jumping = true; self.monsterJumpSound = LK.getSound('monsterJump').play(); } } graphics.y = -Math.max(0, updateCurve) * jumpHeight; graphics.scale.x = player.x < self.x ? -1 : 1; graphics.scale.y = 1 + Math.min(0, updateCurve); shadow.scale.set(1 - shadowReduction * updateCurve); } self.x += jumpVelocityX - velocityX; self.y += jumpVelocityY - velocityY; if (self.y < despawnY || self.y < 0) { if (self.monsterJumpSound) { self.monsterJumpSound.stop(); } return true; } return false; } }); var Obstruction = Container.expand(function (x, y, type) { var self = Container.call(this); var graphics = self.createAsset(type, { anchorX: 0.5, anchorY: 1 }); var hitbox = self.attachAsset('hitbox', { anchorX: 0.5, anchorY: 0.5 }); var randomScale = 0.9 + Math.random() * 0.2; hitbox.y = -25; hitbox.alpha = 0; hitbox.width = graphics.width * 0.8; hitbox.height = graphics.width * 0.45; ; self.x = x; self.y = y; self.active = true; self.hitbox = hitbox; self.deactivate = deactivate; self.scale.set(randomScale, randomScale); ; function deactivate() { self.active = false; graphics.alpha = 0.5; } }); var Pickup = Container.expand(function (x, y) { var self = Container.call(this); var activeDist = 300; var activeDistSqr = activeDist * activeDist; var collectDist = 50; var collectDistSqr = collectDist * collectDist; var collectSpeed = 25; var collectOffset = -50; var active = false; self.variant = nextVariant % 7; //{2Q} // Ensure variant is a property of the Pickup class nextVariant++; var graphics = self.createAsset('pickup' + self.variant, { anchorX: 0.5, anchorY: 0.95 }); var backShadow = self.attachAsset('shadow', { anchorX: 0.5, anchorY: 0.5, alpha: 0.25 }); var foreShadow = self.attachAsset('shadow', { anchorX: 0.5, anchorY: 0.5, alpha: 0.25 }); foreShadow.width = graphics.width; foreShadow.height = graphics.width * 0.25; backShadow.width = graphics.width; backShadow.height = graphics.width * 0.25; backShadow.tint = 0x000000; backShadow.y = -3; ; self.x = x; self.y = y; self._update_migrated = _update_migrated; ; function _update_migrated(velocityX, velocityY, player) { var dx = player.x - self.x; var dy = player.y + collectOffset - self.y; var distSqr = dx * dx + dy * dy; if (self.active) { var dist = Math.sqrt(distSqr); self.x += dx / dist * collectSpeed; self.y += dy / dist * collectSpeed; } else if (distSqr <= activeDistSqr) { self.active = true; backShadow.destroy(); foreShadow.destroy(); } else { self.x -= velocityX; self.y -= velocityY; } if (distSqr <= collectDistSqr) { LK.getSound('bonusSound').play(); return true; } return false; } }); var Player = Container.expand(function (x, y) { var self = Container.call(this); var angle = 0; var speed = 0; var reduction = 0; var travelDistX = 0; var travelDistY = 0; var rampRemaining = 0; var rampDuration = 0; var rampHeight = 0; var rampHeightMax = 0; var tiltSpeedFactor = 100; var playerShadow = self.attachAsset('shadow', { anchorX: 0.5, anchorY: 0.5 }); var platformGraphics = self.attachAsset('platform', { anchorX: 0.4, anchorY: 0.5, x: -30 }); var brokenWagonGraphics = self.attachAsset('brokenWagon', { anchorX: 0.4, anchorY: 0.5, x: -30, alpha: 0 }); var playerGraphics = self.attachAsset('player', { anchorX: 0.575, anchorY: 1 /*x: 30, y: 0 */ }); var hitbox = self.attachAsset('hitbox', { anchorX: 0.5, anchorY: 0.5 }); playerShadow.alpha = 0.5; playerShadow.scale.set(1.0, 0.6); playerShadow.tint = 0x000000; hitbox.width = 25; hitbox.height = 25; hitbox.alpha = 0; ; self.x = x; self.y = y; self.airborne = false; self.invulnerable = false; self.invulnerableTime = 3 * 60; self.invulnerableTimer = 0; self.distanceTravelled = 0; self.hitbox = hitbox; self._update_migrated = _update_migrated; self.ramp = ramp; ; function _update_migrated(targetPosition) { if (hasLost) { playerGraphics.alpha = 0; // Hide the player platformGraphics.alpha = 0; // Hide the player playerShadow.alpha = 0; // Hide the player brokenWagonGraphics.alpha = 1; // show wreck return; } if (self.invulnerable) { if (--self.invulnerableTimer <= 0) { self.invulnerable = false; } self.alpha = self.invulnerableTimer % 60 < 30 ? 1 : 0.5; } if (self.airborne) { if (--rampRemaining <= 0) { self.airborne = false; LK.getSound('landing').play(); } rampHeight = rampHeightMax * Math.sin(Math.PI * rampRemaining / rampDuration); platformGraphics.y = -rampHeight; playerGraphics.y = -rampHeight; } if (!self.airborne) { var dx = targetPosition.x - self.x; var dy = targetPosition.y - self.y; angle = angleClamp(Math.atan2(dy, dx)); } var acceleration = (Math.sin(angle) - 0.1) * 2.0; var boost = self.airborne ? RAMP_SPEED_BOOST : 1; reduction = self.invulnerable ? 1 - self.invulnerableTimer / self.invulnerableTime : 1; speed = Math.max(0, speed * 0.95 + acceleration); var velocityX = Math.cos(angle) * reduction * boost * speed; var velocityY = Math.sin(angle) * reduction * boost * speed; travelDistX += velocityX; travelDistY += velocityY; self.distanceTravelled = Math.sqrt(travelDistX * travelDistX + travelDistY * travelDistY); if (!self.airborne) { var facingSide = targetPosition.x < self.x ? -1 : 1; platformGraphics.rotation = angle; playerGraphics.rotation = speed * reduction / tiltSpeedFactor * (Math.PI / 2) * facingSide; playerGraphics.scale.x = facingSide; playerGraphics.x = platformGraphics.x + 30 * Math.cos(platformGraphics.rotation) + 30 * Math.sin(platformGraphics.rotation); playerGraphics.y = platformGraphics.y + 30 * Math.sin(platformGraphics.rotation); } return { velocityX: velocityX, velocityY: velocityY }; } function angleClamp(angle) { return angle >= 0 ? angle : angle < -Math.PI / 2 ? Math.PI : 0; } function ramp() { if (!self.airborne && speed * reduction > RAMP_CRITICAL_SPEED) { LK.getSound('wagoonJump').play(); var rampFactor = speed * reduction - RAMP_CRITICAL_REDUX; self.airborne = true; rampHeightMax = rampFactor * RAMP_SPEED_HEIGHT; rampDuration = Math.round(rampFactor * RAMP_SPEED_DURATION); rampRemaining = rampDuration; } } }); var Ramp = Container.expand(function (x, y) { var self = Container.call(this); var rampPillar = self.attachAsset('rampPillar', { anchorX: 0.5, anchorY: 0.5 }); var rampShadow = self.attachAsset('rampShadow', { anchorX: 0.5 }); var rampGraphics = self.attachAsset('ramp', { anchorX: 0.5 }); var hitbox = self.attachAsset('hitbox', { anchorX: 0.5 }); rampShadow.rotation = 0.1; rampShadow.alpha = 0.5; rampShadow.tint = 0x000000; rampPillar.y = rampGraphics.height; hitbox.width = rampGraphics.width; hitbox.height = 50; hitbox.alpha = 0; ; self.x = x; self.y = y; self.hitbox = hitbox; self._update_migrated = _update_migrated; ; function _update_migrated(velocityX, velocityY, player) { self.x -= velocityX; self.y -= velocityY; if (self.y < -200) { return true; } if (!player.airborne && player.hitbox.intersects(self.hitbox)) { player.ramp(); } } }); var Trail = Container.expand(function (x, y) { var self = Container.call(this); var trailLength = 60; var trailAlpha = 0.4; var trailElements = []; ; self.x = x; self.y = y; self._update_migrated = _update_migrated; ; function _update_migrated(velocityX, velocityY, active) { if (hasLost) { return; } var trailElement = null; if (active && (velocityX !== 0 || velocityY !== 0)) { trailElement = self.attachAsset('trail', { anchorY: 0.5 }); var angle = Math.atan2(velocityY, velocityX); var speed = Math.sqrt(velocityX * velocityX + velocityY * velocityY); trailElement.rotation = angle; trailElement.scale.x = speed / 100; } trailElements.push(trailElement); if (trailElements.length > trailLength) { var removedElement = trailElements.shift(); if (removedElement) { removedElement.destroy(); } } for (var i = trailElements.length - 1; i >= 0; i--) { var element = trailElements[i]; if (element) { var alpha = trailAlpha * (i / trailLength); element.x -= velocityX; element.y -= velocityY; element.alpha = alpha; } } } }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ function rainbowAnimation() { LK.getSound('rainbowAttack').play(); var confettiCount = 30; var confettiArray = []; for (var i = 0; i < confettiCount; i++) { var angle = Math.random() * 2 * Math.PI; // Random angle in radians var confetti = LK.getAsset('rainbow', { anchorX: 0.5, anchorY: 0.5, width: 100, height: 100 * (113.5 / 256), // Preserve aspect ratio alpha: 1, x: STAGE_WIDTH / 2, y: STAGE_HEIGHT / 2, angle: angle // Store the random angle }); game.addChild(confetti); confettiArray.push(confetti); } var interval = LK.setInterval(function () { for (var i = 0; i < confettiArray.length; i++) { var confetti = confettiArray[i]; confetti.x += Math.cos(confetti.angle) * 30; confetti.y += Math.sin(confetti.angle) * 30; confetti.width += 1; confetti.height = confetti.width * (113.5 / 256); // Preserve aspect ratio confetti.alpha -= 0.005; if (confetti.alpha <= 0) { confetti.destroy(); confettiArray.splice(i, 1); i--; } } if (confettiArray.length === 0) { LK.clearInterval(interval); } }, 16); // Approximately 60 FPS for (var i = monsters.length - 1; i >= 0; i--) { var monster = monsters[i]; effects.push(new BloodsplatterEffect(monster.x, monster.y)); LK.getSound('monsterDying').play(); monster.destroy(); monsters.splice(i, 1); } __interface.changeRainbows(-1); } ; var STAGE_WIDTH = 2048; var STAGE_HEIGHT = 2732; var SCREEN_METERAGE = 1 / 150; var MONSTER_SPAWN_DIST = 100 / SCREEN_METERAGE; var DIFFICULTY_FACTOR_DIST = 150 / SCREEN_METERAGE; var TILE_SIZE = 512; var TILE_MARGIN = TILE_SIZE; var TILE_BACKGROUND_OFFSET = 100; var SPAWN_PICKUP_CHANCE = 0.1; var SPAWN_MONSTER_CHANCE = 0.01; var SPAWN_RAMP_CHANCE = 0.025; var RAMP_CRITICAL_SPEED = 15.0; var RAMP_CRITICAL_REDUX = 10; var RAMP_SPEED_BOOST = 1.25; var RAMP_SPEED_DURATION = 3.0; var RAMP_SPEED_HEIGHT = 20.0; var LAYER_BACKGROUND = 0; var LAYER_MIDGROUND = 1; var LAYER_FOREGROUND = 2; ; var playerPosX = Math.round(STAGE_WIDTH / 2); var playerPosY = Math.round(STAGE_WIDTH / 3); var landscapeLookup = {}; var landscapeTiles = []; var pickups = []; var monsters = []; var effects = []; var rampList = []; var layers = [game.addChild(new Container()), game.addChild(new Container()), game.addChild(new Container())]; var speed = 0; var nextVariant = 0; var __interface = LK.gui.top.addChild(new Interface(0, 10)); var trail = layers[LAYER_BACKGROUND].addChild(new Trail(playerPosX, playerPosY)); var player = layers[LAYER_MIDGROUND].addChild(new Player(playerPosX, playerPosY)); var bgMusic = LK.getSound('bgMusic'); var loopBgMusicInterval; landscapeTiles.push(landscapeLookup['0:0'] = layers[LAYER_FOREGROUND].addChild(new LandscapeTile(STAGE_WIDTH / 2, TILE_SIZE / 2, { density: -1, lookup: landscapeLookup, xIndex: 0, yIndex: 0, key: '0:0', landscapeTiles: landscapeTiles, rampList: rampList, monsters: monsters, pickups: pickups, layers: layers, player: player }))); var totalCollectedPickups = 0; var rainbows = 0; var isStarted = false; var isMouseDown = false; var hasLost = false; var targetPosition = { x: playerPosX, y: playerPosY }; game.setBackgroundColor(0x00FF00); ; game.on('down', function (x, y, obj) { if (!isStarted) { return; } isMouseDown = true; updatePosition(obj); loopBgMusic(); }); game.on('up', function (x, y, obj) { isMouseDown = false; }); game.on('move', function (x, y, obj) { if (isMouseDown) { updatePosition(obj); } }); LK.on('tick', function () { if (!isStarted) { return; } if (typeof player._update_migrated === 'function') { var _player$update = player._update_migrated(targetPosition); if (_player$update) { var velocityX = _player$update.velocityX, velocityY = _player$update.velocityY; } else { console.error("Player update method did not return expected values"); return; } } else { console.error("Player update method is undefined"); return; } trail._update_migrated(velocityX, velocityY, !player.airborne); __interface._update_migrated(player.distanceTravelled); for (var i = landscapeTiles.length - 1; i >= 0; i--) { var landscapeTile = landscapeTiles[i]; if (hasLost) { continue; } landscapeTile.x -= velocityX; landscapeTile.y -= velocityY; if (landscapeTile.y < -(TILE_SIZE / 2 + TILE_MARGIN)) { landscapeTile.destroy(); landscapeTiles.splice(i, 1); landscapeLookup[landscapeTile.key] = undefined; } else if (!landscapeTile.active && landscapeTile.x > -TILE_MARGIN && landscapeTile.x < STAGE_WIDTH + TILE_MARGIN && landscapeTile.y < STAGE_HEIGHT + TILE_MARGIN) { landscapeTile.activate(); } else if (landscapeTile.checkCollision(player)) { if (__interface.changeScore(-1)) { effects.push(new DropEffect(game, player.x, player.y)); } if (!player.invulnerable) { player.invulnerable = true; player.invulnerableTimer = player.invulnerableTime; } } } for (var i = pickups.length - 1; i >= 0; i--) { if (hasLost) { break; } var pickup = pickups[i]; var variant = pickup.variant; // Define the variant variable if (pickup._update_migrated(velocityX, velocityY, player)) { __interface.changeScore(1, variant); // Pass the variant parameter totalCollectedPickups++; if (totalCollectedPickups > 0 && totalCollectedPickups % 7 === 0) { __interface.changeScore(2, Math.floor(Math.random() * 7)); __interface.changeRainbows(1); //rainbowAnimation(); } pickup.destroy(); pickups.splice(i, 1); } else if (pickup.y < -(TILE_SIZE / 2 + TILE_MARGIN) || pickup.active && player.hitbox.intersects(pickup.hitbox)) { pickup.destroy(); pickups.splice(i, 1); } } for (var i = effects.length - 1; i >= 0; i--) { if (hasLost) { break; } var effect = effects[i]; if (effect._update_migrated(velocityX, velocityY)) { effect.destroy(); effects.splice(i, 1); } } for (var i = rampList.length - 1; i >= 0; i--) { if (hasLost) { break; } var ramp = rampList[i]; if (ramp._update_migrated(velocityX, velocityY, player)) { rampList.splice(i, 1); } } for (var i = monsters.length - 1; i >= 0; i--) { if (hasLost) { break; } var monster = monsters[i]; if (monster._update_migrated(velocityX, velocityY, player)) { monster.destroy(); monsters.splice(i, 1); } else if (!monster.jumping && !player.airborne && monster.hitbox.intersects(player.hitbox)) { effects.push(new BloodsplatterEffect(game, monster.x, monster.y)); monster.destroy(); monsters.splice(i, 1); LK.getSound('monsterEat').play(); if (__interface.changeLives(-1)) { hasLost = true; bgMusic.stop(); LK.getSound('wreck').play(); // Play wreck sound LK.setTimeout(function () { LK.getSound('gameoverSound').play(); }, 1000); LK.setTimeout(function () { LK.showGameOver(); }, 3000); } } } }); // Function to loop background music function loopBgMusic() { if (!loopBgMusicInterval && !loopBgMusic.called) { loopBgMusic.called = true; // Play the background music immediately bgMusic.play(); // Set an interval to loop the background music loopBgMusicInterval = LK.setInterval(function () { bgMusic.play(); }, 9500); // Assuming the background music duration is 60 seconds } } function updatePosition(event) { var pos = game.toLocal(event.global); targetPosition.x = pos.x; targetPosition.y = pos.y; } function formatScore(score) { if (score < 10) { return '00' + score; } if (score < 100) { return '0' + score; } return '' + score; } var startButton = LK.getAsset('startButton', { anchorX: 0.5, anchorY: 0.5, x: 2048 / 2, y: 2732 / 2 }); game.addChild(startButton); startButton.on('down', function () { startButton.destroy(); isStarted = true; // Add any additional code to start the game here });
/****
* Classes
****/
var BloodsplatterEffect = Container.expand(function (x, y) {
var self = Container.call(this);
var duration = 15;
var remaining = duration;
var maxScale = 2.0;
var startAlpha = 0.5;
var graphics = self.attachAsset('bloodsplatter', {
anchorX: 0.5,
anchorY: 0.5
});
graphics.alpha = startAlpha;
graphics.y = -graphics.height / 2;
;
self.x = x;
self.y = y;
self._update_migrated = _update_migrated;
;
function _update_migrated(velocityX, velocityY) {
var progress = 1 - remaining-- / duration;
if (remaining <= 0) {
return true;
}
graphics.scale.set(1 + (maxScale - 1) * progress);
graphics.alpha = startAlpha * (1 - progress);
self.x -= velocityX;
self.y -= velocityY;
}
});
var DropEffect = Container.expand(function (x, y) {
var self = Container.call(this);
var angle = Math.random() * Math.PI;
var speed = 10 + Math.random() * 5;
var speedX = Math.cos(angle) * speed;
var speedY = Math.sin(angle) * speed;
var spinSpeed = Math.random() * 0.2 - 0.1;
var scale = 1;
var scaleDecrement = 1 / 60;
var variant = Math.floor(Math.random() * 3);
var graphics = self.createAsset('pickup' + variant, {
anchorX: 0.5,
anchorY: 0.95
});
graphics.rotation = Math.random() * Math.PI * 2;
;
self.x = x;
self.y = y;
self._update_migrated = _update_migrated;
;
function _update_migrated(velocityX, velocityY) {
self.x += speedX - velocityX;
self.y += speedY - velocityY;
scale -= scaleDecrement;
graphics.rotation += spinSpeed;
graphics.scale.set(scale);
return scale <= 0;
}
});
var Interface = Container.expand(function (x, y) {
var self = Container.call(this);
var score = 0;
var lives = 3;
var lifeIcons = [];
var rainbowIcons = [];
var scoreIcon = self.attachAsset('scoreIcon', {
anchorX: 0.5
});
var scoreText = self.addChild(new Text2('| ' + formatScore(score) + ' |', {
size: 70,
fill: "#000000",
align: 'left'
}));
var distanceText = self.addChild(new Text2('0m', {
size: 70,
fill: "#000000",
align: 'right'
}));
for (var i = 0; i < lives; i++) {
var lifeIcon = self.attachAsset('lifeIcon', {
anchorX: 1
});
lifeIcon.x = -115 - i * 60;
lifeIcon.y = scoreIcon.height + 20;
lifeIcons.push(lifeIcon);
}
for (var i = 0; i < 3; i++) {
var rainbowIcon = self.attachAsset('rainbowIcon', {
anchorX: 1
});
rainbowIcon.x = 175 + i * 65;
rainbowIcon.y = scoreIcon.height + 20;
rainbowIcon.alpha = 0.5;
rainbowIcons.push(rainbowIcon);
}
scoreText.x = -110;
scoreText.y = scoreIcon.height;
scoreText.anchor.set(0, 0);
distanceText.x = STAGE_WIDTH / 2 - 350;
distanceText.y = 30;
distanceText.anchor.set(1, 0);
;
self.x = x;
self.y = y;
self._update_migrated = _update_migrated;
self.changeScore = changeScore;
self.changeLives = changeLives;
self.changeRainbows = changeRainbows;
;
function _update_migrated(distance) {
if (hasLost) {
return;
}
distanceText.setText(Math.round(distance * SCREEN_METERAGE) + 'm');
}
function changeLives(amount) {
lives += amount;
for (var i = 0; i < lifeIcons.length; i++) {
lifeIcons[i].alpha = i < lives ? 1 : 0.5;
}
if (amount < 0) {
LK.effects.flashScreen(0xaa0000, 300);
if (lives <= 0) {
hasLost = true;
bgMusic.stop();
LK.getSound('wreck').play(); // Play wreck sound
LK.setTimeout(function () {
LK.getSound('gameoverSound').play();
}, 1000);
LK.setTimeout(function () {
LK.showGameOver();
}, 3000);
return true;
}
}
return false;
}
function changeRainbows(amount) {
rainbows = Math.min(3, rainbows + amount);
for (var i = 0; i < rainbowIcons.length; i++) {
rainbowIcons[i].alpha = i < rainbows ? 1 : 0.5;
}
if (amount > 0) {
LK.getSound('bigBonusSound').play();
LK.effects.flashScreen(0x00ff00, 300);
}
return false;
}
function changeScore(amount, variant) {
var returnValue = amount < 0 && score > 0;
score = Math.min(999, Math.max(0, score + amount));
LK.setScore(score);
scoreText.setText('| ' + formatScore(score) + ' |');
if (amount > 0) {
LK.getSound('bonusSound').play();
var tinyPickup = self.attachAsset('pickup' + variant, {
// Use the variant parameter
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.5,
scaleY: 0.5
});
tinyPickup.x = (Math.random() - 0.5) * scoreIcon.width;
tinyPickup.y = 20 + (Math.random() - 0.5) * scoreIcon.height;
}
return returnValue;
}
});
var LandscapeTile = Container.expand(function (x, y, _ref) {
var self = Container.call(this);
var lookup = _ref.lookup,
xIndex = _ref.xIndex,
yIndex = _ref.yIndex,
density = _ref.density,
pickups = _ref.pickups,
monsters = _ref.monsters,
player = _ref.player,
key = _ref.key,
landscapeTiles = _ref.landscapeTiles,
layers = _ref.layers,
rampList = _ref.rampList;
var obstructions = [];
var background = self.attachAsset('landscapeBackground', {
anchorX: 0.5,
anchorY: 0.5
});
background.width = TILE_SIZE;
background.height = TILE_SIZE;
background.alpha = 0.03;
background.x = yIndex % 2 === 0 ? TILE_BACKGROUND_OFFSET : 0;
;
self.x = x;
self.y = y;
self.activate = activate;
self.active = false;
self.key = key;
self.checkCollision = checkCollision;
;
function checkCollision(player) {
if (!player.airborne) {
var reach = 200;
var dx = Math.abs(player.x - self.x);
var dy = Math.abs(player.y - self.y);
if (dx < TILE_SIZE / 2 + reach && dy < TILE_SIZE / 2 + reach) {
for (var i = 0; i < obstructions.length; i++) {
var obstruction = obstructions[i];
if (obstruction.active && player.hitbox.intersects(obstruction.hitbox)) {
LK.getSound('bump').play();
obstruction.deactivate();
return true;
}
}
}
}
return false;
}
function activate() {
if (!self.active) {
self.active = true;
var densityChance = density * Math.random();
var obstructionTypes = ['largeTree', 'smallTree', 'smallTree', 'deadTree', 'smallRock', 'smallRock', 'largeRock', 'stump'];
var clearanceDist = 300;
var clearanceDistSqr = clearanceDist * clearanceDist;
var spawnBorder = 25;
var cellSize = 150;
var cols = Math.ceil(TILE_SIZE / cellSize);
var rows = Math.ceil(TILE_SIZE / cellSize);
var cellWidth = TILE_SIZE / cols;
var cellHeight = TILE_SIZE / rows;
var landmark;
if (Math.random() <= SPAWN_PICKUP_CHANCE) {
var _randomPointInRect = randomPointInRect(self.x, self.y, TILE_SIZE, TILE_SIZE, clearanceDist),
pointX = _randomPointInRect.pointX,
pointY = _randomPointInRect.pointY;
pickups.push(landmark = self.parent.addChild(new Pickup(pointX, pointY)));
} else if (Math.random() <= SPAWN_RAMP_CHANCE) {
var _randomPointInRect2 = randomPointInRect(self.x, self.y, TILE_SIZE, TILE_SIZE, clearanceDist),
pointX = _randomPointInRect2.pointX,
pointY = _randomPointInRect2.pointY;
rampList.push(landmark = layers[LAYER_BACKGROUND].addChild(new Ramp(pointX, pointY)));
}
if (player.distanceTravelled > MONSTER_SPAWN_DIST && Math.random() <= SPAWN_MONSTER_CHANCE) {
var _randomPointInRect3 = randomPointInRect(self.x, self.y, TILE_SIZE, TILE_SIZE, clearanceDist),
pointX = _randomPointInRect3.pointX,
pointY = _randomPointInRect3.pointY;
monsters.push(self.parent.addChild(new Monster(self.x, self.y)));
}
for (i = 0; i < cols; i++) {
for (j = 0; j < rows; j++) {
if (Math.random() <= densityChance) {
var canPlace = true;
var _randomPointInRect4 = randomPointInRect(cellWidth * i, cellHeight * j, cellWidth, cellHeight, spawnBorder),
pointX = _randomPointInRect4.pointX,
pointY = _randomPointInRect4.pointY;
if (landmark) {
var dx = landmark.x - pointX;
var dy = landmark.y - pointY;
var distSqr = dx * dx + dy * dy;
if (distSqr <= clearanceDistSqr) {
canPlace = false;
}
}
if (canPlace) {
var type = obstructionTypes[Math.floor(Math.random() * obstructionTypes.length)];
obstructions.push(self.addChild(new Obstruction(pointX, pointY, type)));
}
}
}
}
}
var leftKey = xIndex - 1 + ':' + yIndex;
var rightKey = xIndex + 1 + ':' + yIndex;
var downKey = xIndex + ':' + (yIndex + 1);
var newDensity = (1 + Math.pow(Math.log(player.distanceTravelled / DIFFICULTY_FACTOR_DIST + 2), 2)) / 100;
if (!lookup[leftKey]) {
landscapeTiles.push(lookup[leftKey] = self.parent.addChild(new LandscapeTile(self.x - TILE_SIZE, self.y, {
key: leftKey,
density: newDensity,
xIndex: xIndex - 1,
yIndex: yIndex,
landscapeTiles: landscapeTiles,
rampList: rampList,
lookup: lookup,
pickups: pickups,
monsters: monsters,
layers: layers,
player: player
})));
}
if (!lookup[rightKey]) {
landscapeTiles.push(lookup[rightKey] = self.parent.addChild(new LandscapeTile(self.x + TILE_SIZE, self.y, {
key: rightKey,
density: newDensity,
xIndex: xIndex + 1,
yIndex: yIndex,
landscapeTiles: landscapeTiles,
rampList: rampList,
lookup: lookup,
pickups: pickups,
monsters: monsters,
layers: layers,
player: player
})));
}
if (!lookup[downKey]) {
landscapeTiles.push(lookup[downKey] = self.parent.addChild(new LandscapeTile(self.x, self.y + TILE_SIZE, {
key: downKey,
density: newDensity,
yIndex: yIndex + 1,
xIndex: xIndex,
landscapeTiles: landscapeTiles,
rampList: rampList,
lookup: lookup,
pickups: pickups,
monsters: monsters,
layers: layers,
player: player
})));
}
function randomPointInRect(centerX, centerY, width, height) {
var border = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 0;
var startX = centerX - width / 2 + border;
var startY = centerY - height / 2 + border;
var innerWidth = width - border * 2;
var innerHeight = height - border * 2;
var pointX = startX + Math.random() * innerWidth;
var pointY = startY + Math.random() * innerHeight;
return {
pointX: pointX,
pointY: pointY
};
}
}
});
var Monster = Container.expand(function (x, y) {
var self = Container.call(this);
var active = false;
var updateTick = 0;
var updatePeriod = 30;
var jumpSpeedMax = 30;
var jumpHeight = 100;
var jumpVelocityX = 0;
var jumpVelocityY = 0;
var jumpDuration = updatePeriod * 2 / 3;
var invulnerabilityBoost = 1.8;
var jumpDistMax = jumpSpeedMax * jumpDuration;
var jumpDistMaxSqr = jumpDistMax * jumpDistMax;
var activationDist = 1000;
var activationDistSqr = activationDist * activationDist;
var despawnY = -2500;
var shadowReduction = 0.1;
var shadow = self.attachAsset('shadow', {
anchorX: 0.45,
anchorY: 1
});
var graphics = self.attachAsset('monster', {
anchorX: 0.5,
anchorY: 1
});
graphics.scale.x = Math.random() < 0.5 ? 1 : -1;
shadow.width = graphics.width * 0.9;
shadow.height = graphics.width * 0.45;
shadow.tint = 0x000000;
shadow.alpha = 0.0;
;
self.x = x;
self.y = y;
self.jumping = false;
self.hitbox = shadow;
self._update_migrated = _update_migrated;
;
function _update_migrated(velocityX, velocityY, player) {
if (!active) {
var dx = player.x - self.x;
var dy = player.y - self.y;
var distSqr = dx * dx + dy * dy;
if (distSqr <= activationDistSqr) {
active = true;
if (rainbows > 0) {
rainbowAnimation();
}
}
}
if (active) {
var updateCurve = (-Math.cos(++updateTick * Math.PI / (updatePeriod / 2)) + 0.5) / 1.5;
if (self.jumping) {
if (updateCurve <= 0) {
self.jumping = false;
jumpVelocityX = 0;
jumpVelocityY = 0;
}
} else {
if (updateCurve > 0) {
var boost = player.invulnerable ? invulnerabilityBoost : 1;
var targetPosX = player.x + velocityX * jumpDuration * boost;
var targetPosY = player.y + velocityY * jumpDuration * boost;
var ddx = targetPosX - self.x;
var ddy = targetPosY - self.y;
var angle = Math.atan2(ddy, ddx);
var targetDistSqr = ddx * ddx + ddy * ddy;
var jumpSpeed = jumpSpeedMax * (targetDistSqr >= jumpDistMaxSqr ? 1 : Math.sqrt(targetDistSqr) / jumpDistMax);
jumpVelocityX = Math.cos(angle) * jumpSpeed;
jumpVelocityY = Math.sin(angle) * jumpSpeed;
self.jumping = true;
self.monsterJumpSound = LK.getSound('monsterJump').play();
}
}
graphics.y = -Math.max(0, updateCurve) * jumpHeight;
graphics.scale.x = player.x < self.x ? -1 : 1;
graphics.scale.y = 1 + Math.min(0, updateCurve);
shadow.scale.set(1 - shadowReduction * updateCurve);
}
self.x += jumpVelocityX - velocityX;
self.y += jumpVelocityY - velocityY;
if (self.y < despawnY || self.y < 0) {
if (self.monsterJumpSound) {
self.monsterJumpSound.stop();
}
return true;
}
return false;
}
});
var Obstruction = Container.expand(function (x, y, type) {
var self = Container.call(this);
var graphics = self.createAsset(type, {
anchorX: 0.5,
anchorY: 1
});
var hitbox = self.attachAsset('hitbox', {
anchorX: 0.5,
anchorY: 0.5
});
var randomScale = 0.9 + Math.random() * 0.2;
hitbox.y = -25;
hitbox.alpha = 0;
hitbox.width = graphics.width * 0.8;
hitbox.height = graphics.width * 0.45;
;
self.x = x;
self.y = y;
self.active = true;
self.hitbox = hitbox;
self.deactivate = deactivate;
self.scale.set(randomScale, randomScale);
;
function deactivate() {
self.active = false;
graphics.alpha = 0.5;
}
});
var Pickup = Container.expand(function (x, y) {
var self = Container.call(this);
var activeDist = 300;
var activeDistSqr = activeDist * activeDist;
var collectDist = 50;
var collectDistSqr = collectDist * collectDist;
var collectSpeed = 25;
var collectOffset = -50;
var active = false;
self.variant = nextVariant % 7; //{2Q} // Ensure variant is a property of the Pickup class
nextVariant++;
var graphics = self.createAsset('pickup' + self.variant, {
anchorX: 0.5,
anchorY: 0.95
});
var backShadow = self.attachAsset('shadow', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.25
});
var foreShadow = self.attachAsset('shadow', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.25
});
foreShadow.width = graphics.width;
foreShadow.height = graphics.width * 0.25;
backShadow.width = graphics.width;
backShadow.height = graphics.width * 0.25;
backShadow.tint = 0x000000;
backShadow.y = -3;
;
self.x = x;
self.y = y;
self._update_migrated = _update_migrated;
;
function _update_migrated(velocityX, velocityY, player) {
var dx = player.x - self.x;
var dy = player.y + collectOffset - self.y;
var distSqr = dx * dx + dy * dy;
if (self.active) {
var dist = Math.sqrt(distSqr);
self.x += dx / dist * collectSpeed;
self.y += dy / dist * collectSpeed;
} else if (distSqr <= activeDistSqr) {
self.active = true;
backShadow.destroy();
foreShadow.destroy();
} else {
self.x -= velocityX;
self.y -= velocityY;
}
if (distSqr <= collectDistSqr) {
LK.getSound('bonusSound').play();
return true;
}
return false;
}
});
var Player = Container.expand(function (x, y) {
var self = Container.call(this);
var angle = 0;
var speed = 0;
var reduction = 0;
var travelDistX = 0;
var travelDistY = 0;
var rampRemaining = 0;
var rampDuration = 0;
var rampHeight = 0;
var rampHeightMax = 0;
var tiltSpeedFactor = 100;
var playerShadow = self.attachAsset('shadow', {
anchorX: 0.5,
anchorY: 0.5
});
var platformGraphics = self.attachAsset('platform', {
anchorX: 0.4,
anchorY: 0.5,
x: -30
});
var brokenWagonGraphics = self.attachAsset('brokenWagon', {
anchorX: 0.4,
anchorY: 0.5,
x: -30,
alpha: 0
});
var playerGraphics = self.attachAsset('player', {
anchorX: 0.575,
anchorY: 1
/*x: 30,
y: 0
*/
});
var hitbox = self.attachAsset('hitbox', {
anchorX: 0.5,
anchorY: 0.5
});
playerShadow.alpha = 0.5;
playerShadow.scale.set(1.0, 0.6);
playerShadow.tint = 0x000000;
hitbox.width = 25;
hitbox.height = 25;
hitbox.alpha = 0;
;
self.x = x;
self.y = y;
self.airborne = false;
self.invulnerable = false;
self.invulnerableTime = 3 * 60;
self.invulnerableTimer = 0;
self.distanceTravelled = 0;
self.hitbox = hitbox;
self._update_migrated = _update_migrated;
self.ramp = ramp;
;
function _update_migrated(targetPosition) {
if (hasLost) {
playerGraphics.alpha = 0; // Hide the player
platformGraphics.alpha = 0; // Hide the player
playerShadow.alpha = 0; // Hide the player
brokenWagonGraphics.alpha = 1; // show wreck
return;
}
if (self.invulnerable) {
if (--self.invulnerableTimer <= 0) {
self.invulnerable = false;
}
self.alpha = self.invulnerableTimer % 60 < 30 ? 1 : 0.5;
}
if (self.airborne) {
if (--rampRemaining <= 0) {
self.airborne = false;
LK.getSound('landing').play();
}
rampHeight = rampHeightMax * Math.sin(Math.PI * rampRemaining / rampDuration);
platformGraphics.y = -rampHeight;
playerGraphics.y = -rampHeight;
}
if (!self.airborne) {
var dx = targetPosition.x - self.x;
var dy = targetPosition.y - self.y;
angle = angleClamp(Math.atan2(dy, dx));
}
var acceleration = (Math.sin(angle) - 0.1) * 2.0;
var boost = self.airborne ? RAMP_SPEED_BOOST : 1;
reduction = self.invulnerable ? 1 - self.invulnerableTimer / self.invulnerableTime : 1;
speed = Math.max(0, speed * 0.95 + acceleration);
var velocityX = Math.cos(angle) * reduction * boost * speed;
var velocityY = Math.sin(angle) * reduction * boost * speed;
travelDistX += velocityX;
travelDistY += velocityY;
self.distanceTravelled = Math.sqrt(travelDistX * travelDistX + travelDistY * travelDistY);
if (!self.airborne) {
var facingSide = targetPosition.x < self.x ? -1 : 1;
platformGraphics.rotation = angle;
playerGraphics.rotation = speed * reduction / tiltSpeedFactor * (Math.PI / 2) * facingSide;
playerGraphics.scale.x = facingSide;
playerGraphics.x = platformGraphics.x + 30 * Math.cos(platformGraphics.rotation) + 30 * Math.sin(platformGraphics.rotation);
playerGraphics.y = platformGraphics.y + 30 * Math.sin(platformGraphics.rotation);
}
return {
velocityX: velocityX,
velocityY: velocityY
};
}
function angleClamp(angle) {
return angle >= 0 ? angle : angle < -Math.PI / 2 ? Math.PI : 0;
}
function ramp() {
if (!self.airborne && speed * reduction > RAMP_CRITICAL_SPEED) {
LK.getSound('wagoonJump').play();
var rampFactor = speed * reduction - RAMP_CRITICAL_REDUX;
self.airborne = true;
rampHeightMax = rampFactor * RAMP_SPEED_HEIGHT;
rampDuration = Math.round(rampFactor * RAMP_SPEED_DURATION);
rampRemaining = rampDuration;
}
}
});
var Ramp = Container.expand(function (x, y) {
var self = Container.call(this);
var rampPillar = self.attachAsset('rampPillar', {
anchorX: 0.5,
anchorY: 0.5
});
var rampShadow = self.attachAsset('rampShadow', {
anchorX: 0.5
});
var rampGraphics = self.attachAsset('ramp', {
anchorX: 0.5
});
var hitbox = self.attachAsset('hitbox', {
anchorX: 0.5
});
rampShadow.rotation = 0.1;
rampShadow.alpha = 0.5;
rampShadow.tint = 0x000000;
rampPillar.y = rampGraphics.height;
hitbox.width = rampGraphics.width;
hitbox.height = 50;
hitbox.alpha = 0;
;
self.x = x;
self.y = y;
self.hitbox = hitbox;
self._update_migrated = _update_migrated;
;
function _update_migrated(velocityX, velocityY, player) {
self.x -= velocityX;
self.y -= velocityY;
if (self.y < -200) {
return true;
}
if (!player.airborne && player.hitbox.intersects(self.hitbox)) {
player.ramp();
}
}
});
var Trail = Container.expand(function (x, y) {
var self = Container.call(this);
var trailLength = 60;
var trailAlpha = 0.4;
var trailElements = [];
;
self.x = x;
self.y = y;
self._update_migrated = _update_migrated;
;
function _update_migrated(velocityX, velocityY, active) {
if (hasLost) {
return;
}
var trailElement = null;
if (active && (velocityX !== 0 || velocityY !== 0)) {
trailElement = self.attachAsset('trail', {
anchorY: 0.5
});
var angle = Math.atan2(velocityY, velocityX);
var speed = Math.sqrt(velocityX * velocityX + velocityY * velocityY);
trailElement.rotation = angle;
trailElement.scale.x = speed / 100;
}
trailElements.push(trailElement);
if (trailElements.length > trailLength) {
var removedElement = trailElements.shift();
if (removedElement) {
removedElement.destroy();
}
}
for (var i = trailElements.length - 1; i >= 0; i--) {
var element = trailElements[i];
if (element) {
var alpha = trailAlpha * (i / trailLength);
element.x -= velocityX;
element.y -= velocityY;
element.alpha = alpha;
}
}
}
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000000
});
/****
* Game Code
****/
function rainbowAnimation() {
LK.getSound('rainbowAttack').play();
var confettiCount = 30;
var confettiArray = [];
for (var i = 0; i < confettiCount; i++) {
var angle = Math.random() * 2 * Math.PI; // Random angle in radians
var confetti = LK.getAsset('rainbow', {
anchorX: 0.5,
anchorY: 0.5,
width: 100,
height: 100 * (113.5 / 256),
// Preserve aspect ratio
alpha: 1,
x: STAGE_WIDTH / 2,
y: STAGE_HEIGHT / 2,
angle: angle // Store the random angle
});
game.addChild(confetti);
confettiArray.push(confetti);
}
var interval = LK.setInterval(function () {
for (var i = 0; i < confettiArray.length; i++) {
var confetti = confettiArray[i];
confetti.x += Math.cos(confetti.angle) * 30;
confetti.y += Math.sin(confetti.angle) * 30;
confetti.width += 1;
confetti.height = confetti.width * (113.5 / 256); // Preserve aspect ratio
confetti.alpha -= 0.005;
if (confetti.alpha <= 0) {
confetti.destroy();
confettiArray.splice(i, 1);
i--;
}
}
if (confettiArray.length === 0) {
LK.clearInterval(interval);
}
}, 16); // Approximately 60 FPS
for (var i = monsters.length - 1; i >= 0; i--) {
var monster = monsters[i];
effects.push(new BloodsplatterEffect(monster.x, monster.y));
LK.getSound('monsterDying').play();
monster.destroy();
monsters.splice(i, 1);
}
__interface.changeRainbows(-1);
}
;
var STAGE_WIDTH = 2048;
var STAGE_HEIGHT = 2732;
var SCREEN_METERAGE = 1 / 150;
var MONSTER_SPAWN_DIST = 100 / SCREEN_METERAGE;
var DIFFICULTY_FACTOR_DIST = 150 / SCREEN_METERAGE;
var TILE_SIZE = 512;
var TILE_MARGIN = TILE_SIZE;
var TILE_BACKGROUND_OFFSET = 100;
var SPAWN_PICKUP_CHANCE = 0.1;
var SPAWN_MONSTER_CHANCE = 0.01;
var SPAWN_RAMP_CHANCE = 0.025;
var RAMP_CRITICAL_SPEED = 15.0;
var RAMP_CRITICAL_REDUX = 10;
var RAMP_SPEED_BOOST = 1.25;
var RAMP_SPEED_DURATION = 3.0;
var RAMP_SPEED_HEIGHT = 20.0;
var LAYER_BACKGROUND = 0;
var LAYER_MIDGROUND = 1;
var LAYER_FOREGROUND = 2;
;
var playerPosX = Math.round(STAGE_WIDTH / 2);
var playerPosY = Math.round(STAGE_WIDTH / 3);
var landscapeLookup = {};
var landscapeTiles = [];
var pickups = [];
var monsters = [];
var effects = [];
var rampList = [];
var layers = [game.addChild(new Container()), game.addChild(new Container()), game.addChild(new Container())];
var speed = 0;
var nextVariant = 0;
var __interface = LK.gui.top.addChild(new Interface(0, 10));
var trail = layers[LAYER_BACKGROUND].addChild(new Trail(playerPosX, playerPosY));
var player = layers[LAYER_MIDGROUND].addChild(new Player(playerPosX, playerPosY));
var bgMusic = LK.getSound('bgMusic');
var loopBgMusicInterval;
landscapeTiles.push(landscapeLookup['0:0'] = layers[LAYER_FOREGROUND].addChild(new LandscapeTile(STAGE_WIDTH / 2, TILE_SIZE / 2, {
density: -1,
lookup: landscapeLookup,
xIndex: 0,
yIndex: 0,
key: '0:0',
landscapeTiles: landscapeTiles,
rampList: rampList,
monsters: monsters,
pickups: pickups,
layers: layers,
player: player
})));
var totalCollectedPickups = 0;
var rainbows = 0;
var isStarted = false;
var isMouseDown = false;
var hasLost = false;
var targetPosition = {
x: playerPosX,
y: playerPosY
};
game.setBackgroundColor(0x00FF00);
;
game.on('down', function (x, y, obj) {
if (!isStarted) {
return;
}
isMouseDown = true;
updatePosition(obj);
loopBgMusic();
});
game.on('up', function (x, y, obj) {
isMouseDown = false;
});
game.on('move', function (x, y, obj) {
if (isMouseDown) {
updatePosition(obj);
}
});
LK.on('tick', function () {
if (!isStarted) {
return;
}
if (typeof player._update_migrated === 'function') {
var _player$update = player._update_migrated(targetPosition);
if (_player$update) {
var velocityX = _player$update.velocityX,
velocityY = _player$update.velocityY;
} else {
console.error("Player update method did not return expected values");
return;
}
} else {
console.error("Player update method is undefined");
return;
}
trail._update_migrated(velocityX, velocityY, !player.airborne);
__interface._update_migrated(player.distanceTravelled);
for (var i = landscapeTiles.length - 1; i >= 0; i--) {
var landscapeTile = landscapeTiles[i];
if (hasLost) {
continue;
}
landscapeTile.x -= velocityX;
landscapeTile.y -= velocityY;
if (landscapeTile.y < -(TILE_SIZE / 2 + TILE_MARGIN)) {
landscapeTile.destroy();
landscapeTiles.splice(i, 1);
landscapeLookup[landscapeTile.key] = undefined;
} else if (!landscapeTile.active && landscapeTile.x > -TILE_MARGIN && landscapeTile.x < STAGE_WIDTH + TILE_MARGIN && landscapeTile.y < STAGE_HEIGHT + TILE_MARGIN) {
landscapeTile.activate();
} else if (landscapeTile.checkCollision(player)) {
if (__interface.changeScore(-1)) {
effects.push(new DropEffect(game, player.x, player.y));
}
if (!player.invulnerable) {
player.invulnerable = true;
player.invulnerableTimer = player.invulnerableTime;
}
}
}
for (var i = pickups.length - 1; i >= 0; i--) {
if (hasLost) {
break;
}
var pickup = pickups[i];
var variant = pickup.variant; // Define the variant variable
if (pickup._update_migrated(velocityX, velocityY, player)) {
__interface.changeScore(1, variant); // Pass the variant parameter
totalCollectedPickups++;
if (totalCollectedPickups > 0 && totalCollectedPickups % 7 === 0) {
__interface.changeScore(2, Math.floor(Math.random() * 7));
__interface.changeRainbows(1);
//rainbowAnimation();
}
pickup.destroy();
pickups.splice(i, 1);
} else if (pickup.y < -(TILE_SIZE / 2 + TILE_MARGIN) || pickup.active && player.hitbox.intersects(pickup.hitbox)) {
pickup.destroy();
pickups.splice(i, 1);
}
}
for (var i = effects.length - 1; i >= 0; i--) {
if (hasLost) {
break;
}
var effect = effects[i];
if (effect._update_migrated(velocityX, velocityY)) {
effect.destroy();
effects.splice(i, 1);
}
}
for (var i = rampList.length - 1; i >= 0; i--) {
if (hasLost) {
break;
}
var ramp = rampList[i];
if (ramp._update_migrated(velocityX, velocityY, player)) {
rampList.splice(i, 1);
}
}
for (var i = monsters.length - 1; i >= 0; i--) {
if (hasLost) {
break;
}
var monster = monsters[i];
if (monster._update_migrated(velocityX, velocityY, player)) {
monster.destroy();
monsters.splice(i, 1);
} else if (!monster.jumping && !player.airborne && monster.hitbox.intersects(player.hitbox)) {
effects.push(new BloodsplatterEffect(game, monster.x, monster.y));
monster.destroy();
monsters.splice(i, 1);
LK.getSound('monsterEat').play();
if (__interface.changeLives(-1)) {
hasLost = true;
bgMusic.stop();
LK.getSound('wreck').play(); // Play wreck sound
LK.setTimeout(function () {
LK.getSound('gameoverSound').play();
}, 1000);
LK.setTimeout(function () {
LK.showGameOver();
}, 3000);
}
}
}
});
// Function to loop background music
function loopBgMusic() {
if (!loopBgMusicInterval && !loopBgMusic.called) {
loopBgMusic.called = true;
// Play the background music immediately
bgMusic.play();
// Set an interval to loop the background music
loopBgMusicInterval = LK.setInterval(function () {
bgMusic.play();
}, 9500); // Assuming the background music duration is 60 seconds
}
}
function updatePosition(event) {
var pos = game.toLocal(event.global);
targetPosition.x = pos.x;
targetPosition.y = pos.y;
}
function formatScore(score) {
if (score < 10) {
return '00' + score;
}
if (score < 100) {
return '0' + score;
}
return '' + score;
}
var startButton = LK.getAsset('startButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2
});
game.addChild(startButton);
startButton.on('down', function () {
startButton.destroy();
isStarted = true;
// Add any additional code to start the game here
});
Pixel art heart icon . Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
two vertical lines with a blank background.
single green firework explosion . Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
Pixel art of a cute elf. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
tileable green grass texture..
arbre chene.
cerisier avec des fleur. Pixel art.
pixel art of a large rock.
pixel art of a tree stump.
pixel art of a 4 leaf clover.
pixel art of a wooden board.
pixel art of a bush.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
pixel art of a 4 leaf orange clover... Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
pixel art of a 4 leaf purple clover.... Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
pixel art of a 4 leaf indigo clover.... Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
A start button for an elf game in a magic forest. Pixel art.