/**** * Classes ****/ var BoilingLava = Container.expand(function () { var self = Container.call(this); var lavaBubbles = []; self.x = 400; self.y = 400; // Function to create a new lava bubble function createLavaBubble() { var bubble = self.attachAsset('lavaBubble', { anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1, x: Math.random() * 200 - 100, // Random x position within a range y: Math.random() * 200 - 100 // Random y position within a range }); lavaBubbles.push(bubble); } // Update function to animate the lava bubbles self.update = function () { if (LK.ticks % 10 == 0) { // Emit a bubble every second createLavaBubble(); } for (var i = lavaBubbles.length - 1; i >= 0; i--) { var bubble = lavaBubbles[i]; bubble.y -= 2; // Move bubble upwards bubble.alpha -= 0.015; // Fade out bubble if (bubble.alpha <= 0) { bubble.destroy(); lavaBubbles.splice(i, 1); } } }; return self; }); /** * config { * x : Number || 0, * y : Number || 0, * rotation : Number || 0, * } **/ var ConfigContainer = Container.expand(function (config) { var self = Container.call(this); config = config || {}; ; self.x = config.x || 0; self.y = config.y || 0; self.rotation = config.rotation || 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); } ; return self; }); var Wall = ConfigContainer.expand(function (config) { var self = ConfigContainer.call(this, config); var wallIndex = Math.floor(Math.random() * ROAD_GEN_WALL_ASSETS); var scale = 1; //0.9 + 0.2 * Math.random(); self.attachAsset('wall' + wallIndex, { anchorX: 0.5, anchorY: 1, scaleX: scale, //Math.random() < 0.5 ? -scale : scale, scaleY: scale }); self.taken = false; self.update = function () { //log("player.sliding:", player.sliding); if (!player.sliding && player.body.torso.intersects(self)) { if (!isBonusActive) { player.hitWall = true; player.sliding = true; // Keep down player.isDead = true; tintPlayer(0x666666); log("Player hit a wall!"); // Flash screen red for 1 second (1000ms) to show we are dead. LK.effects.flashScreen(0xff0000, 1000); // Play lose sound LK.getSound('lose').play(); callGameOver(true); } else { // TODO : what id hit wall while bonus active ? } } }; return self; }); var Sun = ConfigContainer.expand(function (config) { var self = ConfigContainer.call(this, config); for (var i = 0; i < 5; i++) { self.attachAsset('circle', { width: 200 + 50 * i, height: 200 + 50 * i, color: 0xFFFF00, alpha: 0.1, anchorX: 0.5, anchorY: 0.5 }); } var sunAsset = self.attachAsset('sun', { anchorX: 0.5075, anchorY: 0.5, alpha: 0.75 }); ; self.update = update; ; function update() { if (LK.ticks % 30 == 0) { for (var i = 0; i < self.children.length - 1; i++) { var circle = self.children[i]; if (i % 2 !== 0) { circle.alpha = 0.01 + 0.09 * Math.abs(Math.sin(LK.ticks / 90)); } else { circle.alpha = 0.01 + 0.09 * Math.abs(Math.cos(LK.ticks / 90)); } } } } }); var RoadSegment = ConfigContainer.expand(function (config) { var self = ConfigContainer.call(this, config); var background = self.addChild(new Container()); self.background = background; self.attachAsset('roadSegment', { anchorX: 1, anchorY: 1, tint: 0x62C344 }); self.segment = self.attachAsset('roadSegment', { anchorX: 1, anchorY: 1, scaleX: 0.95, scaleY: 0.95, tint: 0x62C344 // TEMP DEBUG0x555555 }); self.segmentRiver = self.attachAsset('lava', { anchorX: 1, anchorY: 1, scaleX: 0.95, scaleY: 0.95, visible: false }); ; self.update = update; self.regenerate = regenerate; self.fadeout = false; self.distance = config.distance; self.step = config.step; self.stepChance = config.stepChance; self.previous = config.previous; self.isRiver = false; self.isWall = false; ; function attachAssetRadial(parent, asset, obj) { var offsetTotal = ROAD_SEGMENT_HEIGHT - (obj.offset || 10); var rotation = -(1 - obj.anchorR) * ROAD_SEGMENT_ANGLE; parent.attachAsset(asset, { x: Math.sin(rotation) * offsetTotal, y: Math.cos(rotation) * -offsetTotal, width: obj.width, height: obj.height, anchorX: obj.anchorX, anchorY: obj.anchorY, scaleX: obj.scaleX || 1, scaleY: obj.scaleY || 1, rotation: (obj.rotation || 0) + 0 }); } function attachObjectRadial(parent, object, obj) { var offsetTotal = ROAD_SEGMENT_HEIGHT - (obj.offset || 10); var rotation = -(1 - obj.anchorR) * ROAD_SEGMENT_ANGLE; object.x = Math.sin(rotation) * offsetTotal - (obj.levitOffset && !obj.isOver || 0); //125* (obj.levitOffset || 0) - 400 * (obj.flying || 0); object.y = Math.cos(rotation) * -offsetTotal - (obj.levitOffset || 0); //object.width = obj.width; //object.height = obj.height; object.anchorX = obj.anchorX; object.anchorY = obj.anchorY; object.scaleX = obj.scaleX || 1; object.scaleY = obj.scaleY || 1; object.rotation = (obj.rotation || 0) + 0; parent.addChild(object); } function generateBackground() { //log(self.distance); background.removeChildren(); self.isRiver = false; self.isWall = false; self.segmentRiver.visible = false; var bonusTruce = false; var active = isActive(); if (isBonusActive && Date.now() - bonusLastStartTime > BONUS_DURATION_SEC * 1000 - 2000) { // just before bonus end bonusTruce = true; } var plantCount = Math.floor(Math.random() * (ROAD_GEN_PLANT_MAX + 1)); if (self.distance === SCORE_TOTAL_DISTANCE) { attachAssetRadial(background, 'flag', { anchorR: 0.5, anchorX: 0.1, anchorY: 1 }); } else { if (active && Math.random() < ROAD_GEN_DETRITUS_CHANCE) { var detritus = new Detritus(); var scale = 1; //0.9 + 0.2 * Math.random(); attachObjectRadial(background, detritus, { anchorR: 0.1 + 0.8 * Math.random(), anchorX: 0.5, anchorY: 0.1, scaleX: Math.random() < 0.5 ? -scale : scale, scaleY: scale, rotation: -Math.PI * 0.25 + Math.random() * Math.PI * 0.5, levitOffset: 190 }); } else if (active && Math.random() < ROAD_GEN_DETRITUS_FLYING_CHANCE) { var detritusFlying = new DetritusFlying(); var scale = 1; //0.9 + 0.2 * Math.random(); attachObjectRadial(background, detritusFlying, { anchorR: 0.1 + 0.8 * Math.random(), anchorX: 0.5, anchorY: 0.1, scaleX: Math.random() < 0.5 ? -scale : scale, scaleY: scale, rotation: -Math.PI * 0.25 + Math.random() * Math.PI * 0.5, flying: 1, levitOffset: 700 }); } else if (road && active && !bonusTruce && road.riverSegmentCount < ROAD_MAX_RIVER_SEGMENTS && !self.isRiver && !self.isWall && !self.previous.isWall && (!self.previous.previous || !self.previous.previous.isWall) && (!self.previous.previous.previous || !self.previous.previous.previous.isWall) && (!self.previous.previous.previous.previous || !self.previous.previous.previous.previous.isWall) && Math.random() < ROAD_GEN_FRAGILE_NATURE_CHANCE) { self.isRiver = true; self.segmentRiver.visible = true; road.riverSegmentCount++; attachObjectRadial(background, new BoilingLava(), { anchorR: 0.1 + 0.8 * Math.random() }); if (ROAD_GEN_LAVA_SIZE > 1) { self.previous.isRiver = true; self.previous.segmentRiver.visible = true; road.riverSegmentCount++; attachObjectRadial(self.previous.background, new BoilingLava(), { anchorR: 0.1 + 0.8 * Math.random() }); } } else if (road && active && !bonusTruce && road.wallSegmentCount < ROAD_MAX_WALL_SEGMENTS && !self.isWall && !self.isRiver && !self.previous.isRiver && (!self.previous.previous || !self.previous.previous.isRiver) && (!self.previous.previous.previous || !self.previous.previous.previous.isRiver) && (!self.previous.previous.previous.previous || !self.previous.previous.previous.previous.isRiver) && Math.random() < ROAD_GEN_WALL_CHANCE) { self.isWall = true; road.wallSegmentCount++; var wallIndex = Math.floor(Math.random() * ROAD_GEN_WALL_ASSETS); var scale = 1; //0.9 + 0.2 * Math.random(); var anchorR = 0.5; // 0.4 + 0.2 * Math.random(); attachObjectRadial(background, new Wall(), { anchorR: anchorR, anchorX: 0.5, anchorY: 1, scaleX: scale, scaleY: scale }); if (wallIndex == 0) { // for pipes : add 2 other above attachObjectRadial(background, new Wall(), { anchorR: anchorR, anchorX: 0.5, anchorY: 1, scaleX: scale, scaleY: scale, levitOffset: 250, isOver: true }); attachObjectRadial(background, new Wall(), { anchorR: anchorR, anchorX: 0.5, anchorY: 1, scaleX: scale, scaleY: scale, levitOffset: 500, isOver: true }); } } else { if (Math.random() < ROAD_GEN_TREE_CHANCE) { var treeIndex = Math.floor(Math.random() * ROAD_GEN_TREE_ASSETS); var scale = 0.9 + 0.2 * Math.random(); attachAssetRadial(background, 'tree' + treeIndex, { anchorR: 0.4 + 0.2 * Math.random(), anchorX: 0.5, anchorY: 1, scaleX: scale, scaleY: scale }); } for (var i = 0; i < plantCount; i++) { var plantIndex = Math.floor(Math.random() * ROAD_GEN_PLANT_ASSETS); var scale = 0.9 + 0.2 * Math.random(); attachAssetRadial(background, 'plant' + plantIndex, { anchorR: 0.1 + 0.8 * Math.random(), anchorX: 0.5, anchorY: 1, scaleX: Math.random() < 0.5 ? -scale : scale, scaleY: scale }); } } } if (active && Math.random() < ROAD_GEN_CLOUD_CHANCE) { var cloudIndex = Math.floor(Math.random() * ROAD_GEN_CLOUD_ASSETS); var scale = 1.0 + 1.5 * Math.random(); attachAssetRadial(background, 'cloud' + cloudIndex, { offset: -1000, anchorR: -1.0 + 2.0 * Math.random(), anchorX: 0.5, anchorY: 0.5, scaleX: scale, scaleY: scale, rotation: -0.2 }); } } function regenerate(incrementDistance) { if (incrementDistance) { self.distance += SCORE_SEGMENT_DISTANCE * ROAD_SEGMENT_COUNT; } var stepping = self.distance <= SCORE_TOTAL_DISTANCE; var stepChance = self.previous.stepChance; var stepUp = stepping && (self.previous.step < ROAD_STEP_COUNT ? Math.random() < self.stepChance : false); var stepDown = stepping && (self.previous.step > 0 ? Math.random() < self.stepChance : false); if (stepUp || stepDown) { if (stepUp && stepDown) { if (Math.random() < 0.5) { stepDown = false; } else { stepUp = false; } } self.step = self.previous.step + (stepUp ? 1 : -1); self.stepChance = ROAD_STEP_CHANCE_BASE; } else { self.step = self.previous.step; self.stepChance = self.previous.stepChance + ROAD_STEP_CHANCE_INCREMENT; } generateBackground(); self.fadeout = false; rescale(); } function update() { if (!isActive()) { return; } if (self.fadeout && self.alpha > 0) { if ((self.alpha -= ROAD_SEGMENT_FADEOUT) < 0) { self.alpha = 0; } } else if (!self.fadeout && self.alpha < 1) { if ((self.alpha += ROAD_SEGMENT_FADEOUT) > 1) { self.alpha = 1; } } } function rescale() { var newScale = (ROAD_STEP_HEIGHT_BASE + self.step * ROAD_STEP_HEIGHT_INCREMENT) / ROAD_SEGMENT_HEIGHT; // TEMP self.scale.set(newScale); } ; rescale(); }); var Road = ConfigContainer.expand(function (config) { var self = ConfigContainer.call(this, config); var segments = []; self.segments = segments; self.riverSegmentCount = 0; self.wallSegmentCount = 0; for (var i = 0; i < ROAD_SEGMENT_COUNT; i++) { var segment = segments[i] = self.addChild(new RoadSegment({ index: i, previous: i > 0 ? segments[i - 1] : undefined, rotation: i * ROAD_SEGMENT_ANGLE, distance: (i - 1) * SCORE_SEGMENT_DISTANCE, stepChance: ROAD_STEP_CHANCE_BASE, step: ROAD_STEP_DEFAULT })); if (i === ROAD_SEGMENT_COUNT - 1) { segments[0].previous = segment; } if (i >= ROAD_FREE_RUN_COUNT) { segment.regenerate(); } self.attachAsset('roadSegment', { anchorX: 1, anchorY: 1, scaleX: 0.30, scaleY: 0.27, rotation: i * ROAD_SEGMENT_ANGLE, tint: 0x8b4513 }); } self.earthCore = self.attachAsset('earthCore', { width: 470, height: 470, anchorX: 0.5, anchorY: 0.5, alpha: 0.75, //tint: 0x62C344 //tint: 0x87CEEB, tint: 0x888888 }); self.earthCore2 = self.attachAsset('earthCore', { width: 470, height: 470, anchorX: 0.5, anchorY: 0.5, scaleX: -1, scaleY: -1, alpha: 0.75, tint: 0x888888 }); self.attachAsset('ring', { width: 560, height: 560, anchorX: 0.5, anchorY: 0.5, tint: 0xff7518, blendMode: 3 }); var destroyIndex = -1; var regenerateIndex = -1; ; self.rotate = rotate; self.getIndex = getIndex; self.getNode = getNode; self.getNodeDist = getNodeDist; ; function rotate(speed) { if (!isActive()) { return; } self.rotation -= speed; var newDestroyIndex = getIndex(ROAD_SEGMENT_DESTROY); var newRegenerateIndex = getIndex(20); // TEMP if (newDestroyIndex >= 0 && newDestroyIndex !== destroyIndex && !segments[newDestroyIndex].fadeout) { destroyIndex = newDestroyIndex; if (segments[destroyIndex].isRiver) { self.riverSegmentCount--; self.riverSegmentCount = Math.max(0, self.riverSegmentCount); } if (segments[destroyIndex].isWall) { self.wallSegmentCount--; self.wallSegmentCount = Math.max(0, self.wallSegmentCount); } segments[destroyIndex].fadeout = true; } if (newRegenerateIndex >= 0 && newRegenerateIndex !== regenerateIndex && segments[newRegenerateIndex].fadeout) { regenerateIndex = newRegenerateIndex; segments[regenerateIndex].regenerate(true); } self.earthCore.rotation -= 0.001; self.earthCore2.rotation += 0.005; } function getIndex(shift) { return Math.floor(-self.rotation / MATH_2_PI * ROAD_SEGMENT_COUNT + (shift || 0) + 1) % ROAD_SEGMENT_COUNT; } function getNode(shift) { return segments[getIndex(shift)]; } function getNodeDist() { return ROAD_SEGMENT_ANGLE - -self.rotation % ROAD_SEGMENT_ANGLE; } }); var PlayerBody = ConfigContainer.expand(function (config) { var self = ConfigContainer.call(this, config); // Animation settings var animation = animationStand; var alpha = 0; var blendAnimation = undefined; var blendDecrement = 0; var blendAlpha = 0; var afterTint = 0xAAAAAA; // Body settings var bodyOffsetY = config.y || 0; var pelvisOffsetX = -35; // old -10; var armUpperAngle = -3 * MATH_EIGHTH_PI; var armUpperOffsetX = 30; // old 15; var armUpperOffsetY = 10; // old 30 var armLowerAngle = -MATH_QUARTER_PI; var armLowerOffsetY = 90; var legUpperAngle = 3 * MATH_EIGHTH_PI; var legUpperOffsetX = -0; var legLowerOffsetY = 135; var footOffsetY = 100; // Create and attach body parts var armUpperRight = self.addChild(new ConfigContainer({ y: armUpperOffsetY })); var legUpperRight = self.addChild(new ConfigContainer({ x: pelvisOffsetX + legUpperOffsetX })); var torso = self.attachAsset('torso', { anchorX: 0.5 }); var head = self.attachAsset('head', { anchorX: 0.52, //0.15, anchorY: 0.8 //0.95 }); self.head = head; var pelvis = self.attachAsset('pelvis', { x: pelvisOffsetX, y: torso.height - 20, // old torso.height, anchorX: 0.5, anchorY: 0.2, tint: afterTint }); self.pelvis = pelvis; self.torso = torso; var legUpperLeft = self.addChild(new ConfigContainer({ x: pelvisOffsetX - legUpperOffsetX - 30 })); var armUpperLeft = self.addChild(new ConfigContainer({ y: armUpperOffsetY })); // Arm extensions armUpperRight.attachAsset('upperArm', { rotation: armUpperAngle, anchorX: 0.85, anchorY: 0.25, tint: afterTint }); self.armUpperRight = armUpperRight; armUpperLeft.attachAsset('upperArm', { rotation: armUpperAngle, anchorX: 0.85, anchorY: 0.25 }); self.armUpperLeft = armUpperLeft; var armLowerRight = armUpperRight.attachAsset('lowerArm', { y: armLowerOffsetY, rotation: armLowerAngle, anchorX: 0.95, anchorY: 0.05, tint: afterTint }); self.armLowerRight = armLowerRight; var armLowerLeft = armUpperLeft.attachAsset('lowerArm', { y: armLowerOffsetY, rotation: armLowerAngle, anchorX: 0.95, anchorY: 0.05 }); self.armLowerLeft = armLowerLeft; // Leg extensions legUpperRight.attachAsset('upperLeg', { rotation: legUpperAngle, anchorY: 0.1, tint: afterTint }); self.legUpperRight = legUpperRight; legUpperLeft.attachAsset('upperLeg', { rotation: legUpperAngle, anchorY: 0.1 }); self.legUpperLeft = legUpperLeft; var legLowerRight = legUpperRight.attachAsset('lowerLeg', { y: legLowerOffsetY, anchorX: 0.5, // old 0.65, tint: afterTint }); self.legLowerRight = legLowerRight; var legLowerLeft = legUpperLeft.attachAsset('lowerLeg', { y: legLowerOffsetY, anchorX: 0.5 // old 0.65 }); self.legLowerLeft = legLowerLeft; var footRight = legLowerRight.attachAsset('foot', { y: footOffsetY - 10, anchorX: -0.2, // old 0.2, anchorY: 0.2, // old 0.05, tint: afterTint }); self.footRight = footRight; var footLeft = legLowerLeft.attachAsset('foot', { y: footOffsetY, anchorX: -0.2, // old 0.2, anchorY: 0.2 // old 0.05, }); self.footLeft = footLeft; // Additional positioning armUpperLeft.x = -torso.width / 2 + armUpperOffsetX; armUpperRight.x = torso.width / 2 - armUpperOffsetX; legUpperLeft.y = torso.height + pelvis.height / 3 - 40; legUpperRight.y = torso.height + pelvis.height / 3 - 20; ; self.animate = animate; self.pushAnimation = pushAnimation; ; function animate(increment) { alpha = (alpha + increment) % 1; if (blendAlpha > 0) { if ((blendAlpha -= blendDecrement) <= 0) { blendAlpha = 0; blendAnimation = undefined; } } var pose = animation(alpha); var blendPose = blendAnimation && blendAnimation(alpha); self.y = animateProperty(pose, blendPose, blendAlpha, 'BODY_OFFSET', bodyOffsetY); head.rotation = animateProperty(pose, blendPose, blendAlpha, 'HEAD_ROTATION'); armUpperRight.rotation = animateProperty(pose, blendPose, blendAlpha, 'ARM_UPPER_RIGHT_ROTATION'); armUpperLeft.rotation = animateProperty(pose, blendPose, blendAlpha, 'ARM_UPPER_LEFT_ROTATION'); armLowerRight.rotation = animateProperty(pose, blendPose, blendAlpha, 'ARM_LOWER_RIGHT_ROTATION', armLowerAngle); armLowerLeft.rotation = animateProperty(pose, blendPose, blendAlpha, 'ARM_LOWER_LEFT_ROTATION', armLowerAngle); legUpperRight.rotation = animateProperty(pose, blendPose, blendAlpha, 'LEG_UPPER_RIGHT_ROTATION'); legUpperLeft.rotation = animateProperty(pose, blendPose, blendAlpha, 'LEG_UPPER_LEFT_ROTATION'); legLowerRight.rotation = animateProperty(pose, blendPose, blendAlpha, 'LEG_LOWER_RIGHT_ROTATION'); legLowerLeft.rotation = animateProperty(pose, blendPose, blendAlpha, 'LEG_LOWER_LEFT_ROTATION'); footRight.rotation = animateProperty(pose, blendPose, blendAlpha, 'FOOT_RIGHT_ROTATION'); footLeft.rotation = animateProperty(pose, blendPose, blendAlpha, 'FOOT_LEFT_ROTATION'); } function pushAnimation(newAnimation, newBlendDecrement) { blendDecrement = newBlendDecrement; if (newAnimation !== animation) { blendAnimation = animation; animation = newAnimation; blendAlpha = 1; } } ; animate(0); }); var Player = ConfigContainer.expand(function (config) { var self = ConfigContainer.call(this, config); var speedFactor = PLAYER_SPEED_FACTOR_BASE; var stopped = false; var falling = false; var jumping = false; // If the player is currently jumping (and immune to gravity) var jumpAction = false; // Tracks tap releases var jumpStep = 0; // Tracks min/max jump heights var aerialSpeed = 0; // Actual vertical/air speed var baseY = config.y; var step = ROAD_STEP_DEFAULT; var nodeDistance = 0; var nodeTicks = 0; var nodeMulti = 0; var slideTimer = 0; var SLIDE_DURATION = 100; // Duration of slide in frames var body = self.addChild(new PlayerBody({ y: -370 })); self.body = body; self.update = update; self.speedFactor = speedFactor; self.jumpStart = jumpStart; self.jumpEnd = jumpEnd; self.jumping = false; self.sliding = false; self.slideStart = slideStart; self.slideEnd = slideEnd; self.hitWall = false; self.isDead = false; self.intersects = function (obj) { var bounds1 = self.getBounds(); var bounds2 = obj.getBounds(); if (self.sliding) { // When sliding, reduce height for collision check return bounds1.x < bounds2.x + bounds2.width && bounds1.x + bounds1.width > bounds2.x && bounds1.y < bounds2.y + bounds2.height && bounds1.y + bounds1.height / 4 > bounds2.y; } else { return bounds1.x < bounds2.x + bounds2.width && bounds1.x + bounds1.width > bounds2.x && bounds1.y < bounds2.y + bounds2.height && bounds1.y + bounds1.height > bounds2.y; } }; ; self.update = update; self.jumpStart = jumpStart; self.jumpEnd = jumpEnd; self.slideStart = slideStart; self.slideEnd = slideEnd; self.enterBonusState = enterBonusState; self.exitBonusState = exitBonusState; ; function update() { if (!isActive()) { return; } var nextNode = road.getNode(1); var currentNode = nextNode.previous; // Update horizontal movement var outputSpeed = PLAYER_SPEED_BASE * speedFactor; var distance = road.getNodeDist(); // Check for collision with Detritus // Collision check removed from Player update method // Check for collision if (stopped && nextNode.step <= step) { stopped = false; } else if (nextNode.step > step && outputSpeed > distance) { road.rotate(distance - PLAYER_SPACING); speedFactor = PLAYER_SPEED_FACTOR_BASE; stopped = true; if (!isBonusActive && currentNode.step <= step) { body.pushAnimation(animationStand, PLAYER_POSE_TRANSITION); } } if (!stopped) { road.rotate(outputSpeed); if (!gameOver) { if (speedFactor < PLAYER_SPEED_FACTOR_MAX) { if ((speedFactor += PLAYER_SPEED_FACTOR_INCREMENT) >= PLAYER_SPEED_FACTOR_MAX) { speedFactor = PLAYER_SPEED_FACTOR_MAX; } } } else { if (speedFactor > 0) { if ((speedFactor -= PLAYER_SPEED_DECREMENT) <= 0) { speedFactor = 0; } } } } if (isBonusActive) { // Animate player tint using multiple flashy colors var colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff]; var colorIndex = Math.floor(LK.ticks / 5 % colors.length); var tintColor = colors[colorIndex]; tintPlayer(tintColor); if (nextNode && nextNode.background && nextNode.background.children.length > 0) { log(" nextNode.background.children:", nextNode.background.children); for (var i = 0; i < nextNode.background.children.length; i++) { var child = nextNode.background.children[i]; if (child.taken === false) { child.autoTake = true; break; } } } } else { // TOOD: Get new node if applicable // Update vertical movement if (jumping) { //log("speedFactor", speedFactor, "outputSpeed", outputSpeed); jumpStep += aerialSpeed * speedFactor; //log("jumpAction:", jumpAction, "jumpStep:", jumpStep, "PLAYER_JUMP_STEP_MIN:", PLAYER_JUMP_STEP_MIN, "PLAYER_JUMP_STEP_MAX:", PLAYER_JUMP_STEP_MAX); if (!jumpAction && jumpStep <= PLAYER_JUMP_STEP_MIN || jumpStep >= PLAYER_JUMP_STEP_MAX) { falling = true; jumping = false; jumpAction = false; jumpStep = 0; } } else if (falling) { aerialSpeed -= PLAYER_GRAVITY * speedFactor; } else if (step > currentNode.step) { falling = true; body.pushAnimation(animationJump, PLAYER_POSE_TRANSITION); } step += aerialSpeed; //log("falling", falling, "step:", step, "currentNode.step:", currentNode.step); if (falling && step <= currentNode.step) { step = currentNode.step; falling = false; aerialSpeed = 0; body.pushAnimation(stopped ? animationStand : animationRun, PLAYER_POSE_TRANSITION); } if (self.sliding) { slideTimer += 1 * speedFactor; if (slideTimer >= SLIDE_DURATION) { slideEnd(); } // Adjust player's vertical position when sliding self.y = baseY - 500; //- stepHeight / 4; // Adjust to quarter height when sliding } else { self.y = baseY - stepHeight; } } // Score calculations if (!gameOver && currentNode.distance > nodeDistance) { adjustScore(nodeMulti / nodeTicks); currentNode.distance = nodeDistance; nodeMulti = 0; nodeTicks = 0; } nodeMulti += stopped ? SCORE_STOPPED_FACTOR : speedFactor; nodeTicks++; // Adjustments var stepHeight = ROAD_STEP_HEIGHT_BASE + step * ROAD_STEP_HEIGHT_INCREMENT; // TODO: Better scaling if (!self.sliding) { self.y = baseY - stepHeight; // TODO: Better scaling } self.scale.set(PLAYER_SCALE_BASE * stepHeight / ROAD_SEGMENT_HEIGHT); // TODO: Better scaling body.animate(PLAYER_ANIMATION_SPEED_BASE * speedFactor); //multiplierText.setText((stopped ? SCORE_STOPPED_FACTOR : speedFactor).toFixed(1) + 'x'); // Get the current road segment var currentSegment = road.getNode(0); // Check if the current segment is a river segment if (!isBonusActive && currentSegment.isRiver && !jumping && !falling && !jumpAction) { self.isDead = true; tintPlayer(0xff0000); // Player is above a river segment log("Player fell in the river!"); // Flash screen red for 1 second (1000ms) to show we are dead. LK.effects.flashScreen(0xff0000, 1000); // Play lose sound LK.getSound('lose').play(); callGameOver(true); } } function jumpStart() { if (!gameOver && !jumping && !falling && !self.sliding) { LK.getSound('jump').play(); jumping = true; jumpAction = true; aerialSpeed = PLAYER_JUMP_STEP_INCREMENT; body.pushAnimation(animationJump, PLAYER_POSE_TRANSITION); } } function jumpEnd() { jumpAction = false; } function slideStart() { if (!gameOver && !jumping && !falling && !self.sliding) { self.sliding = true; slideTimer = 0; body.pushAnimation(animationSlide, PLAYER_POSE_TRANSITION); body.rotation = -Math.PI / 2; //body.scale.x *= -1; LK.getSound('slide').play(); /* Flies // Rotate the body, not the entire player container body.rotation = -Math.PI / 2; // Flip the body horizontally to ensure correct orientation body.scale.x *= -1; // Play slide sound (if available) // LK.getSound('slide').play(); */ } } function slideEnd() { if (self.sliding && !self.hitWall) { self.sliding = false; slideTimer = 0; body.pushAnimation(stopped ? animationStand : animationRun, PLAYER_POSE_TRANSITION); // Restore body's original rotation and scale body.rotation = 0; //body.scale.x *= -1; } } function enterBonusState() { // ... other bonus state logic ... if (self.sliding) { slideEnd(); } body.pushAnimation(animationBonus, PLAYER_POSE_TRANSITION); body.rotation = Math.PI / 6; speedFactor = PLAYER_SPEED_FACTOR_MAX * 2; } function exitBonusState() { // ... other exit bonus state logic ... body.pushAnimation(stopped ? animationStand : animationRun, PLAYER_POSE_TRANSITION); body.rotation = 0; tintPlayer(0xFFFFFF); speedFactor = PLAYER_SPEED_FACTOR_BASE; } function adjustScore(averageMulti) { var points = Math.floor(averageMulti * SCORE_SEGMENT_POINTS); score = Math.max(0, score + points); scoreText.setText(kgCollected.toFixed(2) + 'kg'); // Old score : scoreText.setText(String(score).padStart(6, '0')); distanceText.setText((distanceRun += SCORE_SEGMENT_DISTANCE) + 'm'); incrementText.setText((points === SCORE_MAX_POINTS ? '[MAX!] +' : '+') + points); } ; body.pushAnimation(animationRun, PLAYER_POSE_TRANSITION); }); var FragileNature = ConfigContainer.expand(function (config) { var self = ConfigContainer.call(this, config); var fragileNatureIndex = Math.floor(Math.random() * ROAD_GEN_FRAGILE_NATURE_ASSETS); var scale = 0.9 + 0.2 * Math.random(); self.attachAsset('fragileNature' + fragileNatureIndex, { anchorX: 0.5, anchorY: 1, scaleX: Math.random() < 0.5 ? -scale : scale, scaleY: scale }); self.taken = false; self.update = function () { if (self.taken) { // TODO : Do domthing when Fragile Nature is hit } else if (!self.taken && player.body.torso.intersects(self)) { self.taken = true; } }; return self; }); var DetritusFlying = ConfigContainer.expand(function (config) { var self = ConfigContainer.call(this, config); var detritusFlyingIndex = Math.floor(Math.random() * ROAD_GEN_DETRITUS_FLYING_ASSETS); var scale = 0.9 + 0.2 * Math.random(); self.attachAsset('detritusFlying' + detritusFlyingIndex, { anchorX: 0.5, anchorY: 0.5, scaleX: Math.random() < 0.5 ? -scale : scale, scaleY: scale }); self.update = function () { if (!isActive()) { return; } if (self.taken) { var targetX = recycleBin.x; var targetY = recycleBin.y; var startX = player.x; var startY = player.y; var speed = 15; var accelerationY = 1.0; // Adjust as needed var dx = targetX - self.x; var dy = targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var velocityX = dx / distance * speed; var velocityY = dy / distance * speed; if (distance > speed) { if (Math.abs(self.x - startX) < Math.abs(dx / 2)) { self.x += velocityX; self.y -= velocityY * 0.1 - accelerationY * 0.2; // Move up } else { self.x += velocityX; self.y += velocityY + accelerationY; // Move down with acceleration } self.scale.x *= 0.996; self.scale.y *= 0.996; self.rotation += 0.1; // Rotate detritusFlying on themselves when taken } else { self.x = targetX; self.y = targetY; self.destroy(); } } else if (!self.taken && (self.autoTake || player.body.torso.intersects(self))) { self.taken = true; // Flash Detritus in green for 500ms LK.effects.flashObject(self, 0x00ff00, 500); kgCollected += 0.01; // Add 0.01 to kgCollected var globalPosition = self.parent.toGlobal(self.position); self.parent.removeChild(self); game.addChildAt(self, game.getChildIndex(recycleBin)); self.position = game.toLocal(globalPosition); LK.getSound('collectDetritus').play(); if (!isBonusActive) { nbCollectedBeforeBonus++; } if (nbCollectedBeforeBonus >= BONUS_COLLECT_THRESHOLD) { log("Gained Bonus !!!"); startBonusAnimation(); } } else { self.rotation += 0.05 + (Math.random() - 0.25) * 0.1; } }; self.taken = false; return self; }); var Detritus = ConfigContainer.expand(function (config) { var self = ConfigContainer.call(this, config); var detritusIndex = Math.floor(Math.random() * ROAD_GEN_DETRITUS_ASSETS); var scale = 0.9 + 0.2 * Math.random(); self.attachAsset('detritus' + detritusIndex, { anchorX: 0.5, anchorY: 0.5, scaleX: Math.random() < 0.5 ? -scale : scale, scaleY: scale }); self.taken = false; self.update = function () { if (!isActive()) { return; } if (self.taken) { var targetX = recycleBin.x; var targetY = recycleBin.y; var startX = player.x; var startY = player.y; var speed = 15; var accelerationY = 1.0; // Adjust as needed var dx = targetX - self.x; var dy = targetY - self.y; var distance = Math.sqrt(dx * dx + dy * dy); var velocityX = dx / distance * speed; var velocityY = dy / distance * speed; if (distance > speed) { if (Math.abs(self.x - startX) < Math.abs(dx / 2)) { self.x += velocityX; self.y -= velocityY * 0.5 - accelerationY * 0.2; // Move up } else { self.x += velocityX; self.y += velocityY + accelerationY; // Move down with acceleration } self.scale.x *= 0.996; self.scale.y *= 0.996; self.rotation += 0.1; // Rotate detritus on themselves when taken } else { self.x = targetX; self.y = targetY; self.destroy(); } } else if (!self.taken && (self.autoTake || player.body.torso.intersects(self))) { self.taken = true; // Flash DetritusFlying in green for 500ms LK.effects.flashObject(self, 0x00ff00, 500); kgCollected += 0.02; // Add 0.01 to kgCollected var globalPosition = self.parent.toGlobal(self.position); self.parent.removeChild(self); game.addChildAt(self, game.getChildIndex(recycleBin)); self.position = game.toLocal(globalPosition); LK.getSound('collectDetritus').play(); if (!isBonusActive) { nbCollectedBeforeBonus++; } if (nbCollectedBeforeBonus >= BONUS_COLLECT_THRESHOLD) { log("Gained Bonus !!!"); startBonusAnimation(); } } }; return self; }); /** * 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, * weight : Number || TEXT_DEFAULT_WEIGHT, * 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 weight = config.weight !== undefined ? config.weight : TEXT_DEFAULT_WEIGHT; 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 textAssets = []; var mainAsset; ; 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 fill = main ? textFill : borderFill; var textAsset = textAssets[i]; if (textAsset) { textAsset.destroy(); } textAsset = self.addChild(new Text2(newText, { fill: fill, font: font, size: size })); textAsset.anchor = { x: anchorX, y: anchorY }; // NOTE: Cannot be set in config textAsset.x = TEXT_OFFSETS[i][0] * weight; // NOTE: Cannot be set in config textAsset.y = TEXT_OFFSETS[i][1] * weight; // NOTE: Cannot be set in config textAssets[i] = textAsset; } mainAsset = textAssets[TEXT_OFFSETS.length - 1]; } ; buildTextAssets(text); return self; }); var IntroPosterTrail = Container.expand(function () { var self = Container.call(this); var trailSquares = []; // Function to create a new trail square function createTrailSquare() { var square = self.attachAsset('square', { anchorX: 0.5, anchorY: 0.5, width: 5, height: 50, alpha: 0.8 }); var angle = Math.random() * Math.PI + Math.PI / 2; square.rotation = angle; trailSquares.push(square); } self.speed = 8; // Update function to animate the trail squares self.update = function () { if (LK.ticks % 2 == 0) { createTrailSquare(); } for (var i = trailSquares.length - 1; i >= 0; i--) { var square = trailSquares[i]; square.alpha -= 0.003; square.x += Math.cos(square.rotation + Math.PI / 2) * self.speed; square.y += Math.sin(square.rotation + Math.PI / 2) * self.speed; if (square.alpha <= 0) { square.destroy(); trailSquares.splice(i, 1); } } }; for (var i = 0; i < 50; i++) { createTrailSquare(); } return self; }); var Starfield = Container.expand(function (config) { var self = Container.call(this); config = config || {}; var starCount = config.starCount || 100; var starColor = config.starColor || 0xFFFFFF; var starSize = config.starSize || 2; for (var i = 0; i < starCount; i++) { var sizeVar = Math.random() * 3; var star = self.attachAsset('circle', { width: starSize + sizeVar, height: starSize + sizeVar, color: starColor, x: Math.random() * GAME_WIDTH, y: Math.random() * GAME_HEIGHT, anchorX: 0.5, anchorY: 0.5 }); star.direction = Math.random() < 0.5 ? 1 : -1; // Randomly set direction to 1 or -1 } // Add alpha animation to random stars self.update = function () { for (var i = 0; i < starsToAnimate.length; i++) { var star = starsToAnimate[i]; star.alpha += star.direction * 0.015; // Change alpha based on direction if (star.alpha <= 0.1 || star.alpha >= 1) { star.direction *= -1; // Reverse direction if alpha goes out of bounds } } if (help1 && help1 && help1.visible) { help1.alpha = 0.7 + 0.3 * Math.sin(LK.ticks / 30 + Math.PI / 2); help2.alpha = 0.7 + 0.3 * Math.sin(LK.ticks / 30); } }; function selectRandomStars() { starsToAnimate = []; for (var i = 0; i < 20; i++) { var randomIndex = Math.floor(Math.random() * self.children.length); starsToAnimate.push(self.children[randomIndex]); } } starAnimationInterval = LK.setInterval(selectRandomStars, 3000); // Select 10 stars every 3 seconds return self; }); var StartButton = Container.expand(function () { var self = Container.call(this); // Attach the start button asset var button = self.attachAsset('startButton', { anchorX: 0.5, anchorY: 0.5 }); // Attach the start recycle asset var recycle = self.attachAsset('startRecycle', { anchorX: 0.5, anchorY: 0.5 }); var startButtonText = self.attachAsset('startText', { anchorX: 0.5, anchorY: 0.5, rotation: -0.03, x: 7, y: 570 }); //startButtonText.x = GAME_WIDTH / 2; //startButtonText.y = road.y + 580; // Add update function to animate recycle rotation self.update = function () { recycle.rotation += 0.005; // Adjust rotation speed as needed button.rotation -= 0.005; // Adjust rotation speed as needed //introPoster.alpha = 0.8 + 0.2 * Math.sin(LK.ticks / 100); // Animate alpha from 0.5 to 1 and vice versa introPosterTrail.alpha = 0.8 + 0.2 * Math.sin(LK.ticks / 100); //recycle.alpha = 0.5 + 0.5 * Math.sin(LK.ticks / 30); // Animate alpha from 1 to 0 and vice versa }; // Event handler for button press self.down = function (x, y, obj) { isStarted = true; LK.getSound('startButton').play(); // Play startButton audio LK.setTimeout(function () { LK.playMusic('bgMusic', { loop: true }); }, 100); recycleBin.visible = true; recycleBinBack.visible = true; recycleBin.alpha = 0; recycleBinBack.alpha = 0; var binFadeInInterval = LK.setInterval(function () { if (recycleBin.alpha < 1) { recycleBin.alpha += 0.05; recycleBinBack.alpha += 0.05; } else { LK.clearInterval(binFadeInInterval); } }, 100); introPoster.destroy(); // Remove the intro poster introPosterTrail.destroy(); // Remove the intro poster trail sun.visible = true; help1.visible = true; help2.visible = true; LK.setTimeout(function () { help1.destroy(); help2.destroy(); }, 5000); self.destroy(); // Remove the start button after it's pressed }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x121B20 // Sky blue color }); /**** * Game Code ****/ /*if (isBonusStartAnim && bonus.update) { bonus.update(); } */ // Always call move and tick methods from the main tick method in the 'Game' class. var isDebug = false; var _console = console; var isStarted = false; var isBonusStartAnim = false; var isBonusActive = false; //============================================================================== ; // Math Constants / Pre-calculations var MATH_4_PI = Math.PI * 4; var MATH_2_PI = Math.PI * 2; var MATH_HALF_PI = Math.PI / 2; var MATH_THIRD_PI = Math.PI / 3; var MATH_QUARTER_PI = Math.PI / 4; var MATH_FIFTH_PI = Math.PI / 5; var MATH_SIXTH_PI = Math.PI / 5; var MATH_EIGHTH_PI = Math.PI / 8; var MATH_HALF_ROOT_3 = Math.sqrt(3) / 2; // Required by: TEXT_OFFSETS, BorderedText, BorderedSymbol, BorderedShape, 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, BorderedShape, SymbolText var TEXT_DEFAULT_WEIGHT = 4; // Required by: BorderedText, BorderedSymbol, BorderedShape, SymbolText var TEXT_DEFAULT_BORDER = '#000000'; // Required by: BorderedText, BorderedSymbol, BorderedShape, SymbolText var TEXT_DEFAULT_FILL = '#FFFFFF'; // Required by: BorderedText, SymbolText var TEXT_DEFAULT_FONT = 'Courier'; // Required by: BorderedText, SymbolText var TEXT_DEFAULT_SIZE = 80; // Required by: BorderedText, SymbolText ; // Game Constants var GAME_TICKS = 60; var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; ; // Road Constants var ROAD_SEGMENT_DISTANCE = 1.0; var ROAD_SEGMENT_POINTS = 100; var ROAD_SEGMENT_COUNT = 30; var ROAD_SEGMENT_ANGLE = MATH_2_PI / ROAD_SEGMENT_COUNT; var ROAD_SEGMENT_HEIGHT = 1000; var ROAD_SEGMENT_DESTROY = -2; var ROAD_SEGMENT_FADEOUT = 0.015; var ROAD_MAX_RIVER_SEGMENTS = 1; var ROAD_MAX_WALL_SEGMENTS = 1; var ROAD_FREE_RUN_COUNT = 5; var ROAD_STEP_COUNT = 5; var ROAD_STEP_DEFAULT = 10; var ROAD_STEP_HEIGHT_BASE = 512; var ROAD_STEP_HEIGHT_INCREMENT = 20; var ROAD_STEP_CHANCE_BASE = 0; //0.1; var ROAD_STEP_CHANCE_INCREMENT = 0; //0.05; var ROAD_GEN_PLANT_ASSETS = 3; var ROAD_GEN_PLANT_MAX = 3; var ROAD_GEN_TREE_ASSETS = 4; var ROAD_GEN_TREE_CHANCE = 0; //0.1; var ROAD_GEN_CLOUD_ASSETS = 4; var ROAD_GEN_CLOUD_CHANCE = 0.2; var ROAD_GEN_DETRITUS_ASSETS = 2; var ROAD_GEN_DETRITUS_CHANCE = 0.2; var ROAD_GEN_DETRITUS_FLYING_ASSETS = 2; var ROAD_GEN_DETRITUS_FLYING_CHANCE = 0.2; var ROAD_GEN_FRAGILE_NATURE_ASSETS = 2; var ROAD_GEN_FRAGILE_NATURE_CHANCE = 0.1; var ROAD_GEN_LAVA_SIZE = 1; var ROAD_GEN_WALL_ASSETS = 1; var ROAD_GEN_WALL_CHANCE = 0.1; ; // Player Constants var PLAYER_SPACING = ROAD_SEGMENT_ANGLE / 8; // 1/8 of a segment var PLAYER_POSE_TRANSITION = 0.1; var PLAYER_GRAVITY = 0.02; var PLAYER_JUMP_STEP_MIN = 1.5; // 720; // 1.0; var PLAYER_JUMP_STEP_MAX = 2.0; //1500; //2.0; var PLAYER_JUMP_STEP_INCREMENT = 0.75; //; 0.15; var PLAYER_ANIMATION_SPEED_BASE = 0.02; var PLAYER_SCALE_BASE = 1; //0.5; var PLAYER_SPEED_BASE = MATH_2_PI / (GAME_TICKS * 10); // 10s to complete a revolution var PLAYER_SPEED_FACTOR_BASE = 1.0; var PLAYER_SPEED_FACTOR_MAX = 2.0; //1.5; var PLAYER_SPEED_FACTOR_INCREMENT = 0.0015; //0.001; var PLAYER_SPEED_DECREMENT = 0.005; ; // Scoring Constants var SCORE_TOTAL_DISTANCE = 500; var SCORE_SEGMENT_DISTANCE = 1; var SCORE_SEGMENT_POINTS = 100; var SCORE_STOPPED_FACTOR = -0.2; var SCORE_MAX_POINTS = SCORE_SEGMENT_POINTS * PLAYER_SPEED_FACTOR_MAX; // Bonus Constants var BONUS_COLLECT_THRESHOLD = 50; var BONUS_DURATION_SEC = 10; //============================================================================== // Game Instances & Variables //============================================================================== ; // Variables var level = 0; var score = 0; var winningTime = 0; var gameOver = false; var distanceRemaining = SCORE_TOTAL_DISTANCE; var distanceRun = 0; var kgCollected = 0; var nbCollectedBeforeBonus = 0; // Number of detritus collected since last bonus var bonusLastStartTime = 0; ; var startY = 0; // Variable to store the initial Y position for swipe detection var swipeStarted = false; // Flag to start detecting swipe only after tap // Instances var background = game.addChild(LK.getAsset('background', { anchorX: 0.5, anchorY: 0.5, x: GAME_WIDTH / 2, y: GAME_HEIGHT / 2 })); var starfield = game.addChild(new Starfield({ starCount: 200, starColor: 0xFFFFFF, starSize: 2 })); var sun = game.addChild(new Sun({ x: GAME_WIDTH / 2, y: 50 })); sun.visible = false; var introPosterTrail = game.addChild(new IntroPosterTrail()); var introPoster = game.addChild(LK.getAsset('introPoster', { anchorX: 0.5, anchorY: 0.5, x: 870, y: 600, alpha: 1 })); introPosterTrail.x = introPoster.x; introPosterTrail.y = introPoster.y + 400; var playerContainer = game.addChild(new Container()); var roadContainer = game.addChild(new Container()); var road = roadContainer.addChild(new Road({ x: GAME_WIDTH / 2, y: GAME_HEIGHT - GAME_WIDTH / 2 })); var player = playerContainer.addChild(new Player({ x: road.x, y: road.y, scale: 0.5 })); var distanceText = game.addChild(new BorderedText(distanceRun + 'm', { x: road.x, y: road.y - TEXT_DEFAULT_SIZE, anchorX: 0.5, anchorY: 1 })); var scoreText = game.addChild(new BorderedText('0.00kg', { x: road.x, y: road.y, size: TEXT_DEFAULT_SIZE * 1.2, anchorX: 0.5, anchorY: 0.5 })); var multiplierText = game.addChild(new BorderedText('collected', { x: road.x, y: road.y + TEXT_DEFAULT_SIZE * 0.8, size: TEXT_DEFAULT_SIZE * 0.8, anchorX: 0.5, anchorY: 0 })); multiplierText.visible = true; var incrementText = game.addChild(new BorderedText('', { x: road.x - 300, y: road.y, anchorX: 1, anchorY: 0.5 })); incrementText.visible = false; var winningText = game.addChild(new BorderedText('', { x: GAME_WIDTH / 2, y: 400, anchorX: 0.5, anchorY: 0.5, size: 2 * TEXT_DEFAULT_SIZE })); ; var recycleBinBack = game.addChild(LK.getAsset('recycleBinBack', { anchorX: 0.5, anchorY: 0.5, x: 250, //450, y: 1750, visible: false })); ; var recycleBin = game.addChild(LK.getAsset('recycleBin', { anchorX: 0.5, anchorY: 0.5, x: 250, //450, y: 1750, visible: false })); ; var startButton = game.addChild(new StartButton()); startButton.x = GAME_WIDTH / 2; startButton.y = road.y; ; var help1 = game.addChild(LK.getAsset('help1', { anchorX: 0.5, anchorY: 0.5, x: 220, y: 2732 - 220, blendMode: 1, visible: false })); ; var help2 = game.addChild(LK.getAsset('help2', { anchorX: 0.5, anchorY: 0.5, x: 2048 - 220, y: 2732 - 220, blendMode: 1, visible: false })); ; //============================================================================== // Global events //============================================================================== ; var startY = 0; game.down = function (x, y, obj) { startY = y; swipeStarted = true; }; game.move = function (x, y, obj) { if (isBonusActive || !swipeStarted) { return; } if (startY - y > 50) { // Detect swipe up player.jumpStart(); } if (startY - y < -50) { // Detect swipe down player.slideStart(); } }; game.up = function (x, y, obj) { if (isBonusActive) { return; } player.jumpEnd(); startY = 0; swipeStarted = false; }; ; //============================================================================== // Global functions //============================================================================== ; // Helpers function log() { if (isDebug) { _console.log.apply(_console, arguments); } } ; function isActive() { return isStarted && !isBonusStartAnim && !player.isDead; } ; function callGameOver(lost) { gameOver = true; winningTime = LK.ticks; LK.setScore(score + kgCollected * 100); LK.stopMusic(); winningText.setText('The end'); distanceText.y += 500; scoreText.y += 500; multiplierText.y += 500; // Check if winningTime is set and show game over screen after 60 ticks game.update = function () { if (lost || winningTime && LK.ticks >= winningTime + 120) { LK.setTimeout(function () { LK.showGameOver(); }, 1000); LK.clearInterval(starAnimationInterval); } }; } ; function startBonusAnimation() { log('startBonusAnimation...'); nbCollectedBeforeBonus = 0; isBonusStartAnim = true; // Set alpha of all taken detritus to 0 for (var i = 0; i < game.children.length; i++) { var child = game.children[i]; if (child.taken) { log("IS TAKEN : ", child); child.alpha = 0; } } // Play bonus start sound LK.getSound('bonusStart').play(); // Flash screen green for 500ms to indicate bonus start LK.effects.flashScreen(0x00ff00, 500); // Create a bonus asset at the recycleBin position var bonus = game.addChild(LK.getAsset('bonus', { anchorX: 0.5, anchorY: 0.5, width: 10, height: 10, x: recycleBin.x, y: recycleBin.y })); // Animate bonus: move to road x,y while increasing width and height to 1024 var targetX = road.x; var targetY = road.y; var targetWidth = 1024; var targetHeight = 1024; var duration = 40; // Duration of the animation in milliseconds var duration2 = 30; // Duration of the animation in milliseconds var startTime = LK.ticks; var startTime2 = LK.ticks; bonus.update = function () { var elapsed = LK.ticks - startTime; var progress = Math.min(elapsed / duration, 1); if (progress < 1) { bonus.x = recycleBin.x + (targetX - recycleBin.x) * progress; bonus.y = recycleBin.y + (targetY - recycleBin.y) * progress; bonus.width = 100 + (targetWidth - 100) * progress; bonus.height = 100 + (targetHeight - 100) * progress; } if (progress > 0.5) { LK.getSound('bonusMove').play(); } if (progress === 1 && elapsed > duration * 2) { // Set new target to player position targetX = player.x; targetY = player.y - player.height / 2; targetWidth = 10; targetHeight = 10; startTime2 = LK.ticks; // Reset start time for the new animation phase bonus.update = function () { var elapsed2 = LK.ticks - startTime2; var progress2 = Math.min(elapsed2 / duration2, 1); if (progress2 > 0.5) { LK.getSound('bonusMove').play(); } if (progress2 < 1) { bonus.x = road.x + (targetX - road.x) * progress2; bonus.y = road.y + (targetY - road.y) * progress2; bonus.width = 1024 + (targetWidth - 1024) * progress2; bonus.height = 1024 + (targetHeight - 1024) * progress2; } if (progress2 === 1) { bonus.destroy(); isBonusActive = true; isBonusStartAnim = false; LK.stopMusic(); LK.playMusic('bonusMusic'); player.enterBonusState(); // Schedule stopBonus to be called after BONUS_DURATION_SEC seconds LK.setTimeout(stopBonus, BONUS_DURATION_SEC * 1000); bonusLastStartTime = Date.now(); } }; } }; } ; function stopBonus() { log('stopBonus...'); player.exitBonusState(); LK.stopMusic(); LK.playMusic('bgMusic', { loop: true }); nbCollectedBeforeBonus = 0; isBonusActive = false; increaseLevel(); } ; function increaseLevel() { log('increaseLevel...'); level++; PLAYER_SPEED_FACTOR_MAX += level / 10; ROAD_GEN_DETRITUS_CHANCE = 0.1; ROAD_GEN_DETRITUS_FLYING_CHANCE = 0.1; ROAD_GEN_FRAGILE_NATURE_CHANCE = 0.2; // Rivers of lava ROAD_GEN_LAVA_SIZE = 2; ROAD_GEN_WALL_CHANCE = 0.2; ROAD_GEN_DETRITUS_ASSETS = 4; ROAD_GEN_DETRITUS_FLYING_ASSETS = 4; } ; // Animations & handlers function animateProperty(animationPose, blendPose, blendAlpha, key, baseValue) { var animationPoseValue = (animationPose || {})[key] || 0; var blendPoseValue = (blendPose || {})[key] || 0; return (baseValue || 0) + animationPoseValue * (1 - blendAlpha) + blendPoseValue * blendAlpha; } function animationRun(alpha) { // Animation constants var armUpperAngle = MATH_FIFTH_PI; var armLowerAngle = -5 * MATH_EIGHTH_PI; var legLowerAngle = MATH_EIGHTH_PI; // Sinusoidals var rightSinusoidal = Math.cos(MATH_2_PI * (alpha + 0.5)); var leftSinusoidal = Math.cos(MATH_2_PI * alpha); var doubleSinusoidal = Math.cos(MATH_4_PI * alpha); // Animation calculations var armUpperRightSwing = -rightSinusoidal * MATH_QUARTER_PI; var armUpperLeftSwing = -leftSinusoidal * MATH_QUARTER_PI; var legUpperRightSwing = rightSinusoidal * MATH_THIRD_PI; var legUpperLeftSwing = leftSinusoidal * MATH_THIRD_PI; return { BODY_OFFSET: doubleSinusoidal * 10, HEAD_ROTATION: (1 + doubleSinusoidal) * Math.PI / 32, ARM_UPPER_RIGHT_ROTATION: armUpperAngle + armUpperRightSwing, ARM_UPPER_LEFT_ROTATION: armUpperAngle + armUpperLeftSwing, ARM_LOWER_RIGHT_ROTATION: armLowerAngle + armUpperRightSwing / 2, ARM_LOWER_LEFT_ROTATION: armLowerAngle + armUpperLeftSwing / 2, LEG_UPPER_RIGHT_ROTATION: legUpperRightSwing, LEG_UPPER_LEFT_ROTATION: legUpperLeftSwing, LEG_LOWER_RIGHT_ROTATION: legLowerAngle + (alpha > 0.5 ? MATH_THIRD_PI + (1 - Math.abs(rightSinusoidal)) * MATH_SIXTH_PI : Math.abs(-legUpperRightSwing)), LEG_LOWER_LEFT_ROTATION: legLowerAngle + (alpha <= 0.5 ? MATH_THIRD_PI + (1 - Math.abs(leftSinusoidal)) * MATH_SIXTH_PI : Math.abs(-legUpperLeftSwing)), FOOT_RIGHT_ROTATION: Math.max(0, legUpperRightSwing * 2 / 3) - legLowerAngle, FOOT_LEFT_ROTATION: Math.max(0, legUpperLeftSwing * 2 / 3) - legLowerAngle }; } function animationJump(alpha) { var sinusoidal = Math.cos(MATH_2_PI * alpha); return animationRun(0.35 + sinusoidal * 0.02); } function animationStand(alpha) { var sinusoidal = Math.cos(MATH_2_PI * alpha); var offsetAngle = Math.PI / 16; var sharedAngle = (2 + sinusoidal) * Math.PI / 64; return { BODY_OFFSET: sinusoidal * 5, ARM_UPPER_RIGHT_ROTATION: 0.5 * offsetAngle, ARM_UPPER_LEFT_ROTATION: 3 * offsetAngle, ARM_LOWER_RIGHT_ROTATION: -4 * offsetAngle, ARM_LOWER_LEFT_ROTATION: -4 * offsetAngle, LEG_UPPER_RIGHT_ROTATION: 1.5 * offsetAngle - sharedAngle, LEG_UPPER_LEFT_ROTATION: -1.5 * offsetAngle - sharedAngle, LEG_LOWER_RIGHT_ROTATION: 2 * sharedAngle, LEG_LOWER_LEFT_ROTATION: 2 * sharedAngle, FOOT_RIGHT_ROTATION: -sharedAngle - 1.5 * offsetAngle, FOOT_LEFT_ROTATION: -sharedAngle + 1.5 * offsetAngle }; } // On belly head front function animationSlide(alpha) { return { BODY_ROTATION: Math.PI / 2, // Rotate body 90 degrees BODY_OFFSET: -30, // Adjust body position to align with ground HEAD_ROTATION: Math.PI / 4, // Arm Right ARM_UPPER_RIGHT_ROTATION: Math.PI / 4, ARM_LOWER_RIGHT_ROTATION: -Math.PI / 4, // Arm Left ARM_UPPER_LEFT_ROTATION: -Math.PI, ARM_LOWER_LEFT_ROTATION: -Math.PI / 2, // Upper Legs LEG_UPPER_RIGHT_ROTATION: Math.PI / 10, LEG_UPPER_LEFT_ROTATION: Math.PI / 10, // Lower legs LEG_LOWER_RIGHT_ROTATION: Math.PI / 6, LEG_LOWER_LEFT_ROTATION: Math.PI / 6, FOOT_RIGHT_ROTATION: 0, FOOT_LEFT_ROTATION: 0 }; } function animationBonus(alpha) { // Use a sinusoidal movement for a slight floating effect var floatOffset = Math.sin(MATH_2_PI * alpha) * 10; return { BODY_ROTATION: Math.PI / 4, // 45° angle BODY_OFFSET: -50 + floatOffset, // Raised in the air with a slight float // Head tilted slightly back for a heroic look HEAD_ROTATION: -Math.PI / 8, // Right arm outstretched in front ARM_UPPER_RIGHT_ROTATION: -Math.PI / 1.5, // Shoulder at 90° ARM_LOWER_RIGHT_ROTATION: -Math.PI / 8, // Slight bend at elbow // Left arm slightly bent and back ARM_UPPER_LEFT_ROTATION: Math.PI / 4, ARM_LOWER_LEFT_ROTATION: -Math.PI / 3, // Legs in a dynamic pose LEG_UPPER_RIGHT_ROTATION: Math.PI / 6, LEG_LOWER_RIGHT_ROTATION: Math.PI / 3, LEG_UPPER_LEFT_ROTATION: Math.PI / 6, LEG_LOWER_LEFT_ROTATION: Math.PI / 3, /* LEG_UPPER_LEFT_ROTATION: Math.PI / 8, LEG_LOWER_LEFT_ROTATION: Math.PI / 2, */ // Feet angled for a more dynamic pose FOOT_RIGHT_ROTATION: -Math.PI / 6, FOOT_LEFT_ROTATION: -Math.PI / 4 }; } function tintPlayer(color) { player.body.torso.tint = color; // Tint the player's torso player.body.legUpperLeft.children[0].tint = color; // Tint the player's upper left leg player.body.legUpperRight.children[0].tint = color; // Tint the player's upper right leg player.body.armUpperLeft.children[0].tint = color; // Tint the player's upper left arm player.body.armUpperRight.children[0].tint = color; // Tint the player's upper right arm player.body.armLowerLeft.tint = color; player.body.armLowerRight.tint = color; player.body.legLowerLeft.tint = color; player.body.legLowerRight.tint = color; player.body.pelvis.tint = color; // Tint the player's pelvis player.body.head.tint = color; // Tint the player's head player.body.footRight.tint = color; player.body.footLeft.tint = color; } var starAnimationInterval; var starsToAnimate = [];
/****
* Classes
****/
var BoilingLava = Container.expand(function () {
var self = Container.call(this);
var lavaBubbles = [];
self.x = 400;
self.y = 400;
// Function to create a new lava bubble
function createLavaBubble() {
var bubble = self.attachAsset('lavaBubble', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1,
scaleY: 1,
x: Math.random() * 200 - 100,
// Random x position within a range
y: Math.random() * 200 - 100 // Random y position within a range
});
lavaBubbles.push(bubble);
}
// Update function to animate the lava bubbles
self.update = function () {
if (LK.ticks % 10 == 0) {
// Emit a bubble every second
createLavaBubble();
}
for (var i = lavaBubbles.length - 1; i >= 0; i--) {
var bubble = lavaBubbles[i];
bubble.y -= 2; // Move bubble upwards
bubble.alpha -= 0.015; // Fade out bubble
if (bubble.alpha <= 0) {
bubble.destroy();
lavaBubbles.splice(i, 1);
}
}
};
return self;
});
/**
* config {
* x : Number || 0,
* y : Number || 0,
* rotation : Number || 0,
* }
**/
var ConfigContainer = Container.expand(function (config) {
var self = Container.call(this);
config = config || {};
;
self.x = config.x || 0;
self.y = config.y || 0;
self.rotation = config.rotation || 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);
}
;
return self;
});
var Wall = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var wallIndex = Math.floor(Math.random() * ROAD_GEN_WALL_ASSETS);
var scale = 1; //0.9 + 0.2 * Math.random();
self.attachAsset('wall' + wallIndex, {
anchorX: 0.5,
anchorY: 1,
scaleX: scale,
//Math.random() < 0.5 ? -scale : scale,
scaleY: scale
});
self.taken = false;
self.update = function () {
//log("player.sliding:", player.sliding);
if (!player.sliding && player.body.torso.intersects(self)) {
if (!isBonusActive) {
player.hitWall = true;
player.sliding = true; // Keep down
player.isDead = true;
tintPlayer(0x666666);
log("Player hit a wall!");
// Flash screen red for 1 second (1000ms) to show we are dead.
LK.effects.flashScreen(0xff0000, 1000);
// Play lose sound
LK.getSound('lose').play();
callGameOver(true);
} else {
// TODO : what id hit wall while bonus active ?
}
}
};
return self;
});
var Sun = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
for (var i = 0; i < 5; i++) {
self.attachAsset('circle', {
width: 200 + 50 * i,
height: 200 + 50 * i,
color: 0xFFFF00,
alpha: 0.1,
anchorX: 0.5,
anchorY: 0.5
});
}
var sunAsset = self.attachAsset('sun', {
anchorX: 0.5075,
anchorY: 0.5,
alpha: 0.75
});
;
self.update = update;
;
function update() {
if (LK.ticks % 30 == 0) {
for (var i = 0; i < self.children.length - 1; i++) {
var circle = self.children[i];
if (i % 2 !== 0) {
circle.alpha = 0.01 + 0.09 * Math.abs(Math.sin(LK.ticks / 90));
} else {
circle.alpha = 0.01 + 0.09 * Math.abs(Math.cos(LK.ticks / 90));
}
}
}
}
});
var RoadSegment = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var background = self.addChild(new Container());
self.background = background;
self.attachAsset('roadSegment', {
anchorX: 1,
anchorY: 1,
tint: 0x62C344
});
self.segment = self.attachAsset('roadSegment', {
anchorX: 1,
anchorY: 1,
scaleX: 0.95,
scaleY: 0.95,
tint: 0x62C344 // TEMP DEBUG0x555555
});
self.segmentRiver = self.attachAsset('lava', {
anchorX: 1,
anchorY: 1,
scaleX: 0.95,
scaleY: 0.95,
visible: false
});
;
self.update = update;
self.regenerate = regenerate;
self.fadeout = false;
self.distance = config.distance;
self.step = config.step;
self.stepChance = config.stepChance;
self.previous = config.previous;
self.isRiver = false;
self.isWall = false;
;
function attachAssetRadial(parent, asset, obj) {
var offsetTotal = ROAD_SEGMENT_HEIGHT - (obj.offset || 10);
var rotation = -(1 - obj.anchorR) * ROAD_SEGMENT_ANGLE;
parent.attachAsset(asset, {
x: Math.sin(rotation) * offsetTotal,
y: Math.cos(rotation) * -offsetTotal,
width: obj.width,
height: obj.height,
anchorX: obj.anchorX,
anchorY: obj.anchorY,
scaleX: obj.scaleX || 1,
scaleY: obj.scaleY || 1,
rotation: (obj.rotation || 0) + 0
});
}
function attachObjectRadial(parent, object, obj) {
var offsetTotal = ROAD_SEGMENT_HEIGHT - (obj.offset || 10);
var rotation = -(1 - obj.anchorR) * ROAD_SEGMENT_ANGLE;
object.x = Math.sin(rotation) * offsetTotal - (obj.levitOffset && !obj.isOver || 0); //125* (obj.levitOffset || 0) - 400 * (obj.flying || 0);
object.y = Math.cos(rotation) * -offsetTotal - (obj.levitOffset || 0);
//object.width = obj.width;
//object.height = obj.height;
object.anchorX = obj.anchorX;
object.anchorY = obj.anchorY;
object.scaleX = obj.scaleX || 1;
object.scaleY = obj.scaleY || 1;
object.rotation = (obj.rotation || 0) + 0;
parent.addChild(object);
}
function generateBackground() {
//log(self.distance);
background.removeChildren();
self.isRiver = false;
self.isWall = false;
self.segmentRiver.visible = false;
var bonusTruce = false;
var active = isActive();
if (isBonusActive && Date.now() - bonusLastStartTime > BONUS_DURATION_SEC * 1000 - 2000) {
// just before bonus end
bonusTruce = true;
}
var plantCount = Math.floor(Math.random() * (ROAD_GEN_PLANT_MAX + 1));
if (self.distance === SCORE_TOTAL_DISTANCE) {
attachAssetRadial(background, 'flag', {
anchorR: 0.5,
anchorX: 0.1,
anchorY: 1
});
} else {
if (active && Math.random() < ROAD_GEN_DETRITUS_CHANCE) {
var detritus = new Detritus();
var scale = 1; //0.9 + 0.2 * Math.random();
attachObjectRadial(background, detritus, {
anchorR: 0.1 + 0.8 * Math.random(),
anchorX: 0.5,
anchorY: 0.1,
scaleX: Math.random() < 0.5 ? -scale : scale,
scaleY: scale,
rotation: -Math.PI * 0.25 + Math.random() * Math.PI * 0.5,
levitOffset: 190
});
} else if (active && Math.random() < ROAD_GEN_DETRITUS_FLYING_CHANCE) {
var detritusFlying = new DetritusFlying();
var scale = 1; //0.9 + 0.2 * Math.random();
attachObjectRadial(background, detritusFlying, {
anchorR: 0.1 + 0.8 * Math.random(),
anchorX: 0.5,
anchorY: 0.1,
scaleX: Math.random() < 0.5 ? -scale : scale,
scaleY: scale,
rotation: -Math.PI * 0.25 + Math.random() * Math.PI * 0.5,
flying: 1,
levitOffset: 700
});
} else if (road && active && !bonusTruce && road.riverSegmentCount < ROAD_MAX_RIVER_SEGMENTS && !self.isRiver && !self.isWall && !self.previous.isWall && (!self.previous.previous || !self.previous.previous.isWall) && (!self.previous.previous.previous || !self.previous.previous.previous.isWall) && (!self.previous.previous.previous.previous || !self.previous.previous.previous.previous.isWall) && Math.random() < ROAD_GEN_FRAGILE_NATURE_CHANCE) {
self.isRiver = true;
self.segmentRiver.visible = true;
road.riverSegmentCount++;
attachObjectRadial(background, new BoilingLava(), {
anchorR: 0.1 + 0.8 * Math.random()
});
if (ROAD_GEN_LAVA_SIZE > 1) {
self.previous.isRiver = true;
self.previous.segmentRiver.visible = true;
road.riverSegmentCount++;
attachObjectRadial(self.previous.background, new BoilingLava(), {
anchorR: 0.1 + 0.8 * Math.random()
});
}
} else if (road && active && !bonusTruce && road.wallSegmentCount < ROAD_MAX_WALL_SEGMENTS && !self.isWall && !self.isRiver && !self.previous.isRiver && (!self.previous.previous || !self.previous.previous.isRiver) && (!self.previous.previous.previous || !self.previous.previous.previous.isRiver) && (!self.previous.previous.previous.previous || !self.previous.previous.previous.previous.isRiver) && Math.random() < ROAD_GEN_WALL_CHANCE) {
self.isWall = true;
road.wallSegmentCount++;
var wallIndex = Math.floor(Math.random() * ROAD_GEN_WALL_ASSETS);
var scale = 1; //0.9 + 0.2 * Math.random();
var anchorR = 0.5; // 0.4 + 0.2 * Math.random();
attachObjectRadial(background, new Wall(), {
anchorR: anchorR,
anchorX: 0.5,
anchorY: 1,
scaleX: scale,
scaleY: scale
});
if (wallIndex == 0) {
// for pipes : add 2 other above
attachObjectRadial(background, new Wall(), {
anchorR: anchorR,
anchorX: 0.5,
anchorY: 1,
scaleX: scale,
scaleY: scale,
levitOffset: 250,
isOver: true
});
attachObjectRadial(background, new Wall(), {
anchorR: anchorR,
anchorX: 0.5,
anchorY: 1,
scaleX: scale,
scaleY: scale,
levitOffset: 500,
isOver: true
});
}
} else {
if (Math.random() < ROAD_GEN_TREE_CHANCE) {
var treeIndex = Math.floor(Math.random() * ROAD_GEN_TREE_ASSETS);
var scale = 0.9 + 0.2 * Math.random();
attachAssetRadial(background, 'tree' + treeIndex, {
anchorR: 0.4 + 0.2 * Math.random(),
anchorX: 0.5,
anchorY: 1,
scaleX: scale,
scaleY: scale
});
}
for (var i = 0; i < plantCount; i++) {
var plantIndex = Math.floor(Math.random() * ROAD_GEN_PLANT_ASSETS);
var scale = 0.9 + 0.2 * Math.random();
attachAssetRadial(background, 'plant' + plantIndex, {
anchorR: 0.1 + 0.8 * Math.random(),
anchorX: 0.5,
anchorY: 1,
scaleX: Math.random() < 0.5 ? -scale : scale,
scaleY: scale
});
}
}
}
if (active && Math.random() < ROAD_GEN_CLOUD_CHANCE) {
var cloudIndex = Math.floor(Math.random() * ROAD_GEN_CLOUD_ASSETS);
var scale = 1.0 + 1.5 * Math.random();
attachAssetRadial(background, 'cloud' + cloudIndex, {
offset: -1000,
anchorR: -1.0 + 2.0 * Math.random(),
anchorX: 0.5,
anchorY: 0.5,
scaleX: scale,
scaleY: scale,
rotation: -0.2
});
}
}
function regenerate(incrementDistance) {
if (incrementDistance) {
self.distance += SCORE_SEGMENT_DISTANCE * ROAD_SEGMENT_COUNT;
}
var stepping = self.distance <= SCORE_TOTAL_DISTANCE;
var stepChance = self.previous.stepChance;
var stepUp = stepping && (self.previous.step < ROAD_STEP_COUNT ? Math.random() < self.stepChance : false);
var stepDown = stepping && (self.previous.step > 0 ? Math.random() < self.stepChance : false);
if (stepUp || stepDown) {
if (stepUp && stepDown) {
if (Math.random() < 0.5) {
stepDown = false;
} else {
stepUp = false;
}
}
self.step = self.previous.step + (stepUp ? 1 : -1);
self.stepChance = ROAD_STEP_CHANCE_BASE;
} else {
self.step = self.previous.step;
self.stepChance = self.previous.stepChance + ROAD_STEP_CHANCE_INCREMENT;
}
generateBackground();
self.fadeout = false;
rescale();
}
function update() {
if (!isActive()) {
return;
}
if (self.fadeout && self.alpha > 0) {
if ((self.alpha -= ROAD_SEGMENT_FADEOUT) < 0) {
self.alpha = 0;
}
} else if (!self.fadeout && self.alpha < 1) {
if ((self.alpha += ROAD_SEGMENT_FADEOUT) > 1) {
self.alpha = 1;
}
}
}
function rescale() {
var newScale = (ROAD_STEP_HEIGHT_BASE + self.step * ROAD_STEP_HEIGHT_INCREMENT) / ROAD_SEGMENT_HEIGHT; // TEMP
self.scale.set(newScale);
}
;
rescale();
});
var Road = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var segments = [];
self.segments = segments;
self.riverSegmentCount = 0;
self.wallSegmentCount = 0;
for (var i = 0; i < ROAD_SEGMENT_COUNT; i++) {
var segment = segments[i] = self.addChild(new RoadSegment({
index: i,
previous: i > 0 ? segments[i - 1] : undefined,
rotation: i * ROAD_SEGMENT_ANGLE,
distance: (i - 1) * SCORE_SEGMENT_DISTANCE,
stepChance: ROAD_STEP_CHANCE_BASE,
step: ROAD_STEP_DEFAULT
}));
if (i === ROAD_SEGMENT_COUNT - 1) {
segments[0].previous = segment;
}
if (i >= ROAD_FREE_RUN_COUNT) {
segment.regenerate();
}
self.attachAsset('roadSegment', {
anchorX: 1,
anchorY: 1,
scaleX: 0.30,
scaleY: 0.27,
rotation: i * ROAD_SEGMENT_ANGLE,
tint: 0x8b4513
});
}
self.earthCore = self.attachAsset('earthCore', {
width: 470,
height: 470,
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.75,
//tint: 0x62C344
//tint: 0x87CEEB,
tint: 0x888888
});
self.earthCore2 = self.attachAsset('earthCore', {
width: 470,
height: 470,
anchorX: 0.5,
anchorY: 0.5,
scaleX: -1,
scaleY: -1,
alpha: 0.75,
tint: 0x888888
});
self.attachAsset('ring', {
width: 560,
height: 560,
anchorX: 0.5,
anchorY: 0.5,
tint: 0xff7518,
blendMode: 3
});
var destroyIndex = -1;
var regenerateIndex = -1;
;
self.rotate = rotate;
self.getIndex = getIndex;
self.getNode = getNode;
self.getNodeDist = getNodeDist;
;
function rotate(speed) {
if (!isActive()) {
return;
}
self.rotation -= speed;
var newDestroyIndex = getIndex(ROAD_SEGMENT_DESTROY);
var newRegenerateIndex = getIndex(20); // TEMP
if (newDestroyIndex >= 0 && newDestroyIndex !== destroyIndex && !segments[newDestroyIndex].fadeout) {
destroyIndex = newDestroyIndex;
if (segments[destroyIndex].isRiver) {
self.riverSegmentCount--;
self.riverSegmentCount = Math.max(0, self.riverSegmentCount);
}
if (segments[destroyIndex].isWall) {
self.wallSegmentCount--;
self.wallSegmentCount = Math.max(0, self.wallSegmentCount);
}
segments[destroyIndex].fadeout = true;
}
if (newRegenerateIndex >= 0 && newRegenerateIndex !== regenerateIndex && segments[newRegenerateIndex].fadeout) {
regenerateIndex = newRegenerateIndex;
segments[regenerateIndex].regenerate(true);
}
self.earthCore.rotation -= 0.001;
self.earthCore2.rotation += 0.005;
}
function getIndex(shift) {
return Math.floor(-self.rotation / MATH_2_PI * ROAD_SEGMENT_COUNT + (shift || 0) + 1) % ROAD_SEGMENT_COUNT;
}
function getNode(shift) {
return segments[getIndex(shift)];
}
function getNodeDist() {
return ROAD_SEGMENT_ANGLE - -self.rotation % ROAD_SEGMENT_ANGLE;
}
});
var PlayerBody = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
// Animation settings
var animation = animationStand;
var alpha = 0;
var blendAnimation = undefined;
var blendDecrement = 0;
var blendAlpha = 0;
var afterTint = 0xAAAAAA;
// Body settings
var bodyOffsetY = config.y || 0;
var pelvisOffsetX = -35; // old -10;
var armUpperAngle = -3 * MATH_EIGHTH_PI;
var armUpperOffsetX = 30; // old 15;
var armUpperOffsetY = 10; // old 30
var armLowerAngle = -MATH_QUARTER_PI;
var armLowerOffsetY = 90;
var legUpperAngle = 3 * MATH_EIGHTH_PI;
var legUpperOffsetX = -0;
var legLowerOffsetY = 135;
var footOffsetY = 100;
// Create and attach body parts
var armUpperRight = self.addChild(new ConfigContainer({
y: armUpperOffsetY
}));
var legUpperRight = self.addChild(new ConfigContainer({
x: pelvisOffsetX + legUpperOffsetX
}));
var torso = self.attachAsset('torso', {
anchorX: 0.5
});
var head = self.attachAsset('head', {
anchorX: 0.52,
//0.15,
anchorY: 0.8 //0.95
});
self.head = head;
var pelvis = self.attachAsset('pelvis', {
x: pelvisOffsetX,
y: torso.height - 20,
// old torso.height,
anchorX: 0.5,
anchorY: 0.2,
tint: afterTint
});
self.pelvis = pelvis;
self.torso = torso;
var legUpperLeft = self.addChild(new ConfigContainer({
x: pelvisOffsetX - legUpperOffsetX - 30
}));
var armUpperLeft = self.addChild(new ConfigContainer({
y: armUpperOffsetY
}));
// Arm extensions
armUpperRight.attachAsset('upperArm', {
rotation: armUpperAngle,
anchorX: 0.85,
anchorY: 0.25,
tint: afterTint
});
self.armUpperRight = armUpperRight;
armUpperLeft.attachAsset('upperArm', {
rotation: armUpperAngle,
anchorX: 0.85,
anchorY: 0.25
});
self.armUpperLeft = armUpperLeft;
var armLowerRight = armUpperRight.attachAsset('lowerArm', {
y: armLowerOffsetY,
rotation: armLowerAngle,
anchorX: 0.95,
anchorY: 0.05,
tint: afterTint
});
self.armLowerRight = armLowerRight;
var armLowerLeft = armUpperLeft.attachAsset('lowerArm', {
y: armLowerOffsetY,
rotation: armLowerAngle,
anchorX: 0.95,
anchorY: 0.05
});
self.armLowerLeft = armLowerLeft;
// Leg extensions
legUpperRight.attachAsset('upperLeg', {
rotation: legUpperAngle,
anchorY: 0.1,
tint: afterTint
});
self.legUpperRight = legUpperRight;
legUpperLeft.attachAsset('upperLeg', {
rotation: legUpperAngle,
anchorY: 0.1
});
self.legUpperLeft = legUpperLeft;
var legLowerRight = legUpperRight.attachAsset('lowerLeg', {
y: legLowerOffsetY,
anchorX: 0.5,
// old 0.65,
tint: afterTint
});
self.legLowerRight = legLowerRight;
var legLowerLeft = legUpperLeft.attachAsset('lowerLeg', {
y: legLowerOffsetY,
anchorX: 0.5 // old 0.65
});
self.legLowerLeft = legLowerLeft;
var footRight = legLowerRight.attachAsset('foot', {
y: footOffsetY - 10,
anchorX: -0.2,
// old 0.2,
anchorY: 0.2,
// old 0.05,
tint: afterTint
});
self.footRight = footRight;
var footLeft = legLowerLeft.attachAsset('foot', {
y: footOffsetY,
anchorX: -0.2,
// old 0.2,
anchorY: 0.2 // old 0.05,
});
self.footLeft = footLeft;
// Additional positioning
armUpperLeft.x = -torso.width / 2 + armUpperOffsetX;
armUpperRight.x = torso.width / 2 - armUpperOffsetX;
legUpperLeft.y = torso.height + pelvis.height / 3 - 40;
legUpperRight.y = torso.height + pelvis.height / 3 - 20;
;
self.animate = animate;
self.pushAnimation = pushAnimation;
;
function animate(increment) {
alpha = (alpha + increment) % 1;
if (blendAlpha > 0) {
if ((blendAlpha -= blendDecrement) <= 0) {
blendAlpha = 0;
blendAnimation = undefined;
}
}
var pose = animation(alpha);
var blendPose = blendAnimation && blendAnimation(alpha);
self.y = animateProperty(pose, blendPose, blendAlpha, 'BODY_OFFSET', bodyOffsetY);
head.rotation = animateProperty(pose, blendPose, blendAlpha, 'HEAD_ROTATION');
armUpperRight.rotation = animateProperty(pose, blendPose, blendAlpha, 'ARM_UPPER_RIGHT_ROTATION');
armUpperLeft.rotation = animateProperty(pose, blendPose, blendAlpha, 'ARM_UPPER_LEFT_ROTATION');
armLowerRight.rotation = animateProperty(pose, blendPose, blendAlpha, 'ARM_LOWER_RIGHT_ROTATION', armLowerAngle);
armLowerLeft.rotation = animateProperty(pose, blendPose, blendAlpha, 'ARM_LOWER_LEFT_ROTATION', armLowerAngle);
legUpperRight.rotation = animateProperty(pose, blendPose, blendAlpha, 'LEG_UPPER_RIGHT_ROTATION');
legUpperLeft.rotation = animateProperty(pose, blendPose, blendAlpha, 'LEG_UPPER_LEFT_ROTATION');
legLowerRight.rotation = animateProperty(pose, blendPose, blendAlpha, 'LEG_LOWER_RIGHT_ROTATION');
legLowerLeft.rotation = animateProperty(pose, blendPose, blendAlpha, 'LEG_LOWER_LEFT_ROTATION');
footRight.rotation = animateProperty(pose, blendPose, blendAlpha, 'FOOT_RIGHT_ROTATION');
footLeft.rotation = animateProperty(pose, blendPose, blendAlpha, 'FOOT_LEFT_ROTATION');
}
function pushAnimation(newAnimation, newBlendDecrement) {
blendDecrement = newBlendDecrement;
if (newAnimation !== animation) {
blendAnimation = animation;
animation = newAnimation;
blendAlpha = 1;
}
}
;
animate(0);
});
var Player = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var speedFactor = PLAYER_SPEED_FACTOR_BASE;
var stopped = false;
var falling = false;
var jumping = false; // If the player is currently jumping (and immune to gravity)
var jumpAction = false; // Tracks tap releases
var jumpStep = 0; // Tracks min/max jump heights
var aerialSpeed = 0; // Actual vertical/air speed
var baseY = config.y;
var step = ROAD_STEP_DEFAULT;
var nodeDistance = 0;
var nodeTicks = 0;
var nodeMulti = 0;
var slideTimer = 0;
var SLIDE_DURATION = 100; // Duration of slide in frames
var body = self.addChild(new PlayerBody({
y: -370
}));
self.body = body;
self.update = update;
self.speedFactor = speedFactor;
self.jumpStart = jumpStart;
self.jumpEnd = jumpEnd;
self.jumping = false;
self.sliding = false;
self.slideStart = slideStart;
self.slideEnd = slideEnd;
self.hitWall = false;
self.isDead = false;
self.intersects = function (obj) {
var bounds1 = self.getBounds();
var bounds2 = obj.getBounds();
if (self.sliding) {
// When sliding, reduce height for collision check
return bounds1.x < bounds2.x + bounds2.width && bounds1.x + bounds1.width > bounds2.x && bounds1.y < bounds2.y + bounds2.height && bounds1.y + bounds1.height / 4 > bounds2.y;
} else {
return bounds1.x < bounds2.x + bounds2.width && bounds1.x + bounds1.width > bounds2.x && bounds1.y < bounds2.y + bounds2.height && bounds1.y + bounds1.height > bounds2.y;
}
};
;
self.update = update;
self.jumpStart = jumpStart;
self.jumpEnd = jumpEnd;
self.slideStart = slideStart;
self.slideEnd = slideEnd;
self.enterBonusState = enterBonusState;
self.exitBonusState = exitBonusState;
;
function update() {
if (!isActive()) {
return;
}
var nextNode = road.getNode(1);
var currentNode = nextNode.previous;
// Update horizontal movement
var outputSpeed = PLAYER_SPEED_BASE * speedFactor;
var distance = road.getNodeDist();
// Check for collision with Detritus
// Collision check removed from Player update method
// Check for collision
if (stopped && nextNode.step <= step) {
stopped = false;
} else if (nextNode.step > step && outputSpeed > distance) {
road.rotate(distance - PLAYER_SPACING);
speedFactor = PLAYER_SPEED_FACTOR_BASE;
stopped = true;
if (!isBonusActive && currentNode.step <= step) {
body.pushAnimation(animationStand, PLAYER_POSE_TRANSITION);
}
}
if (!stopped) {
road.rotate(outputSpeed);
if (!gameOver) {
if (speedFactor < PLAYER_SPEED_FACTOR_MAX) {
if ((speedFactor += PLAYER_SPEED_FACTOR_INCREMENT) >= PLAYER_SPEED_FACTOR_MAX) {
speedFactor = PLAYER_SPEED_FACTOR_MAX;
}
}
} else {
if (speedFactor > 0) {
if ((speedFactor -= PLAYER_SPEED_DECREMENT) <= 0) {
speedFactor = 0;
}
}
}
}
if (isBonusActive) {
// Animate player tint using multiple flashy colors
var colors = [0xff0000, 0x00ff00, 0x0000ff, 0xffff00, 0xff00ff, 0x00ffff];
var colorIndex = Math.floor(LK.ticks / 5 % colors.length);
var tintColor = colors[colorIndex];
tintPlayer(tintColor);
if (nextNode && nextNode.background && nextNode.background.children.length > 0) {
log(" nextNode.background.children:", nextNode.background.children);
for (var i = 0; i < nextNode.background.children.length; i++) {
var child = nextNode.background.children[i];
if (child.taken === false) {
child.autoTake = true;
break;
}
}
}
} else {
// TOOD: Get new node if applicable
// Update vertical movement
if (jumping) {
//log("speedFactor", speedFactor, "outputSpeed", outputSpeed);
jumpStep += aerialSpeed * speedFactor;
//log("jumpAction:", jumpAction, "jumpStep:", jumpStep, "PLAYER_JUMP_STEP_MIN:", PLAYER_JUMP_STEP_MIN, "PLAYER_JUMP_STEP_MAX:", PLAYER_JUMP_STEP_MAX);
if (!jumpAction && jumpStep <= PLAYER_JUMP_STEP_MIN || jumpStep >= PLAYER_JUMP_STEP_MAX) {
falling = true;
jumping = false;
jumpAction = false;
jumpStep = 0;
}
} else if (falling) {
aerialSpeed -= PLAYER_GRAVITY * speedFactor;
} else if (step > currentNode.step) {
falling = true;
body.pushAnimation(animationJump, PLAYER_POSE_TRANSITION);
}
step += aerialSpeed;
//log("falling", falling, "step:", step, "currentNode.step:", currentNode.step);
if (falling && step <= currentNode.step) {
step = currentNode.step;
falling = false;
aerialSpeed = 0;
body.pushAnimation(stopped ? animationStand : animationRun, PLAYER_POSE_TRANSITION);
}
if (self.sliding) {
slideTimer += 1 * speedFactor;
if (slideTimer >= SLIDE_DURATION) {
slideEnd();
}
// Adjust player's vertical position when sliding
self.y = baseY - 500; //- stepHeight / 4; // Adjust to quarter height when sliding
} else {
self.y = baseY - stepHeight;
}
}
// Score calculations
if (!gameOver && currentNode.distance > nodeDistance) {
adjustScore(nodeMulti / nodeTicks);
currentNode.distance = nodeDistance;
nodeMulti = 0;
nodeTicks = 0;
}
nodeMulti += stopped ? SCORE_STOPPED_FACTOR : speedFactor;
nodeTicks++;
// Adjustments
var stepHeight = ROAD_STEP_HEIGHT_BASE + step * ROAD_STEP_HEIGHT_INCREMENT; // TODO: Better scaling
if (!self.sliding) {
self.y = baseY - stepHeight; // TODO: Better scaling
}
self.scale.set(PLAYER_SCALE_BASE * stepHeight / ROAD_SEGMENT_HEIGHT); // TODO: Better scaling
body.animate(PLAYER_ANIMATION_SPEED_BASE * speedFactor);
//multiplierText.setText((stopped ? SCORE_STOPPED_FACTOR : speedFactor).toFixed(1) + 'x');
// Get the current road segment
var currentSegment = road.getNode(0);
// Check if the current segment is a river segment
if (!isBonusActive && currentSegment.isRiver && !jumping && !falling && !jumpAction) {
self.isDead = true;
tintPlayer(0xff0000);
// Player is above a river segment
log("Player fell in the river!");
// Flash screen red for 1 second (1000ms) to show we are dead.
LK.effects.flashScreen(0xff0000, 1000);
// Play lose sound
LK.getSound('lose').play();
callGameOver(true);
}
}
function jumpStart() {
if (!gameOver && !jumping && !falling && !self.sliding) {
LK.getSound('jump').play();
jumping = true;
jumpAction = true;
aerialSpeed = PLAYER_JUMP_STEP_INCREMENT;
body.pushAnimation(animationJump, PLAYER_POSE_TRANSITION);
}
}
function jumpEnd() {
jumpAction = false;
}
function slideStart() {
if (!gameOver && !jumping && !falling && !self.sliding) {
self.sliding = true;
slideTimer = 0;
body.pushAnimation(animationSlide, PLAYER_POSE_TRANSITION);
body.rotation = -Math.PI / 2;
//body.scale.x *= -1;
LK.getSound('slide').play();
/* Flies
// Rotate the body, not the entire player container
body.rotation = -Math.PI / 2;
// Flip the body horizontally to ensure correct orientation
body.scale.x *= -1;
// Play slide sound (if available)
// LK.getSound('slide').play();
*/
}
}
function slideEnd() {
if (self.sliding && !self.hitWall) {
self.sliding = false;
slideTimer = 0;
body.pushAnimation(stopped ? animationStand : animationRun, PLAYER_POSE_TRANSITION);
// Restore body's original rotation and scale
body.rotation = 0;
//body.scale.x *= -1;
}
}
function enterBonusState() {
// ... other bonus state logic ...
if (self.sliding) {
slideEnd();
}
body.pushAnimation(animationBonus, PLAYER_POSE_TRANSITION);
body.rotation = Math.PI / 6;
speedFactor = PLAYER_SPEED_FACTOR_MAX * 2;
}
function exitBonusState() {
// ... other exit bonus state logic ...
body.pushAnimation(stopped ? animationStand : animationRun, PLAYER_POSE_TRANSITION);
body.rotation = 0;
tintPlayer(0xFFFFFF);
speedFactor = PLAYER_SPEED_FACTOR_BASE;
}
function adjustScore(averageMulti) {
var points = Math.floor(averageMulti * SCORE_SEGMENT_POINTS);
score = Math.max(0, score + points);
scoreText.setText(kgCollected.toFixed(2) + 'kg');
// Old score : scoreText.setText(String(score).padStart(6, '0'));
distanceText.setText((distanceRun += SCORE_SEGMENT_DISTANCE) + 'm');
incrementText.setText((points === SCORE_MAX_POINTS ? '[MAX!] +' : '+') + points);
}
;
body.pushAnimation(animationRun, PLAYER_POSE_TRANSITION);
});
var FragileNature = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var fragileNatureIndex = Math.floor(Math.random() * ROAD_GEN_FRAGILE_NATURE_ASSETS);
var scale = 0.9 + 0.2 * Math.random();
self.attachAsset('fragileNature' + fragileNatureIndex, {
anchorX: 0.5,
anchorY: 1,
scaleX: Math.random() < 0.5 ? -scale : scale,
scaleY: scale
});
self.taken = false;
self.update = function () {
if (self.taken) {
// TODO : Do domthing when Fragile Nature is hit
} else if (!self.taken && player.body.torso.intersects(self)) {
self.taken = true;
}
};
return self;
});
var DetritusFlying = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var detritusFlyingIndex = Math.floor(Math.random() * ROAD_GEN_DETRITUS_FLYING_ASSETS);
var scale = 0.9 + 0.2 * Math.random();
self.attachAsset('detritusFlying' + detritusFlyingIndex, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: Math.random() < 0.5 ? -scale : scale,
scaleY: scale
});
self.update = function () {
if (!isActive()) {
return;
}
if (self.taken) {
var targetX = recycleBin.x;
var targetY = recycleBin.y;
var startX = player.x;
var startY = player.y;
var speed = 15;
var accelerationY = 1.0; // Adjust as needed
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var velocityX = dx / distance * speed;
var velocityY = dy / distance * speed;
if (distance > speed) {
if (Math.abs(self.x - startX) < Math.abs(dx / 2)) {
self.x += velocityX;
self.y -= velocityY * 0.1 - accelerationY * 0.2; // Move up
} else {
self.x += velocityX;
self.y += velocityY + accelerationY; // Move down with acceleration
}
self.scale.x *= 0.996;
self.scale.y *= 0.996;
self.rotation += 0.1; // Rotate detritusFlying on themselves when taken
} else {
self.x = targetX;
self.y = targetY;
self.destroy();
}
} else if (!self.taken && (self.autoTake || player.body.torso.intersects(self))) {
self.taken = true;
// Flash Detritus in green for 500ms
LK.effects.flashObject(self, 0x00ff00, 500);
kgCollected += 0.01; // Add 0.01 to kgCollected
var globalPosition = self.parent.toGlobal(self.position);
self.parent.removeChild(self);
game.addChildAt(self, game.getChildIndex(recycleBin));
self.position = game.toLocal(globalPosition);
LK.getSound('collectDetritus').play();
if (!isBonusActive) {
nbCollectedBeforeBonus++;
}
if (nbCollectedBeforeBonus >= BONUS_COLLECT_THRESHOLD) {
log("Gained Bonus !!!");
startBonusAnimation();
}
} else {
self.rotation += 0.05 + (Math.random() - 0.25) * 0.1;
}
};
self.taken = false;
return self;
});
var Detritus = ConfigContainer.expand(function (config) {
var self = ConfigContainer.call(this, config);
var detritusIndex = Math.floor(Math.random() * ROAD_GEN_DETRITUS_ASSETS);
var scale = 0.9 + 0.2 * Math.random();
self.attachAsset('detritus' + detritusIndex, {
anchorX: 0.5,
anchorY: 0.5,
scaleX: Math.random() < 0.5 ? -scale : scale,
scaleY: scale
});
self.taken = false;
self.update = function () {
if (!isActive()) {
return;
}
if (self.taken) {
var targetX = recycleBin.x;
var targetY = recycleBin.y;
var startX = player.x;
var startY = player.y;
var speed = 15;
var accelerationY = 1.0; // Adjust as needed
var dx = targetX - self.x;
var dy = targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var velocityX = dx / distance * speed;
var velocityY = dy / distance * speed;
if (distance > speed) {
if (Math.abs(self.x - startX) < Math.abs(dx / 2)) {
self.x += velocityX;
self.y -= velocityY * 0.5 - accelerationY * 0.2; // Move up
} else {
self.x += velocityX;
self.y += velocityY + accelerationY; // Move down with acceleration
}
self.scale.x *= 0.996;
self.scale.y *= 0.996;
self.rotation += 0.1; // Rotate detritus on themselves when taken
} else {
self.x = targetX;
self.y = targetY;
self.destroy();
}
} else if (!self.taken && (self.autoTake || player.body.torso.intersects(self))) {
self.taken = true;
// Flash DetritusFlying in green for 500ms
LK.effects.flashObject(self, 0x00ff00, 500);
kgCollected += 0.02; // Add 0.01 to kgCollected
var globalPosition = self.parent.toGlobal(self.position);
self.parent.removeChild(self);
game.addChildAt(self, game.getChildIndex(recycleBin));
self.position = game.toLocal(globalPosition);
LK.getSound('collectDetritus').play();
if (!isBonusActive) {
nbCollectedBeforeBonus++;
}
if (nbCollectedBeforeBonus >= BONUS_COLLECT_THRESHOLD) {
log("Gained Bonus !!!");
startBonusAnimation();
}
}
};
return self;
});
/**
* 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,
* weight : Number || TEXT_DEFAULT_WEIGHT,
* 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 weight = config.weight !== undefined ? config.weight : TEXT_DEFAULT_WEIGHT;
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 textAssets = [];
var mainAsset;
;
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 fill = main ? textFill : borderFill;
var textAsset = textAssets[i];
if (textAsset) {
textAsset.destroy();
}
textAsset = self.addChild(new Text2(newText, {
fill: fill,
font: font,
size: size
}));
textAsset.anchor = {
x: anchorX,
y: anchorY
}; // NOTE: Cannot be set in config
textAsset.x = TEXT_OFFSETS[i][0] * weight; // NOTE: Cannot be set in config
textAsset.y = TEXT_OFFSETS[i][1] * weight; // NOTE: Cannot be set in config
textAssets[i] = textAsset;
}
mainAsset = textAssets[TEXT_OFFSETS.length - 1];
}
;
buildTextAssets(text);
return self;
});
var IntroPosterTrail = Container.expand(function () {
var self = Container.call(this);
var trailSquares = [];
// Function to create a new trail square
function createTrailSquare() {
var square = self.attachAsset('square', {
anchorX: 0.5,
anchorY: 0.5,
width: 5,
height: 50,
alpha: 0.8
});
var angle = Math.random() * Math.PI + Math.PI / 2;
square.rotation = angle;
trailSquares.push(square);
}
self.speed = 8;
// Update function to animate the trail squares
self.update = function () {
if (LK.ticks % 2 == 0) {
createTrailSquare();
}
for (var i = trailSquares.length - 1; i >= 0; i--) {
var square = trailSquares[i];
square.alpha -= 0.003;
square.x += Math.cos(square.rotation + Math.PI / 2) * self.speed;
square.y += Math.sin(square.rotation + Math.PI / 2) * self.speed;
if (square.alpha <= 0) {
square.destroy();
trailSquares.splice(i, 1);
}
}
};
for (var i = 0; i < 50; i++) {
createTrailSquare();
}
return self;
});
var Starfield = Container.expand(function (config) {
var self = Container.call(this);
config = config || {};
var starCount = config.starCount || 100;
var starColor = config.starColor || 0xFFFFFF;
var starSize = config.starSize || 2;
for (var i = 0; i < starCount; i++) {
var sizeVar = Math.random() * 3;
var star = self.attachAsset('circle', {
width: starSize + sizeVar,
height: starSize + sizeVar,
color: starColor,
x: Math.random() * GAME_WIDTH,
y: Math.random() * GAME_HEIGHT,
anchorX: 0.5,
anchorY: 0.5
});
star.direction = Math.random() < 0.5 ? 1 : -1; // Randomly set direction to 1 or -1
}
// Add alpha animation to random stars
self.update = function () {
for (var i = 0; i < starsToAnimate.length; i++) {
var star = starsToAnimate[i];
star.alpha += star.direction * 0.015; // Change alpha based on direction
if (star.alpha <= 0.1 || star.alpha >= 1) {
star.direction *= -1; // Reverse direction if alpha goes out of bounds
}
}
if (help1 && help1 && help1.visible) {
help1.alpha = 0.7 + 0.3 * Math.sin(LK.ticks / 30 + Math.PI / 2);
help2.alpha = 0.7 + 0.3 * Math.sin(LK.ticks / 30);
}
};
function selectRandomStars() {
starsToAnimate = [];
for (var i = 0; i < 20; i++) {
var randomIndex = Math.floor(Math.random() * self.children.length);
starsToAnimate.push(self.children[randomIndex]);
}
}
starAnimationInterval = LK.setInterval(selectRandomStars, 3000); // Select 10 stars every 3 seconds
return self;
});
var StartButton = Container.expand(function () {
var self = Container.call(this);
// Attach the start button asset
var button = self.attachAsset('startButton', {
anchorX: 0.5,
anchorY: 0.5
});
// Attach the start recycle asset
var recycle = self.attachAsset('startRecycle', {
anchorX: 0.5,
anchorY: 0.5
});
var startButtonText = self.attachAsset('startText', {
anchorX: 0.5,
anchorY: 0.5,
rotation: -0.03,
x: 7,
y: 570
});
//startButtonText.x = GAME_WIDTH / 2;
//startButtonText.y = road.y + 580;
// Add update function to animate recycle rotation
self.update = function () {
recycle.rotation += 0.005; // Adjust rotation speed as needed
button.rotation -= 0.005; // Adjust rotation speed as needed
//introPoster.alpha = 0.8 + 0.2 * Math.sin(LK.ticks / 100); // Animate alpha from 0.5 to 1 and vice versa
introPosterTrail.alpha = 0.8 + 0.2 * Math.sin(LK.ticks / 100);
//recycle.alpha = 0.5 + 0.5 * Math.sin(LK.ticks / 30); // Animate alpha from 1 to 0 and vice versa
};
// Event handler for button press
self.down = function (x, y, obj) {
isStarted = true;
LK.getSound('startButton').play(); // Play startButton audio
LK.setTimeout(function () {
LK.playMusic('bgMusic', {
loop: true
});
}, 100);
recycleBin.visible = true;
recycleBinBack.visible = true;
recycleBin.alpha = 0;
recycleBinBack.alpha = 0;
var binFadeInInterval = LK.setInterval(function () {
if (recycleBin.alpha < 1) {
recycleBin.alpha += 0.05;
recycleBinBack.alpha += 0.05;
} else {
LK.clearInterval(binFadeInInterval);
}
}, 100);
introPoster.destroy(); // Remove the intro poster
introPosterTrail.destroy(); // Remove the intro poster trail
sun.visible = true;
help1.visible = true;
help2.visible = true;
LK.setTimeout(function () {
help1.destroy();
help2.destroy();
}, 5000);
self.destroy(); // Remove the start button after it's pressed
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x121B20 // Sky blue color
});
/****
* Game Code
****/
/*if (isBonusStartAnim && bonus.update) {
bonus.update();
}
*/
// Always call move and tick methods from the main tick method in the 'Game' class.
var isDebug = false;
var _console = console;
var isStarted = false;
var isBonusStartAnim = false;
var isBonusActive = false;
//==============================================================================
;
// Math Constants / Pre-calculations
var MATH_4_PI = Math.PI * 4;
var MATH_2_PI = Math.PI * 2;
var MATH_HALF_PI = Math.PI / 2;
var MATH_THIRD_PI = Math.PI / 3;
var MATH_QUARTER_PI = Math.PI / 4;
var MATH_FIFTH_PI = Math.PI / 5;
var MATH_SIXTH_PI = Math.PI / 5;
var MATH_EIGHTH_PI = Math.PI / 8;
var MATH_HALF_ROOT_3 = Math.sqrt(3) / 2; // Required by: TEXT_OFFSETS, BorderedText, BorderedSymbol, BorderedShape, 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, BorderedShape, SymbolText
var TEXT_DEFAULT_WEIGHT = 4; // Required by: BorderedText, BorderedSymbol, BorderedShape, SymbolText
var TEXT_DEFAULT_BORDER = '#000000'; // Required by: BorderedText, BorderedSymbol, BorderedShape, SymbolText
var TEXT_DEFAULT_FILL = '#FFFFFF'; // Required by: BorderedText, SymbolText
var TEXT_DEFAULT_FONT = 'Courier'; // Required by: BorderedText, SymbolText
var TEXT_DEFAULT_SIZE = 80; // Required by: BorderedText, SymbolText
;
// Game Constants
var GAME_TICKS = 60;
var GAME_WIDTH = 2048;
var GAME_HEIGHT = 2732;
;
// Road Constants
var ROAD_SEGMENT_DISTANCE = 1.0;
var ROAD_SEGMENT_POINTS = 100;
var ROAD_SEGMENT_COUNT = 30;
var ROAD_SEGMENT_ANGLE = MATH_2_PI / ROAD_SEGMENT_COUNT;
var ROAD_SEGMENT_HEIGHT = 1000;
var ROAD_SEGMENT_DESTROY = -2;
var ROAD_SEGMENT_FADEOUT = 0.015;
var ROAD_MAX_RIVER_SEGMENTS = 1;
var ROAD_MAX_WALL_SEGMENTS = 1;
var ROAD_FREE_RUN_COUNT = 5;
var ROAD_STEP_COUNT = 5;
var ROAD_STEP_DEFAULT = 10;
var ROAD_STEP_HEIGHT_BASE = 512;
var ROAD_STEP_HEIGHT_INCREMENT = 20;
var ROAD_STEP_CHANCE_BASE = 0; //0.1;
var ROAD_STEP_CHANCE_INCREMENT = 0; //0.05;
var ROAD_GEN_PLANT_ASSETS = 3;
var ROAD_GEN_PLANT_MAX = 3;
var ROAD_GEN_TREE_ASSETS = 4;
var ROAD_GEN_TREE_CHANCE = 0; //0.1;
var ROAD_GEN_CLOUD_ASSETS = 4;
var ROAD_GEN_CLOUD_CHANCE = 0.2;
var ROAD_GEN_DETRITUS_ASSETS = 2;
var ROAD_GEN_DETRITUS_CHANCE = 0.2;
var ROAD_GEN_DETRITUS_FLYING_ASSETS = 2;
var ROAD_GEN_DETRITUS_FLYING_CHANCE = 0.2;
var ROAD_GEN_FRAGILE_NATURE_ASSETS = 2;
var ROAD_GEN_FRAGILE_NATURE_CHANCE = 0.1;
var ROAD_GEN_LAVA_SIZE = 1;
var ROAD_GEN_WALL_ASSETS = 1;
var ROAD_GEN_WALL_CHANCE = 0.1;
;
// Player Constants
var PLAYER_SPACING = ROAD_SEGMENT_ANGLE / 8; // 1/8 of a segment
var PLAYER_POSE_TRANSITION = 0.1;
var PLAYER_GRAVITY = 0.02;
var PLAYER_JUMP_STEP_MIN = 1.5; // 720; // 1.0;
var PLAYER_JUMP_STEP_MAX = 2.0; //1500; //2.0;
var PLAYER_JUMP_STEP_INCREMENT = 0.75; //; 0.15;
var PLAYER_ANIMATION_SPEED_BASE = 0.02;
var PLAYER_SCALE_BASE = 1; //0.5;
var PLAYER_SPEED_BASE = MATH_2_PI / (GAME_TICKS * 10); // 10s to complete a revolution
var PLAYER_SPEED_FACTOR_BASE = 1.0;
var PLAYER_SPEED_FACTOR_MAX = 2.0; //1.5;
var PLAYER_SPEED_FACTOR_INCREMENT = 0.0015; //0.001;
var PLAYER_SPEED_DECREMENT = 0.005;
;
// Scoring Constants
var SCORE_TOTAL_DISTANCE = 500;
var SCORE_SEGMENT_DISTANCE = 1;
var SCORE_SEGMENT_POINTS = 100;
var SCORE_STOPPED_FACTOR = -0.2;
var SCORE_MAX_POINTS = SCORE_SEGMENT_POINTS * PLAYER_SPEED_FACTOR_MAX;
// Bonus Constants
var BONUS_COLLECT_THRESHOLD = 50;
var BONUS_DURATION_SEC = 10;
//==============================================================================
// Game Instances & Variables
//==============================================================================
;
// Variables
var level = 0;
var score = 0;
var winningTime = 0;
var gameOver = false;
var distanceRemaining = SCORE_TOTAL_DISTANCE;
var distanceRun = 0;
var kgCollected = 0;
var nbCollectedBeforeBonus = 0; // Number of detritus collected since last bonus
var bonusLastStartTime = 0;
;
var startY = 0; // Variable to store the initial Y position for swipe detection
var swipeStarted = false; // Flag to start detecting swipe only after tap
// Instances
var background = game.addChild(LK.getAsset('background', {
anchorX: 0.5,
anchorY: 0.5,
x: GAME_WIDTH / 2,
y: GAME_HEIGHT / 2
}));
var starfield = game.addChild(new Starfield({
starCount: 200,
starColor: 0xFFFFFF,
starSize: 2
}));
var sun = game.addChild(new Sun({
x: GAME_WIDTH / 2,
y: 50
}));
sun.visible = false;
var introPosterTrail = game.addChild(new IntroPosterTrail());
var introPoster = game.addChild(LK.getAsset('introPoster', {
anchorX: 0.5,
anchorY: 0.5,
x: 870,
y: 600,
alpha: 1
}));
introPosterTrail.x = introPoster.x;
introPosterTrail.y = introPoster.y + 400;
var playerContainer = game.addChild(new Container());
var roadContainer = game.addChild(new Container());
var road = roadContainer.addChild(new Road({
x: GAME_WIDTH / 2,
y: GAME_HEIGHT - GAME_WIDTH / 2
}));
var player = playerContainer.addChild(new Player({
x: road.x,
y: road.y,
scale: 0.5
}));
var distanceText = game.addChild(new BorderedText(distanceRun + 'm', {
x: road.x,
y: road.y - TEXT_DEFAULT_SIZE,
anchorX: 0.5,
anchorY: 1
}));
var scoreText = game.addChild(new BorderedText('0.00kg', {
x: road.x,
y: road.y,
size: TEXT_DEFAULT_SIZE * 1.2,
anchorX: 0.5,
anchorY: 0.5
}));
var multiplierText = game.addChild(new BorderedText('collected', {
x: road.x,
y: road.y + TEXT_DEFAULT_SIZE * 0.8,
size: TEXT_DEFAULT_SIZE * 0.8,
anchorX: 0.5,
anchorY: 0
}));
multiplierText.visible = true;
var incrementText = game.addChild(new BorderedText('', {
x: road.x - 300,
y: road.y,
anchorX: 1,
anchorY: 0.5
}));
incrementText.visible = false;
var winningText = game.addChild(new BorderedText('', {
x: GAME_WIDTH / 2,
y: 400,
anchorX: 0.5,
anchorY: 0.5,
size: 2 * TEXT_DEFAULT_SIZE
}));
;
var recycleBinBack = game.addChild(LK.getAsset('recycleBinBack', {
anchorX: 0.5,
anchorY: 0.5,
x: 250,
//450,
y: 1750,
visible: false
}));
;
var recycleBin = game.addChild(LK.getAsset('recycleBin', {
anchorX: 0.5,
anchorY: 0.5,
x: 250,
//450,
y: 1750,
visible: false
}));
;
var startButton = game.addChild(new StartButton());
startButton.x = GAME_WIDTH / 2;
startButton.y = road.y;
;
var help1 = game.addChild(LK.getAsset('help1', {
anchorX: 0.5,
anchorY: 0.5,
x: 220,
y: 2732 - 220,
blendMode: 1,
visible: false
}));
;
var help2 = game.addChild(LK.getAsset('help2', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 - 220,
y: 2732 - 220,
blendMode: 1,
visible: false
}));
;
//==============================================================================
// Global events
//==============================================================================
;
var startY = 0;
game.down = function (x, y, obj) {
startY = y;
swipeStarted = true;
};
game.move = function (x, y, obj) {
if (isBonusActive || !swipeStarted) {
return;
}
if (startY - y > 50) {
// Detect swipe up
player.jumpStart();
}
if (startY - y < -50) {
// Detect swipe down
player.slideStart();
}
};
game.up = function (x, y, obj) {
if (isBonusActive) {
return;
}
player.jumpEnd();
startY = 0;
swipeStarted = false;
};
;
//==============================================================================
// Global functions
//==============================================================================
;
// Helpers
function log() {
if (isDebug) {
_console.log.apply(_console, arguments);
}
}
;
function isActive() {
return isStarted && !isBonusStartAnim && !player.isDead;
}
;
function callGameOver(lost) {
gameOver = true;
winningTime = LK.ticks;
LK.setScore(score + kgCollected * 100);
LK.stopMusic();
winningText.setText('The end');
distanceText.y += 500;
scoreText.y += 500;
multiplierText.y += 500;
// Check if winningTime is set and show game over screen after 60 ticks
game.update = function () {
if (lost || winningTime && LK.ticks >= winningTime + 120) {
LK.setTimeout(function () {
LK.showGameOver();
}, 1000);
LK.clearInterval(starAnimationInterval);
}
};
}
;
function startBonusAnimation() {
log('startBonusAnimation...');
nbCollectedBeforeBonus = 0;
isBonusStartAnim = true;
// Set alpha of all taken detritus to 0
for (var i = 0; i < game.children.length; i++) {
var child = game.children[i];
if (child.taken) {
log("IS TAKEN : ", child);
child.alpha = 0;
}
}
// Play bonus start sound
LK.getSound('bonusStart').play();
// Flash screen green for 500ms to indicate bonus start
LK.effects.flashScreen(0x00ff00, 500);
// Create a bonus asset at the recycleBin position
var bonus = game.addChild(LK.getAsset('bonus', {
anchorX: 0.5,
anchorY: 0.5,
width: 10,
height: 10,
x: recycleBin.x,
y: recycleBin.y
}));
// Animate bonus: move to road x,y while increasing width and height to 1024
var targetX = road.x;
var targetY = road.y;
var targetWidth = 1024;
var targetHeight = 1024;
var duration = 40; // Duration of the animation in milliseconds
var duration2 = 30; // Duration of the animation in milliseconds
var startTime = LK.ticks;
var startTime2 = LK.ticks;
bonus.update = function () {
var elapsed = LK.ticks - startTime;
var progress = Math.min(elapsed / duration, 1);
if (progress < 1) {
bonus.x = recycleBin.x + (targetX - recycleBin.x) * progress;
bonus.y = recycleBin.y + (targetY - recycleBin.y) * progress;
bonus.width = 100 + (targetWidth - 100) * progress;
bonus.height = 100 + (targetHeight - 100) * progress;
}
if (progress > 0.5) {
LK.getSound('bonusMove').play();
}
if (progress === 1 && elapsed > duration * 2) {
// Set new target to player position
targetX = player.x;
targetY = player.y - player.height / 2;
targetWidth = 10;
targetHeight = 10;
startTime2 = LK.ticks; // Reset start time for the new animation phase
bonus.update = function () {
var elapsed2 = LK.ticks - startTime2;
var progress2 = Math.min(elapsed2 / duration2, 1);
if (progress2 > 0.5) {
LK.getSound('bonusMove').play();
}
if (progress2 < 1) {
bonus.x = road.x + (targetX - road.x) * progress2;
bonus.y = road.y + (targetY - road.y) * progress2;
bonus.width = 1024 + (targetWidth - 1024) * progress2;
bonus.height = 1024 + (targetHeight - 1024) * progress2;
}
if (progress2 === 1) {
bonus.destroy();
isBonusActive = true;
isBonusStartAnim = false;
LK.stopMusic();
LK.playMusic('bonusMusic');
player.enterBonusState();
// Schedule stopBonus to be called after BONUS_DURATION_SEC seconds
LK.setTimeout(stopBonus, BONUS_DURATION_SEC * 1000);
bonusLastStartTime = Date.now();
}
};
}
};
}
;
function stopBonus() {
log('stopBonus...');
player.exitBonusState();
LK.stopMusic();
LK.playMusic('bgMusic', {
loop: true
});
nbCollectedBeforeBonus = 0;
isBonusActive = false;
increaseLevel();
}
;
function increaseLevel() {
log('increaseLevel...');
level++;
PLAYER_SPEED_FACTOR_MAX += level / 10;
ROAD_GEN_DETRITUS_CHANCE = 0.1;
ROAD_GEN_DETRITUS_FLYING_CHANCE = 0.1;
ROAD_GEN_FRAGILE_NATURE_CHANCE = 0.2; // Rivers of lava
ROAD_GEN_LAVA_SIZE = 2;
ROAD_GEN_WALL_CHANCE = 0.2;
ROAD_GEN_DETRITUS_ASSETS = 4;
ROAD_GEN_DETRITUS_FLYING_ASSETS = 4;
}
;
// Animations & handlers
function animateProperty(animationPose, blendPose, blendAlpha, key, baseValue) {
var animationPoseValue = (animationPose || {})[key] || 0;
var blendPoseValue = (blendPose || {})[key] || 0;
return (baseValue || 0) + animationPoseValue * (1 - blendAlpha) + blendPoseValue * blendAlpha;
}
function animationRun(alpha) {
// Animation constants
var armUpperAngle = MATH_FIFTH_PI;
var armLowerAngle = -5 * MATH_EIGHTH_PI;
var legLowerAngle = MATH_EIGHTH_PI;
// Sinusoidals
var rightSinusoidal = Math.cos(MATH_2_PI * (alpha + 0.5));
var leftSinusoidal = Math.cos(MATH_2_PI * alpha);
var doubleSinusoidal = Math.cos(MATH_4_PI * alpha);
// Animation calculations
var armUpperRightSwing = -rightSinusoidal * MATH_QUARTER_PI;
var armUpperLeftSwing = -leftSinusoidal * MATH_QUARTER_PI;
var legUpperRightSwing = rightSinusoidal * MATH_THIRD_PI;
var legUpperLeftSwing = leftSinusoidal * MATH_THIRD_PI;
return {
BODY_OFFSET: doubleSinusoidal * 10,
HEAD_ROTATION: (1 + doubleSinusoidal) * Math.PI / 32,
ARM_UPPER_RIGHT_ROTATION: armUpperAngle + armUpperRightSwing,
ARM_UPPER_LEFT_ROTATION: armUpperAngle + armUpperLeftSwing,
ARM_LOWER_RIGHT_ROTATION: armLowerAngle + armUpperRightSwing / 2,
ARM_LOWER_LEFT_ROTATION: armLowerAngle + armUpperLeftSwing / 2,
LEG_UPPER_RIGHT_ROTATION: legUpperRightSwing,
LEG_UPPER_LEFT_ROTATION: legUpperLeftSwing,
LEG_LOWER_RIGHT_ROTATION: legLowerAngle + (alpha > 0.5 ? MATH_THIRD_PI + (1 - Math.abs(rightSinusoidal)) * MATH_SIXTH_PI : Math.abs(-legUpperRightSwing)),
LEG_LOWER_LEFT_ROTATION: legLowerAngle + (alpha <= 0.5 ? MATH_THIRD_PI + (1 - Math.abs(leftSinusoidal)) * MATH_SIXTH_PI : Math.abs(-legUpperLeftSwing)),
FOOT_RIGHT_ROTATION: Math.max(0, legUpperRightSwing * 2 / 3) - legLowerAngle,
FOOT_LEFT_ROTATION: Math.max(0, legUpperLeftSwing * 2 / 3) - legLowerAngle
};
}
function animationJump(alpha) {
var sinusoidal = Math.cos(MATH_2_PI * alpha);
return animationRun(0.35 + sinusoidal * 0.02);
}
function animationStand(alpha) {
var sinusoidal = Math.cos(MATH_2_PI * alpha);
var offsetAngle = Math.PI / 16;
var sharedAngle = (2 + sinusoidal) * Math.PI / 64;
return {
BODY_OFFSET: sinusoidal * 5,
ARM_UPPER_RIGHT_ROTATION: 0.5 * offsetAngle,
ARM_UPPER_LEFT_ROTATION: 3 * offsetAngle,
ARM_LOWER_RIGHT_ROTATION: -4 * offsetAngle,
ARM_LOWER_LEFT_ROTATION: -4 * offsetAngle,
LEG_UPPER_RIGHT_ROTATION: 1.5 * offsetAngle - sharedAngle,
LEG_UPPER_LEFT_ROTATION: -1.5 * offsetAngle - sharedAngle,
LEG_LOWER_RIGHT_ROTATION: 2 * sharedAngle,
LEG_LOWER_LEFT_ROTATION: 2 * sharedAngle,
FOOT_RIGHT_ROTATION: -sharedAngle - 1.5 * offsetAngle,
FOOT_LEFT_ROTATION: -sharedAngle + 1.5 * offsetAngle
};
}
// On belly head front
function animationSlide(alpha) {
return {
BODY_ROTATION: Math.PI / 2,
// Rotate body 90 degrees
BODY_OFFSET: -30,
// Adjust body position to align with ground
HEAD_ROTATION: Math.PI / 4,
// Arm Right
ARM_UPPER_RIGHT_ROTATION: Math.PI / 4,
ARM_LOWER_RIGHT_ROTATION: -Math.PI / 4,
// Arm Left
ARM_UPPER_LEFT_ROTATION: -Math.PI,
ARM_LOWER_LEFT_ROTATION: -Math.PI / 2,
// Upper Legs
LEG_UPPER_RIGHT_ROTATION: Math.PI / 10,
LEG_UPPER_LEFT_ROTATION: Math.PI / 10,
// Lower legs
LEG_LOWER_RIGHT_ROTATION: Math.PI / 6,
LEG_LOWER_LEFT_ROTATION: Math.PI / 6,
FOOT_RIGHT_ROTATION: 0,
FOOT_LEFT_ROTATION: 0
};
}
function animationBonus(alpha) {
// Use a sinusoidal movement for a slight floating effect
var floatOffset = Math.sin(MATH_2_PI * alpha) * 10;
return {
BODY_ROTATION: Math.PI / 4,
// 45° angle
BODY_OFFSET: -50 + floatOffset,
// Raised in the air with a slight float
// Head tilted slightly back for a heroic look
HEAD_ROTATION: -Math.PI / 8,
// Right arm outstretched in front
ARM_UPPER_RIGHT_ROTATION: -Math.PI / 1.5,
// Shoulder at 90°
ARM_LOWER_RIGHT_ROTATION: -Math.PI / 8,
// Slight bend at elbow
// Left arm slightly bent and back
ARM_UPPER_LEFT_ROTATION: Math.PI / 4,
ARM_LOWER_LEFT_ROTATION: -Math.PI / 3,
// Legs in a dynamic pose
LEG_UPPER_RIGHT_ROTATION: Math.PI / 6,
LEG_LOWER_RIGHT_ROTATION: Math.PI / 3,
LEG_UPPER_LEFT_ROTATION: Math.PI / 6,
LEG_LOWER_LEFT_ROTATION: Math.PI / 3,
/*
LEG_UPPER_LEFT_ROTATION: Math.PI / 8,
LEG_LOWER_LEFT_ROTATION: Math.PI / 2,
*/
// Feet angled for a more dynamic pose
FOOT_RIGHT_ROTATION: -Math.PI / 6,
FOOT_LEFT_ROTATION: -Math.PI / 4
};
}
function tintPlayer(color) {
player.body.torso.tint = color; // Tint the player's torso
player.body.legUpperLeft.children[0].tint = color; // Tint the player's upper left leg
player.body.legUpperRight.children[0].tint = color; // Tint the player's upper right leg
player.body.armUpperLeft.children[0].tint = color; // Tint the player's upper left arm
player.body.armUpperRight.children[0].tint = color; // Tint the player's upper right arm
player.body.armLowerLeft.tint = color;
player.body.armLowerRight.tint = color;
player.body.legLowerLeft.tint = color;
player.body.legLowerRight.tint = color;
player.body.pelvis.tint = color; // Tint the player's pelvis
player.body.head.tint = color; // Tint the player's head
player.body.footRight.tint = color;
player.body.footLeft.tint = color;
}
var starAnimationInterval;
var starsToAnimate = [];
white
circle sliced into many pieces, flat image. 2d, white background, shadowless.
pixel art of a tall, tree. game asset, 2d, white background, shadowless.
pixel art cloud. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
dark space.
flying lava bubble. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
a bonus crystal ball with the recycle symbol. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows.
top view A green round start button empty in the center like a ring.