User prompt
let the music start as soon as the game starts
User prompt
add music
User prompt
Make you have to collect everything at level 18
User prompt
Start the shadows in Level 10 in more mixed spots
User prompt
Earn 1 life when lighOrb is collected
User prompt
Reduce the health bar to 3
User prompt
Add too many shadows to Level 20
User prompt
Place the LIGHORB in Level 18 further away from the door
User prompt
fix it level 18
User prompt
Add more shadows to Level 17
User prompt
Level 17, 18 and October 19a add a shadow
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading '0')' in or related to this line: 's.x = triangle[i].x;' Line Number: 837
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading '0')' in or related to this line: 's.x = cross[i].x;' Line Number: 666
User prompt
activate the health bar
User prompt
Bring back the shadows
User prompt
Place the LIGHORB in Level 8 further away from the door
User prompt
fix it level 20
User prompt
I can't pass level 18 to 19 fix it
User prompt
Fix level 19
User prompt
Remove the requirements at Level 17
User prompt
Remove the requirements at Level 14
User prompt
Level 14 is wrong fix it
User prompt
Level 17 is wrong, fix it
User prompt
Make it mandatory to collect all everything at level 14, level 16,level 17 and level 18
User prompt
Make it mandatory to collect all everything at level 10
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
// Artifact class: collectible for score
var Artifact = Container.expand(function () {
var self = Container.call(this);
var artifactSprite = self.attachAsset('artifact', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = artifactSprite.width / 2;
self.collected = false;
self.getCenter = function () {
return {
x: self.x,
y: self.y
};
};
return self;
});
// Door class: exit to next area
var Door = Container.expand(function () {
var self = Container.call(this);
var doorSprite = self.attachAsset('door', {
anchorX: 0.5,
anchorY: 0.5
});
self.width = doorSprite.width;
self.height = doorSprite.height;
self.getRect = function () {
return {
x: self.x - self.width / 2,
y: self.y - self.height / 2,
width: self.width,
height: self.height
};
};
return self;
});
// Hero class: player character
var Hero = Container.expand(function () {
var self = Container.call(this);
var heroSprite = self.attachAsset('hero', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = heroSprite.width / 2;
self.lightRadius = 300; // Initial light radius
self.hasLight = false; // If currently holding a light orb
// For dragging
self.isDragging = false;
// For upgrades
self.abilities = {
lightRadius: 300
};
// For collision detection
self.getCenter = function () {
return {
x: self.x,
y: self.y
};
};
// For light effect
self.setLightRadius = function (r) {
self.lightRadius = r;
};
return self;
});
// LightOrb class: collectible light source
var LightOrb = Container.expand(function () {
var self = Container.call(this);
var orbSprite = self.attachAsset('lightOrb', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = orbSprite.width / 2;
self.collected = false;
self.getCenter = function () {
return {
x: self.x,
y: self.y
};
};
return self;
});
// Shadow class: obstacles/enemies
var Shadow = Container.expand(function () {
var self = Container.call(this);
var shadowSprite = self.attachAsset('shadow', {
anchorX: 0.5,
anchorY: 0.5
});
self.radius = shadowSprite.width / 2;
self.speed = 2 + Math.random() * 2; // Random speed for movement
self.direction = Math.random() * Math.PI * 2; // Random direction
// For simple movement (patrol)
self.update = function () {
// If reactive (Survival Camp), chase or avoid hero based on light
if (self.isReactive && typeof hero !== "undefined") {
var dx = hero.x - self.x;
var dy = hero.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (hero.hasLight && dist < hero.lightRadius + 100) {
// Avoid hero if hero has light
var angle = Math.atan2(-dy, -dx);
self.direction = angle + (Math.random() - 0.5) * 0.5;
self.speed = 3.5 + Math.random() * 1.5;
} else if (dist < 600) {
// Chase hero if close and not lit
var angle = Math.atan2(dy, dx);
self.direction = angle + (Math.random() - 0.5) * 0.2;
self.speed = 3 + Math.random();
}
}
self.x += Math.cos(self.direction) * self.speed;
self.y += Math.sin(self.direction) * self.speed;
// Bounce off walls
if (self.x < self.radius || self.x > 2048 - self.radius) {
self.direction = Math.PI - self.direction;
}
if (self.y < self.radius + 100 || self.y > 2732 - self.radius) {
self.direction = -self.direction;
}
};
// For collision detection
self.getCenter = function () {
return {
x: self.x,
y: self.y
};
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x111122
});
/****
* Game Code
****/
// --- Global variables ---
/*
We use simple shapes for the MVP:
- 'hero': the player character (circle, bright color)
- 'shadow': shadow obstacles (ellipse, dark color)
- 'lightOrb': collectible light source (circle, yellowish)
- 'door': exit to next area (rectangle, light blue)
- 'fog': semi-transparent overlay for darkness (rectangle, black, alpha)
- 'artifact': collectible (star-shaped, but use ellipse for MVP, gold)
*/
// Music asset (id: 'bgmusic', volume: 0.7)
var hero;
var shadows = [];
var lightOrbs = [];
var artifacts = [];
var door;
var fogOverlay;
var dragging = false;
var dragOffset = {
x: 0,
y: 0
};
var lastTouch = {
x: 0,
y: 0
};
var level = 1;
var maxLevel = 20;
var artifactCount = 0;
var artifactTotal = 0;
var lightFadeTween = null;
// --- GUI ---
var artifactTxt = new Text2('Artifacts: 0/0', {
size: 80,
fill: 0xFFE066
});
artifactTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(artifactTxt);
var levelTxt = new Text2('Level 1', {
size: 80,
fill: 0x66CCFF
});
levelTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(levelTxt);
// Health bar: show as "Health: [|||]" (3 max)
var heroMaxHealth = 3;
var heroHealth = heroMaxHealth;
var healthBarTxt = new Text2('Health: |||', {
size: 80,
fill: 0xFF6666
});
healthBarTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(healthBarTxt);
// Position GUI elements
artifactTxt.y = 100;
levelTxt.y = 10;
healthBarTxt.y = 190;
// Helper to update health bar display
function updateHealthBar() {
var bars = '';
for (var i = 0; i < heroHealth; i++) bars += '|';
for (var i = heroHealth; i < heroMaxHealth; i++) bars += ' ';
healthBarTxt.setText('Health: ' + bars);
}
updateHealthBar();
// --- Helper functions ---
function distance(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
return Math.sqrt(dx * dx + dy * dy);
}
function circleRectIntersect(cx, cy, cr, rx, ry, rw, rh) {
// Find the closest point to the circle within the rectangle
var closestX = Math.max(rx, Math.min(cx, rx + rw));
var closestY = Math.max(ry, Math.min(cy, ry + rh));
// Calculate the distance between the circle's center and this closest point
var dx = cx - closestX;
var dy = cy - closestY;
// If the distance is less than the circle's radius, an intersection occurs
return dx * dx + dy * dy < cr * cr;
}
// --- Level setup ---
function setupLevel(lvl) {
// Clear previous
for (var i = 0; i < shadows.length; i++) {
shadows[i].destroy();
}
for (var i = 0; i < lightOrbs.length; i++) {
lightOrbs[i].destroy();
}
for (var i = 0; i < artifacts.length; i++) {
artifacts[i].destroy();
}
if (door) door.destroy();
if (fogOverlay) fogOverlay.destroy();
shadows = [];
lightOrbs = [];
artifacts = [];
door = null;
fogOverlay = null;
artifactCount = 0;
// Hero position
if (!hero) {
hero = new Hero();
game.addChild(hero);
}
hero.x = 400;
hero.y = 2732 / 2;
hero.setLightRadius(300 + (lvl - 1) * 40);
hero.hasLight = false;
// Reset health for new level
heroHealth = heroMaxHealth;
updateHealthBar();
// --- Location logic ---
// Ruined Laboratory (level 1): default
// Ruined City (level 2-4): more shadows, more artifacts, shadows move faster
// Survival Camp (level 5+): shadows react to hero, more light orbs, artifacts clustered
if (lvl === 1) {
// Ruined Laboratory: default
// Shadows: none for level 1
// Light orb: always one, placed randomly
var orb = new LightOrb();
orb.x = 400 + Math.random() * (2048 - 800);
orb.y = 300 + Math.random() * (2732 - 600);
lightOrbs.push(orb);
game.addChild(orb);
// Artifacts: 1-2
artifactTotal = 1 + Math.floor(Math.random() * 2);
for (var i = 0; i < artifactTotal; i++) {
var a = new Artifact();
var tries = 0;
do {
a.x = 400 + Math.random() * (2048 - 800);
a.y = 300 + Math.random() * (2732 - 600);
var ok = true;
if (distance({
x: a.x,
y: a.y
}, {
x: hero.x,
y: hero.y
}) < 300) ok = false;
for (var j = 0; j < artifacts.length; j++) {
if (distance({
x: a.x,
y: a.y
}, {
x: artifacts[j].x,
y: artifacts[j].y
}) < 200) ok = false;
}
tries++;
if (tries > 10) break;
} while (!ok);
artifacts.push(a);
game.addChild(a);
}
} else if (lvl >= 2 && lvl <= 4) {
// Ruined City: more/faster shadows, more artifacts
// Shadows: 2-4, move faster
var shadowCount = 2 + Math.floor(Math.random() * 3);
for (var i = 0; i < shadowCount; i++) {
var s = new Shadow();
s.x = 800 + Math.random() * (2048 - 1200);
s.y = 400 + Math.random() * (2732 - 800);
s.speed = 3 + Math.random() * 2;
shadows.push(s);
game.addChild(s);
}
// Light orb: always one, placed randomly
var orb = new LightOrb();
orb.x = 400 + Math.random() * (2048 - 800);
orb.y = 300 + Math.random() * (2732 - 600);
lightOrbs.push(orb);
game.addChild(orb);
// Artifacts: 2-4
artifactTotal = 2 + Math.floor(Math.random() * 3);
for (var i = 0; i < artifactTotal; i++) {
var a = new Artifact();
var tries = 0;
do {
a.x = 400 + Math.random() * (2048 - 800);
a.y = 300 + Math.random() * (2732 - 600);
var ok = true;
if (distance({
x: a.x,
y: a.y
}, {
x: hero.x,
y: hero.y
}) < 300) ok = false;
for (var j = 0; j < artifacts.length; j++) {
if (distance({
x: a.x,
y: a.y
}, {
x: artifacts[j].x,
y: artifacts[j].y
}) < 200) ok = false;
}
tries++;
if (tries > 10) break;
} while (!ok);
artifacts.push(a);
game.addChild(a);
}
} else if (lvl >= 5 && lvl <= 7) {
// Survival Camp (lvl 5-7): shadows react to hero, more light orbs, artifacts clustered
// Shadows: 3-5, reactive to hero
var shadowCount = 3 + Math.floor(Math.random() * 3);
for (var i = 0; i < shadowCount; i++) {
var s = new Shadow();
s.x = 1000 + Math.random() * 800;
s.y = 800 + Math.random() * 1200;
s.isReactive = true;
shadows.push(s);
game.addChild(s);
}
// Two light orbs, placed near each other
for (var i = 0; i < 2; i++) {
var orb = new LightOrb();
orb.x = 1200 + Math.random() * 400;
orb.y = 1000 + Math.random() * 400;
lightOrbs.push(orb);
game.addChild(orb);
}
// Artifacts: 3-5, clustered
artifactTotal = 3 + Math.floor(Math.random() * 3);
var clusterX = 1500 + Math.random() * 300;
var clusterY = 1500 + Math.random() * 300;
for (var i = 0; i < artifactTotal; i++) {
var a = new Artifact();
a.x = clusterX + Math.random() * 120 - 60;
a.y = clusterY + Math.random() * 120 - 60;
artifacts.push(a);
game.addChild(a);
}
} else if (lvl === 8) {
// Level 8: "Collapsed Tunnels" - narrow corridor, many shadows, few orbs
// Shadows: 6, placed along the tunnel
for (var i = 0; i < 6; i++) {
var s = new Shadow();
s.x = 800 + i * 120;
s.y = 600 + Math.random() * 1500;
shadows.push(s);
game.addChild(s);
}
// Only one light orb, placed at far end
var orb = new LightOrb();
orb.x = 1500;
orb.y = 2732 / 2;
lightOrbs.push(orb);
game.addChild(orb);
// Artifacts: 2, placed at dangerous spots
artifactTotal = 2;
for (var i = 0; i < artifactTotal; i++) {
var a = new Artifact();
a.x = 1200 + i * 300;
a.y = 800 + Math.random() * 1200;
artifacts.push(a);
game.addChild(a);
}
} else if (lvl === 9) {
// Level 9: "Watcher’s Lair" - fewer, but very fast shadows, more orbs, artifacts near center
// Shadows: 3, very fast
for (var i = 0; i < 3; i++) {
var s = new Shadow();
s.x = 900 + i * 200;
s.y = 1000 + Math.random() * 800;
s.speed = 5 + Math.random() * 2;
shadows.push(s);
game.addChild(s);
}
// Three light orbs, spread out
for (var i = 0; i < 3; i++) {
var orb = new LightOrb();
orb.x = 700 + i * 400;
orb.y = 700 + Math.random() * 1500;
lightOrbs.push(orb);
game.addChild(orb);
}
// Artifacts: 4, near center
artifactTotal = 4;
for (var i = 0; i < artifactTotal; i++) {
var a = new Artifact();
a.x = 1000 + Math.random() * 400;
a.y = 1200 + Math.random() * 400;
artifacts.push(a);
game.addChild(a);
}
} else if (lvl === 10) {
// Level 10: "Shadow Nexus" - final, many reactive shadows, clustered orbs/artifacts
// Shadows: 6, all reactive, but start in more mixed spots
var shadowPositions = [{
x: 1550 + Math.random() * 200,
y: 1200 + Math.random() * 200
}, {
x: 1700 + Math.random() * 200,
y: 1500 + Math.random() * 200
}, {
x: 1200 + Math.random() * 300,
y: 1100 + Math.random() * 600
}, {
x: 1400 + Math.random() * 400,
y: 1700 + Math.random() * 300
}, {
x: 1800 + Math.random() * 100,
y: 1300 + Math.random() * 400
}, {
x: 1300 + Math.random() * 400,
y: 1200 + Math.random() * 400
}];
for (var i = 0; i < 6; i++) {
var s = new Shadow();
s.x = shadowPositions[i].x;
s.y = shadowPositions[i].y;
s.isReactive = true;
shadows.push(s);
game.addChild(s);
}
// Four light orbs, clustered
for (var i = 0; i < 4; i++) {
var orb = new LightOrb();
orb.x = 1600 + Math.random() * 200;
orb.y = 1200 + Math.random() * 400;
lightOrbs.push(orb);
game.addChild(orb);
}
// Artifacts: 5, clustered
artifactTotal = 5;
var clusterX = 1700;
var clusterY = 1400;
for (var i = 0; i < artifactTotal; i++) {
var a = new Artifact();
a.x = clusterX + Math.random() * 100 - 50;
a.y = clusterY + Math.random() * 100 - 50;
artifacts.push(a);
game.addChild(a);
}
} else if (lvl === 11) {
// Level 11: "The Forgotten Vault" - maze-like, slow but smart shadows, orbs at dead ends
// Shadows: 4, slow, placed at maze entrances
for (var i = 0; i < 4; i++) {
var s = new Shadow();
s.x = 600 + i * 350;
s.y = 400 + Math.random() * (2732 - 800);
s.speed = 1.5 + Math.random();
s.isReactive = true;
shadows.push(s);
game.addChild(s);
}
// Two light orbs, at far left and right
for (var i = 0; i < 2; i++) {
var orb = new LightOrb();
orb.x = i === 0 ? 400 : 2048 - 400;
orb.y = 400 + Math.random() * (2732 - 800);
lightOrbs.push(orb);
game.addChild(orb);
}
// Artifacts: 3, spaced out
artifactTotal = 3;
for (var i = 0; i < artifactTotal; i++) {
var a = new Artifact();
a.x = 700 + i * 400;
a.y = 800 + Math.random() * 1200;
artifacts.push(a);
game.addChild(a);
}
} else if (lvl === 12) {
// Level 12: "Flooded Passage" - orbs clustered bottom right, artifacts top left and top right
// Shadows: 3, slow, near bottom
for (var i = 0; i < 3; i++) {
var s = new Shadow();
s.x = 1200 + i * 200;
s.y = 2200 + Math.random() * 200;
s.speed = 1.5 + Math.random();
shadows.push(s);
game.addChild(s);
}
// Three light orbs, clustered near bottom right
for (var i = 0; i < 3; i++) {
var orb = new LightOrb();
orb.x = 1500 + Math.random() * 300;
orb.y = 2200 + Math.random() * 300;
lightOrbs.push(orb);
game.addChild(orb);
}
// Artifacts: 2, one at top left, one at top right
artifactTotal = 2;
var artifactPositions = [{
x: 350,
y: 350 + Math.random() * 150
}, {
x: 2048 - 350,
y: 350 + Math.random() * 150
}];
for (var i = 0; i < artifactTotal; i++) {
var a = new Artifact();
a.x = artifactPositions[i].x;
a.y = artifactPositions[i].y;
artifacts.push(a);
game.addChild(a);
}
} else if (lvl === 13) {
// Level 13: "The Mirror Hall" - shadows mirror hero's movement, orbs/artifacts in corners
// Shadows: 2, mirror hero's movement (simulate by following hero with offset)
for (var i = 0; i < 2; i++) {
var s = new Shadow();
s.x = i === 0 ? 200 : 1848;
s.y = i === 0 ? 2532 : 200;
s.isReactive = true;
shadows.push(s);
game.addChild(s);
}
// Four light orbs, one in each corner
var corners = [{
x: 200,
y: 200
}, {
x: 1848,
y: 200
}, {
x: 200,
y: 2532
}, {
x: 1848,
y: 2532
}];
for (var i = 0; i < 4; i++) {
var orb = new LightOrb();
orb.x = corners[i].x;
orb.y = corners[i].y;
lightOrbs.push(orb);
game.addChild(orb);
}
// Artifacts: 4, one in each corner (offset from orbs)
artifactTotal = 4;
for (var i = 0; i < artifactTotal; i++) {
var a = new Artifact();
a.x = corners[i].x + 60;
a.y = corners[i].y + 60;
artifacts.push(a);
game.addChild(a);
}
} else if (lvl === 14) {
// Level 14: "The Gauntlet" - many fast, non-reactive shadows, orbs in a vertical line, artifacts in zigzag spanning the play area
// Shadows: 7, fast, non-reactive
for (var i = 0; i < 7; i++) {
var s = new Shadow();
s.x = 400 + i * 220;
s.y = 400 + i % 2 * 1800;
s.speed = 4 + Math.random() * 2;
shadows.push(s);
game.addChild(s);
}
// Five light orbs, in a vertical line
for (var i = 0; i < 5; i++) {
var orb = new LightOrb();
orb.x = 1024;
orb.y = 400 + i * 500;
lightOrbs.push(orb);
game.addChild(orb);
}
// Artifacts: 5, zigzag pattern spanning the play area
artifactTotal = 5;
for (var i = 0; i < artifactTotal; i++) {
var a = new Artifact();
a.x = 500 + i * 300;
a.y = 600 + (i % 2 === 0 ? 600 : 1800);
artifacts.push(a);
game.addChild(a);
}
} else if (lvl === 15) {
// Level 15: "The Final Eclipse" - all shadows reactive, orbs/artifacts at center, max difficulty
// Shadows: 8, all reactive, clustered at center
for (var i = 0; i < 8; i++) {
var s = new Shadow();
s.x = 1024 + Math.cos(i / 8 * Math.PI * 2) * 200;
s.y = 1366 + Math.sin(i / 8 * Math.PI * 2) * 200;
s.isReactive = true;
shadows.push(s);
game.addChild(s);
}
// Six light orbs, clustered at center
for (var i = 0; i < 6; i++) {
var orb = new LightOrb();
orb.x = 1024 + Math.random() * 120 - 60;
orb.y = 1366 + Math.random() * 120 - 60;
lightOrbs.push(orb);
game.addChild(orb);
}
// Artifacts: 6, clustered at center
artifactTotal = 6;
for (var i = 0; i < artifactTotal; i++) {
var a = new Artifact();
a.x = 1024 + Math.random() * 100 - 50;
a.y = 1366 + Math.random() * 100 - 50;
artifacts.push(a);
game.addChild(a);
}
} else if (lvl === 16) {
// Level 16: "Crystal Crossing" - orbs in a cross, artifacts at each arm tip
// Five light orbs, one at center, four at cross arms
var cross = [{
x: 1024,
y: 1366
},
// center
{
x: 1024,
y: 566
},
// top
{
x: 1024,
y: 2166
},
// bottom
{
x: 324,
y: 1366
},
// left
{
x: 1724,
y: 1366
} // right
];
// Shadows: 5, at cross arms
for (var i = 0; i < 5; i++) {
var s = new Shadow();
s.x = cross[i].x;
s.y = cross[i].y;
shadows.push(s);
game.addChild(s);
}
for (var i = 0; i < cross.length; i++) {
var orb = new LightOrb();
orb.x = cross[i].x;
orb.y = cross[i].y;
lightOrbs.push(orb);
game.addChild(orb);
}
// Artifacts: 4, at the tips of the cross (not center)
artifactTotal = 4;
var tips = [{
x: 1024,
y: 566
},
// top
{
x: 1024,
y: 2166
},
// bottom
{
x: 324,
y: 1366
},
// left
{
x: 1724,
y: 1366
} // right
];
for (var i = 0; i < artifactTotal; i++) {
var a = new Artifact();
a.x = tips[i].x;
a.y = tips[i].y;
artifacts.push(a);
game.addChild(a);
}
} else if (lvl === 17) {
// Level 17: "Spiral of Light" - orbs in a spiral, artifacts at spiral endpoints
// Shadows: 8, distributed along the spiral (denser, more challenging)
var spiralCenterX = 1024;
var spiralCenterY = 1366;
var spiralRadiusStart = 200;
var spiralRadiusEnd = 800;
var spiralTurns = 2.25; // more turns for a clear spiral
var spiralOrbs = 8;
var shadowCount = 8;
for (var i = 0; i < shadowCount; i++) {
var t = i / (shadowCount - 1);
var angle = t * spiralTurns * Math.PI * 2;
var radius = spiralRadiusStart + (spiralRadiusEnd - spiralRadiusStart) * t;
var s = new Shadow();
s.x = spiralCenterX + Math.cos(angle) * radius;
s.y = spiralCenterY + Math.sin(angle) * radius;
shadows.push(s);
game.addChild(s);
}
// Add an extra shadow at the spiral endpoint (for extra challenge)
var endpointAngle = spiralTurns * Math.PI * 2;
var endpointX = spiralCenterX + Math.cos(endpointAngle) * spiralRadiusEnd;
var endpointY = spiralCenterY + Math.sin(endpointAngle) * spiralRadiusEnd;
var sEndpoint = new Shadow();
sEndpoint.x = endpointX;
sEndpoint.y = endpointY;
shadows.push(sEndpoint);
game.addChild(sEndpoint);
// Place 8 light orbs in a spiral pattern (tighter spiral, more visually clear)
for (var i = 0; i < spiralOrbs; i++) {
var t = i / (spiralOrbs - 1);
var angle = t * spiralTurns * Math.PI * 2;
var radius = spiralRadiusStart + (spiralRadiusEnd - spiralRadiusStart) * t;
var orb = new LightOrb();
orb.x = spiralCenterX + Math.cos(angle) * radius;
orb.y = spiralCenterY + Math.sin(angle) * radius;
lightOrbs.push(orb);
game.addChild(orb);
}
// Artifacts: 2, at the start and end of the spiral
artifactTotal = 2;
var artifactPositions = [{
x: spiralCenterX + Math.cos(0) * spiralRadiusStart,
y: spiralCenterY + Math.sin(0) * spiralRadiusStart
}, {
x: spiralCenterX + Math.cos(spiralTurns * Math.PI * 2) * spiralRadiusEnd,
y: spiralCenterY + Math.sin(spiralTurns * Math.PI * 2) * spiralRadiusEnd
}];
for (var i = 0; i < artifactTotal; i++) {
var a = new Artifact();
a.x = artifactPositions[i].x;
a.y = artifactPositions[i].y;
artifacts.push(a);
game.addChild(a);
}
} else if (lvl === 18) {
// Level 18: "Circle of Light" - orbs in a ring, artifacts at cardinal points
var ringCenterX = 1024;
var ringCenterY = 1366;
var ringRadius = 900; // Increased from 700 to 900 to move orbs further from the door
// Shadows: 4, at cardinal points of the ring
for (var i = 0; i < 4; i++) {
var angle = i / 4 * Math.PI * 2;
var s = new Shadow();
s.x = ringCenterX + Math.cos(angle) * (ringRadius - 120);
s.y = ringCenterY + Math.sin(angle) * (ringRadius - 120);
shadows.push(s);
game.addChild(s);
}
// Add a shadow at the center of the ring
var sCenter = new Shadow();
sCenter.x = ringCenterX;
sCenter.y = ringCenterY;
shadows.push(sCenter);
game.addChild(sCenter);
// Place 8 light orbs in a circle
var ringOrbs = 8;
for (var i = 0; i < ringOrbs; i++) {
var angle = i / ringOrbs * Math.PI * 2;
var orb = new LightOrb();
orb.x = ringCenterX + Math.cos(angle) * ringRadius;
orb.y = ringCenterY + Math.sin(angle) * ringRadius;
lightOrbs.push(orb);
game.addChild(orb);
}
// Artifacts: 4, at N/E/S/W points of the ring
artifactTotal = 4;
var artifactPositions = [{
x: ringCenterX,
y: ringCenterY - ringRadius
}, {
x: ringCenterX + ringRadius,
y: ringCenterY
}, {
x: ringCenterX,
y: ringCenterY + ringRadius
}, {
x: ringCenterX - ringRadius,
y: ringCenterY
} // West
];
for (var i = 0; i < artifactTotal; i++) {
var a = new Artifact();
a.x = artifactPositions[i].x;
a.y = artifactPositions[i].y;
artifacts.push(a);
game.addChild(a);
}
} else if (lvl === 19) {
// Level 19: "Zigzag Triangle" - orbs in a zigzag, artifacts in a triangle
// Artifacts and Shadows: 3, at triangle points
var triangle = [{
x: 1024,
y: 500
}, {
x: 400,
y: 2200
}, {
x: 1648,
y: 2200
}];
// Shadows: 3, at triangle points
for (var i = 0; i < 3; i++) {
var s = new Shadow();
s.x = triangle[i].x;
s.y = triangle[i].y;
shadows.push(s);
game.addChild(s);
}
// Add a shadow at the centroid of the triangle
var centroidX = (triangle[0].x + triangle[1].x + triangle[2].x) / 3;
var centroidY = (triangle[0].y + triangle[1].y + triangle[2].y) / 3;
var sCentroid = new Shadow();
sCentroid.x = centroidX;
sCentroid.y = centroidY;
shadows.push(sCentroid);
game.addChild(sCentroid);
// Place 6 light orbs in a zigzag pattern
var zigzagOrbs = 6;
for (var i = 0; i < zigzagOrbs; i++) {
var orb = new LightOrb();
orb.x = 400 + i * 280;
orb.y = i % 2 === 0 ? 700 : 2000;
lightOrbs.push(orb);
game.addChild(orb);
}
// Artifacts: 3, at triangle points
artifactTotal = 3;
for (var i = 0; i < artifactTotal; i++) {
var a = new Artifact();
a.x = triangle[i].x;
a.y = triangle[i].y;
artifacts.push(a);
game.addChild(a);
}
} else if (lvl === 20) {
// Level 20: "Starfall Sanctuary" - orbs in a star, artifacts at star points
// Shadows: way too many, dense starburst pattern
var starCenterX = 1024;
var starCenterY = 1366;
var starRadius = 700;
var starPoints = 5;
// Add a dense burst of shadows radiating from the center
var shadowRings = 6; // number of concentric rings
var shadowsPerRing = 18; // number of shadows per ring
for (var ring = 1; ring <= shadowRings; ring++) {
var r = (starRadius - 200) * (ring / shadowRings) + 120;
for (var j = 0; j < shadowsPerRing; j++) {
var angle = j / shadowsPerRing * Math.PI * 2 + ring % 2 * (Math.PI / shadowsPerRing);
var s = new Shadow();
s.x = starCenterX + Math.cos(angle) * r;
s.y = starCenterY + Math.sin(angle) * r;
// Make some move faster for extra chaos
if (ring % 2 === 0) s.speed += 1.5;
shadows.push(s);
game.addChild(s);
}
}
// Add extra shadows at the star points (for visual emphasis)
for (var i = 0; i < starPoints; i++) {
var angle = -Math.PI / 2 + i * (2 * Math.PI / starPoints);
var s = new Shadow();
s.x = starCenterX + Math.cos(angle) * (starRadius - 100);
s.y = starCenterY + Math.sin(angle) * (starRadius - 100);
s.speed += 2;
shadows.push(s);
game.addChild(s);
}
// Place 5 light orbs in a star pattern (center + 4 points)
for (var i = 0; i < starPoints; i++) {
var angle = -Math.PI / 2 + i * (2 * Math.PI / starPoints);
var orb = new LightOrb();
orb.x = starCenterX + Math.cos(angle) * starRadius;
orb.y = starCenterY + Math.sin(angle) * starRadius;
lightOrbs.push(orb);
game.addChild(orb);
}
// Center orb
var centerOrb = new LightOrb();
centerOrb.x = starCenterX;
centerOrb.y = starCenterY;
lightOrbs.push(centerOrb);
game.addChild(centerOrb);
// Artifacts: 5, at the star points
artifactTotal = 5;
for (var i = 0; i < starPoints; i++) {
var angle = -Math.PI / 2 + i * (2 * Math.PI / starPoints);
var a = new Artifact();
a.x = starCenterX + Math.cos(angle) * (starRadius + 120);
a.y = starCenterY + Math.sin(angle) * (starRadius + 120);
artifacts.push(a);
game.addChild(a);
}
}
// Door: always at far right
door = new Door();
door.x = 2048 - 200;
door.y = 2732 / 2;
game.addChild(door);
// Fog overlay: covers the whole screen, alpha depends on light
fogOverlay = LK.getAsset('fog', {
anchorX: 1,
anchorY: 0,
x: 2048,
y: 0
});
fogOverlay.alpha = 0.7;
game.addChild(fogOverlay);
// Update GUI
artifactTxt.setText('Artifacts: 0/' + artifactTotal);
levelTxt.setText('Level ' + lvl + ' / ' + maxLevel);
// --- Save progress for persistence ---
if (typeof storage !== "undefined") {
storage.level = lvl;
storage.artifacts = artifactCount;
}
}
// --- Light effect ---
function updateFogAlpha() {
// Fog alpha is lower if hero has light
if (!fogOverlay) return;
var targetAlpha = hero.hasLight ? 0.2 : 0.7;
if (lightFadeTween) tween.stop(fogOverlay, {
alpha: true
});
lightFadeTween = tween(fogOverlay, {
alpha: targetAlpha
}, {
duration: 400,
easing: tween.easeInOut
});
}
// --- Touch controls ---
game.down = function (x, y, obj) {
// Only start drag if touch is inside hero
var local = {
x: x,
y: y
};
if (distance(local, {
x: hero.x,
y: hero.y
}) < hero.radius + 30) {
dragging = true;
dragOffset.x = hero.x - x;
dragOffset.y = hero.y - y;
lastTouch.x = x;
lastTouch.y = y;
}
};
game.move = function (x, y, obj) {
if (dragging) {
// Clamp hero position to screen
var nx = x + dragOffset.x;
var ny = y + dragOffset.y;
nx = Math.max(hero.radius, Math.min(2048 - hero.radius, nx));
ny = Math.max(hero.radius + 100, Math.min(2732 - hero.radius, ny));
hero.x = nx;
hero.y = ny;
lastTouch.x = x;
lastTouch.y = y;
}
};
game.up = function (x, y, obj) {
dragging = false;
};
// --- Main update loop ---
game.update = function () {
// Shadows update and collision
for (var i = 0; i < shadows.length; i++) {
var s = shadows[i];
if (typeof s.lastX === "undefined") s.lastX = s.x;
if (typeof s.lastY === "undefined") s.lastY = s.y;
if (typeof s.lastWasIntersecting === "undefined") s.lastWasIntersecting = false;
if (typeof s.update === "function") s.update();
// Collision with hero
var isIntersecting = distance(hero.getCenter(), s.getCenter()) < hero.radius + s.radius - 10;
if (!s.lastWasIntersecting && isIntersecting) {
// Decrease health, update bar, flash hero, game over if 0
heroHealth--;
updateHealthBar();
LK.effects.flashObject(hero, 0xFF2222, 600);
if (heroHealth <= 0) {
LK.effects.flashScreen(0x000000, 1000);
LK.showGameOver();
return;
}
}
s.lastX = s.x;
s.lastY = s.y;
s.lastWasIntersecting = isIntersecting;
}
// Light orb collection
for (var i = 0; i < lightOrbs.length; i++) {
var orb = lightOrbs[i];
if (!orb.collected && distance(hero.getCenter(), orb.getCenter()) < hero.radius + orb.radius + 10) {
orb.collected = true;
hero.hasLight = true;
hero.setLightRadius(500 + (level - 1) * 50);
updateFogAlpha();
// Earn 1 life (up to max)
if (heroHealth < heroMaxHealth) {
heroHealth++;
updateHealthBar();
}
// Animate orb fade out
tween(orb, {
alpha: 0
}, {
duration: 400,
onFinish: function onFinish() {
orb.destroy();
}
});
}
}
// Artifact collection
for (var i = 0; i < artifacts.length; i++) {
var a = artifacts[i];
if (!a.collected && distance(hero.getCenter(), a.getCenter()) < hero.radius + a.radius + 10) {
a.collected = true;
artifactCount++;
artifactTxt.setText('Artifacts: ' + artifactCount + '/' + artifactTotal);
tween(a, {
alpha: 0
}, {
duration: 400,
onFinish: function onFinish() {
a.destroy();
}
});
// --- Story-driven choice: prompt on artifact collection ---
if (level >= 2) {
// Only show for city/camp, not tutorial
LK.setTimeout(function () {
// Show a simple choice popup (simulate with LK.effects.flashScreen and Text2 for MVP)
var choiceTxt = new Text2("You found a mysterious artifact!\nDo you use it for power or save it for later?\n(Tap left: Power, Tap right: Save)", {
size: 70,
fill: 0xFFD700
});
choiceTxt.anchor.set(0.5, 0.5);
choiceTxt.x = 2048 / 2;
choiceTxt.y = 2732 / 2;
LK.gui.center.addChild(choiceTxt);
// Listen for tap (simulate with game.down)
var choiceHandler = function choiceHandler(x, y, obj) {
if (x < 2048 / 2) {
// Power: increase light radius
hero.setLightRadius(hero.lightRadius + 80);
LK.effects.flashObject(hero, 0xFFE066, 800);
} else {
// Save: bonus score (simulate with artifactCount++)
if (artifactCount < artifactTotal) {
artifactCount++;
artifactTxt.setText('Artifacts: ' + artifactCount + '/' + artifactTotal);
}
LK.effects.flashObject(artifactTxt, 0xFFD700, 800);
}
LK.gui.center.removeChild(choiceTxt);
game.down = origDown;
};
var origDown = game.down;
game.down = choiceHandler;
}, 500);
}
}
}
// Door: check if hero is at door and has light
var canExit = false;
if (level === 8 || level === 10 || level === 16 || level === 18) {
// On these levels, require all artifacts and all light orbs to be collected
var allArtifactsCollected = true;
for (var i = 0; i < artifacts.length; i++) {
if (!artifacts[i].collected) {
allArtifactsCollected = false;
break;
}
}
var allOrbsCollected = true;
for (var i = 0; i < lightOrbs.length; i++) {
if (!lightOrbs[i].collected) {
allOrbsCollected = false;
break;
}
}
if (hero.hasLight && allArtifactsCollected && allOrbsCollected && circleRectIntersect(hero.x, hero.y, hero.radius, door.x - door.width / 2, door.y - door.height / 2, door.width, door.height)) {
canExit = true;
}
} else {
if (hero.hasLight && circleRectIntersect(hero.x, hero.y, hero.radius, door.x - door.width / 2, door.y - door.height / 2, door.width, door.height)) {
canExit = true;
}
}
if (canExit) {
// Next level or win
if (level < maxLevel) {
level++;
setupLevel(level);
} else {
LK.showYouWin();
}
return;
}
// No shadows to collide with hero
// Fog overlay: create a "light hole" around hero if has light
if (fogOverlay) {
// For MVP, just set fog alpha lower if hero has light
// (Advanced: could use a mask, but not supported in MVP)
// Optionally, animate a "pulse" when light is collected
}
};
// --- Start game ---
// Start background music (looping by default)
LK.playMusic('bgmusic');
setupLevel(level); ===================================================================
--- original.js
+++ change.js
@@ -147,9 +147,9 @@
/****
* Game Code
****/
-// Music asset (id: 'bgmusic', volume: 0.7)
+// --- Global variables ---
/*
We use simple shapes for the MVP:
- 'hero': the player character (circle, bright color)
- 'shadow': shadow obstacles (ellipse, dark color)
@@ -157,9 +157,9 @@
- 'door': exit to next area (rectangle, light blue)
- 'fog': semi-transparent overlay for darkness (rectangle, black, alpha)
- 'artifact': collectible (star-shaped, but use ellipse for MVP, gold)
*/
-// --- Global variables ---
+// Music asset (id: 'bgmusic', volume: 0.7)
var hero;
var shadows = [];
var lightOrbs = [];
var artifacts = [];
@@ -1152,7 +1152,7 @@
// Optionally, animate a "pulse" when light is collected
}
};
// --- Start game ---
-setupLevel(level);
// Start background music (looping by default)
-LK.playMusic('bgmusic');
\ No newline at end of file
+LK.playMusic('bgmusic');
+setupLevel(level);
\ No newline at end of file
food. In-Game asset. 2d. High contrast. No shadows
Shadow creature. In-Game asset. 2d. High contrast. No shadows
first aid kit. In-Game asset. 2d. High contrast. No shadows
✅ Hair: Medium-length, slightly unkempt dark hair—indicating survival hardships. ✅ Height: Around 1.75m (5'9"), agile but not overly strong. ✅ Build: Lean, slightly worn-out clothing, showing signs of past struggles. ✅ Eyes: Piercing green or amber eyes, reflecting a determined personality. ✅ Accessories: A damaged jacket with scratches, adding realism to the post-apocalyptic theme. ✅ Hand Item: A broken flashlight – essential for revealing enemies but risky in the dark! Arin’s design should reflect their survival skills—not looking overly combat-ready but practical for the harsh environment. The flashlight mechanic will play a big role in interacting with the game’s shadow creatures.. In-Game asset. 2d. High contrast. No shadows
The door. In-Game asset. 2d. High contrast. No shadows