/****
* 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.