/****
* Classes
****/
/****************************************************************************************** */
/**************************************** CLASSES ***************************************** */
/****************************************************************************************** */
/****************************************************************************************** */
/************************************* ATHLETE CLASS ************************************** */
/****************************************************************************************** */
var Athlete = Container.expand(function (line) {
var self = Container.call(this);
self.speedX = -1 * globalSpeedPerLine[line];
self.isRunning = false;
self.isBlocked = false;
self.nextHurdleSpeedX = 0;
self.jumpSpeedDefault = -20; // -17;
self.jumpSpeedBonus = -40;
self.jumpSpeed = self.jumpSpeedDefault;
self.lastJumpTime = 0;
self.interJumpDelayMs = 500;
self.gravity = 0.5;
self.lineIndex = line;
self.isAi = line > 0;
self.isOnGround = true;
self.isRunning = false;
self.isFalling = false; // Indicates if the athlete is currently falling
self.currentPosture = ""; // Store the name of the current posture
self.isChangingPosture = false; // 0 when idle or moving down, 1 when moving up
self.nextPosture = null;
self.tint = currentRoundColors[line];
self.previousX = 0;
self.justTookBonus = false;
self.isWaiting = true; // Player at speed 0
self.nbJumps = 0;
self.shadowBaseWidth = 180;
self.shadowBaseHeight = 30;
self.faceAsset = line ? 'funnyFace' + line : 'head';
// BODY PARTS
self.trunk = self.attachAsset('bodyPart', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 1,
scaleY: 1,
rotation: 0.125,
tint: 0x000000
});
self.head = self.trunk.attachAsset(self.faceAsset, {
anchorX: 0.5,
anchorY: 2,
scaleX: 1,
scaleY: 1
});
self.rightArm = self.trunk.attachAsset('bodyPart', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1,
scaleY: 1,
tint: self.tint
});
// Right hand asset attachment removed
self.leftArm = self.trunk.attachAsset('bodyPart', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1,
scaleY: 1,
tint: self.tint
});
// Left hand asset attachment removed
self.rightLeg = self.trunk.attachAsset('bodyPart', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1,
scaleY: 1.0,
tint: self.tint
});
// Right foot asset attachment removed
self.leftLeg = self.trunk.attachAsset('bodyPart', {
anchorX: 0.5,
anchorY: 0,
scaleX: 1,
scaleY: 1.0,
tint: self.tint
});
// Left foot asset attachment removed
// Shadow asset for the athlete
self.shadow = self.trunk.attachAsset('shadow', {
anchorX: 0.5,
anchorY: 1.0,
width: self.shadowBaseWidth,
height: self.shadowBaseHeight,
x: 0,
y: 300,
tint: 0x000000,
alpha: 0.5,
visible: false
});
// FUNCTIONS
self.updatePosture = function (force) {
var speed = force ? 1 : 0.2; // Speed of transition
if (!self.targetPosture) {
//log("no targetPosture.");
return;
}
self.head.x += (self.targetPosture.head.x - self.head.x) * speed;
self.trunk.rotation += (self.targetPosture.trunk.r - self.trunk.rotation) * speed;
self.head.y += (self.targetPosture.head.y - self.head.y) * speed;
self.head.rotation += (self.targetPosture.head.r - self.head.rotation) * speed;
self.rightArm.x += (self.targetPosture.rightArm.x - self.rightArm.x) * speed;
self.rightArm.y += (self.targetPosture.rightArm.y - self.rightArm.y) * speed;
self.rightArm.rotation += (self.targetPosture.rightArm.r - self.rightArm.rotation) * speed;
//log("Posture rightArm.rotation=" + self.rightArm.rotation);
// Right hand updates removed due to hand assets being removed
self.leftArm.x += (self.targetPosture.leftArm.x - self.leftArm.x) * speed;
self.leftArm.y += (self.targetPosture.leftArm.y - self.leftArm.y) * speed;
self.leftArm.rotation += (self.targetPosture.leftArm.r - self.leftArm.rotation) * speed;
// Left hand updates removed due to hand assets being removed
self.trunk.x += (self.targetPosture.trunk.x - self.trunk.x) * speed;
self.trunk.y += (self.targetPosture.trunk.y - self.trunk.y) * speed;
self.rightLeg.x += (self.targetPosture.rightLeg.x - self.rightLeg.x) * speed;
self.rightLeg.y += (self.targetPosture.rightLeg.y - self.rightLeg.y) * speed;
self.rightLeg.rotation += (self.targetPosture.rightLeg.r - self.rightLeg.rotation) * speed;
self.rightLeg.height += (self.targetPosture.rightLeg.h - self.rightLeg.height) * speed;
// Right foot updates removed due to foot assets being removed
self.leftLeg.x += (self.targetPosture.leftLeg.x - self.leftLeg.x) * speed;
self.leftLeg.y += (self.targetPosture.leftLeg.y - self.leftLeg.y) * speed;
self.leftLeg.rotation += (self.targetPosture.leftLeg.r - self.leftLeg.rotation) * speed;
self.leftLeg.height += (self.targetPosture.leftLeg.h - self.leftLeg.height) * speed;
// Shadow updates removed due to shadow assets being removed
self.shadow.x = self.targetPosture.shadow.x;
self.shadow.y = linesGroundLevels[self.lineIndex] - self.y + self.targetPosture.shadow.y;
self.shadow.rotation = -self.trunk.rotation;
if (self.lineIndex == 0) {
log("self.shadow Pos posture =" + self.shadow.x + ",", self.shadow.y);
}
// Check if the target posture for hands and legs is reached
//log("self.targetPosture: " + self.targetPosture.name + " : self.rightHand.x: " + self.rightHand.x, " self.targetPosture.rightHand.x: " + self.targetPosture.rightHand.x);
var handsAndLegsReached = Math.abs(self.rightLeg.x - self.targetPosture.rightLeg.x) < 1 && Math.abs(self.rightLeg.y - self.targetPosture.rightLeg.y) < 1 && Math.abs(self.leftLeg.x - self.targetPosture.leftLeg.x) < 1 && Math.abs(self.leftLeg.y - self.targetPosture.leftLeg.y) < 1 && Math.abs(self.head.x - self.targetPosture.head.x) < 1 && Math.abs(self.head.y - self.targetPosture.head.y) < 1;
if (handsAndLegsReached) {
if (self.lineIndex == 1) {
log("handsAndLegsReached !");
}
// Update currentPosture to the name of the targetPosture when hands and legs posture is reached
self.currentPosture = self.targetPosture.name;
self.targetPosture = null;
if (!self.visible) {
self.visible = true;
self.shadow.visible = true;
}
self.isChangingPosture = false;
if (self.nextPosture) {
self.setPosture(self.nextPosture);
}
return;
} else {
self.isChangingPosture = true;
}
};
self._update_migrated = function () {
/* ********************************************************** AI ATHLETE **************************************************** */
if (self.isAi) {
// AI Athlete
var nextX = self.x;
self.previousX = self.x;
if (self.isRunning) {
var deltaSpeed = Math.abs(globalSpeedPerLine[self.lineIndex]) - Math.abs(globalSpeedPerLine[0]);
nextX += deltaSpeed;
if (!self.isFalling) {
// Normal running
if (self.isOnGround) {
// Running
self.leftLeg.height = 200;
self.leftArm.height = 200;
if (self.isRunning) {
self.trunk.rotation = 0.125;
self.shadow.rotation = -self.trunk.rotation;
self.runAnim();
}
// Auto jump first hurdle of the line
if (obstacles[self.lineIndex].length) {
var distance = obstacles[self.lineIndex][0].x - nextX;
if (self.lineIndex == 1) {
//log((self.isOnGround ? 'On ground' : 'In air 1') + " / x=" + self.x + " / y=" + self.y + " / DeltaY=" + (obstacles[self.lineIndex][0].y - obstacles[self.lineIndex][0].centralRod.y - self.y) + " / distance=" + distance);
}
if (self.x > aiJumpLeftLimit && self.x < aiJumpRightLimit && distance > 0 && distance <= 300) {
self.jump();
} else {
// Reduce advance when out of screen
var normalize = self.nextHurdleSpeedX;
normalize = self.x < -200 ? Math.abs(normalize) : normalize;
normalize = self.x > 2100 ? -10 + Math.random() * 2 : normalize;
normalize = self.x > 3000 ? -20 : normalize;
nextX += normalize;
}
}
} else {
if (obstacles[self.lineIndex].length) {
var distance = obstacles[self.lineIndex][0].x - nextX;
if (self.lineIndex == 1) {
//log((self.isOnGround ? 'On ground' : 'In air 2') + " / x=" + self.x + " / y=" + self.y + " / DeltaY=" + (obstacles[self.lineIndex][0].y - obstacles[self.lineIndex][0].centralRod.y - self.y) + " / distance=" + distance);
}
}
// Jumping - if not running, finish the jump
self.speedY += self.gravity * Math.abs(globalSpeedPerLine[self.lineIndex] / 10) + (!self.isRunning ? 0.5 : 0);
self.y += self.speedY * Math.abs(globalSpeedPerLine[self.lineIndex] / 10) + (!self.isRunning ? self.speedY : 0);
self.shadow.y = linesGroundLevels[self.lineIndex] - self.y + 300;
if (self.y >= linesGroundLevels[self.lineIndex]) {
// Reached the ground
self.isOnGround = true;
self.speedY = 0;
self.y = linesGroundLevels[self.lineIndex];
self.shadow.y = linesGroundLevels[self.lineIndex] - self.y + 300;
// Define speed for next hurdle => Defines the Difficulty
//self.nextHurdleSpeedX = -1 + (-5 + 10 * Math.random()); // Level 1/3
//self.nextHurdleSpeedX = 0 + (-5 + 15 * Math.random()); // Level 2/3
self.nextHurdleSpeedX = -2 * Math.min(0, 2 - difficultyLevel) + (-5 + 10 * Math.random() * (1 + 0.5 * (difficultyLevel - 1))); // Level n
if (difficultyLevel == 1 && obstacles[self.lineIndex].length <= 3 && self.x > athlete.x) {
self.nextHurdleSpeedX = -2 - 1 * Math.abs(self.nextHurdleSpeedX); // Level 1/3
}
} else {
self.jumpAnim();
}
}
} else {
// Falling
self.y += 5;
self.shadow.y = linesGroundLevels[self.lineIndex] - self.y + 300;
if (self.y >= linesGroundLevels[self.lineIndex]) {
self.isOnGround = true;
self.speedY = 0;
self.y = linesGroundLevels[self.lineIndex];
self.shadow.y = linesGroundLevels[self.lineIndex] - self.y + 300;
} else {
self.fallAnim();
}
}
} else {
if (self.lineIndex == 1) {
//log("WAITING / x=" + self.x + " / y=" + self.y + " / globalSpeedPerLine[0]=" + globalSpeedPerLine[0]);
}
if (self.finishPosition == 1) {
self.victoryAnim();
}
if (athlete.isRunning && !self.isBlocked) {
nextX += -1 * globalSpeedPerLine[0];
}
if (self.isBlocked) {
nextX += globalSpeedPerLine[0];
}
}
// Update athlete position
self.x = nextX;
self.previousX = self.x;
} else {
// Player Athlete
/* ********************************************************** PLAYER ATHLETE **************************************************** */
if (self.isRunning) {
if (!self.isFalling) {
// Hide shadow when athlete is falling to simulate elevation from the ground
self.shadow.visible = true;
// Normal running
if (self.isOnGround) {
// Running
self.leftLeg.height = 200;
self.leftArm.height = 200;
/*self.trunk.rotation = 0.125;
self.shadow.rotation = -self.trunk.rotation;
self.runAnim();
*/
if (globalSpeedPerLine[0] != 0) {
self.isWaiting = false;
self.trunk.rotation = 0.125;
self.shadow.rotation = -self.trunk.rotation;
self.runAnim();
} else {
if (!self.isWaiting) {
log("NOW Player Waiting...");
self.isWaiting = true;
self.trunk.rotation = 0.0;
self.shadow.rotation = -self.trunk.rotation;
self.setPosture(idlePosture);
}
}
} else {
// Jumping
self.speedY += self.gravity * 2;
self.y += self.speedY * 2;
self.shadow.y = linesGroundLevels[self.lineIndex] - self.y + 300;
if (self.y >= linesGroundLevels[self.lineIndex]) {
// Reached the ground
self.isOnGround = true;
self.speedY = 0;
self.y = linesGroundLevels[self.lineIndex];
self.shadow.y = linesGroundLevels[self.lineIndex] - self.y + 300;
if (self.justTookBonus) {
log("Restore previous Speed..." + self.speedBeforeBonus);
globalSpeedPerLine[self.lineIndex] = self.speedBeforeBonus;
self.speedBeforeBonus = 0;
}
// Restore pos
if (globalSpeedPerLine[0] != 0) {
self.isWaiting = false;
self.trunk.rotation = 0.125;
self.shadow.rotation = -self.trunk.rotation;
self.runAnim();
} else {
if (!self.isWaiting) {
self.isWaiting = true;
self.trunk.rotation = 0.0;
self.shadow.rotation = -self.trunk.rotation;
self.setPosture(idlePosture);
}
}
} else {
if (self.justTookBonus) {
log("Apply Bonus Speed...");
if (!self.speedBeforeBonus) {
log("Save previous Speed..." + globalSpeedPerLine[self.lineIndex]);
self.speedBeforeBonus = globalSpeedPerLine[self.lineIndex];
}
globalSpeedPerLine[self.lineIndex] = self.speedBeforeBonus * 2;
}
self.jumpAnim();
}
}
// Update athlete speed
if (self.isOnGround && !finishLine.isCut && Date.now() - lastRunTime > 1000) {
// Decelerate when not pressing button
speedGauge = Math.max(0, speedGauge - 0.02 * (Date.now() - lastRunTime) / 1000);
globalSpeedPerLine[0] = globalBaseSpeed * speedGauge;
}
} else {
// Falling
// Hide shadow when athlete is falling to simulate elevation from the ground
self.shadow.visible = false;
self.y += 5;
self.shadow.y = linesGroundLevels[self.lineIndex] - self.y + 300;
self.fallAnim();
if (self.y >= linesGroundLevels[self.lineIndex] + 200) {
// 200 for horizontal trunk offset
self.isOnGround = true;
self.speedY = 0;
self.y = linesGroundLevels[self.lineIndex] + 200;
self.shadow.y = linesGroundLevels[self.lineIndex] - self.y + 300;
}
}
} else {
if (self.finishPosition == 1) {
self.victoryAnim();
}
}
}
if (self.finishPosition != 1) {
// Update shadow position to follow the athlete
//self.shadow.x = self.x;
//self.shadow.y = self.y + 105; // Keep the shadow directly below the athlete at a fixed distance
// If the athlete jumps, adjust the shadow's scaleX and scaleY to simulate shadow behavior
self.shadow.rotation = -self.trunk.rotation;
self.shadow.y = linesGroundLevels[self.lineIndex] - self.y + (self.currentPosture == "startingBlockPosture" ? 360 : 300);
if (self.lineIndex == 1) {
log("self.shadow Pos upd =" + self.shadow.x + ",", self.shadow.y, " / w=" + self.shadow.width, " / posture=" + self.currentPosture);
}
if (!self.isOnGround) {
//self.shadow.scaleX = 1.8; // Shadow grows when athlete jumps
//self.shadow.scaleY = 0.2; // Shadow becomes flatter when athlete jumps
var shadowRatio = (linesGroundLevels[self.lineIndex] - self.y) / 400;
shadowRatio = Math.max(0.5, Math.min(0.8, shadowRatio));
if (self.lineIndex == 0) {
log("shadowRatio=" + shadowRatio);
}
self.shadow.width = self.shadowBaseWidth * shadowRatio; // Shadow grows when athlete jumps
self.shadow.height = self.shadowBaseHeight * shadowRatio; // Shadow becomes flatter when athlete jumps
} else {
if (self.lineIndex == 1) {
log("self.shadow.width=" + self.shadow.width, " / self.currentPosture=" + self.currentPosture);
}
self.shadow.width = self.shadowBaseWidth + (self.currentPosture == "startingBlockPosture" ? 130 : 0); // Shadow grows when athlete jumps
self.shadow.height = self.shadowBaseHeight; // Shadow becomes flatter when athlete jumps
}
}
self.updatePosture();
};
self.jump = function () {
if (self.isAi || Date.now() - self.lastJumpTime > self.interJumpDelayMs) {
if (self.isOnGround) {
self.isOnGround = false;
// Handle jump bonus
if (!self.isAi && self.justTookBonus) {
self.nbJumps++;
if (self.nbJumps > nbBonusJumps) {
log("JUMPS FINISHED " + self.nbJumps);
self.justTookBonus = false;
self.jumpSpeed = self.jumpSpeedDefault;
self.speedBeforeBonus = 0;
self.nbJumps = 0;
globalSpeedPerLine[self.lineIndex] = globalBaseSpeed * speedGauge;
bonusManager.endBonus();
}
}
self.speedY = self.jumpSpeed;
self.lastJumpTime = Date.now();
//globalSpeedPerLine[self.lineIndex] = globalSpeedPerLine[self.lineIndex] * 1.1;
if (self.lineIndex == 0) {
log("--------------- JUMP -------------------- speedY :" + self.speedY);
}
}
}
};
self.runAnim = function () {
if (self.lineIndex == 1) {
//log("runAnim #1...self.rightArm.rotation=" + self.rightArm.rotation);
}
if (!self.isAi && speedGauge < 0.2) {
return;
}
if (self.lineIndex == 1) {
//log("runAnim #1...go anim!");
}
var ocsilDelay = 5 * (self.isAi ? 1 : 2 - speedGauge);
var armsAmplitude = 0.5;
var legsAmplitude = 0.5;
// Simulate running by continuously balancing the arms and legs
var runningArmAngle = Math.sin(LK.ticks / ocsilDelay) * armsAmplitude; // Oscillate arm angle to simulate running faster
var runningLegAngle = Math.sin(LK.ticks / ocsilDelay) * legsAmplitude; // Oscillate leg angle to simulate running faster
var armSwitchProgress = (Math.sin(LK.ticks / 5) + 0) / 2; // Normalize between 0 and 1
self.rightArm.x = 10 * (1 - armSwitchProgress) - 5 * armSwitchProgress;
self.leftArm.x = -15 * (1 - armSwitchProgress) + 20 * armSwitchProgress;
self.rightLeg.x = 5 * (1 - armSwitchProgress) - 5 * armSwitchProgress;
self.leftLeg.x = -10 * (1 - armSwitchProgress) + 10 * armSwitchProgress;
//log("Run rightArm.rotation=" + runningArmAngle);
self.rightArm.rotation = runningArmAngle;
self.leftArm.rotation = -runningArmAngle;
self.rightLeg.rotation = -runningLegAngle;
self.leftLeg.rotation = runningLegAngle;
self.trunk.width = 30 - 10 * (runningArmAngle * Math.PI * 0.5);
self.head.width = 50 - 20 * (runningArmAngle * Math.PI * 0.5);
};
self.jumpAnim = function () {
if (self.lineIndex == 1) {
//log("jumpAnim #1...");
}
var ocsilDelay = 5;
var armsAmplitude = 1;
var runningArmAngle = Math.sin(LK.ticks / ocsilDelay) * armsAmplitude; // Oscillate arm angle to simulate running faster
var armSwitchProgress = Math.sin(LK.ticks / 5) * 0.5; // Normalize between 0 and 1
self.rightArm.x = 10 * (1 - armSwitchProgress) - 5 * armSwitchProgress;
self.leftArm.x = -15 * (1 - armSwitchProgress) + 20 * armSwitchProgress;
self.rightLeg.x = 5 * (1 - armSwitchProgress) - 5 * armSwitchProgress;
self.leftLeg.x = -10 * (1 - armSwitchProgress) + 10 * armSwitchProgress;
//self.trunk.width = 30 - 10 * (runningArmAngle * Math.PI * 0.5);
self.trunk.rotation = 0.333;
self.shadow.rotation = -self.trunk.rotation;
var rotationSpeed = 0.001; //0.05; // Speed of rotation change
if (self.speedY > 0) {
var rr = self.rightArm.rotation - self.rightArm.rotation * rotationSpeed; // % (Math.PI * 2);
self.rightArm.rotation = rr;
var lr = self.leftArm.rotation - self.leftArm.rotation * rotationSpeed; // % (Math.PI * 2);
self.leftArm.rotation = lr;
//self.rightArm.rotation += (0 - self.rightArm.rotation) * rotationSpeed % (Math.PI * 2);
//self.leftArm.rotation += (0 - self.leftArm.rotation) * rotationSpeed % (Math.PI * 2);
// Return legs to initial rotation when descending
self.rightLeg.rotation += (0 - self.rightLeg.rotation) * rotationSpeed;
self.leftLeg.rotation += (0 - self.leftLeg.rotation) * rotationSpeed;
self.leftLeg.height -= 30 * rotationSpeed;
self.leftArm.height -= 20 * rotationSpeed;
} else {
//log("self.rightArm.rotation=" + self.rightArm.rotation, " + " + -Math.PI * 3 * rotationSpeed % (Math.PI * 2));
var rr = -Math.PI * 0.5; //self.rightArm.rotation - Math.PI * 3 * rotationSpeed; // % (Math.PI * 2);
self.rightArm.rotation = rr;
var lr = Math.PI * 0.2; //self.leftArm.rotation + Math.PI * 1.75 * rotationSpeed; // % (Math.PI * 2);
self.leftArm.rotation = lr;
//self.rightArm.rotation += -Math.PI * 3 * rotationSpeed % (Math.PI * 2);
//self.leftArm.rotation += Math.PI * 1.75 * rotationSpeed % (Math.PI * 2);
// Continue with jump animation adjustments
self.rightLeg.rotation = -Math.PI * 0.6; //+= -0.55 - self.rightLeg.rotation * rotationSpeed;
self.leftLeg.rotation = Math.PI * 0.2; //+= (1 - self.leftLeg.rotation) * rotationSpeed;
//self.leftLeg.height -= 0 * rotationSpeed;
//self.leftArm.height -= 35 * rotationSpeed;
}
};
self.fallAnim = function () {
if (self.lineIndex == 1) {
//log("Check finish for #3 at x=" + opponents[line - 1].x + " vs " + (finishLine.x + 512));
}
var fallSpeed = 0.1; // Speed of fall animation
// Adjust body parts to simulate falling
self.trunk.rotation += (Math.PI / 2 - self.trunk.rotation) * fallSpeed;
self.shadow.rotation = -self.trunk.rotation;
self.rightArm.rotation += (Math.PI / 2 - self.rightArm.rotation) * fallSpeed;
self.leftArm.rotation += (Math.PI / 2 - self.leftArm.rotation) * fallSpeed;
self.rightLeg.rotation += (Math.PI / 2 - self.rightLeg.rotation) * fallSpeed;
self.leftLeg.rotation += (Math.PI / 2 - self.leftLeg.rotation) * fallSpeed;
};
self.victoryAnim = function () {
if (self.lineIndex == 1) {
log("Victory #1");
}
var jumpHeight = 10;
var waveSpeed = 0.1;
var armWaveAmplitude = 0.5;
// Make the athlete do little jumps
if (self.isOnGround) {
self.speedY = -10; // Jump a little
self.isOnGround = false;
} else {
self.speedY += self.gravity;
self.y += self.speedY;
self.shadow.y = linesGroundLevels[self.lineIndex] - self.y + 300;
if (self.y >= linesGroundLevels[self.lineIndex]) {
self.y = linesGroundLevels[self.lineIndex];
self.shadow.y = linesGroundLevels[self.lineIndex] - self.y + 300;
self.isOnGround = true;
}
var shadowRatio = 1 - Math.abs(self.y - linesGroundLevels[self.lineIndex]) / 300;
shadowRatio = Math.max(0.5, Math.min(1.0, shadowRatio));
if (self.lineIndex == 0) {
//log("victory shadowRatio=" + shadowRatio);
}
self.shadow.width = 180 * shadowRatio; // Shadow grows when athlete jumps
self.shadow.height = 30 * shadowRatio; // Shadow becomes flatter when athlete jumps
}
// Wave arms
self.rightArm.rotation = Math.max(-armWaveAmplitude, Math.min(armWaveAmplitude, Math.sin(LK.ticks * waveSpeed) * armWaveAmplitude)) + Math.PI;
self.leftArm.rotation = Math.max(-armWaveAmplitude, Math.min(armWaveAmplitude, -Math.sin(LK.ticks * waveSpeed) * armWaveAmplitude * 0.5));
};
self.setPosture = function (newPosture, force) {
if (self.lineIndex == 1) {
log("Set posture: " + newPosture.name);
}
self.targetPosture = newPosture;
if (force) {
self.updatePosture(true);
}
// Directly update the shadow size
//self.shadow.width = self.shadowBaseWidth + (newPosture.name == "startingBlockPosture" ? 130 : 0); // Shadow grows when athlete jumps
};
self.restore = function () {
log("restore...");
self.stop();
self.hasFlashed = false;
self.hasFlashedGround = false;
self.isRunning = true;
};
self.stop = function () {
log("stop...");
self.x = self.previousX || startX + 100 * self.lineIndex;
self.y = linesGroundLevels[self.lineIndex];
self.shadow.y = linesGroundLevels[self.lineIndex] - self.y + 300;
self.speedY = 0;
self.trunk.rotation = 0;
self.shadow.rotation = -self.trunk.rotation;
self.rightArm.rotation = idlePosture.rightArm.r;
self.leftArm.rotation = 0;
self.rightLeg.rotation = 0;
self.leftLeg.rotation = 0;
self.setPosture(idlePosture);
self.isFalling = false;
self.isOnGround = true;
};
self.useBonus = function () {
log("Player useBonus...", self.justTookBonus);
if (self.justTookBonus) {
return;
}
self.justTookBonus = true;
self.jumpSpeed = self.jumpSpeedBonus; // Adjust jumpSpeed for bonus
};
// Function to set the winner face for the athlete
self.setFace = function (newFace) {
game.removeChild(self.head);
self.head.destroy();
self.head = null;
self.head = self.trunk.attachAsset(newFace, {
anchorX: 0.5,
anchorY: 2,
scaleX: 1,
scaleY: 1
});
};
});
/****************************************************************************************** */
/************************************** BILLBOARD CLASS ************************************ */
/****************************************************************************************** */
var Billboard = Container.expand(function () {
var self = Container.call(this);
self.billboardGraphics = self.attachAsset('billboard', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 612
});
self.billboardShadow = self.attachAsset('shadow', {
anchorX: 0.5,
anchorY: 0.5,
x: 1024,
y: 1100,
tint: 0x000000,
alpha: 0.5,
width: 1024,
height: 50
});
// Add a message text to the billboard
self.welcomeMessage = "WELCOME TO\r\n \r\n ";
self.messageText = new Text2("", {
// \r\n\r\n AthleSticks!\r\n\r\n Level 1 : Juniors
size: 80,
fill: "#ffffff",
weight: 1000,
dropShadow: true
});
self.messageText.anchor.set(0.5, 0.5); // Center the text horizontally and vertically
self.messageText.x = 1024; // Center horizontally relative to the billboard
self.messageText.y = 200;
self.addChild(self.messageText);
self.messageTextBig = new Text2("AthleSticks!", {
size: 150,
fill: "#ffffff",
weight: 1000,
dropShadow: true
});
self.messageTextBig.anchor.set(0.5, 0.5); // Center the text horizontally and vertically
self.messageTextBig.x = 1024; // Center horizontally relative to the billboard
self.messageTextBig.y = 450;
self.addChild(self.messageTextBig);
self.speedX = globalSpeedPerLine[0];
self._update_migrated = function () {
self.speedX = globalSpeedPerLine[0];
self.x += self.speedX;
// Start or during race
if (!(finishLine && finishLine.isCut)) {
if (self.x < -2048) {
//log("Billboard PAssed.x=" + self.x + "/ a.x=" + athlete.x);
// Push to finishline when passed
self.x = finishLine.x + 256;
}
} else {
// End of race
if (self.x < athlete.x - 600) {
// Follow player
//log("Billboard follow.x=" + self.x + "/ a.x=" + athlete.x);
self.x = athlete.x - 600;
}
}
};
self.setMessage = function (newMessage, bigMessage) {
var nbLineFeeds = (newMessage.match(/\r\n|\r|\n/g) || []).length;
self.messageText.y = 220 + 42 * nbLineFeeds;
self.messageText.setText(newMessage);
if (bigMessage && bigMessage.length > 0) {
self.messageTextBig.visible = true;
self.messageTextBig.setText(bigMessage);
} else {
self.messageTextBig.visible = false;
}
};
self.setLevelMessage = function (go) {
var newMessage = "";
switch (difficultyLevel) {
case 2:
newMessage = "Level 2: Amateurs\r\n \r\n \r\n ";
break;
case 3:
newMessage = "Level 3: Pros\r\n \r\n \r\n";
break;
case 4:
newMessage = "Level 4: Super Heroes\r\n \r\n \r\n ";
break;
default:
newMessage = "Level 1: Juniors\r\n \r\n \r\n ";
break;
}
newMessage += go ? "" : "GET READY!";
self.setMessage(newMessage, go ? " GO!" : "");
};
self.setMessage(self.welcomeMessage, "AthleSticks!");
});
/****************************************************************************************** */
/********************************** BLOCKER CLASS ********************************** */
/****************************************************************************************** */
var Blocker = Container.expand(function (athleteX, groundLevelY) {
var self = Container.call(this);
self.blockerGraphics = self.attachAsset('blocker01', {
anchorX: 0.5,
anchorY: 0.0,
x: athleteX + 2048 + 512,
y: groundLevelY + 75
});
self.speedX = globalSpeedPerLine[0];
self._update_migrated = function () {
self.speedX = globalSpeedPerLine[0];
self.x += self.speedX;
};
});
/****************************************************************************************** */
/********************************** BONUS CLASS ********************************** */
/****************************************************************************************** */
var BonusManager = Container.expand(function () {
var self = Container.call(this);
self.isUsed = false;
self.alphaDirection = -1;
// Bonus asset
self.bonusGraphics = self.attachAsset('bonus1', {
anchorX: 0.5,
anchorY: 0.5,
width: 100,
height: 100,
x: game.width - 150,
y: 75,
visible: false
});
// Update method for animations or effects over time
self._update_migrated = function () {
if (!self.isUsed) {
return;
}
if (LK.ticks % 120) {
self.bonusGraphics.alpha += self.alphaDirection * 0.1;
if (self.bonusGraphics.alpha <= 0 || self.bonusGraphics.alpha >= 1) {
self.alphaDirection *= -1;
}
}
};
self.useBonus = function () {
log("BonusManager useBonus...");
if (self.isUsed) {
log("Alredy used ...");
return;
}
LK.effects.flashScreen(0x00ff00, 500); // Flash screen red for half a second
self.isUsed = true;
drone.useBonus();
athlete.useBonus();
self.bonusGraphics.visible = true;
};
self.endBonus = function () {
log("BonusManager endBonus...");
self.bonusGraphics.visible = false;
self.isUsed = false;
};
});
/****************************************************************************************** */
/************************************** CONFETTI CLASS ************************************ */
/****************************************************************************************** */
var Confetti = Container.expand(function (isFinal) {
var self = Container.call(this);
self.particles = [];
self.particleCount = isFinal ? 30 : 100;
self.getParticle = function (isMedal) {
if (isMedal) {
return self.attachAsset('medal', {
anchorX: 0.5,
anchorY: 0.5,
x: Math.random() * 2048,
y: Math.random() * -2732,
rotation: Math.random() * Math.PI * 2
});
}
return self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5,
width: 10,
height: 30,
scaleX: Math.random() * 0.5 + 0.5,
scaleY: Math.random() * 0.5 + 0.5,
x: Math.random() * 2048,
y: Math.random() * -2732,
rotation: Math.random() * Math.PI * 2,
tint: Math.random() * 0xFFFFFF
});
};
self.spawConfettis = function (isFinal) {
for (var i = 0; i < self.particleCount; i++) {
self.particles.push(self.getParticle(isFinal));
}
};
self._update_migrated = function () {
self.particles.forEach(function (particle) {
particle.y += 10; // Speed of falling
particle.rotation += 0.1; // Rotation speed
if (particle.y > 2732) {
// Reset if it goes below the screen
particle.y = Math.random() * -2732;
}
});
};
self.spawConfettis(isFinal);
});
/****************************************************************************************** */
/************************************** DRONE CLASS ************************************ */
/****************************************************************************************** */
var DeliveryDrone = Container.expand(function () {
var self = Container.call(this);
self.speedX = 0;
self.packetSpeed = 0;
self.entered = false;
self.enterTime = 0;
self.leaveSpeed = 5;
self.packetDropped = false;
self.packetFalling = false;
self.movePacket = false;
self.droneLeaving = false;
// Create layers for packets and drones
self.packetLayer = new Container();
self.droneLayer = new Container();
game.addChild(self.packetLayer);
game.addChild(self.droneLayer);
self.packet = self.packetLayer.attachAsset('packet1', {
anchorX: 0.5,
anchorY: 0.5,
y: 75
});
self.bonus = self.packetLayer.attachAsset('bonus1', {
anchorX: 0.5,
anchorY: 0.5,
y: 75,
visible: false,
isUsed: false
});
self.drone1 = self.droneLayer.attachAsset('drone', {
anchorX: 0.5,
anchorY: 0.5
});
self.drone2 = self.droneLayer.attachAsset('drone2', {
anchorX: 0.5,
anchorY: 0.5
});
self.drone1.visible = true;
self.drone2.visible = false;
self.dropPacket = function () {
if (!self.packetDropped) {
// Make packet fall to the ground
self.packetDropped = true;
self.packetFalling = true;
log("THROW PACKET! self.packet.y < groundLevel : " + self.packet.y + " < " + groundLevel);
}
};
self.packetLayer.down = function (x, y, obj) {
self.dropPacket();
};
// Down works on last layer only ?
self.droneLayer.down = function (x, y, obj) {
self.dropPacket();
};
self._update_migrated = function () {
self.x += self.speedX;
// Reverse direction at screen edges
if (self.x < 2048 - 300) {
self.speedX = 0;
}
if (self.x > 2048 + 512) {
self.speedX = 0;
}
// Toggle visibility every 120 ticks (2 seconds)
if (LK.ticks % 2 == 0) {
self.drone1.visible = !self.drone1.visible;
self.drone2.visible = !self.drone2.visible;
}
// Update packet1 position to simulate falling
if (self.packetFalling && self.packet.y < groundLevel - self.packet.height / 2) {
// Ensure packet stops at ground level
self.packetSpeed += 1;
self.packet.y += self.packetSpeed; // Adjust speed as necessary
if (self.packet.y >= groundLevel - self.packet.height / 2) {
self.packet.y = groundLevel - self.packet.height / 2;
game.removeChild(self.packet);
self.packet.destroy();
// Open the packet
self.packet = self.packetLayer.attachAsset('packet2', {
anchorX: 0.5,
anchorY: 0.5,
y: groundLevel - self.packet.height / 2
});
self.packetFalling = false;
self.movePacket = true;
self.droneLeaving = true;
// Show the bonus
self.bonus = self.packetLayer.attachAsset('bonus1', {
anchorX: 0.5,
anchorY: 0.5,
visible: true,
y: groundLevel - self.packet.height * 1.5,
isUsed: false
});
}
}
self.packetLayer.x = self.x;
self.packetLayer.y = self.y;
self.droneLayer.x = self.x;
self.droneLayer.y = self.y;
if (self.movePacket && self.x + self.packet.x > -512 && athlete.isRunning) {
self.packet.x += globalSpeedPerLine[0];
self.bonus.x += globalSpeedPerLine[0];
}
if (self.droneLeaving) {
if (self.x + self.drone1.x > -512) {
self.leaveSpeed++;
self.drone1.x -= self.leaveSpeed;
self.drone2.x -= self.leaveSpeed;
if (!self.packetDropped) {
self.packet.x -= self.leaveSpeed;
self.bonus.x -= self.leaveSpeed;
}
} else {
self.droneLeaving = false;
}
}
if (self.entered && Date.now() - self.enterTime > droneStaySeconds * 1000) {
self.droneLeaving = true;
}
};
self.prepare = function () {
self.x = 2048 + 300;
self.y = 128;
self.packetSpeed = 0;
self.leaveSpeed = 5;
if (self.packet) {
game.removeChild(self.packet);
self.packet.destroy();
}
self.packet = self.packetLayer.attachAsset('packet1', {
anchorX: 0.5,
anchorY: 0.5,
y: 75
});
self.visible = false;
};
self.enter = function () {
if (!self.entered) {
self.visible = true;
self.speedX = -4;
self.enterTime = Date.now();
self.entered = true;
}
};
self.useBonus = function () {
log("Drone useBonus...");
if (self.bonus && !self.bonus.isUsed) {
self.bonus.isUsed = true;
self.bonus.visible = false;
game.removeChild(self.bonus);
self.bonus.destroy();
}
};
self.clean = function () {
log("Drone clean...");
if (self.bonus) {
game.removeChild(self.bonus);
self.bonus.destroy();
}
if (self.packet) {
game.removeChild(self.packet);
self.packet.destroy();
}
};
});
/****************************************************************************************** */
/************************************** FINISH LINE CLASS ************************************ */
/****************************************************************************************** */
var FinishLine = Container.expand(function () {
var self = Container.call(this);
self.isCut = false;
// End Line
self.endLine = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 1,
x: 35,
y: 0,
width: 20,
height: 1124,
rotation: Math.PI * 0.125,
tint: 0xFFFFFF,
alpha: 1
});
self.rightRod = self.attachAsset('finishFlag', {
anchorX: 0.5,
anchorY: 0.0,
x: 580,
y: -1540
});
self.centralRodRight = self.attachAsset('finishLine', {
anchorX: 0.5,
anchorY: 1,
x: 0,
y: -200,
rotation: Math.PI * 0.125,
tint: 0xC80000,
height: 600
});
self.centralRodRightShadow = self.attachAsset('finishLine', {
anchorX: 0.5,
anchorY: 1,
x: -20,
y: 0,
width: 20,
height: 600,
rotation: Math.PI * 0.125,
tint: 0x000000,
alpha: 0.5
});
self.centralRodLeft = self.attachAsset('finishLine', {
anchorX: 0.5,
anchorY: 0,
x: 455,
y: -1300,
rotation: Math.PI * 2.125,
tint: 0xC80000,
height: 600
});
self.centralRodLeftShadow = self.attachAsset('finishLine', {
anchorX: 0.5,
anchorY: 0,
x: 430,
y: -1080,
width: 20,
height: 600,
rotation: Math.PI * 2.125,
tint: 0x000000,
alpha: 0.5
});
self.leftRod = self.attachAsset('finishFlag', {
anchorX: 0.1,
anchorY: 1.0,
y: 100
});
self.speedX = globalSpeedPerLine[0];
self._update_migrated = function () {
self.speedX = globalSpeedPerLine[0];
self.x += self.speedX;
};
self.cut = function () {
var cutHeight = 30;
var cutInterval = LK.setInterval(function () {
if (self.centralRodRight.height > 0) {
var newHeight = Math.max(0, self.centralRodRight.height - cutHeight);
self.centralRodRight.height = newHeight;
self.centralRodLeft.height = newHeight;
self.centralRodRightShadow.height = newHeight;
self.centralRodLeftShadow.height = newHeight;
} else {
LK.clearInterval(cutInterval);
}
}, 25);
finishLine.isCut = true;
};
});
/****************************************************************************************** */
/************************************** OBSTACLE CLASS ************************************ */
/****************************************************************************************** */
// Obstacle class
var Obstacle = Container.expand(function (line) {
var self = Container.call(this);
self.lineIndex = line;
self.centralRodShadow = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 1,
x: -40,
y: 0,
width: 20,
rotation: Math.PI * 0.135,
tint: 0x000000,
alpha: 0.5
});
self.leftRod = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 1.0,
tint: 0x999999
});
self.rightRod = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.0,
x: 95,
y: -445,
//-460
tint: 0x999999
});
self.centralRod = self.attachAsset('centralRod', {
anchorX: 0.5,
anchorY: 1,
y: -200,
width: 20,
rotation: Math.PI * 0.125,
tint: 0xFFFFFF
});
self.speedX = globalSpeedPerLine[0];
self._update_migrated = function () {
self.speedX = globalSpeedPerLine[0];
self.x += self.speedX;
};
});
/****************************************************************************************** */
/********************************** STARTING BLOCK CLASS ********************************** */
/****************************************************************************************** */
var StartingBlocks = Container.expand(function (athleteX, groundLevelY, line) {
var self = Container.call(this);
self.lines = self.attachAsset('startingBlocks', {
anchorX: 0.5,
anchorY: 0.0,
x: athleteX - 100 - 50,
y: groundLevelY + 75,
height: 200,
rotation: Math.PI * 0.25
});
self.block1 = self.attachAsset('startingBlocks', {
anchorX: 0.5,
anchorY: 1.0,
x: athleteX - 100 - 90,
y: groundLevelY + 105,
width: 60,
height: 30,
rotation: Math.PI * 0.5
});
self.block2 = self.attachAsset('startingBlocks', {
anchorX: 0.5,
anchorY: 1.0,
x: athleteX - 100 - 170,
y: groundLevelY + 170,
width: 60,
height: 30,
rotation: Math.PI * 0.5
});
// Pointer
self.pointer = self.attachAsset('pointer', {
anchorX: 0.5,
anchorY: 0.5,
x: athleteX - 440,
y: groundLevelY + 145,
rotation: 0.03,
visible: line == 0 && currentRoundNumber == 1
});
// Start line
self.startLine = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 1,
width: 20,
height: 255,
x: athleteX - 10,
y: groundLevelY + 330,
rotation: Math.PI * 0.135,
tint: 0xFFFFFF,
alpha: 1
});
self.speedX = globalSpeedPerLine[0];
self._update_migrated = function () {
self.speedX = globalSpeedPerLine[0];
self.x += self.speedX;
};
});
/****************************************************************************************** */
/************************************** TRACK CLASS ************************************ */
/****************************************************************************************** */
var Track = Container.expand(function () {
var self = Container.call(this);
// Attach track asset
self.trackAsset = self.attachAsset('track', {
anchorX: 0.0,
anchorY: 0.0
});
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB // Light blue background to represent the sky
});
/****
* Game Code
****/
/****************************************************************************************** */
/************************************** GLOBAL VARIABLES ********************************** */
/****************************************************************************************** */
// Enumeration for game states
var GAME_STATE = {
INIT: 'INIT',
MENU: 'MENU',
HELP: 'HELP',
STARTING: 'STARTING',
NEW_ROUND: 'NEW_ROUND',
PLAYING: 'PLAYING',
SCORE: 'SCORE'
};
var gameState = GAME_STATE.INIT;
var athlete;
var opponents = [];
var numberOfOpponents = 4; // Adjust based on the actual number of opponents in the game
var groundLevel = 2732 - 200; // Ground level set 200px from the bottom
var linesGroundLevels = []; // Ground level set 200px from the bottom for each line
var numberOfLines = 1 + numberOfOpponents;
var numberOfObstacles = 2; //20; // Total number of hurdles
var obstacles = [[]];
var obstacleSpawnTicker = 0;
var obstacleSpawnDistanceRate = 1.5; // Ratio of screen width between obstacles
var obstacleJustPassed = false;
var nbBonusJumps = 3;
var startingBlocks = [];
var blockers = [];
var startX = 600;
var lastRunTime = 0;
var aiJumpLeftLimit = -350;
var aiJumpRightLimit = 2048 + 512;
var finishLine;
var stadium;
var stadiumOdd;
var field;
var fieldOdd;
var farField;
var farFieldOdd;
var track;
var scoreTxt;
var drone;
var bonusManager;
var currentFinishPosition = 1;
var difficultyLevel = 1;
var difficultyLevelMax = 4;
var globalBaseSpeed = -20 - 3 * difficultyLevel * difficultyLevel;
var globalSpeedPerLine = []; // Global speed for tracks and obstacles default -10 / min = -4
var speedGauge = 0;
var raceFinishTime = 0;
var raceStartTime = 0;
var currentRoundNumber = 1;
var droneEnrtyObstacleNumber = Math.max(5, 10 - difficultyLevel);
var droneStaySeconds = 3;
var colorsArray = [0x1188FF,
// Blue
0xFF0000,
// Red
0x00FF00,
// Green
0xFFFF00,
// Yellow
0xFF00FF,
// Magenta
0x00FFFF,
// Cyan
0x800000,
// Maroon
0x808000,
// Olive
0x800080,
// Purple
0x008080 // Teal;
];
var currentRoundColors = [];
var isDebug = true;
var debugMarker;
// UI
var hintMobileText;
var startText;
var startButton;
var runButton;
var jumpButton;
var runButtonHelpText;
var jumpButtonHelpText;
var billboard;
var confettisAnim;
/****************************************************************************************** */
/************************************** POSTURES ****************************************** */
/****************************************************************************************** */
var idlePosture = {
name: "idlePosture",
trunk: {
x: 0,
y: 0,
r: 0
},
head: {
x: 0,
y: 0,
r: 0
},
rightArm: {
x: 20,
y: -80,
r: Math.PI * 2 * -0.025
},
leftArm: {
x: -20,
y: -80,
r: Math.PI * 2 * 0.025
},
rightLeg: {
x: 5,
y: 100,
h: 200,
r: Math.PI * 2 * -0.025
},
leftLeg: {
x: -5,
y: 100,
h: 200,
r: Math.PI * 2 * 0.025
},
shadow: {
x: 0,
y: 300,
h: 100,
r: 0
}
};
var startingBlockPosture = {
name: "startingBlockPosture",
trunk: {
x: 0,
y: 0,
r: Math.PI * 0.35
},
head: {
x: 35,
y: 0,
r: Math.PI * -0.10
},
rightArm: {
x: 15,
y: -60,
r: Math.PI * 2 * -0.15
},
leftArm: {
x: -15,
y: -60,
r: Math.PI * 2 * -0.15
},
rightLeg: {
x: 10,
y: 80,
h: 150,
r: Math.PI * 2 * -0.15
},
leftLeg: {
x: -10,
y: 80,
h: 200,
r: Math.PI * 2 * -0.05
},
shadow: {
x: 140,
y: 300,
h: 230,
r: 0
}
};
/****************************************************************************************** */
/*********************************** UTILITY FUNCTIONS ************************************ */
/****************************************************************************************** */
function log() {
if (isDebug) {
var _console;
(_console = console).log.apply(_console, arguments);
}
}
/****************************************************************************************** */
/************************************** INPUT HANDLERS ************************************ */
/****************************************************************************************** */
// Touch event to make the athlete jump
game.on('down', function (x, y, obj) {
switch (gameState) {
case GAME_STATE.MENU:
gameMenuDown(x, y, obj);
break;
case GAME_STATE.STARTING:
gameStartingDown(x, y, obj);
break;
case GAME_STATE.PLAYING:
gamePlayingDown(x, y, obj);
break;
case GAME_STATE.SCORE:
// Handle score display logic here
break;
}
});
function gameMenuDown(x, y, obj) {
log("gameMenuDown...");
cleanMenuState();
initStartingState();
}
function gameStartingDown(x, y, obj) {
cleanStartingState();
initPlayingState();
}
function gamePlayingDown(x, y, obj) {
log("gamePlayingDown ...");
if (!finishLine.isCut) {
if (athlete.isRunning && !athlete.isFalling) {
var speedGaugeDelta = 0;
if (x >= game.width / 2) {
lastRunTime = Date.now();
// Make the athlete run
speedGaugeDelta = speedGauge ? 0.1 : 0.6;
speedGauge = Math.min(1.1, speedGauge + speedGaugeDelta);
globalSpeedPerLine[0] = globalBaseSpeed * speedGauge;
animateButtonPress(runButton);
} else {
speedGaugeDelta = speedGauge ? 0.05 : 0.1;
speedGauge = Math.min(1.1, speedGauge + speedGaugeDelta);
globalSpeedPerLine[0] = globalBaseSpeed * speedGauge;
// Make the athlete jump
athlete.jump();
animateButtonPress(jumpButton);
}
}
} else {
log("gamePlayingDown : raceFinishTime : " + raceFinishTime + " => " + (Date.now() - raceFinishTime));
if (Date.now() - raceFinishTime > 1000) {
if (difficultyLevel == difficultyLevelMax && athlete.finishPosition == 1) {
LK.showGameOver();
} else {
// Next round
cleanPlayingState();
initMenuState();
}
}
}
}
/****************************************************************************************** */
/************************************* AI FUNCTIONS *************************************** */
/****************************************************************************************** */
/****************************************************************************************** */
/************************************* GAME STATES **************************************** */
/****************************************************************************************** */
// GAME INITIALIZE
function gameInitialize() {
log("Game initialize...");
// Add background asset
var background = LK.getAsset('background', {
anchorX: 0.0,
anchorY: 0.0,
y: -256,
visible: true
});
game.addChild(background);
var fieldBackground = LK.getAsset('fieldBackground', {
anchorX: 0.0,
anchorY: 0.0,
x: -50,
y: 2732 - 1024 - 600
});
game.addChild(fieldBackground);
var farFieldBackground = LK.getAsset('farFieldBackground', {
anchorX: 0.0,
anchorY: 0.0,
x: -50,
y: 2732 - 1024 - 600 - 512
});
game.addChild(farFieldBackground);
field = LK.getAsset('field', {
anchorX: 0.0,
anchorY: 0.0,
x: -50,
y: 2732 - 1024 - 600,
visible: true
});
game.addChild(field);
fieldOdd = LK.getAsset('fieldOdd', {
anchorX: 0.0,
anchorY: 0.0,
x: 2048 + 50,
y: 2732 - 1024 - 600
});
game.addChild(fieldOdd);
farField = LK.getAsset('farField', {
anchorX: 0.0,
anchorY: 0.0,
x: -50,
y: 2732 - 1024 - 600 - 512,
visible: true
});
game.addChild(farField);
farFieldOdd = LK.getAsset('farFieldOdd', {
anchorX: 0.0,
anchorY: 0.0,
x: 2048 + 50,
y: 2732 - 1024 - 600 - 512
});
game.addChild(farFieldOdd);
stadium = LK.getAsset('stadium', {
anchorX: 0.0,
anchorY: 0.0,
x: -50,
y: 150
});
game.addChild(stadium);
stadiumOdd = LK.getAsset('stadiumOdd', {
anchorX: 0.0,
anchorY: 0.0,
x: 2048 + 50,
y: 150
});
game.addChild(stadiumOdd);
// Initialize track using Track class
track = game.addChild(new Track());
track.x = -50;
track.y = 2732 - 1024;
// Track lines
for (var i = 0; i < 6; i++) {
var trackLine = LK.getAsset('trackLine', {
anchorX: 0.0,
anchorY: 0.0,
x: 0,
y: groundLevel + 400 - 215 * i
});
game.addChild(trackLine);
}
billboard = game.addChild(new Billboard());
bonusManager = game.addChild(new BonusManager());
hintMobileText = new Text2('Best on Mobile', {
size: 90,
fill: "#FFFFFF",
weight: 1000,
dropShadow: true
});
hintMobileText.anchor.set(0.5, 0.5); // Center the text horizontally and vertically
hintMobileText.x = 2048 / 2; // Center horizontally
hintMobileText.y = 2732 - 120; // Center vertically
hintMobileText.rotation = -0.2;
game.addChild(hintMobileText);
gameState = GAME_STATE.MENU;
initMenuState();
if (isDebug) {
// Debug Marker
debugMarker = LK.getAsset('debugMarker', {
anchorX: 0.5,
anchorY: 0.5
});
game.addChild(debugMarker);
debugMarker.x = 0;
debugMarker.y = groundLevel;
}
}
// GAME MENU
function initMenuState() {
log("initMenuState...");
// Add Button in the center of the screen
startButton = LK.getAsset('button', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 2,
y: 2732 / 2
});
game.addChild(startButton);
// Add Start Text in the center of the screen
startText = new Text2(currentRoundNumber == 1 ? 'START' : 'NEXT', {
size: 200,
fill: "#ffffff",
weight: 600
});
startText.anchor.set(0.5, 0.5); // Center the text horizontally and vertically
startText.x = 2048 / 2; // Center horizontally
startText.y = 2732 / 2; // Center vertically
game.addChild(startText);
// Reset fields and stadium
field.x = -50;
fieldOdd.x = 2048 + 50;
farField.x = -50;
farFieldOdd.x = 2048 + 50;
stadium.x = -50;
stadiumOdd.x = 2048 + 50;
setRoundColors();
gameState = GAME_STATE.MENU;
}
function menuHandling() {
if (drone) {
drone._update_migrated();
}
}
function cleanMenuState() {
game.removeChild(startText);
startText.destroy();
game.removeChild(startButton);
startButton.destroy();
if (hintMobileText) {
game.removeChild(hintMobileText);
hintMobileText.destroy();
hintMobileText = null;
}
}
// STARTING
function initStartingState() {
log("initStartingState...");
// Initialize ground levels & speeds
for (var line = 0; line < numberOfLines; line++) {
linesGroundLevels[line] = groundLevel - 200 - 220 * line;
globalSpeedPerLine[line] = globalBaseSpeed * (line ? 1.0 : speedGauge);
}
// Spawn obstacles
spawnObstacles();
// Add starting blocks to each athlete's starting position
for (var i = 0; i <= numberOfOpponents; i++) {
startingBlocks[i] = new StartingBlocks(startX + 10 + 100 * i, linesGroundLevels[i] + 145, i);
game.addChild(startingBlocks[i]);
}
// Initialize opponents
for (var i = numberOfOpponents - 1; i >= 0; i--) {
opponents[i] = game.addChild(new Athlete(i + 1));
opponents[i].visible = false;
opponents[i].restore();
opponents[i].isRunning = false;
opponents[i].y += 120;
opponents[i].setPosture(startingBlockPosture);
}
// Initialize athlete
athlete = game.addChild(new Athlete(0));
athlete.visible = false;
athlete.restore();
athlete.isRunning = false;
athlete.setPosture(startingBlockPosture);
athlete.y += 120;
runButton = LK.getAsset('runButton', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 1,
x: 2048 * 3 / 4,
y: 2732 / 3,
tint: 0xcccccc,
visible: false
});
game.addChild(runButton);
// Add second semi-transparent runButton in the upper half of the screen
jumpButton = LK.getAsset('runButton', {
anchorX: 0.5,
anchorY: 0.5,
x: 2048 / 4,
y: 2732 / 3,
alpha: 1,
tint: 0xcccccc,
rotation: -1 * Math.PI / 2,
visible: false
});
game.addChild(jumpButton);
// Buttons help texts
runButtonHelpText = new Text2('Run', {
size: 100,
fill: "#333333"
});
runButtonHelpText.anchor.set(0.5, 0.5);
runButtonHelpText.x = 365;
runButtonHelpText.y = -80;
LK.gui.center.addChild(runButtonHelpText);
jumpButtonHelpText = new Text2('Jump', {
size: 100,
fill: "#333333"
});
jumpButtonHelpText.anchor.set(0.5, 0.5);
jumpButtonHelpText.x = -365;
jumpButtonHelpText.y = -80;
LK.gui.center.addChild(jumpButtonHelpText);
showRunButtons();
billboard.setLevelMessage();
gameState = GAME_STATE.STARTING;
}
function cleanStartingState() {
for (var i = numberOfOpponents - 1; i >= 0; i--) {
opponents[i].setPosture(idlePosture, true);
opponents[i].isRunning = true;
}
athlete.setPosture(idlePosture, true);
athlete.isRunning = true;
runButton.alpha = 0.5;
jumpButton.alpha = 0.5;
runButtonHelpText.visible = false;
jumpButtonHelpText.visible = false;
}
function gameStarting() {
//log("gameStarting...");
for (var i = 0; i <= numberOfOpponents; i++) {
var athletes = [athlete].concat(opponents);
athletes[i]._update_migrated();
}
if (drone) {
drone._update_migrated();
}
}
// PLAYING
function initPlayingState() {
scoreTxt = new Text2("0/" + numberOfObstacles.toString(), {
size: 80,
fill: "#FFFFFF"
});
scoreTxt.anchor.set(0.25, 0.125); // Center the score text horizontally
LK.gui.top.addChild(scoreTxt); // Add the score text to the GUI overlay at the top center
currentFinishPosition = 1;
// Remove starting blocks offset
for (var i = numberOfOpponents - 1; i >= 0; i--) {
opponents[i].y -= 120;
}
athlete.y -= 120;
speedGauge = 0.6;
globalSpeedPerLine[0] = globalBaseSpeed * speedGauge;
drone = new DeliveryDrone(); // false for odd drone graphic
drone.prepare();
bonusManager.endBonus();
game.addChild(drone);
raceStartTime = Date.now();
billboard.setLevelMessage(true);
gameState = GAME_STATE.PLAYING;
}
function gamePlaying() {
//log("Game playing...");
// Update fields
updateFields();
// Update obstacles
updateObstacles();
// Update Progress
updateProgress();
// Update opponents
for (var i = numberOfOpponents - 1; i >= 0; i--) {
opponents[i]._update_migrated();
game.addChild(opponents[i]);
}
// Update athlete
athlete._update_migrated();
game.addChild(athlete);
// Check for collisions
checkForCollisions();
// Update drone
drone._update_migrated();
bonusManager._update_migrated();
if (confettisAnim) {
confettisAnim._update_migrated();
}
}
function cleanPlayingState() {
// Increment round number
currentRoundNumber++;
// Increase difficulty if the athlete has won the race
if (athlete.finishPosition == 1) {
difficultyLevel++;
if (difficultyLevel > difficultyLevelMax) {
// TODO SWITCH TO SCORE STATE
LK.showGameOver();
}
globalBaseSpeed = -20 - 3 * difficultyLevel * difficultyLevel;
}
// Set level message
billboard.setLevelMessage();
// Remove Opponents & Athlete
for (var i = numberOfOpponents - 1; i >= 0; i--) {
game.removeChild(opponents[i]);
opponents[i].destroy();
}
game.removeChild(athlete);
athlete.destroy();
// Remove Obstacles
for (var i = 0; i < numberOfLines; i++) {
for (var j = 0; j < obstacles[i].length; j++) {
if (obstacles[i][j]) {
game.removeChild(obstacles[i][j]);
obstacles[i][j].destroy();
}
}
}
// Remove Drone
if (drone) {
drone.clean();
game.removeChild(drone);
drone.destroy();
}
// Remove Finish Line
game.removeChild(finishLine);
finishLine.destroy();
// Remove Score Text
LK.gui.top.removeChild(scoreTxt);
scoreTxt.destroy();
if (confettisAnim) {
game.removeChild(confettisAnim);
confettisAnim.destroy();
confettisAnim = null;
}
}
/****************************************************************************************** */
/************************************** GAME FUNCTIONS ************************************ */
/****************************************************************************************** */
function updateFields() {
if (!(athlete.isFalling && athlete.isOnGround)) {
field.x += globalSpeedPerLine[0];
var offset = 20;
//log("Field Decalage :" + globalSpeedPerLine[0]);
// Reset position to create a continuous effect
if (field.x <= -2048 - offset) {
field.x = 2048 + offset;
}
fieldOdd.x += globalSpeedPerLine[0];
// Reset position to create a continuous effect
if (fieldOdd.x <= -2048 - offset) {
fieldOdd.x = 2048 + offset;
}
// Far field
farField.x += globalSpeedPerLine[0] * 0.8;
// Reset position to create a continuous effect
if (farField.x <= -2048 - offset) {
farField.x = 2048 + offset;
}
farFieldOdd.x += globalSpeedPerLine[0] * 0.8;
// Reset position to create a continuous effect
if (farFieldOdd.x <= -2048 - offset) {
farFieldOdd.x = 2048 + offset;
}
stadium.x += globalSpeedPerLine[0] * 0.4;
// Reset position to create a continuous effect
if (stadium.x <= -2048 - offset) {
stadium.x = 2048 + offset;
}
stadiumOdd.x += globalSpeedPerLine[0] * 0.4;
// Reset position to create a continuous effect
if (stadiumOdd.x <= -2048 - offset) {
stadiumOdd.x = 2048 + offset;
}
}
}
function spawnObstacles() {
// Set obstacles for line 0
obstacles[0] = [];
for (var i = 0; i < numberOfObstacles; i++) {
// 3 obstacles per line
var newObstacle = new Obstacle(0);
newObstacle.x = 2048 * obstacleSpawnDistanceRate * (i + 1); // Start from the right edge
//log("Obstacle "+ 0+","+i+" +> x="+ newObstacle.x);
newObstacle.y = groundLevel + 200;
obstacles[0].push(newObstacle);
game.addChild(newObstacle);
}
// Set obstacles for other lines
for (var line = 1; line < numberOfLines; line++) {
obstacles[line] = [];
for (var i = 0; i < numberOfObstacles; i++) {
// 3 obstacles per line
var newObstacle = new Obstacle(line);
newObstacle.x = obstacles[0][i].x + 100 * line; // Start from the right edge
//log("Obstacle "+ line+","+i+" +> x="+ newObstacle.x);
newObstacle.y = groundLevel + 200 - 212 * line; //- 220 * line;
obstacles[line].push(newObstacle);
game.addChild(newObstacle);
}
}
finishLine = new FinishLine();
finishLine.x = 2048 * obstacleSpawnDistanceRate * (numberOfObstacles + 1) + 1024; // Start from the right edge
finishLine.y = groundLevel + 200;
game.addChild(finishLine);
}
function updateObstacles() {
// Update obstacles
for (var line = 0; line < numberOfLines; line++) {
obstacles[line] = obstacles[line] || [];
for (var i = obstacles[line].length - 1; i >= 0; i--) {
obstacles[line][i]._update_migrated();
// Remove obstacle if it moves off-screen and increase global speed
if (obstacles[line][i].x < -100) {
// Assuming obstacle width is less than 100px
obstacles[line][i].destroy();
obstacles[line].splice(i, 1);
if (line == 0) {
obstacleJustPassed = true;
}
}
}
}
for (var i = 0; i <= numberOfOpponents; i++) {
startingBlocks[i]._update_migrated();
if (startingBlocks[i].x < -1024) {
game.removeChild(startingBlocks[i]);
startingBlocks[i].destroy();
}
}
for (var line = 0; line < numberOfLines; line++) {
if (blockers[line]) {
blockers[line]._update_migrated();
}
}
finishLine._update_migrated();
billboard._update_migrated();
}
function updateProgress() {
if (obstacleJustPassed && !finishLine.isCut) {
// Update Progress
obstacleJustPassed = false;
scoreTxt.setText(numberOfObstacles - obstacles[0].length + "/" + numberOfObstacles); // Update score text with prefix
}
for (var line = 0; line < numberOfLines; line++) {
if (line == 0 && !athlete.finishPosition && athlete.x > finishLine.x) {
athlete.finishPosition = getFinishPosition();
log("Finish athlete => #" + athlete.finishPosition);
}
if (line > 0 && opponents[line - 1].x > finishLine.x) {
if (!opponents[line - 1].finishPosition) {
opponents[line - 1].finishPosition = getFinishPosition();
log("Finish opponent " + line + "=> #" + opponents[line - 1].finishPosition);
opponents[line - 1].finishStandingOffset = 512 * Math.random();
}
// Don't go to far after finish
var finishStandingPosition = finishLine.x + 2048 - 200 - (opponents[line - 1].finishStandingOffset || 0);
opponents[line - 1].x = Math.min(finishStandingPosition, opponents[line - 1].x);
if (opponents[line - 1].x >= finishStandingPosition) {
// Stop running when reach final position
opponents[line - 1].isRunning = false;
}
}
}
if (finishLine.isCut) {
for (var line = 0; line < numberOfLines; line++) {
if (line > 0 && opponents[line - 1].x < finishLine.x + 512) {
continue;
}
// Handle stoping slowly when AI is late
if (globalSpeedPerLine[line] < -1) {
globalSpeedPerLine[line] += 0.5 + 0.5 * Math.random(); // Decrement speed gradually
} else if (globalSpeedPerLine[line] > 1) {
globalSpeedPerLine[line] -= 0.5 + 0.5 * Math.random(); // Increment speed gradually if somehow above 0
} else {
globalSpeedPerLine[line] = 0; // Set to 0 if very close to it
if (line == 0 && athlete.isRunning) {
athlete.isRunning = false;
athlete.stop();
}
if (line > 0 && opponents[line - 1].isRunning) {
log("Opop #" + line + " stopping at " + opponents[line - 1].x);
opponents[line - 1].isRunning = false;
opponents[line - 1].stop();
}
}
}
} else if (finishLine.x - athlete.x > 2048) {
// Handle stoping when AI has big advance
for (var line = 1; line < numberOfLines; line++) {
if (line == 1) {
//log("Check finish for #3 at x=" + opponents[line - 1].x + " vs " + (finishLine.x + 512));
}
if (opponents[line - 1].x > finishLine.x + 512 && opponents[line - 1].isRunning) {
if (line == 1) {
log("OK #3 Passed the line !");
}
// stop opponents at finish
log("Stoping #" + line + " at " + opponents[line - 1].x + " > " + finishLine.x);
opponents[line - 1].isRunning = false;
opponents[line - 1].stop();
globalSpeedPerLine[line] = 0;
}
}
}
if (drone && !drone.entered && difficultyLevel > 1 && numberOfObstacles - obstacles[0].length > droneEnrtyObstacleNumber) {
drone.enter();
}
}
function checkForCollisions() {
if (finishLine.isCut) {
return;
}
for (var line = 0; line < numberOfLines; line++) {
obstacles[line] = obstacles[line] || [];
if (obstacles[line].length) {
var obstacle = obstacles[line][0]; // Check only the first obstacle in the line
if (line === 0) {
if (athlete.intersects(obstacle.centralRod)) {
athlete.isFalling = true;
athlete.isOnGround = false;
}
} else {
if (opponents[line - 1].x > aiJumpLeftLimit && opponents[line - 1].x < aiJumpRightLimit && opponents[line - 1].intersects(obstacle.centralRod)) {
log("Line " + line + " : opponent " + opponents[line - 1].lineIndex + " collided with obstacle " + obstacle.lineIndex);
opponents[line - 1].isFalling = true;
opponents[line - 1].isOnGround = false;
}
if (blockers[line] && opponents[line - 1].isRunning && opponents[line - 1].head.intersects(blockers[line])) {
var blocker = blockers[line];
log("Line " + line + " : opponent " + opponents[line - 1].lineIndex + " collided with Blocker ", blocker);
LK.effects.flashScreen(0xff0000, 500); // Flash screen red for half a second
opponents[line - 1].isRunning = false;
opponents[line - 1].isBlocked = true;
(function (line) {
LK.setTimeout(function () {
log("Line " + line + " : opponent " + opponents[line - 1].lineIndex + " Restore after block");
game.removeChild(blockers[line]);
blockers[line].destroy();
blockers.splice(line, 1);
opponents[line - 1].isRunning = true;
opponents[line - 1].isBlocked = false;
opponents[line - 1].restore();
}, 2000); // Delay game over to allow fall animation to be seen
})(line);
}
}
}
}
handleFallEvent();
if (!obstacles[0].length && !finishLine.isCut) {
var isReached = athlete.x > finishLine.x;
if (isReached) {
raceFinishTime = Date.now();
var raceTime = (raceFinishTime - raceStartTime) / 1000;
if (raceTime >= 100) {
raceTime = 99.99;
}
var raceTimeText = raceTime.toFixed(2) + " sec";
raceTimeText = raceTime < 10 ? "0" + raceTimeText : raceTimeText;
var rankText = "Rank: #" + athlete.finishPosition + "\r\n";
var isFinal = difficultyLevel == difficultyLevelMax;
if (isFinal && athlete.finishPosition == 1) {
rankText = rankText + "WINNER!";
} else {
rankText = athlete.finishPosition == 1 ? " " + rankText + "QUALIFIED!" : " " + rankText + "Not qualified";
}
scoreTxt.setText("");
finishLine.isCut = true;
finishLine.cut();
billboard.setMessage(rankText, raceTimeText);
bonusManager.endBonus();
if (drone) {
drone.clean();
}
if (athlete.finishPosition == 1) {
confettisAnim = game.addChild(new Confetti(isFinal));
} else {
for (var i = numberOfOpponents - 1; i >= 0; i--) {
if (opponents[i].finishPosition == 1) {
opponents[i].setFace('winnerFace');
} else {
opponents[i].setFace('loserFace');
}
}
}
hideRunButtons();
}
}
// Take Bonus
if (drone && drone.bonus && !drone.bonus.isUsed && athlete.trunk.intersects(drone.bonus)) {
log("bonusManager useBonus...");
bonusManager.useBonus();
}
}
function handleFallEvent() {
if (athlete.isFalling && !athlete.hasFlashed) {
LK.effects.flashScreen(0xaaaaaa, 500); // Flash screen red for half a second
athlete.hasFlashed = true;
globalSpeedPerLine[0] /= 1.5;
}
if (athlete.hasFlashed && !athlete.hasFlashedGround && athlete.isFalling && athlete.isOnGround) {
LK.effects.flashScreen(0xff0000, 500); // Flash screen red for half a second
athlete.hasFlashedGround = true;
globalSpeedPerLine[0] = 0;
LK.setTimeout(function () {
globalSpeedPerLine[0] = globalBaseSpeed * speedGauge;
athlete.restore();
}, 1000); // Delay game over to allow fall animation to be seen
}
opponents.forEach(function (opponent, index) {
if (opponent.isFalling && !opponent.hasFlashed && !opponent.isOnGround) {
opponent.hasFlashed = true;
LK.setTimeout(function () {
globalSpeedPerLine[opponent.lineIndex] = globalBaseSpeed;
opponent.restore();
}, 2000);
}
});
}
function getFinishPosition() {
return currentFinishPosition++;
}
function setRoundColors() {
currentRoundColors = [0x1188FF]; // Fixed color for the player
var usedColors = [0x1188FF]; // Initialize with player's color to avoid repetition
for (var i = 1; i <= numberOfOpponents; i++) {
var color;
do {
color = colorsArray[Math.floor(Math.random() * colorsArray.length)];
} while (usedColors.includes(color)); // Ensure unique color selection
currentRoundColors.push(color);
usedColors.push(color);
}
}
function showRunButtons() {
runButton.visible = true;
jumpButton.visible = true;
if (currentRoundNumber == 1) {
// show help texts only 1st time
runButtonHelpText.visible = true;
jumpButtonHelpText.visible = true;
} else {
runButtonHelpText.visible = false;
jumpButtonHelpText.visible = false;
}
}
function hideRunButtons() {
runButton.visible = false;
jumpButton.visible = false;
runButtonHelpText.visible = false;
jumpButtonHelpText.visible = false;
}
function animateButtonPress(button) {
// Animate runButton to simulate a button press
button.width = 512 * 0.95;
button.height = 512 * 0.95;
button.alpha = 0.8;
LK.setTimeout(function () {
button.width = 512;
button.height = 512;
button.alpha = 0.6;
}, 100);
}
/****************************************************************************************** */
/************************************** MAIN GAME LOOP ************************************ */
/****************************************************************************************** */
LK.on('tick', function () {
switch (gameState) {
case GAME_STATE.MENU:
menuHandling();
break;
case GAME_STATE.STARTING:
gameStarting();
break;
case GAME_STATE.PLAYING:
gamePlaying();
break;
case GAME_STATE.SCORE:
// Handle score display logic here
break;
}
});
gameInitialize();
Elongated elipse with black top half and white bottom half.
full close and front view of empty stands. retro gaming style
delete
delete
Basquettes à ressort futuriste. vue de profile. Retro gaming style
a blue iron man style armor flying. Retro gaming style
a blue iron man style armor flying horizontally. Retro gaming style
round button with a big "up" arrow icon and a small line under it. UI
A big black horizontal arrow pointing left with centred text 'YOU' in capital letters, painted on an orange floor.. horizontal and pointing left
remove
gold athletics medal with ribbon. retro gaming style
a black oval with a crying smiley face.