User prompt
Interesante actualización, pero no debe tomar en cuenta la ubicación del puntero, que al saltar solo tome en cuenta su ubicación y que debe saltar al muro opuesto al que se encuentra de manera perpendicular
User prompt
Pero el problema es que el stickman al hacer su salto lo hace desde el centro del muro en el que se encuentra cuando debería ser desde la ubicación desde donde se encuentra
Code edit (1 edits merged)
Please save this source code
User prompt
Wall Runner Impact
Initial prompt
Quiero que realices un juego de la siguiente manera, un stickman puede correr por las paredes del marco de la pantalla, puede saltar pero cuando usa su habilidad salta al lado opuesto de la pantalla volando a gran velocidad y generando un impacto sobre el marco, hay una bolita brillante que rebota por las paredes volando de manera aleatoria, cuando el stickman la toca se repara el terreno, sin embargo, el stickman cada vez se le acaba el terreno conforme lo va destruyendo
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Track number of times orbs have been generated
var Orb = Container.expand(function () {
var self = Container.call(this);
var orbGraphics = self.attachAsset('orb', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = (Math.random() - 0.5) * 16;
self.velocityY = (Math.random() - 0.5) * 16;
// Start random rotation animation
self.startRotation = function () {
var rotationDirection = Math.random() < 0.5 ? 1 : -1; // Random clockwise or counterclockwise
var rotationSpeed = (Math.random() * 2 + 1) * rotationDirection; // Random speed between 1-3, with direction
var currentRotation = orbGraphics.rotation;
var targetRotation = currentRotation + Math.PI * 2 * rotationSpeed; // Full rotation in chosen direction
tween(orbGraphics, {
rotation: targetRotation
}, {
duration: 2000 + Math.random() * 3000,
// Random duration between 2-5 seconds
easing: tween.linear,
onFinish: function onFinish() {
self.startRotation(); // Continue rotating with new random parameters
}
});
};
self.update = function () {
// Store previous position
var prevX = self.x;
var prevY = self.y;
self.x += self.velocityX;
self.y += self.velocityY;
// Check collision with terrain blocks
var orbRadius = 48; // Approximate orb radius
var collided = false;
for (var i = 0; i < terrainBlocks.length; i++) {
var block = terrainBlocks[i];
var blockHalfWidth = 92; // terrain block half width
var blockHalfHeight = 30.56; // terrain block half height
// Check if orb intersects with terrain block
var dx = Math.abs(self.x - block.x);
var dy = Math.abs(self.y - block.y);
if (dx < orbRadius + blockHalfWidth && dy < orbRadius + blockHalfHeight) {
// Collision detected - determine which side to bounce off
var overlapX = orbRadius + blockHalfWidth - dx;
var overlapY = orbRadius + blockHalfHeight - dy;
if (overlapX < overlapY) {
// Bounce horizontally
if (Math.random() < 0.1) {
// 10% chance to change direction randomly
self.velocityX = (Math.random() - 0.5) * 16;
} else {
// Normal bounce
self.velocityX *= -1;
}
// Push orb out of collision
if (self.x < block.x) {
self.x = block.x - blockHalfWidth - orbRadius;
} else {
self.x = block.x + blockHalfWidth + orbRadius;
}
} else {
// Bounce vertically
if (Math.random() < 0.1) {
// 10% chance to change direction randomly
self.velocityY = (Math.random() - 0.5) * 16;
} else {
// Normal bounce
self.velocityY *= -1;
}
// Push orb out of collision
if (self.y < block.y) {
self.y = block.y - blockHalfHeight - orbRadius;
} else {
self.y = block.y + blockHalfHeight + orbRadius;
}
}
collided = true;
break; // Only handle one collision per frame
}
}
// Bounce off screen boundaries as fallback (if no terrain collision)
if (!collided) {
if (self.x <= 40 || self.x >= 2008) {
if (Math.random() < 0.1) {
// 10% chance to change direction
self.velocityX *= -1;
} else {
// Always reverse velocity to prevent wall penetration
self.velocityX *= -1;
}
self.x = Math.max(40, Math.min(2008, self.x));
}
if (self.y <= 40 || self.y >= 2692) {
if (Math.random() < 0.1) {
// 10% chance to change direction
self.velocityY *= -1;
} else {
// Always reverse velocity to prevent wall penetration
self.velocityY *= -1;
}
self.y = Math.max(40, Math.min(2692, self.y));
}
}
};
// Start rotation animation when orb is created
self.startRotation();
return self;
});
var Stickman = Container.expand(function () {
var self = Container.call(this);
var stickmanGraphics = self.attachAsset('STICKMAN', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.wall = 'bottom'; // current wall: 'top', 'right', 'bottom', 'left'
self.wallProgress = 0; // progress along current wall (0-1)
self.isJumping = false;
self.jumpStartWall = '';
self.jumpStartProgress = 0;
self.jumpProgress = 0;
self.jumpTargetWall = null; // Target wall for redirected jumps
self.update = function () {
if (self.isJumping) {
// Apply acceleration when approaching destination wall
var baseSpeed = 0.04;
var accelerationFactor = 1;
// Apply slow motion in the center of the jump (40%-60%)
if (self.jumpProgress >= 0.4 && self.jumpProgress <= 0.6) {
// Calculate how close we are to the center (0.4 to 0.6 maps to 1.0 to 0.0 to 1.0)
var centerDistance = Math.abs(0.5 - self.jumpProgress) / 0.1; // 0 at center, 1 at edges
var slowMotionFactor = 0.4875 + centerDistance * 0.5125; // 0.4875x speed at center (25% more slow motion), 1.0x at edges
accelerationFactor *= slowMotionFactor;
}
// Start accelerating when past 60% of the jump
if (self.jumpProgress > 0.6) {
// Calculate how close we are to the end (0.6 to 1.0 maps to 0.0 to 1.0)
var accelerationProgress = (self.jumpProgress - 0.6) / 0.4;
// Apply quadratic acceleration (starts slow, gets faster)
accelerationFactor = 1 + accelerationProgress * accelerationProgress * 1;
}
self.jumpProgress += baseSpeed * accelerationFactor;
var startPos;
if (self.jumpStartWall === 'current') {
// Use current position as start for redirected jumps
startPos = {
x: self.x,
y: self.y
};
} else {
startPos = self.getWallPosition(self.jumpStartWall, self.jumpStartProgress);
}
if (self.isParabolicJump) {
// Parabolic jump - elevate relative to current wall
var targetWall = self.jumpTargetWall || self.getOppositeWall(self.jumpStartWall);
var endPos = self.getWallPosition(targetWall, 0.5);
// Calculate base trajectory
var linearX = startPos.x + (endPos.x - startPos.x) * self.jumpProgress;
var linearY = startPos.y + (endPos.y - startPos.y) * self.jumpProgress;
// Add parabolic elevation with tween easing
var parabolaHeight = 400; // Maximum height of parabola
var elevationFactor = tween.easeInOut(self.jumpProgress);
var parabolicOffset = parabolaHeight * (1 - Math.pow(2 * self.jumpProgress - 1, 2));
// Apply elevation perpendicular to the wall direction
var wallDirection = self.getWallDirection(self.jumpStartWall);
self.x = linearX + wallDirection.perpX * parabolicOffset;
self.y = linearY + wallDirection.perpY * parabolicOffset;
} else {
// Regular jump - straight line
// Use jumpTargetWall if set, otherwise use opposite wall
var targetWall = self.jumpTargetWall || self.getOppositeWall(self.jumpStartWall);
// For direct jumps, target the perpendicular position on opposite wall
var targetProgress = self.jumpTargetWall ? self.jumpStartProgress : 0.5;
var endPos = self.getWallPosition(targetWall, targetProgress);
self.x = startPos.x + (endPos.x - startPos.x) * self.jumpProgress;
self.y = startPos.y + (endPos.y - startPos.y) * self.jumpProgress;
}
if (self.jumpProgress >= 1) {
self.isJumping = false;
var targetWall = self.jumpTargetWall || self.getOppositeWall(self.jumpStartWall);
self.wall = targetWall;
// For direct jumps, maintain perpendicular position; for others use center
self.wallProgress = self.jumpTargetWall ? self.jumpStartProgress : 0.5;
self.jumpProgress = 0;
self.jumpTargetWall = null; // Reset target wall
self.isParabolicJump = false; // Reset parabolic flag
// Create impact effect
createImpactEffect(self.x, self.y);
damageTerrain(self.x, self.y);
LK.getSound('impact').play();
}
} else {
self.wallProgress += self.speed / 1000;
if (self.wallProgress > 1) {
self.wallProgress = 0;
self.wall = self.getNextWall();
}
var pos = self.getWallPosition(self.wall, self.wallProgress);
self.x = pos.x;
self.y = pos.y;
// Check if stickman is on terrain (only when not jumping)
if (!self.isJumping) {
var onTerrain = false;
var stickmanRadius = 70; // Half of stickman width
// Check collision with all terrain blocks
for (var t = 0; t < terrainBlocks.length; t++) {
var block = terrainBlocks[t];
var blockHalfWidth = 92; // terrain block half width
var blockHalfHeight = 30.56; // terrain block half height
// Check if stickman overlaps with terrain block
var dx = Math.abs(self.x - block.x);
var dy = Math.abs(self.y - block.y);
if (dx < stickmanRadius + blockHalfWidth && dy < stickmanRadius + blockHalfHeight) {
onTerrain = true;
break;
}
}
// Track last terrain state
if (self.lastOnTerrain === undefined) self.lastOnTerrain = true;
// If we just stepped off terrain (transition from on terrain to off terrain)
if (self.lastOnTerrain && !onTerrain) {
// Deduct 1 point
var currentScore = LK.getScore();
var newScore = Math.max(0, currentScore - 1);
LK.setScore(newScore);
scoreTxt.setText('Score: ' + LK.getScore());
// Check if score reached 0
if (newScore <= 0) {
LK.showGameOver();
}
}
// Update last terrain state
self.lastOnTerrain = onTerrain;
}
// Damage terrain as stickman runs
if (LK.ticks % 15 === 0) {
damageTerrain(self.x, self.y);
}
}
// Rotate STICKMAN so top points toward center
var centerX = 2048 / 2;
var centerY = 2732 / 2;
var angleToCenter = Math.atan2(centerY - self.y, centerX - self.x);
// Add 90 degrees (Math.PI/2) so top of asset points toward center instead of side
stickmanGraphics.rotation = angleToCenter + Math.PI / 2;
// Randomly rotate sprite in any direction (but not in first third of wall and not while jumping)
if (Math.random() < 0.005 && self.wallProgress > 0.33 && !self.isJumping) {
// 0.5% chance per frame for less frequent but more varied rotations
var currentRotation = stickmanGraphics.rotation;
// Random rotation between -360 to 360 degrees (in radians)
var randomRotation = (Math.random() - 0.5) * 4 * Math.PI;
var targetRotation = currentRotation + randomRotation;
tween(stickmanGraphics, {
rotation: targetRotation
}, {
duration: 1000 + Math.random() * 1000,
// Random duration between 1-2 seconds
easing: tween.easeInOut
});
}
// Randomly flip sprite horizontally
if (Math.random() < 0.003) {
// 0.3% chance per frame for horizontal flipping
var currentScaleX = stickmanGraphics.scaleX;
var targetScaleX = currentScaleX * -1; // Flip horizontally
tween(stickmanGraphics, {
scaleX: targetScaleX
}, {
duration: 800 + Math.random() * 400,
// Random duration between 0.8-1.2 seconds
easing: tween.easeInOut,
onFinish: function onFinish() {
// After 2 seconds, return to normal horizontal orientation
LK.setTimeout(function () {
tween(stickmanGraphics, {
scaleX: 1
}, {
duration: 800,
easing: tween.easeInOut
});
}, 2000);
}
});
}
};
self.getWallPosition = function (wall, progress) {
// Stickman sprite half-width (140/2 = 70)
var stickmanHalfWidth = 70;
// Terrain block height is approximately 61.12, so half is about 30.56
var terrainHalfHeight = 30.56;
switch (wall) {
case 'top':
return {
x: progress * 2048,
y: stickmanHalfWidth + terrainHalfHeight // Position above terrain pixels
};
case 'right':
return {
x: 2048 - stickmanHalfWidth - terrainHalfHeight,
// Position above terrain pixels
y: 60 + progress * (2732 - 120)
};
case 'bottom':
return {
x: (1 - progress) * 2048,
y: 2732 - stickmanHalfWidth - terrainHalfHeight // Position above terrain pixels
};
case 'left':
return {
x: stickmanHalfWidth + terrainHalfHeight,
// Position above terrain pixels
y: 2672 - progress * (2732 - 120)
};
}
};
self.getNextWall = function () {
var walls = ['top', 'right', 'bottom', 'left'];
var currentIndex = walls.indexOf(self.wall);
return walls[(currentIndex - 1 + 4) % 4];
};
self.getOppositeWall = function (wall) {
switch (wall) {
case 'top':
return 'bottom';
case 'bottom':
return 'top';
case 'left':
return 'right';
case 'right':
return 'left';
}
};
self.jump = function () {
if (!self.isJumping) {
self.isJumping = true;
self.jumpStartWall = self.wall;
self.jumpStartProgress = self.wallProgress;
self.jumpProgress = 0;
self.jumpTargetWall = null; // Reset target wall for new jump
self.isParabolicJump = false; // Regular jump
// Decrease score by 1 point for each jump
var currentScore = LK.getScore();
var newScore = Math.max(0, currentScore - 1);
LK.setScore(newScore);
scoreTxt.setText('Score: ' + LK.getScore());
// Check if score reached 0 after jump
if (newScore <= 0) {
LK.showGameOver();
}
LK.getSound('jump').play();
}
};
self.parabolicJump = function () {
if (!self.isJumping) {
self.isJumping = true;
self.jumpStartWall = self.wall;
self.jumpStartProgress = self.wallProgress;
self.jumpProgress = 0;
self.jumpTargetWall = null;
self.isParabolicJump = true; // Parabolic jump flag
// Decrease score by 1 point for parabolic jump
var currentScore = LK.getScore();
var newScore = Math.max(0, currentScore - 1);
LK.setScore(newScore);
scoreTxt.setText('Score: ' + LK.getScore());
// Check if score reached 0 after jump
if (newScore <= 0) {
LK.showGameOver();
}
LK.getSound('jump').play();
}
};
self.directJump = function () {
if (!self.isJumping) {
self.isJumping = true;
self.jumpStartWall = self.wall;
self.jumpStartProgress = self.wallProgress;
self.jumpProgress = 0;
// Set target to opposite wall maintaining perpendicular position
self.jumpTargetWall = self.getOppositeWall(self.wall);
self.isParabolicJump = false; // Direct jump flag
// Decrease score by 1 point for direct jump
var currentScore = LK.getScore();
var newScore = Math.max(0, currentScore - 1);
LK.setScore(newScore);
scoreTxt.setText('Score: ' + LK.getScore());
// Check if score reached 0 after jump
if (newScore <= 0) {
LK.showGameOver();
}
LK.getSound('jump').play();
}
};
self.getWallDirection = function (wall) {
// Returns perpendicular direction vectors for parabolic elevation
switch (wall) {
case 'top':
return {
perpX: 0,
perpY: 1
};
// Elevate downward from top wall
case 'bottom':
return {
perpX: 0,
perpY: -1
};
// Elevate upward from bottom wall
case 'left':
return {
perpX: 1,
perpY: 0
};
// Elevate rightward from left wall
case 'right':
return {
perpX: -1,
perpY: 0
};
// Elevate leftward from right wall
}
};
return self;
});
var TerrainBlock = Container.expand(function () {
var self = Container.call(this);
var terrainGraphics = self.attachAsset('terrain', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 3;
self.maxHealth = 3;
self.damage = function () {
self.health--;
terrainGraphics.alpha = self.health / self.maxHealth;
if (self.health <= 0) {
self.destroy();
return true; // Block destroyed
}
return false;
};
self.repair = function () {
self.health = self.maxHealth;
terrainGraphics.alpha = 1;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
var orbGenerationCount = 0;
var stickman;
var orbs = [];
var terrainBlocks = [];
var orbsCollected = 0;
var gameTime = 0;
var destructionRate = 1;
var orbSpawnTimer = 10 * 60; // Start with 10 seconds
var orbSpawnActive = true; // Start active so timer is always running
var orbSpawnTxt = null;
// Create timer display
var timerTxt = new Text2('Time: 0', {
size: 60,
fill: 0xFFFFFF
});
timerTxt.anchor.set(0.5, 0.5);
timerTxt.y = -80; // Position above the score text
timerTxt.alpha = 0.4; // Reduce opacity to make it less prominent
LK.gui.center.addChild(timerTxt);
// Create score display
var scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0.5);
scoreTxt.alpha = 0.4; // Reduce opacity to make it less prominent
LK.gui.center.addChild(scoreTxt);
// Create orbs collected display (hidden)
var orbTxt = new Text2('Orbs: 0', {
size: 60,
fill: 0xFFD700
});
orbTxt.anchor.set(0.5, 0.5);
orbTxt.y = 100; // Position below the score text
orbTxt.alpha = 0; // Hide the orb counter completely
// Note: Not adding to LK.gui.center to keep it hidden
// Create orb spawn countdown display (always visible)
orbSpawnTxt = new Text2('Next Orb: 10', {
size: 70,
fill: 0xFF4500
});
orbSpawnTxt.anchor.set(0.5, 0.5);
orbSpawnTxt.y = 200; // Position below orb count
orbSpawnTxt.alpha = 0.4; // Always visible with reduced opacity
LK.gui.center.addChild(orbSpawnTxt);
// Initialize score to 1000 points
LK.setScore(1000);
scoreTxt.setText('Score: ' + LK.getScore());
// Initialize stickman
stickman = game.addChild(new Stickman());
var pos = stickman.getWallPosition('bottom', 0);
stickman.x = pos.x;
stickman.y = pos.y;
// Initialize orb
var firstOrb = game.addChild(new Orb());
firstOrb.x = 1024;
firstOrb.y = 1366;
orbs.push(firstOrb);
// Create initial terrain blocks around perimeter
function createInitialTerrain() {
// Top wall
for (var i = 0; i < 17; i++) {
var block = new TerrainBlock();
block.x = i * 120 + 60;
block.y = 20;
terrainBlocks.push(block);
game.addChild(block);
}
// Bottom wall
for (var i = 0; i < 17; i++) {
var block = new TerrainBlock();
block.x = i * 120 + 60;
block.y = 2712;
terrainBlocks.push(block);
game.addChild(block);
}
// Left wall
for (var i = 1; i < 22; i++) {
var block = new TerrainBlock();
block.x = 20;
block.y = i * 120 + 60;
block.rotation = Math.PI / 2; // Rotate 90 degrees toward center
terrainBlocks.push(block);
game.addChild(block);
}
// Right wall
for (var i = 1; i < 22; i++) {
var block = new TerrainBlock();
block.x = 2028;
block.y = i * 120 + 60;
block.rotation = -Math.PI / 2; // Rotate 90 degrees toward center
terrainBlocks.push(block);
game.addChild(block);
}
}
function createImpactEffect(x, y) {
var effect = LK.getAsset('impactEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: y,
alpha: 0.8
});
game.addChild(effect);
tween(effect, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
effect.destroy();
}
});
}
function damageTerrain(x, y) {
var damageRadius = 150;
for (var i = terrainBlocks.length - 1; i >= 0; i--) {
var block = terrainBlocks[i];
var distance = Math.sqrt((block.x - x) * (block.x - x) + (block.y - y) * (block.y - y));
if (distance < damageRadius) {
if (block.damage()) {
terrainBlocks.splice(i, 1);
}
}
}
}
function repairTerrain() {
for (var i = 0; i < terrainBlocks.length; i++) {
terrainBlocks[i].repair();
}
}
function createNewOrb() {
if (orbs.length >= 3) return null; // Limit maximum orbs
var newOrb = game.addChild(new Orb());
newOrb.x = 200 + Math.random() * 1648;
newOrb.y = 200 + Math.random() * 2332;
newOrb.velocityX = (Math.random() - 0.5) * 16;
newOrb.velocityY = (Math.random() - 0.5) * 16;
orbs.push(newOrb);
return newOrb;
}
function createCenterOrb() {
var newOrb = game.addChild(new Orb());
newOrb.x = 1024; // Center X
newOrb.y = 1366; // Center Y
newOrb.velocityX = (Math.random() - 0.5) * 16;
newOrb.velocityY = (Math.random() - 0.5) * 16;
orbs.push(newOrb);
return newOrb;
}
function checkCollisions() {
// Check orb-to-orb collisions and repulsion
for (var i = 0; i < orbs.length; i++) {
var orb1 = orbs[i];
// Initialize collision tracking if not exists
if (!orb1.collidingWith) orb1.collidingWith = [];
if (!orb1.lastCollidingWith) orb1.lastCollidingWith = [];
for (var j = i + 1; j < orbs.length; j++) {
var orb2 = orbs[j];
// Initialize collision tracking if not exists
if (!orb2.collidingWith) orb2.collidingWith = [];
if (!orb2.lastCollidingWith) orb2.lastCollidingWith = [];
var dx = orb2.x - orb1.x;
var dy = orb2.y - orb1.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Check if orbs are colliding (assuming orb radius of about 48)
var isColliding = distance < 96;
var wasColliding = orb1.lastCollidingWith.indexOf(j) !== -1;
if (isColliding && !wasColliding) {
// Collision just started - apply repulsion only once
var repulsionForce = 8; // Strength of repulsion
var angle = Math.atan2(dy, dx);
// Apply repulsion to both orbs
var forceX = Math.cos(angle) * repulsionForce;
var forceY = Math.sin(angle) * repulsionForce;
// Push orbs apart
orb1.velocityX -= forceX;
orb1.velocityY -= forceY;
orb2.velocityX += forceX;
orb2.velocityY += forceY;
}
// Update collision tracking
if (isColliding) {
if (orb1.collidingWith.indexOf(j) === -1) orb1.collidingWith.push(j);
if (orb2.collidingWith.indexOf(i) === -1) orb2.collidingWith.push(i);
} else {
var index1 = orb1.collidingWith.indexOf(j);
if (index1 !== -1) orb1.collidingWith.splice(index1, 1);
var index2 = orb2.collidingWith.indexOf(i);
if (index2 !== -1) orb2.collidingWith.splice(index2, 1);
}
}
// Update last frame collision state for all orbs
orb1.lastCollidingWith = orb1.collidingWith.slice(); // Copy current state
}
// Check stickman-orb collisions
for (var i = orbs.length - 1; i >= 0; i--) {
var currentOrb = orbs[i];
var distance = Math.sqrt((stickman.x - currentOrb.x) * (stickman.x - currentOrb.x) + (stickman.y - currentOrb.y) * (stickman.y - currentOrb.y));
if (distance < 70) {
// Only allow collection and rewards if we haven't reached maximum collected orbs
if (orbsCollected < 3) {
orbsCollected++;
LK.setScore(LK.getScore() + 300);
scoreTxt.setText('Score: ' + LK.getScore());
// Repair terrain
repairTerrain();
// Regenerate 5 terrain blocks with wall center preference
regenerateTerrainBlocks(5);
// Remove collected orb
currentOrb.destroy();
orbs.splice(i, 1);
// Split into 2 new orbs (if under limit)
createNewOrb();
createNewOrb();
LK.getSound('collect').play();
// Create glow effect around stickman
tween(stickman, {
tint: 0xFFD700
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(stickman, {
tint: 0xFFFFFF
}, {
duration: 150,
easing: tween.easeIn
});
}
});
break; // Only collect one orb per frame
} else {
// At maximum orbs - make orb disappear and regenerate terrain
currentOrb.destroy();
orbs.splice(i, 1);
// Regenerate 5 terrain blocks with wall center preference
regenerateTerrainBlocks(5);
break; // Only process one orb per frame
}
}
}
}
function regenerateTerrainBlocks(count) {
// Define wall center positions with higher priority
var wallCenters = [{
x: 1024,
y: 20,
rotation: 0
},
// Top wall center
{
x: 1024,
y: 2712,
rotation: 0
},
// Bottom wall center
{
x: 20,
y: 1366,
rotation: Math.PI / 2
},
// Left wall center
{
x: 2028,
y: 1366,
rotation: -Math.PI / 2
} // Right wall center
];
// Define all possible terrain positions
var allPositions = [];
// Top wall positions
for (var i = 0; i < 17; i++) {
allPositions.push({
x: i * 120 + 60,
y: 20,
rotation: 0,
isCenter: i === 8
});
}
// Bottom wall positions
for (var i = 0; i < 17; i++) {
allPositions.push({
x: i * 120 + 60,
y: 2712,
rotation: 0,
isCenter: i === 8
});
}
// Left wall positions
for (var i = 1; i < 22; i++) {
allPositions.push({
x: 20,
y: i * 120 + 60,
rotation: Math.PI / 2,
isCenter: i === 11
});
}
// Right wall positions
for (var i = 1; i < 22; i++) {
allPositions.push({
x: 2028,
y: i * 120 + 60,
rotation: -Math.PI / 2,
isCenter: i === 11
});
}
// Filter out positions that already have terrain blocks
var availablePositions = [];
for (var p = 0; p < allPositions.length; p++) {
var pos = allPositions[p];
var hasBlock = false;
for (var t = 0; t < terrainBlocks.length; t++) {
var block = terrainBlocks[t];
var dx = Math.abs(block.x - pos.x);
var dy = Math.abs(block.y - pos.y);
if (dx < 30 && dy < 30) {
// Close enough to consider same position
hasBlock = true;
break;
}
}
if (!hasBlock) {
availablePositions.push(pos);
}
}
// Create blocks with preference for wall centers
var blocksCreated = 0;
while (blocksCreated < count && availablePositions.length > 0) {
var selectedIndex;
// 70% chance to prefer center positions if available
if (Math.random() < 0.7) {
var centerPositions = [];
for (var a = 0; a < availablePositions.length; a++) {
if (availablePositions[a].isCenter) {
centerPositions.push(a);
}
}
if (centerPositions.length > 0) {
selectedIndex = centerPositions[Math.floor(Math.random() * centerPositions.length)];
} else {
selectedIndex = Math.floor(Math.random() * availablePositions.length);
}
} else {
selectedIndex = Math.floor(Math.random() * availablePositions.length);
}
var selectedPos = availablePositions[selectedIndex];
var newBlock = new TerrainBlock();
newBlock.x = selectedPos.x;
newBlock.y = selectedPos.y;
newBlock.rotation = selectedPos.rotation;
terrainBlocks.push(newBlock);
game.addChild(newBlock);
// Remove used position
availablePositions.splice(selectedIndex, 1);
blocksCreated++;
}
}
function checkGameOver() {
if (terrainBlocks.length < 10) {
LK.showGameOver();
}
}
// Initialize terrain
createInitialTerrain();
// Play Flash music during the game
LK.playMusic('Flash');
// Context menu prevention is handled by LK engine automatically
// Game controls
game.down = function (x, y, obj) {
console.log('Game down event triggered', obj.event ? obj.event.button : 'no button info');
if (obj.event && obj.event.button !== undefined) {
// Handle mouse button clicks during jumping
if (stickman.isJumping) {
if (obj.event.button === 0) {
// Left click - redirect to bottom wall
stickman.jumpTargetWall = 'bottom';
// Update jump start position to current position for smooth redirection
stickman.jumpStartWall = 'current';
stickman.jumpStartProgress = 0;
stickman.jumpProgress = 0;
} else if (obj.event.button === 2) {
// Right click - redirect to top wall
stickman.jumpTargetWall = 'top';
// Update jump start position to current position for smooth redirection
stickman.jumpStartWall = 'current';
stickman.jumpStartProgress = 0;
stickman.jumpProgress = 0;
}
} else {
// Handle mouse button clicks when not jumping
// Check if no orbs are visible on screen
if (orbs.length === 0) {
// No orbs visible - trigger direct jump to opposite wall
console.log('No orbs visible - direct jump');
stickman.directJump();
} else {
// Orbs are visible - handle normal controls
if (obj.event.button === 0) {
// Left click - regular jump to opposite wall
console.log('Left click - regular jump');
stickman.jump();
} else if (obj.event.button === 2) {
// Right click - parabolic jump relative to current wall
console.log('Right click - parabolic jump');
stickman.parabolicJump();
}
}
}
} else {
// Fallback for touch devices
// Check if no orbs are visible on screen
if (orbs.length === 0) {
// No orbs visible - trigger direct jump to opposite wall
console.log('Touch fallback - no orbs visible - direct jump');
stickman.directJump();
} else {
// Orbs are visible - regular jump
console.log('Touch fallback - regular jump');
stickman.jump();
}
}
};
// Main game loop
game.update = function () {
gameTime++;
// Increase destruction rate over time
if (gameTime % 1800 === 0) {
// Every 30 seconds
destructionRate += 0.5;
}
// Random terrain destruction
if (Math.random() < 0.001 * destructionRate && terrainBlocks.length > 0) {
var randomIndex = Math.floor(Math.random() * terrainBlocks.length);
var block = terrainBlocks[randomIndex];
if (block.damage()) {
terrainBlocks.splice(randomIndex, 1);
}
}
// Update timer display only (no score increase over time)
if (gameTime % 60 === 0) {
// Every second
var seconds = Math.floor(gameTime / 60);
timerTxt.setText('Time: ' + seconds);
}
// Decrease score by 10 every 2 seconds (120 ticks)
if (gameTime % 120 === 0) {
var currentScore = LK.getScore();
var newScore = Math.max(0, currentScore - 10);
LK.setScore(newScore);
scoreTxt.setText('Score: ' + LK.getScore());
// Check if score reached 0
if (newScore <= 0) {
LK.showGameOver();
}
}
// Only update orb spawn countdown if no orbs exist
if (orbs.length === 0) {
orbSpawnTimer--;
var secondsLeft = Math.ceil(orbSpawnTimer / 60);
orbSpawnTxt.setText('Next Orb: ' + secondsLeft);
// When timer reaches 0, spawn orb and reset
if (orbSpawnTimer <= 0) {
createCenterOrb();
orbSpawnTimer = 10 * 60; // Reset to 10 seconds
orbGenerationCount++; // Increment generation counter
// Change background to green on second generation
if (orbGenerationCount === 2) {
tween(game, {
backgroundColor: 0x00FF00
}, {
duration: 1000,
easing: tween.easeInOut
});
}
}
} else {
// Reset timer when orbs exist so it starts fresh when they're all gone
orbSpawnTimer = 10 * 60;
orbSpawnTxt.setText('Next Orb: 10');
}
checkCollisions();
checkGameOver();
}; /****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Track number of times orbs have been generated
var Orb = Container.expand(function () {
var self = Container.call(this);
var orbGraphics = self.attachAsset('orb', {
anchorX: 0.5,
anchorY: 0.5
});
self.velocityX = (Math.random() - 0.5) * 16;
self.velocityY = (Math.random() - 0.5) * 16;
// Start random rotation animation
self.startRotation = function () {
var rotationDirection = Math.random() < 0.5 ? 1 : -1; // Random clockwise or counterclockwise
var rotationSpeed = (Math.random() * 2 + 1) * rotationDirection; // Random speed between 1-3, with direction
var currentRotation = orbGraphics.rotation;
var targetRotation = currentRotation + Math.PI * 2 * rotationSpeed; // Full rotation in chosen direction
tween(orbGraphics, {
rotation: targetRotation
}, {
duration: 2000 + Math.random() * 3000,
// Random duration between 2-5 seconds
easing: tween.linear,
onFinish: function onFinish() {
self.startRotation(); // Continue rotating with new random parameters
}
});
};
self.update = function () {
// Store previous position
var prevX = self.x;
var prevY = self.y;
self.x += self.velocityX;
self.y += self.velocityY;
// Check collision with terrain blocks
var orbRadius = 48; // Approximate orb radius
var collided = false;
for (var i = 0; i < terrainBlocks.length; i++) {
var block = terrainBlocks[i];
var blockHalfWidth = 92; // terrain block half width
var blockHalfHeight = 30.56; // terrain block half height
// Check if orb intersects with terrain block
var dx = Math.abs(self.x - block.x);
var dy = Math.abs(self.y - block.y);
if (dx < orbRadius + blockHalfWidth && dy < orbRadius + blockHalfHeight) {
// Collision detected - determine which side to bounce off
var overlapX = orbRadius + blockHalfWidth - dx;
var overlapY = orbRadius + blockHalfHeight - dy;
if (overlapX < overlapY) {
// Bounce horizontally
if (Math.random() < 0.1) {
// 10% chance to change direction randomly
self.velocityX = (Math.random() - 0.5) * 16;
} else {
// Normal bounce
self.velocityX *= -1;
}
// Push orb out of collision
if (self.x < block.x) {
self.x = block.x - blockHalfWidth - orbRadius;
} else {
self.x = block.x + blockHalfWidth + orbRadius;
}
} else {
// Bounce vertically
if (Math.random() < 0.1) {
// 10% chance to change direction randomly
self.velocityY = (Math.random() - 0.5) * 16;
} else {
// Normal bounce
self.velocityY *= -1;
}
// Push orb out of collision
if (self.y < block.y) {
self.y = block.y - blockHalfHeight - orbRadius;
} else {
self.y = block.y + blockHalfHeight + orbRadius;
}
}
collided = true;
break; // Only handle one collision per frame
}
}
// Bounce off screen boundaries as fallback (if no terrain collision)
if (!collided) {
if (self.x <= 40 || self.x >= 2008) {
if (Math.random() < 0.1) {
// 10% chance to change direction
self.velocityX *= -1;
} else {
// Always reverse velocity to prevent wall penetration
self.velocityX *= -1;
}
self.x = Math.max(40, Math.min(2008, self.x));
}
if (self.y <= 40 || self.y >= 2692) {
if (Math.random() < 0.1) {
// 10% chance to change direction
self.velocityY *= -1;
} else {
// Always reverse velocity to prevent wall penetration
self.velocityY *= -1;
}
self.y = Math.max(40, Math.min(2692, self.y));
}
}
};
// Start rotation animation when orb is created
self.startRotation();
return self;
});
var Stickman = Container.expand(function () {
var self = Container.call(this);
var stickmanGraphics = self.attachAsset('STICKMAN', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 12;
self.wall = 'bottom'; // current wall: 'top', 'right', 'bottom', 'left'
self.wallProgress = 0; // progress along current wall (0-1)
self.isJumping = false;
self.jumpStartWall = '';
self.jumpStartProgress = 0;
self.jumpProgress = 0;
self.jumpTargetWall = null; // Target wall for redirected jumps
self.update = function () {
if (self.isJumping) {
// Apply acceleration when approaching destination wall
var baseSpeed = 0.04;
var accelerationFactor = 1;
// Apply slow motion in the center of the jump (40%-60%)
if (self.jumpProgress >= 0.4 && self.jumpProgress <= 0.6) {
// Calculate how close we are to the center (0.4 to 0.6 maps to 1.0 to 0.0 to 1.0)
var centerDistance = Math.abs(0.5 - self.jumpProgress) / 0.1; // 0 at center, 1 at edges
var slowMotionFactor = 0.4875 + centerDistance * 0.5125; // 0.4875x speed at center (25% more slow motion), 1.0x at edges
accelerationFactor *= slowMotionFactor;
}
// Start accelerating when past 60% of the jump
if (self.jumpProgress > 0.6) {
// Calculate how close we are to the end (0.6 to 1.0 maps to 0.0 to 1.0)
var accelerationProgress = (self.jumpProgress - 0.6) / 0.4;
// Apply quadratic acceleration (starts slow, gets faster)
accelerationFactor = 1 + accelerationProgress * accelerationProgress * 1;
}
self.jumpProgress += baseSpeed * accelerationFactor;
var startPos;
if (self.jumpStartWall === 'current') {
// Use current position as start for redirected jumps
startPos = {
x: self.x,
y: self.y
};
} else {
startPos = self.getWallPosition(self.jumpStartWall, self.jumpStartProgress);
}
if (self.isParabolicJump) {
// Parabolic jump - elevate relative to current wall
var targetWall = self.jumpTargetWall || self.getOppositeWall(self.jumpStartWall);
var endPos = self.getWallPosition(targetWall, 0.5);
// Calculate base trajectory
var linearX = startPos.x + (endPos.x - startPos.x) * self.jumpProgress;
var linearY = startPos.y + (endPos.y - startPos.y) * self.jumpProgress;
// Add parabolic elevation with tween easing
var parabolaHeight = 400; // Maximum height of parabola
var elevationFactor = tween.easeInOut(self.jumpProgress);
var parabolicOffset = parabolaHeight * (1 - Math.pow(2 * self.jumpProgress - 1, 2));
// Apply elevation perpendicular to the wall direction
var wallDirection = self.getWallDirection(self.jumpStartWall);
self.x = linearX + wallDirection.perpX * parabolicOffset;
self.y = linearY + wallDirection.perpY * parabolicOffset;
} else {
// Regular jump - straight line
// Use jumpTargetWall if set, otherwise use opposite wall
var targetWall = self.jumpTargetWall || self.getOppositeWall(self.jumpStartWall);
// For direct jumps, target the perpendicular position on opposite wall
var targetProgress = self.jumpTargetWall ? self.jumpStartProgress : 0.5;
var endPos = self.getWallPosition(targetWall, targetProgress);
self.x = startPos.x + (endPos.x - startPos.x) * self.jumpProgress;
self.y = startPos.y + (endPos.y - startPos.y) * self.jumpProgress;
}
if (self.jumpProgress >= 1) {
self.isJumping = false;
var targetWall = self.jumpTargetWall || self.getOppositeWall(self.jumpStartWall);
self.wall = targetWall;
// For direct jumps, maintain perpendicular position; for others use center
self.wallProgress = self.jumpTargetWall ? self.jumpStartProgress : 0.5;
self.jumpProgress = 0;
self.jumpTargetWall = null; // Reset target wall
self.isParabolicJump = false; // Reset parabolic flag
// Create impact effect
createImpactEffect(self.x, self.y);
damageTerrain(self.x, self.y);
LK.getSound('impact').play();
}
} else {
self.wallProgress += self.speed / 1000;
if (self.wallProgress > 1) {
self.wallProgress = 0;
self.wall = self.getNextWall();
}
var pos = self.getWallPosition(self.wall, self.wallProgress);
self.x = pos.x;
self.y = pos.y;
// Check if stickman is on terrain (only when not jumping)
if (!self.isJumping) {
var onTerrain = false;
var stickmanRadius = 70; // Half of stickman width
// Check collision with all terrain blocks
for (var t = 0; t < terrainBlocks.length; t++) {
var block = terrainBlocks[t];
var blockHalfWidth = 92; // terrain block half width
var blockHalfHeight = 30.56; // terrain block half height
// Check if stickman overlaps with terrain block
var dx = Math.abs(self.x - block.x);
var dy = Math.abs(self.y - block.y);
if (dx < stickmanRadius + blockHalfWidth && dy < stickmanRadius + blockHalfHeight) {
onTerrain = true;
break;
}
}
// Track last terrain state
if (self.lastOnTerrain === undefined) self.lastOnTerrain = true;
// If we just stepped off terrain (transition from on terrain to off terrain)
if (self.lastOnTerrain && !onTerrain) {
// Deduct 1 point
var currentScore = LK.getScore();
var newScore = Math.max(0, currentScore - 1);
LK.setScore(newScore);
scoreTxt.setText('Score: ' + LK.getScore());
// Check if score reached 0
if (newScore <= 0) {
LK.showGameOver();
}
}
// Update last terrain state
self.lastOnTerrain = onTerrain;
}
// Damage terrain as stickman runs
if (LK.ticks % 15 === 0) {
damageTerrain(self.x, self.y);
}
}
// Rotate STICKMAN so top points toward center
var centerX = 2048 / 2;
var centerY = 2732 / 2;
var angleToCenter = Math.atan2(centerY - self.y, centerX - self.x);
// Add 90 degrees (Math.PI/2) so top of asset points toward center instead of side
stickmanGraphics.rotation = angleToCenter + Math.PI / 2;
// Randomly rotate sprite in any direction (but not in first third of wall and not while jumping)
if (Math.random() < 0.005 && self.wallProgress > 0.33 && !self.isJumping) {
// 0.5% chance per frame for less frequent but more varied rotations
var currentRotation = stickmanGraphics.rotation;
// Random rotation between -360 to 360 degrees (in radians)
var randomRotation = (Math.random() - 0.5) * 4 * Math.PI;
var targetRotation = currentRotation + randomRotation;
tween(stickmanGraphics, {
rotation: targetRotation
}, {
duration: 1000 + Math.random() * 1000,
// Random duration between 1-2 seconds
easing: tween.easeInOut
});
}
// Randomly flip sprite horizontally
if (Math.random() < 0.003) {
// 0.3% chance per frame for horizontal flipping
var currentScaleX = stickmanGraphics.scaleX;
var targetScaleX = currentScaleX * -1; // Flip horizontally
tween(stickmanGraphics, {
scaleX: targetScaleX
}, {
duration: 800 + Math.random() * 400,
// Random duration between 0.8-1.2 seconds
easing: tween.easeInOut,
onFinish: function onFinish() {
// After 2 seconds, return to normal horizontal orientation
LK.setTimeout(function () {
tween(stickmanGraphics, {
scaleX: 1
}, {
duration: 800,
easing: tween.easeInOut
});
}, 2000);
}
});
}
};
self.getWallPosition = function (wall, progress) {
// Stickman sprite half-width (140/2 = 70)
var stickmanHalfWidth = 70;
// Terrain block height is approximately 61.12, so half is about 30.56
var terrainHalfHeight = 30.56;
switch (wall) {
case 'top':
return {
x: progress * 2048,
y: stickmanHalfWidth + terrainHalfHeight // Position above terrain pixels
};
case 'right':
return {
x: 2048 - stickmanHalfWidth - terrainHalfHeight,
// Position above terrain pixels
y: 60 + progress * (2732 - 120)
};
case 'bottom':
return {
x: (1 - progress) * 2048,
y: 2732 - stickmanHalfWidth - terrainHalfHeight // Position above terrain pixels
};
case 'left':
return {
x: stickmanHalfWidth + terrainHalfHeight,
// Position above terrain pixels
y: 2672 - progress * (2732 - 120)
};
}
};
self.getNextWall = function () {
var walls = ['top', 'right', 'bottom', 'left'];
var currentIndex = walls.indexOf(self.wall);
return walls[(currentIndex - 1 + 4) % 4];
};
self.getOppositeWall = function (wall) {
switch (wall) {
case 'top':
return 'bottom';
case 'bottom':
return 'top';
case 'left':
return 'right';
case 'right':
return 'left';
}
};
self.jump = function () {
if (!self.isJumping) {
self.isJumping = true;
self.jumpStartWall = self.wall;
self.jumpStartProgress = self.wallProgress;
self.jumpProgress = 0;
self.jumpTargetWall = null; // Reset target wall for new jump
self.isParabolicJump = false; // Regular jump
// Decrease score by 1 point for each jump
var currentScore = LK.getScore();
var newScore = Math.max(0, currentScore - 1);
LK.setScore(newScore);
scoreTxt.setText('Score: ' + LK.getScore());
// Check if score reached 0 after jump
if (newScore <= 0) {
LK.showGameOver();
}
LK.getSound('jump').play();
}
};
self.parabolicJump = function () {
if (!self.isJumping) {
self.isJumping = true;
self.jumpStartWall = self.wall;
self.jumpStartProgress = self.wallProgress;
self.jumpProgress = 0;
self.jumpTargetWall = null;
self.isParabolicJump = true; // Parabolic jump flag
// Decrease score by 1 point for parabolic jump
var currentScore = LK.getScore();
var newScore = Math.max(0, currentScore - 1);
LK.setScore(newScore);
scoreTxt.setText('Score: ' + LK.getScore());
// Check if score reached 0 after jump
if (newScore <= 0) {
LK.showGameOver();
}
LK.getSound('jump').play();
}
};
self.directJump = function () {
if (!self.isJumping) {
self.isJumping = true;
self.jumpStartWall = self.wall;
self.jumpStartProgress = self.wallProgress;
self.jumpProgress = 0;
// Set target to opposite wall maintaining perpendicular position
self.jumpTargetWall = self.getOppositeWall(self.wall);
self.isParabolicJump = false; // Direct jump flag
// Decrease score by 1 point for direct jump
var currentScore = LK.getScore();
var newScore = Math.max(0, currentScore - 1);
LK.setScore(newScore);
scoreTxt.setText('Score: ' + LK.getScore());
// Check if score reached 0 after jump
if (newScore <= 0) {
LK.showGameOver();
}
LK.getSound('jump').play();
}
};
self.getWallDirection = function (wall) {
// Returns perpendicular direction vectors for parabolic elevation
switch (wall) {
case 'top':
return {
perpX: 0,
perpY: 1
};
// Elevate downward from top wall
case 'bottom':
return {
perpX: 0,
perpY: -1
};
// Elevate upward from bottom wall
case 'left':
return {
perpX: 1,
perpY: 0
};
// Elevate rightward from left wall
case 'right':
return {
perpX: -1,
perpY: 0
};
// Elevate leftward from right wall
}
};
return self;
});
var TerrainBlock = Container.expand(function () {
var self = Container.call(this);
var terrainGraphics = self.attachAsset('terrain', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 3;
self.maxHealth = 3;
self.damage = function () {
self.health--;
terrainGraphics.alpha = self.health / self.maxHealth;
if (self.health <= 0) {
self.destroy();
return true; // Block destroyed
}
return false;
};
self.repair = function () {
self.health = self.maxHealth;
terrainGraphics.alpha = 1;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x87CEEB
});
/****
* Game Code
****/
var orbGenerationCount = 0;
var stickman;
var orbs = [];
var terrainBlocks = [];
var orbsCollected = 0;
var gameTime = 0;
var destructionRate = 1;
var orbSpawnTimer = 10 * 60; // Start with 10 seconds
var orbSpawnActive = true; // Start active so timer is always running
var orbSpawnTxt = null;
// Create timer display
var timerTxt = new Text2('Time: 0', {
size: 60,
fill: 0xFFFFFF
});
timerTxt.anchor.set(0.5, 0.5);
timerTxt.y = -80; // Position above the score text
timerTxt.alpha = 0.4; // Reduce opacity to make it less prominent
LK.gui.center.addChild(timerTxt);
// Create score display
var scoreTxt = new Text2('Score: 0', {
size: 80,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0.5);
scoreTxt.alpha = 0.4; // Reduce opacity to make it less prominent
LK.gui.center.addChild(scoreTxt);
// Create orbs collected display (hidden)
var orbTxt = new Text2('Orbs: 0', {
size: 60,
fill: 0xFFD700
});
orbTxt.anchor.set(0.5, 0.5);
orbTxt.y = 100; // Position below the score text
orbTxt.alpha = 0; // Hide the orb counter completely
// Note: Not adding to LK.gui.center to keep it hidden
// Create orb spawn countdown display (always visible)
orbSpawnTxt = new Text2('Next Orb: 10', {
size: 70,
fill: 0xFF4500
});
orbSpawnTxt.anchor.set(0.5, 0.5);
orbSpawnTxt.y = 200; // Position below orb count
orbSpawnTxt.alpha = 0.4; // Always visible with reduced opacity
LK.gui.center.addChild(orbSpawnTxt);
// Initialize score to 1000 points
LK.setScore(1000);
scoreTxt.setText('Score: ' + LK.getScore());
// Initialize stickman
stickman = game.addChild(new Stickman());
var pos = stickman.getWallPosition('bottom', 0);
stickman.x = pos.x;
stickman.y = pos.y;
// Initialize orb
var firstOrb = game.addChild(new Orb());
firstOrb.x = 1024;
firstOrb.y = 1366;
orbs.push(firstOrb);
// Create initial terrain blocks around perimeter
function createInitialTerrain() {
// Top wall
for (var i = 0; i < 17; i++) {
var block = new TerrainBlock();
block.x = i * 120 + 60;
block.y = 20;
terrainBlocks.push(block);
game.addChild(block);
}
// Bottom wall
for (var i = 0; i < 17; i++) {
var block = new TerrainBlock();
block.x = i * 120 + 60;
block.y = 2712;
terrainBlocks.push(block);
game.addChild(block);
}
// Left wall
for (var i = 1; i < 22; i++) {
var block = new TerrainBlock();
block.x = 20;
block.y = i * 120 + 60;
block.rotation = Math.PI / 2; // Rotate 90 degrees toward center
terrainBlocks.push(block);
game.addChild(block);
}
// Right wall
for (var i = 1; i < 22; i++) {
var block = new TerrainBlock();
block.x = 2028;
block.y = i * 120 + 60;
block.rotation = -Math.PI / 2; // Rotate 90 degrees toward center
terrainBlocks.push(block);
game.addChild(block);
}
}
function createImpactEffect(x, y) {
var effect = LK.getAsset('impactEffect', {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: y,
alpha: 0.8
});
game.addChild(effect);
tween(effect, {
scaleX: 2,
scaleY: 2,
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
effect.destroy();
}
});
}
function damageTerrain(x, y) {
var damageRadius = 150;
for (var i = terrainBlocks.length - 1; i >= 0; i--) {
var block = terrainBlocks[i];
var distance = Math.sqrt((block.x - x) * (block.x - x) + (block.y - y) * (block.y - y));
if (distance < damageRadius) {
if (block.damage()) {
terrainBlocks.splice(i, 1);
}
}
}
}
function repairTerrain() {
for (var i = 0; i < terrainBlocks.length; i++) {
terrainBlocks[i].repair();
}
}
function createNewOrb() {
if (orbs.length >= 3) return null; // Limit maximum orbs
var newOrb = game.addChild(new Orb());
newOrb.x = 200 + Math.random() * 1648;
newOrb.y = 200 + Math.random() * 2332;
newOrb.velocityX = (Math.random() - 0.5) * 16;
newOrb.velocityY = (Math.random() - 0.5) * 16;
orbs.push(newOrb);
return newOrb;
}
function createCenterOrb() {
var newOrb = game.addChild(new Orb());
newOrb.x = 1024; // Center X
newOrb.y = 1366; // Center Y
newOrb.velocityX = (Math.random() - 0.5) * 16;
newOrb.velocityY = (Math.random() - 0.5) * 16;
orbs.push(newOrb);
return newOrb;
}
function checkCollisions() {
// Check orb-to-orb collisions and repulsion
for (var i = 0; i < orbs.length; i++) {
var orb1 = orbs[i];
// Initialize collision tracking if not exists
if (!orb1.collidingWith) orb1.collidingWith = [];
if (!orb1.lastCollidingWith) orb1.lastCollidingWith = [];
for (var j = i + 1; j < orbs.length; j++) {
var orb2 = orbs[j];
// Initialize collision tracking if not exists
if (!orb2.collidingWith) orb2.collidingWith = [];
if (!orb2.lastCollidingWith) orb2.lastCollidingWith = [];
var dx = orb2.x - orb1.x;
var dy = orb2.y - orb1.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Check if orbs are colliding (assuming orb radius of about 48)
var isColliding = distance < 96;
var wasColliding = orb1.lastCollidingWith.indexOf(j) !== -1;
if (isColliding && !wasColliding) {
// Collision just started - apply repulsion only once
var repulsionForce = 8; // Strength of repulsion
var angle = Math.atan2(dy, dx);
// Apply repulsion to both orbs
var forceX = Math.cos(angle) * repulsionForce;
var forceY = Math.sin(angle) * repulsionForce;
// Push orbs apart
orb1.velocityX -= forceX;
orb1.velocityY -= forceY;
orb2.velocityX += forceX;
orb2.velocityY += forceY;
}
// Update collision tracking
if (isColliding) {
if (orb1.collidingWith.indexOf(j) === -1) orb1.collidingWith.push(j);
if (orb2.collidingWith.indexOf(i) === -1) orb2.collidingWith.push(i);
} else {
var index1 = orb1.collidingWith.indexOf(j);
if (index1 !== -1) orb1.collidingWith.splice(index1, 1);
var index2 = orb2.collidingWith.indexOf(i);
if (index2 !== -1) orb2.collidingWith.splice(index2, 1);
}
}
// Update last frame collision state for all orbs
orb1.lastCollidingWith = orb1.collidingWith.slice(); // Copy current state
}
// Check stickman-orb collisions
for (var i = orbs.length - 1; i >= 0; i--) {
var currentOrb = orbs[i];
var distance = Math.sqrt((stickman.x - currentOrb.x) * (stickman.x - currentOrb.x) + (stickman.y - currentOrb.y) * (stickman.y - currentOrb.y));
if (distance < 70) {
// Only allow collection and rewards if we haven't reached maximum collected orbs
if (orbsCollected < 3) {
orbsCollected++;
LK.setScore(LK.getScore() + 300);
scoreTxt.setText('Score: ' + LK.getScore());
// Repair terrain
repairTerrain();
// Regenerate 5 terrain blocks with wall center preference
regenerateTerrainBlocks(5);
// Remove collected orb
currentOrb.destroy();
orbs.splice(i, 1);
// Split into 2 new orbs (if under limit)
createNewOrb();
createNewOrb();
LK.getSound('collect').play();
// Create glow effect around stickman
tween(stickman, {
tint: 0xFFD700
}, {
duration: 150,
easing: tween.easeOut,
onFinish: function onFinish() {
tween(stickman, {
tint: 0xFFFFFF
}, {
duration: 150,
easing: tween.easeIn
});
}
});
break; // Only collect one orb per frame
} else {
// At maximum orbs - make orb disappear and regenerate terrain
currentOrb.destroy();
orbs.splice(i, 1);
// Regenerate 5 terrain blocks with wall center preference
regenerateTerrainBlocks(5);
break; // Only process one orb per frame
}
}
}
}
function regenerateTerrainBlocks(count) {
// Define wall center positions with higher priority
var wallCenters = [{
x: 1024,
y: 20,
rotation: 0
},
// Top wall center
{
x: 1024,
y: 2712,
rotation: 0
},
// Bottom wall center
{
x: 20,
y: 1366,
rotation: Math.PI / 2
},
// Left wall center
{
x: 2028,
y: 1366,
rotation: -Math.PI / 2
} // Right wall center
];
// Define all possible terrain positions
var allPositions = [];
// Top wall positions
for (var i = 0; i < 17; i++) {
allPositions.push({
x: i * 120 + 60,
y: 20,
rotation: 0,
isCenter: i === 8
});
}
// Bottom wall positions
for (var i = 0; i < 17; i++) {
allPositions.push({
x: i * 120 + 60,
y: 2712,
rotation: 0,
isCenter: i === 8
});
}
// Left wall positions
for (var i = 1; i < 22; i++) {
allPositions.push({
x: 20,
y: i * 120 + 60,
rotation: Math.PI / 2,
isCenter: i === 11
});
}
// Right wall positions
for (var i = 1; i < 22; i++) {
allPositions.push({
x: 2028,
y: i * 120 + 60,
rotation: -Math.PI / 2,
isCenter: i === 11
});
}
// Filter out positions that already have terrain blocks
var availablePositions = [];
for (var p = 0; p < allPositions.length; p++) {
var pos = allPositions[p];
var hasBlock = false;
for (var t = 0; t < terrainBlocks.length; t++) {
var block = terrainBlocks[t];
var dx = Math.abs(block.x - pos.x);
var dy = Math.abs(block.y - pos.y);
if (dx < 30 && dy < 30) {
// Close enough to consider same position
hasBlock = true;
break;
}
}
if (!hasBlock) {
availablePositions.push(pos);
}
}
// Create blocks with preference for wall centers
var blocksCreated = 0;
while (blocksCreated < count && availablePositions.length > 0) {
var selectedIndex;
// 70% chance to prefer center positions if available
if (Math.random() < 0.7) {
var centerPositions = [];
for (var a = 0; a < availablePositions.length; a++) {
if (availablePositions[a].isCenter) {
centerPositions.push(a);
}
}
if (centerPositions.length > 0) {
selectedIndex = centerPositions[Math.floor(Math.random() * centerPositions.length)];
} else {
selectedIndex = Math.floor(Math.random() * availablePositions.length);
}
} else {
selectedIndex = Math.floor(Math.random() * availablePositions.length);
}
var selectedPos = availablePositions[selectedIndex];
var newBlock = new TerrainBlock();
newBlock.x = selectedPos.x;
newBlock.y = selectedPos.y;
newBlock.rotation = selectedPos.rotation;
terrainBlocks.push(newBlock);
game.addChild(newBlock);
// Remove used position
availablePositions.splice(selectedIndex, 1);
blocksCreated++;
}
}
function checkGameOver() {
if (terrainBlocks.length < 10) {
LK.showGameOver();
}
}
// Initialize terrain
createInitialTerrain();
// Play Flash music during the game
LK.playMusic('Flash');
// Context menu prevention is handled by LK engine automatically
// Game controls
game.down = function (x, y, obj) {
console.log('Game down event triggered', obj.event ? obj.event.button : 'no button info');
if (obj.event && obj.event.button !== undefined) {
// Handle mouse button clicks during jumping
if (stickman.isJumping) {
if (obj.event.button === 0) {
// Left click - redirect to bottom wall
stickman.jumpTargetWall = 'bottom';
// Update jump start position to current position for smooth redirection
stickman.jumpStartWall = 'current';
stickman.jumpStartProgress = 0;
stickman.jumpProgress = 0;
} else if (obj.event.button === 2) {
// Right click - redirect to top wall
stickman.jumpTargetWall = 'top';
// Update jump start position to current position for smooth redirection
stickman.jumpStartWall = 'current';
stickman.jumpStartProgress = 0;
stickman.jumpProgress = 0;
}
} else {
// Handle mouse button clicks when not jumping
// Check if no orbs are visible on screen
if (orbs.length === 0) {
// No orbs visible - trigger direct jump to opposite wall
console.log('No orbs visible - direct jump');
stickman.directJump();
} else {
// Orbs are visible - handle normal controls
if (obj.event.button === 0) {
// Left click - regular jump to opposite wall
console.log('Left click - regular jump');
stickman.jump();
} else if (obj.event.button === 2) {
// Right click - parabolic jump relative to current wall
console.log('Right click - parabolic jump');
stickman.parabolicJump();
}
}
}
} else {
// Fallback for touch devices
// Check if no orbs are visible on screen
if (orbs.length === 0) {
// No orbs visible - trigger direct jump to opposite wall
console.log('Touch fallback - no orbs visible - direct jump');
stickman.directJump();
} else {
// Orbs are visible - regular jump
console.log('Touch fallback - regular jump');
stickman.jump();
}
}
};
// Main game loop
game.update = function () {
gameTime++;
// Increase destruction rate over time
if (gameTime % 1800 === 0) {
// Every 30 seconds
destructionRate += 0.5;
}
// Random terrain destruction
if (Math.random() < 0.001 * destructionRate && terrainBlocks.length > 0) {
var randomIndex = Math.floor(Math.random() * terrainBlocks.length);
var block = terrainBlocks[randomIndex];
if (block.damage()) {
terrainBlocks.splice(randomIndex, 1);
}
}
// Update timer display only (no score increase over time)
if (gameTime % 60 === 0) {
// Every second
var seconds = Math.floor(gameTime / 60);
timerTxt.setText('Time: ' + seconds);
}
// Decrease score by 10 every 2 seconds (120 ticks)
if (gameTime % 120 === 0) {
var currentScore = LK.getScore();
var newScore = Math.max(0, currentScore - 10);
LK.setScore(newScore);
scoreTxt.setText('Score: ' + LK.getScore());
// Check if score reached 0
if (newScore <= 0) {
LK.showGameOver();
}
}
// Only update orb spawn countdown if no orbs exist
if (orbs.length === 0) {
orbSpawnTimer--;
var secondsLeft = Math.ceil(orbSpawnTimer / 60);
orbSpawnTxt.setText('Next Orb: ' + secondsLeft);
// When timer reaches 0, spawn orb and reset
if (orbSpawnTimer <= 0) {
createCenterOrb();
orbSpawnTimer = 10 * 60; // Reset to 10 seconds
orbGenerationCount++; // Increment generation counter
// Change background to green on second generation
if (orbGenerationCount === 2) {
tween(game, {
backgroundColor: 0x00FF00
}, {
duration: 1000,
easing: tween.easeInOut
});
}
}
} else {
// Reset timer when orbs exist so it starts fresh when they're all gone
orbSpawnTimer = 10 * 60;
orbSpawnTxt.setText('Next Orb: 10');
}
checkCollisions();
checkGameOver();
};
Un stickman con una posición de corredor. In-Game asset. 2d. High contrast. No shadows
Cesped en forma de rectangulo como con el pasto arriba tierra y algunas rocas irregulares. In-Game asset. 2d. High contrast. No shadows
Stickman delgado en posición fetal y que este todo relleno de negro
Quitale el fondo
Bebidas energéticas verdes. In-Game asset. 2d. High contrast. No shadows
1 Lata verde con un rayo, sin fondo. In-Game asset. 2d. High contrast. No shadows
Change the green of the grass to dark green