/****
* Classes
****/
// Coin class
var Coin = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.rotationSpeed = 0.05; // Radians per frame
self.update = function () {
self.rotation += self.rotationSpeed;
};
return self;
});
// Base size, will be scaled in code
// No plugins needed for this version. Tween could be used for rotation, but manual rotation is sufficient.
// var tween = LK.import('@upit/tween.v1');
// Player class
var Player = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// No update needed here as movement is handled in the main game loop
return self;
});
// Wall class
var Wall = Container.expand(function (width, height) {
var self = Container.call(this);
// We get a base wall asset and scale it to the desired dimensions
var graphics = self.attachAsset('wall', {
anchorX: 0.0,
// Anchor top-left for easier positioning and scaling
anchorY: 0.0,
width: width,
// Set the actual width
height: height // Set the actual height
});
// Store dimensions for collision checks
self.wallWidth = width;
self.wallHeight = height;
// No update needed for static walls
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x111111 // Dark background
});
/****
* Game Code
****/
// Game constants and variables
// Define assets used in the game. These will be automatically created and loaded.
function _typeof2(o) {
"@babel/helpers - typeof";
return _typeof2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof2(o);
}
function _defineProperty2(e, r, t) {
return (r = _toPropertyKey2(r)) in e ? Object.defineProperty(e, r, {
value: t,
enumerable: !0,
configurable: !0,
writable: !0
}) : e[r] = t, e;
}
function _toPropertyKey2(t) {
var i = _toPrimitive2(t, "string");
return "symbol" == _typeof2(i) ? i : i + "";
}
function _toPrimitive2(t, r) {
if ("object" != _typeof2(t) || !t) {
return t;
}
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r || "default");
if ("object" != _typeof2(i)) {
return i;
}
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
function _defineProperty(e, r, t) {
return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
value: t,
enumerable: !0,
configurable: !0,
writable: !0
}) : e[r] = t, e;
}
function _toPropertyKey(t) {
var i = _toPrimitive(t, "string");
return "symbol" == _typeof(i) ? i : i + "";
}
function _toPrimitive(t, r) {
if ("object" != _typeof(t) || !t) {
return t;
}
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r || "default");
if ("object" != _typeof(i)) {
return i;
}
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
var gameWidth = 2048;
var gameHeight = 2732;
var levelTimeLimit = 60; // Seconds
var totalLevels = 10; // As per description
var requiredCoinsPerLevel = 10;
// Maze definitions for 10 levels with increasing difficulty.
// Each level: walls array [{x, y, width, height}], coins array [{x, y}], startPos {x, y}
var mazeLevels = [
// Level 1: Spiral Maze
{
walls: [
// --- Outer Borders ---
{
x: 100,
y: 200,
width: 1848,
height: 50
},
// Top
{
x: 100,
y: 2482,
width: 1848,
height: 50
},
// Bottom
{
x: 100,
y: 250,
width: 50,
height: 2232
},
// Left
{
x: 1898,
y: 250,
width: 50,
height: 2232
},
// Right
// --- Spiral Arm 1 (Outer) ---
{
x: 150,
y: 450,
width: 1600,
height: 50
},
// H1: Top segment (Leaves gap right: 1750-1898)
{
x: 1750,
y: 500,
width: 50,
height: 1800
},
// V1: Right segment (Leaves gap bottom: 2300-2482)
{
x: 300,
y: 2300,
width: 1500,
height: 50
},
// H2: Bottom segment (Leaves gap left: 150-300)
{
x: 300,
y: 600,
width: 50,
height: 1700
},
// V2: Left segment (Leaves gap top: 450-600)
// --- Spiral Arm 2 (Inner) --- Path width ~150px
{
x: 450,
y: 600,
width: 1150,
height: 50
},
// H3: Top segment (Leaves gap right: 1600-1750)
{
x: 1600,
y: 650,
width: 50,
height: 1500
},
// V3: Right segment (Leaves gap bottom: 2150-2300)
{
x: 450,
y: 2150,
width: 1200,
height: 50
},
// H4: Bottom segment (Leaves gap left: 300-450)
{
x: 450,
y: 750,
width: 50,
height: 1400
},
// V4: Left segment (Leaves gap top: 600-750)
// --- Spiral Arm 3 (Inner) --- Path width ~150px
{
x: 600,
y: 750,
width: 850,
height: 50
},
// H5: Top segment (Leaves gap right: 1450-1600)
{
x: 1450,
y: 800,
width: 50,
height: 1200
},
// V5: Right segment (Leaves gap bottom: 2000-2150)
{
x: 600,
y: 2000,
width: 900,
height: 50
},
// H6: Bottom segment (Leaves gap left: 450-600)
{
x: 600,
y: 900,
width: 50,
height: 1100
},
// V6: Left segment (Leaves gap top: 750-900)
// --- Spiral Arm 4 (Innermost) --- Path width ~150px
{
x: 750,
y: 900,
width: 550,
height: 50
},
// H7: Top segment (Leaves gap right: 1300-1450)
{
x: 1300,
y: 950,
width: 50,
height: 900
},
// V7: Right segment (Leaves gap bottom: 1850-2000)
{
x: 750,
y: 1850,
width: 600,
height: 50
},
// H8: Bottom segment (Leaves gap left: 600-750)
{
x: 750,
y: 1050,
width: 50,
height: 800
},
// V8: Left segment (Leaves gap top: 900-1050)
// --- Center Area Termination --- Blocks the direct path to the center
{
x: 900,
y: 1050,
width: 250,
height: 50
},
// Central H block
{
x: 1100,
y: 1100,
width: 50,
height: 600
} // Central V block
],
coins: [
// Place coins along the spiral path
{
x: 1000,
y: 350
},
// Path 1 Top
{
x: 1825,
y: 1000
},
// Path 1 Right
{
x: 1000,
y: 2400
},
// Path 1 Bottom
{
x: 225,
y: 1500
},
// Path 1 Left
{
x: 1000,
y: 675
},
// Path 2 Top
{
x: 1675,
y: 1500
},
// Path 2 Right
{
x: 1000,
y: 2075
},
// Path 2 Bottom
{
x: 525,
y: 1500
},
// Path 2 Left
{
x: 1000,
y: 975
},
// Path 3 Top
{
x: 1024,
y: 1400
} // Center area (end of spiral)
],
startPos: {
x: 200,
y: 350
} // Start at the entrance near top-left
},
// Level 2: Hypnotic Maze with Doors (Harder)
{
walls: [
// --- Outer Borders (Solid) ---
{
x: 100,
y: 200,
width: 1848,
height: 50
},
// Top
{
x: 100,
y: 2482,
width: 1848,
height: 50
},
// Bottom
{
x: 100,
y: 250,
width: 50,
height: 2232
},
// Left
{
x: 1898,
y: 250,
width: 50,
height: 2232
},
// Right
// --- Layer 1 (Outer Square - Gap Top) --- approx 200px path
{
x: 300,
y: 400,
width: 700,
height: 50
},
// Top Left
{
x: 1150,
y: 400,
width: 700,
height: 50
},
// Top Right (Gap: 1000-1150)
{
x: 300,
y: 2282,
width: 1600,
height: 50
},
// Bottom Full
{
x: 300,
y: 450,
width: 50,
height: 1832
},
// Left Full
// --- Layer 2 (Gap Left) --- approx 200px path
{
x: 500,
y: 600,
width: 1300,
height: 50
},
// Top Full
{
x: 500,
y: 2082,
width: 1300,
height: 50
},
// Bottom Full
{
x: 500,
y: 650,
width: 50,
height: 650
},
// Left Top (Gap: 1300-1432)
{
//{4B} // Reused identifier
x: 500,
y: 1432,
width: 50,
height: 650
},
// Left Bottom
// --- Layer 3 (Gap Bottom) --- approx 200px path
{
x: 700,
y: 800,
width: 900,
height: 50
},
// Top Full
{
x: 700,
y: 1882,
width: 400,
height: 50
},
// Bottom Left (Gap: 1100-1250)
{
//{4G} // Reused identifier
x: 1250,
y: 1882,
width: 350,
height: 50
},
// Bottom Right
{
x: 700,
y: 850,
width: 50,
height: 1032
},
// Left Full
{
x: 1598,
// Adjusted right wall x
y: 850,
width: 50,
height: 1032
},
// Right Full
// --- Layer 4 (Gap Right) --- approx 200px path
{
//{5A} // Reused identifier
x: 900,
y: 1000,
width: 500,
height: 50
},
// Top Full
{
//{5G} // Reused identifier
x: 900,
y: 1682,
width: 500,
height: 50
},
// Bottom Full
{
//{5L} // Reused identifier
x: 900,
y: 1050,
width: 50,
height: 632
},
// Left Full
{
//{5Q} // Reused identifier
x: 1398,
y: 1050,
width: 50,
height: 250 // Right Top (Gap: 1300-1432)
}, {
//{5U} // Reused identifier
x: 1398,
y: 1432,
width: 50,
height: 250 // Right Bottom
},
// --- Central Area (Solid Block) ---
{
//{5F} // Reused identifier
x: 1100,
y: 1200,
width: 200,
height: 282 // Adjusted to be smaller central block
},
// --- Added Dead Ends ---
// Removed small horizontal wall in bottom right
{}],
//{6D} // End walls array
coins: [
// Place coins in accessible locations for the harder maze
{
x: 1800,
y: 2400
},
// Near start
{
x: 400,
y: 2182
},
// Path Layer 1 Bottom Left
{
x: 400,
y: 500
},
// Path Layer 2 Top Left
{
x: 600,
y: 1366 // Near Layer 2 Left Gap
}, {
x: 1700,
y: 700
},
// Path Layer 2 Top Right
{
x: 1700,
y: 1982
},
// Path Layer 3 Bottom Right (near dead end)
{
x: 800,
y: 1982 // Near Layer 3 Bottom Gap
}, {
x: 800,
y: 900
},
// Path Layer 3 Top Left
{
x: 1000,
y: 1100
},
// Path Layer 4 Top Left
{
x: 1300,
y: 1366 // Center Area (final coin)
}],
// End coins
startPos: {
x: 1800,
// Bottom right x
y: 2400 // Bottom right y
} // Start bottom right corner //{83} //{84}
},
// Level 3: S-Shape Path with Doors
{
walls: [{
x: 100,
y: 200,
width: 1848,
height: 50
},
// Top
{
x: 100,
y: 2482,
width: 1848,
height: 50
},
// Bottom
{
x: 100,
y: 250,
width: 50,
height: 2232
},
// Left
{
x: 1898,
y: 250,
width: 50,
height: 2232
},
// Right
// S-shape walls with gaps (doors)
// Top H segment (split for door)
{
x: 100,
y: 700,
width: 650,
// Ends at 750
height: 50
}, {
x: 900,
// Starts at 900 (gap 750-900)
y: 700,
width: 650,
// Ends at 1550
height: 50
},
// Middle H segment (split for door)
{
x: 500,
y: 1200,
width: 650,
// Ends at 1150
height: 50
}, {
x: 1300,
// Starts at 1300 (gap 1150-1300)
y: 1200,
width: 648,
// Ends at 1948
height: 50
},
// Bottom H segment (split for door)
{
x: 100,
y: 1700,
width: 650,
// Ends at 750
height: 50
}, {
x: 900,
// Starts at 900 (gap 750-900)
y: 1700,
width: 650,
// Ends at 1550
height: 50
},
// V segment 1 (top right) - Keep solid for now, rely on H gap
{
x: 1500,
y: 250,
width: 50,
height: 450
},
// V segment 2 (middle left - split for door)
{
x: 500,
y: 750,
width: 50,
height: 200 // Ends at 950
}, {
x: 500,
y: 1050,
// Starts at 1050 (gap 950-1050)
width: 50,
height: 150 // Ends at 1200
},
// V segment 3 (middle right - split for door)
{
x: 1500,
y: 1250,
width: 50,
height: 200 // Ends at 1450
}, {
x: 1500,
y: 1550,
// Starts at 1550 (gap 1450-1550)
width: 50,
height: 150 // Ends at 1700
},
// V segment 4 (bottom left) - Split to create door (gap y:2050-2200)
{
// V segment 4 Top part
x: 500,
y: 1750,
width: 50,
height: 300 // Ends at y=2050
}, {
// V segment 4 Bottom part
x: 500,
y: 2200,
// Starts after gap
width: 50,
height: 282 // Ends at y=2482
}],
coins: [{
x: 300,
y: 300
}, {
x: 1700,
y: 450
}, {
x: 300,
y: 950
}, {
x: 1700,
y: 950
}, {
x: 300,
y: 1450
}, {
x: 1700,
y: 1450
}, {
x: 300,
y: 2000
}, {
x: 1700,
y: 2000
}, {
x: 1000,
y: 1450
}, {
x: 1000,
y: 950
}],
startPos: {
x: 200,
y: 300
}
},
// Level 4: Asymmetric Cross (Modified Doors)
{
walls: [
// --- Outer Borders ---
{
x: 100,
y: 200,
width: 1848,
height: 50
},
// Top
{
x: 100,
y: 2482,
width: 1848,
height: 50
},
// Bottom
{
x: 100,
y: 250,
width: 50,
height: 2232
},
// Left
{
x: 1898,
y: 250,
width: 50,
height: 2232
},
// Right
// --- Internal Walls ---
// Horizontal dividers
// Top H (Gap left: 100-600 -> Wider Door 1)
{
x: 600,
//{9o} // Increased start X
y: 800,
width: 1348,
//{9q} // Adjusted width
height: 50
},
// Bottom H (Gap right: 1448-1898 -> Wider Door 2, split for new door)
// Part 1 (Ends at x=500)
{
x: 100,
y: 1900,
width: 400,
//{9w} // Shorter width
height: 50
},
// Part 2 (Starts at x=700, ends at 1448 -> Gap x:500-700 = New Door Bottom-Left)
{
x: 700,
y: 1900,
width: 748,
// Covers remaining section leaving gap
height: 50
},
// Vertical dividers
// Left V (Gap top: 250-950 -> Wider Door 3)
{
x: 600,
y: 950,
//{9C} // Increased start Y
width: 50,
height: 1532 // Adjusted height
},
// Right V (Gap bottom: 1750-2482 -> Wider Door 4, split for new door)
// Part 1 (Ends at y=700)
{
x: 1400,
y: 250,
width: 50,
height: 450 // Shorter height
},
// Part 2 (Starts at y=900, ends at 1750 -> Gap y:700-900 = New Door Top-Right)
{
x: 1400,
y: 900,
width: 50,
height: 850 // Covers remaining section leaving gap
},
// Twist/Blocking walls (Unchanged)
{
x: 150,
y: 1000,
width: 400,
height: 50
},
// Twist block top-left section
{
x: 1450,
y: 1000,
width: 400,
height: 50
},
// Twist block top-right section
{
x: 150,
y: 1700,
width: 400,
height: 50
},
// Twist block bottom-left section
{
x: 1450,
y: 1700,
width: 400,
height: 50
} // Twist block bottom-right section
],
coins: [{
x: 300,
y: 500
},
// Top Left area (before twist)
{
x: 300,
y: 1300
},
// Top Left area (after twist)
{
x: 1700,
y: 500
},
// Top Right area (before twist)
{
x: 1700,
y: 1300
},
// Top Right area (after twist)
{
x: 300,
y: 2200
},
// Bottom Left area (near corner)
{
x: 300,
y: 1800
},
// Bottom Left area (after twist)
{
x: 1700,
y: 2200
},
// Bottom Right area (near corner)
{
x: 1700,
y: 1800
},
// Bottom Right area (after twist)
{
x: 1024,
y: 1000
},
// Center Top area
{
x: 1024,
y: 1500
} // Center Bottom area
],
startPos: {
x: 200,
y: 300
} // Start Top Left
},
// Level 5: Central Hub Branching
{
walls: [{
x: 100,
y: 200,
width: 1848,
height: 50
},
// Top
{
x: 100,
y: 2482,
width: 1848,
height: 50
},
// Bottom
{
x: 100,
y: 250,
width: 50,
height: 2232
},
// Left
{
x: 1898,
y: 250,
width: 50,
height: 2232
},
// Right
// Central Hub Walls (creating a box ~800x800 in center)
{
x: 624,
y: 966,
width: 800,
height: 50
},
// Hub Top
{
x: 624,
y: 1716,
width: 800,
height: 50
},
// Hub Bottom
{
x: 624,
y: 1016,
width: 50,
height: 700
},
// Hub Left
{
x: 1374,
y: 1016,
width: 50,
height: 700
},
// Hub Right
// Branch Walls (connecting hub/borders to create paths)
{
x: 150,
y: 600,
width: 1700,
height: 50
},
// Top Branch Divider
{
x: 150,
y: 2066,
width: 1700,
height: 50
},
// Bottom Branch Divider
{
x: 350,
y: 650,
width: 50,
height: 1416
},
// Left Branch Divider (Inner)
{
x: 1648,
y: 650,
width: 50,
height: 1416
},
// Right Branch Divider (Inner)
// Add some dead ends or twists within branches
{
x: 150,
y: 1341,
width: 150,
height: 50
},
// Left branch block
{
x: 1748,
y: 1341,
width: 150,
height: 50
},
// Right branch block
{
x: 1000,
y: 250,
width: 50,
height: 300
},
// Top branch block
{
x: 1000,
y: 2116,
width: 50,
height: 300
} // Bottom branch block
],
coins: [{
x: 1024,
y: 1341
},
// Center Hub
{
x: 250,
y: 400
},
// Top Left Branch
{
x: 1798,
y: 400
},
// Top Right Branch
{
x: 250,
y: 2250
},
// Bottom Left Branch
{
x: 1798,
y: 2250
},
// Bottom Right Branch
{
x: 250,
y: 1341
},
// Mid Left Branch end
{
x: 1798,
y: 1341
},
// Mid Right Branch end
{
x: 1024,
y: 350
},
// Mid Top Branch end
{
x: 1024,
y: 2300
},
// Mid Bottom Branch end
{
x: 800,
y: 1500
} // Inside Hub offset
],
startPos: {
x: 1024,
y: 1341
} // Start in Center Hub
},
// Level 6: More complex branching/connections
{
walls: [{
x: 100,
y: 200,
width: 1848,
height: 50
}, {
x: 100,
y: 2482,
width: 1848,
height: 50
}, {
x: 100,
y: 250,
width: 50,
height: 2232
}, {
x: 1898,
y: 250,
width: 50,
height: 2232
},
// Horizontal dividers
{
x: 100,
y: 800,
width: 800,
height: 50
}, {
x: 1148,
y: 800,
width: 800,
height: 50
}, {
x: 100,
y: 1300,
width: 1848,
height: 50
}, {
x: 100,
y: 1800,
width: 800,
height: 50
}, {
x: 1148,
y: 1800,
width: 800,
height: 50
},
// Vertical dividers
{
x: 500,
y: 250,
width: 50,
height: 500
}, {
x: 500,
y: 850,
width: 50,
height: 400
}, {
x: 500,
y: 1350,
width: 50,
height: 400
}, {
x: 500,
y: 1850,
width: 50,
height: 632
}, {
x: 1500,
y: 250,
width: 50,
height: 500
}, {
x: 1500,
y: 850,
width: 50,
height: 400
}, {
x: 1500,
y: 1350,
width: 50,
height: 400
}, {
x: 1500,
y: 1850,
width: 50,
height: 632
}],
coins: [{
x: 300,
y: 300
}, {
x: 1024,
y: 300
}, {
x: 1748,
y: 300
}, {
x: 300,
y: 1050
}, {
x: 1748,
y: 1050
}, {
x: 1024,
y: 1550
},
// Center area coin
{
x: 300,
y: 2100
}, {
x: 1748,
y: 2100
}, {
x: 800,
y: 500
}, {
x: 1248,
y: 2000
}],
startPos: {
x: 1024,
y: 1550
} // Start near center
},
// Level 7: Grid with missing segments
{
walls: [{
x: 100,
y: 200,
width: 1848,
height: 50
}, {
x: 100,
y: 2482,
width: 1848,
height: 50
}, {
x: 100,
y: 250,
width: 50,
height: 2232
}, {
x: 1898,
y: 250,
width: 50,
height: 2232
},
// Grid lines (approx every 450px)
{
x: 100,
y: 650,
width: 1848,
height: 50
}, {
x: 100,
y: 1100,
width: 1848,
height: 50
}, {
x: 100,
y: 1550,
width: 1848,
height: 50
}, {
x: 100,
y: 2000,
width: 1848,
height: 50
}, {
x: 550,
y: 250,
width: 50,
height: 2232
}, {
x: 1000,
y: 250,
width: 50,
height: 2232
}, {
x: 1450,
y: 250,
width: 50,
height: 2232
}
// Remove segments to create path
// (For simplicity, just adding coins in grid cells)
],
coins: [
// Place coins in different grid cells
{
x: 325,
y: 425
}, {
x: 775,
y: 425
}, {
x: 1225,
y: 425
}, {
x: 1675,
y: 425
}, {
x: 325,
y: 875
}, {
x: 1675,
y: 875
},
// Skip middle cells
{
x: 325,
y: 1325
}, {
x: 1675,
y: 1325
}, {
x: 775,
y: 1775
}, {
x: 1225,
y: 2225
}],
startPos: {
x: 325,
y: 2225
} // Start bottom left
},
// Level 8: Spiral (Approximation with Rectangles)
{
walls: [{
x: 100,
y: 200,
width: 1848,
height: 50
}, {
x: 100,
y: 2482,
width: 1848,
height: 50
}, {
x: 100,
y: 250,
width: 50,
height: 2232
}, {
x: 1898,
y: 250,
width: 50,
height: 2232
},
// Outer spiral path
{
x: 100,
y: 700,
width: 1600,
height: 50
},
// H1
{
x: 1650,
y: 250,
width: 50,
height: 900
},
// V1
{
x: 300,
y: 1150,
width: 1400,
height: 50
},
// H2
{
x: 250,
y: 750,
width: 50,
height: 850
},
// V2
{
x: 250,
y: 1600,
width: 1200,
height: 50
},
// H3
{
x: 1450,
y: 1150,
width: 50,
height: 900
},
// V3
{
x: 450,
y: 2050,
width: 1050,
height: 50
},
// H4 (inner end)
{
x: 400,
y: 1650,
width: 50,
height: 400
} // V4 (inner end)
],
coins: [
// Place along the spiral path
{
x: 200,
y: 400
}, {
x: 1000,
y: 400
}, {
x: 1800,
y: 400
},
// Outer Top
{
x: 1800,
y: 900
}, {
x: 1800,
y: 1400
},
// Right Down
{
x: 1000,
y: 900
},
// Inner loop 1
{
x: 400,
y: 900
}, {
x: 400,
y: 1400
},
// Left Down/Up
{
x: 400,
y: 1800
}, {
x: 1000,
y: 1800
} // Inner end area
],
startPos: {
x: 200,
y: 300
} // Start near entrance
},
// Level 9: Dense Obstacles
{
walls: [{
x: 100,
y: 200,
width: 1848,
height: 50
}, {
x: 100,
y: 2482,
width: 1848,
height: 50
}, {
x: 100,
y: 250,
width: 50,
height: 2232
}, {
x: 1898,
y: 250,
width: 50,
height: 2232
},
// Many small walls creating tight paths
{
x: 200,
y: 400,
width: 200,
height: 50
}, {
x: 500,
y: 400,
width: 200,
height: 50
}, {
x: 800,
y: 400,
width: 200,
height: 50
}, {
x: 1100,
y: 400,
width: 200,
height: 50
}, {
x: 1400,
y: 400,
width: 200,
height: 50
}, {
x: 1700,
y: 400,
width: 200,
height: 50
}, {
x: 300,
y: 600,
width: 50,
height: 200
}, {
x: 600,
y: 600,
width: 50,
height: 200
}, {
x: 900,
y: 600,
width: 50,
height: 200
}, {
x: 1200,
y: 600,
width: 50,
height: 200
}, {
x: 1500,
y: 600,
width: 50,
height: 200
}, {
x: 1800,
y: 600,
width: 50,
height: 200
},
// ... Continue pattern downwards ...
{
x: 200,
y: 900,
width: 200,
height: 50
}, {
x: 500,
y: 900,
width: 200,
height: 50
},
// etc.
{
x: 300,
y: 1100,
width: 50,
height: 200
}, {
x: 600,
y: 1100,
width: 50,
height: 200
},
// etc.
{
x: 200,
y: 1400,
width: 200,
height: 50
}, {
x: 500,
y: 1400,
width: 200,
height: 50
},
// etc.
{
x: 300,
y: 1600,
width: 50,
height: 200
}, {
x: 600,
y: 1600,
width: 50,
height: 200
},
// etc.
{
x: 200,
y: 1900,
width: 200,
height: 50
}, {
x: 500,
y: 1900,
width: 200,
height: 50
},
// etc.
{
x: 300,
y: 2100,
width: 50,
height: 200
}, {
x: 600,
y: 2100,
width: 50,
height: 200
},
// etc.
// Mirror pattern on right side
{
x: 1100,
y: 900,
width: 200,
height: 50
}, {
x: 1400,
y: 900,
width: 200,
height: 50
}, {
x: 1700,
y: 900,
width: 200,
height: 50
}, {
x: 1200,
y: 1100,
width: 50,
height: 200
}, {
x: 1500,
y: 1100,
width: 50,
height: 200
}, {
x: 1800,
y: 1100,
width: 50,
height: 200
}, {
x: 1100,
y: 1400,
width: 200,
height: 50
}, {
x: 1400,
y: 1400,
width: 200,
height: 50
}, {
x: 1700,
y: 1400,
width: 200,
height: 50
}, {
x: 1200,
y: 1600,
width: 50,
height: 200
}, {
x: 1500,
y: 1600,
width: 50,
height: 200
}, {
x: 1800,
y: 1600,
width: 50,
height: 200
}, {
x: 1100,
y: 1900,
width: 200,
height: 50
}, {
x: 1400,
y: 1900,
width: 200,
height: 50
}, {
x: 1700,
y: 1900,
width: 200,
height: 50
}, {
x: 1200,
y: 2100,
width: 50,
height: 200
}, {
x: 1500,
y: 2100,
width: 50,
height: 200
}, {
x: 1800,
y: 2100,
width: 50,
height: 200
}],
coins: [
// Place in tricky spots within the dense grid
{
x: 250,
y: 300
}, {
x: 1800,
y: 300
}, {
x: 400,
y: 700
}, {
x: 1650,
y: 700
}, {
x: 750,
y: 1000
}, {
x: 1300,
y: 1000
}, {
x: 250,
y: 1500
}, {
x: 1800,
y: 1500
}, {
x: 400,
y: 2200
}, {
x: 1650,
y: 2200
}],
startPos: {
x: 1024,
y: 300
} // Start top center
},
// Level 10: Final Challenge - Combination
{
walls: [{
x: 100,
y: 200,
width: 1848,
height: 50
}, {
x: 100,
y: 2482,
width: 1848,
height: 50
}, {
x: 100,
y: 250,
width: 50,
height: 2232
}, {
x: 1898,
y: 250,
width: 50,
height: 2232
},
// Mix of grid, long paths, tight spots
// Section 1 (Top Grid)
{
x: 100,
y: 700,
width: 1848,
height: 50
}, {
x: 550,
y: 250,
width: 50,
height: 450
}, {
x: 1000,
y: 250,
width: 50,
height: 450
}, {
x: 1450,
y: 250,
width: 50,
height: 450
},
// Section 2 (Middle Maze)
{
x: 100,
y: 1200,
width: 800,
height: 50
}, {
x: 1148,
y: 1200,
width: 800,
height: 50
}, {
x: 500,
y: 750,
width: 50,
height: 900
}, {
x: 1500,
y: 750,
width: 50,
height: 900
}, {
x: 100,
y: 1650,
width: 1848,
height: 50
},
// Section 3 (Bottom Spiral-like element)
{
x: 300,
y: 2100,
width: 1448,
height: 50
}, {
x: 1748,
y: 1700,
width: 50,
height: 400
}, {
x: 300,
y: 1700,
width: 50,
height: 400
}, {
x: 500,
y: 1900,
width: 1048,
height: 50
} // Inner H
],
coins: [{
x: 325,
y: 400
}, {
x: 1675,
y: 400
},
// Top Grid
{
x: 200,
y: 900
}, {
x: 1800,
y: 900
},
// Mid sides
{
x: 1024,
y: 1400
},
// Mid center
{
x: 400,
y: 1800
}, {
x: 1600,
y: 1800
},
// Lower Mid
{
x: 200,
y: 2300
}, {
x: 1800,
y: 2300
},
// Bottom Corners
{
x: 1024,
y: 2000
} // Bottom Center (inner)
],
startPos: {
x: 1024,
y: 300
} // Start Top Center
}
// NOTE: Circular mazes are hard to represent with rectangular walls.
// This implementation uses only rectangular walls.
];
// Game state variables
var currentLevelIndex = 0;
var walls = [];
var coins = [];
var player = null;
var score = 0;
var timeLeft = levelTimeLimit;
var timerInterval = null;
var scoreTxt = null;
var timerTxt = null;
var levelTxt = null;
var levelComplete = false;
var isDragging = false;
var dragOffset = {
x: 0,
y: 0
}; // Offset between touch point and player center
// --- Helper Functions ---
// Clears current level elements
function clearLevel() {
walls.forEach(function (wall) {
wall.destroy();
});
walls = [];
coins.forEach(function (coin) {
coin.destroy();
});
coins = [];
if (player) {
player.destroy();
player = null;
}
levelComplete = false;
isDragging = false;
}
// Loads a level by index
function loadLevel(levelIndex) {
clearLevel();
if (levelIndex >= mazeLevels.length) {
// Handle case where we run out of predefined levels but haven't reached totalLevels
console.log("Warning: Ran out of predefined maze data, repeating last level.");
levelIndex = mazeLevels.length - 1; // Repeat the last available level
// Ideally, generate levels procedurally or add more data.
if (levelIndex < 0) {
// Should not happen if mazeLevels has data
LK.showGameOver(); // No levels defined at all
return;
}
}
currentLevelIndex = levelIndex; // Update the global index tracker
var levelData = mazeLevels[levelIndex];
// Create walls
levelData.walls.forEach(function (wallData) {
var wall = new Wall(wallData.width, wallData.height);
wall.x = wallData.x;
wall.y = wallData.y;
game.addChild(wall);
walls.push(wall);
});
// Create coins
levelData.coins.forEach(function (coinData) {
var coin = new Coin();
coin.x = coinData.x;
coin.y = coinData.y;
game.addChild(coin);
coins.push(coin);
});
// Create player
player = new Player();
player.x = levelData.startPos.x;
player.y = levelData.startPos.y;
game.addChild(player);
// Reset score and timer for the new level
score = 0;
timeLeft = levelTimeLimit;
updateScoreDisplay();
updateTimerDisplay();
updateLevelDisplay(); // Update level text
startTimer();
}
// Checks collision between player (at potential new position) and all walls
function checkWallCollision(targetX, targetY) {
if (!player) {
return true;
} // No player, collision is effectively true
var playerRadius = player.width / 2; // Assuming player is a circle
// Create a hypothetical bounding box for the player at the target position
var playerBounds = {
left: targetX - playerRadius,
right: targetX + playerRadius,
top: targetY - playerRadius,
bottom: targetY + playerRadius
};
for (var i = 0; i < walls.length; i++) {
var wall = walls[i];
var wallBounds = {
left: wall.x,
right: wall.x + wall.wallWidth,
// Use stored dimensions
top: wall.y,
bottom: wall.y + wall.wallHeight // Use stored dimensions
};
// Basic AABB collision check
if (playerBounds.right > wallBounds.left && playerBounds.left < wallBounds.right && playerBounds.bottom > wallBounds.top && playerBounds.top < wallBounds.bottom) {
return true; // Collision detected
}
}
return false; // No collision
}
// Updates the score display
function updateScoreDisplay() {
scoreTxt.setText("Coins: " + score + "/" + requiredCoinsPerLevel);
}
// Updates the timer display
function updateTimerDisplay() {
timerTxt.setText("Time: " + timeLeft);
}
// Updates the level display
function updateLevelDisplay() {
levelTxt.setText("Level: " + (currentLevelIndex + 1));
}
// Timer tick function
function handleTimerTick() {
if (levelComplete) {
return;
} // Stop timer if level is done
timeLeft--;
updateTimerDisplay();
if (timeLeft <= 0) {
LK.clearInterval(timerInterval);
timerInterval = null;
LK.showGameOver(); // LK handles the game over state
}
}
// Starts the level timer
function startTimer() {
if (timerInterval) {
LK.clearInterval(timerInterval);
}
timeLeft = levelTimeLimit; // Reset time
updateTimerDisplay();
timerInterval = LK.setInterval(handleTimerTick, 1000);
}
// Stops the level timer
function stopTimer() {
if (timerInterval) {
LK.clearInterval(timerInterval);
timerInterval = null;
}
}
// --- GUI Setup ---
// Score display
scoreTxt = new Text2('Coins: 0/' + requiredCoinsPerLevel, {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0); // Anchor middle-top
LK.gui.top.addChild(scoreTxt); // Add to top-center GUI area
scoreTxt.y = 20; // Add some padding from the top edge
// Timer display
timerTxt = new Text2('Time: ' + levelTimeLimit, {
size: 60,
fill: 0xFFFFFF
});
timerTxt.anchor.set(1, 0); // Anchor top-right
LK.gui.topRight.addChild(timerTxt); // Add to top-right GUI area
timerTxt.y = 20; // Match score padding
timerTxt.x = -20; // Add some padding from the right edge
// --- Event Handlers ---
// Level display
levelTxt = new Text2('Level: 1', {
size: 60,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0, 0); // Anchor top-left
LK.gui.topLeft.addChild(levelTxt); // Add to top-left GUI area (near pause)
levelTxt.x = 110; // Offset from the very corner (reserved area)
levelTxt.y = 20; // Match other UI padding
game.down = function (x, y, obj) {
if (levelComplete) {
// If level is complete, tap anywhere to proceed
currentLevelIndex++;
if (currentLevelIndex < totalLevels) {
loadLevel(currentLevelIndex);
} else {
LK.showYouWin(); // Completed all levels
}
return; // Don't process drag start
}
// Check if the press is on the player
var playerPos = player.position;
var dx = x - playerPos.x;
var dy = y - playerPos.y;
var distSq = dx * dx + dy * dy;
var playerRadius = player.width / 2;
// Allow starting drag if touching the player
// Add a small tolerance for easier touch
if (distSq < playerRadius * playerRadius * 1.5 * 1.5) {
isDragging = true;
// Calculate offset from player center to touch point
// The x, y arguments are already in the game's local coordinate system
dragOffset.x = player.x - x;
dragOffset.y = player.y - y;
} else {
isDragging = false;
}
};
game.move = function (x, y, obj) {
if (!isDragging || levelComplete || !player) {
return;
}
// The x, y arguments are already in the game's local coordinate system
var targetX = x + dragOffset.x;
var targetY = y + dragOffset.y;
// Check for collisions *before* moving
if (!checkWallCollision(targetX, targetY)) {
player.x = targetX;
player.y = targetY;
} else {
// Optional: Try moving only horizontally or vertically if diagonal move fails
// Check X movement only
if (!checkWallCollision(targetX, player.y)) {
player.x = targetX;
}
// Check Y movement only
if (!checkWallCollision(player.x, targetY)) {
player.y = targetY;
}
// If both fail, the player stays put
}
};
game.up = function (x, y, obj) {
isDragging = false;
};
// --- Game Update Loop ---
game.update = function () {
if (levelComplete || !player) {
return;
} // Don't update if level is done or no player
// Check for coin collection
var playerRadius = player.width / 2;
for (var i = coins.length - 1; i >= 0; i--) {
var coin = coins[i];
var dx = player.x - coin.x;
var dy = player.y - coin.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var coinRadius = coin.width / 2;
if (distance < playerRadius + coinRadius) {
// Collision detected - collect coin
score++;
updateScoreDisplay();
coin.destroy();
coins.splice(i, 1);
// Check for level completion
if (score >= requiredCoinsPerLevel) {
levelComplete = true;
stopTimer();
// Optionally show a "Level Clear! Tap to continue" message
// For now, just rely on the game.down handler checking levelComplete
console.log("Level " + (currentLevelIndex + 1) + " Complete!");
// Note: LK engine might handle transitions, but the prompt asks for tap to continue.
if (currentLevelIndex + 1 >= totalLevels) {
// If this was the last level according to totalLevels count
LK.showYouWin();
}
// Otherwise, wait for tap in game.down
break; // Exit loop after collecting a coin and potentially completing level
}
}
}
};
// --- Start Game ---
loadLevel(currentLevelIndex); /****
* Classes
****/
// Coin class
var Coin = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.rotationSpeed = 0.05; // Radians per frame
self.update = function () {
self.rotation += self.rotationSpeed;
};
return self;
});
// Base size, will be scaled in code
// No plugins needed for this version. Tween could be used for rotation, but manual rotation is sufficient.
// var tween = LK.import('@upit/tween.v1');
// Player class
var Player = Container.expand(function () {
var self = Container.call(this);
var graphics = self.attachAsset('player', {
anchorX: 0.5,
anchorY: 0.5
});
// No update needed here as movement is handled in the main game loop
return self;
});
// Wall class
var Wall = Container.expand(function (width, height) {
var self = Container.call(this);
// We get a base wall asset and scale it to the desired dimensions
var graphics = self.attachAsset('wall', {
anchorX: 0.0,
// Anchor top-left for easier positioning and scaling
anchorY: 0.0,
width: width,
// Set the actual width
height: height // Set the actual height
});
// Store dimensions for collision checks
self.wallWidth = width;
self.wallHeight = height;
// No update needed for static walls
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x111111 // Dark background
});
/****
* Game Code
****/
// Game constants and variables
// Define assets used in the game. These will be automatically created and loaded.
function _typeof2(o) {
"@babel/helpers - typeof";
return _typeof2 = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof2(o);
}
function _defineProperty2(e, r, t) {
return (r = _toPropertyKey2(r)) in e ? Object.defineProperty(e, r, {
value: t,
enumerable: !0,
configurable: !0,
writable: !0
}) : e[r] = t, e;
}
function _toPropertyKey2(t) {
var i = _toPrimitive2(t, "string");
return "symbol" == _typeof2(i) ? i : i + "";
}
function _toPrimitive2(t, r) {
if ("object" != _typeof2(t) || !t) {
return t;
}
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r || "default");
if ("object" != _typeof2(i)) {
return i;
}
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
} : function (o) {
return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o;
}, _typeof(o);
}
function _defineProperty(e, r, t) {
return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, {
value: t,
enumerable: !0,
configurable: !0,
writable: !0
}) : e[r] = t, e;
}
function _toPropertyKey(t) {
var i = _toPrimitive(t, "string");
return "symbol" == _typeof(i) ? i : i + "";
}
function _toPrimitive(t, r) {
if ("object" != _typeof(t) || !t) {
return t;
}
var e = t[Symbol.toPrimitive];
if (void 0 !== e) {
var i = e.call(t, r || "default");
if ("object" != _typeof(i)) {
return i;
}
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return ("string" === r ? String : Number)(t);
}
var gameWidth = 2048;
var gameHeight = 2732;
var levelTimeLimit = 60; // Seconds
var totalLevels = 10; // As per description
var requiredCoinsPerLevel = 10;
// Maze definitions for 10 levels with increasing difficulty.
// Each level: walls array [{x, y, width, height}], coins array [{x, y}], startPos {x, y}
var mazeLevels = [
// Level 1: Spiral Maze
{
walls: [
// --- Outer Borders ---
{
x: 100,
y: 200,
width: 1848,
height: 50
},
// Top
{
x: 100,
y: 2482,
width: 1848,
height: 50
},
// Bottom
{
x: 100,
y: 250,
width: 50,
height: 2232
},
// Left
{
x: 1898,
y: 250,
width: 50,
height: 2232
},
// Right
// --- Spiral Arm 1 (Outer) ---
{
x: 150,
y: 450,
width: 1600,
height: 50
},
// H1: Top segment (Leaves gap right: 1750-1898)
{
x: 1750,
y: 500,
width: 50,
height: 1800
},
// V1: Right segment (Leaves gap bottom: 2300-2482)
{
x: 300,
y: 2300,
width: 1500,
height: 50
},
// H2: Bottom segment (Leaves gap left: 150-300)
{
x: 300,
y: 600,
width: 50,
height: 1700
},
// V2: Left segment (Leaves gap top: 450-600)
// --- Spiral Arm 2 (Inner) --- Path width ~150px
{
x: 450,
y: 600,
width: 1150,
height: 50
},
// H3: Top segment (Leaves gap right: 1600-1750)
{
x: 1600,
y: 650,
width: 50,
height: 1500
},
// V3: Right segment (Leaves gap bottom: 2150-2300)
{
x: 450,
y: 2150,
width: 1200,
height: 50
},
// H4: Bottom segment (Leaves gap left: 300-450)
{
x: 450,
y: 750,
width: 50,
height: 1400
},
// V4: Left segment (Leaves gap top: 600-750)
// --- Spiral Arm 3 (Inner) --- Path width ~150px
{
x: 600,
y: 750,
width: 850,
height: 50
},
// H5: Top segment (Leaves gap right: 1450-1600)
{
x: 1450,
y: 800,
width: 50,
height: 1200
},
// V5: Right segment (Leaves gap bottom: 2000-2150)
{
x: 600,
y: 2000,
width: 900,
height: 50
},
// H6: Bottom segment (Leaves gap left: 450-600)
{
x: 600,
y: 900,
width: 50,
height: 1100
},
// V6: Left segment (Leaves gap top: 750-900)
// --- Spiral Arm 4 (Innermost) --- Path width ~150px
{
x: 750,
y: 900,
width: 550,
height: 50
},
// H7: Top segment (Leaves gap right: 1300-1450)
{
x: 1300,
y: 950,
width: 50,
height: 900
},
// V7: Right segment (Leaves gap bottom: 1850-2000)
{
x: 750,
y: 1850,
width: 600,
height: 50
},
// H8: Bottom segment (Leaves gap left: 600-750)
{
x: 750,
y: 1050,
width: 50,
height: 800
},
// V8: Left segment (Leaves gap top: 900-1050)
// --- Center Area Termination --- Blocks the direct path to the center
{
x: 900,
y: 1050,
width: 250,
height: 50
},
// Central H block
{
x: 1100,
y: 1100,
width: 50,
height: 600
} // Central V block
],
coins: [
// Place coins along the spiral path
{
x: 1000,
y: 350
},
// Path 1 Top
{
x: 1825,
y: 1000
},
// Path 1 Right
{
x: 1000,
y: 2400
},
// Path 1 Bottom
{
x: 225,
y: 1500
},
// Path 1 Left
{
x: 1000,
y: 675
},
// Path 2 Top
{
x: 1675,
y: 1500
},
// Path 2 Right
{
x: 1000,
y: 2075
},
// Path 2 Bottom
{
x: 525,
y: 1500
},
// Path 2 Left
{
x: 1000,
y: 975
},
// Path 3 Top
{
x: 1024,
y: 1400
} // Center area (end of spiral)
],
startPos: {
x: 200,
y: 350
} // Start at the entrance near top-left
},
// Level 2: Hypnotic Maze with Doors (Harder)
{
walls: [
// --- Outer Borders (Solid) ---
{
x: 100,
y: 200,
width: 1848,
height: 50
},
// Top
{
x: 100,
y: 2482,
width: 1848,
height: 50
},
// Bottom
{
x: 100,
y: 250,
width: 50,
height: 2232
},
// Left
{
x: 1898,
y: 250,
width: 50,
height: 2232
},
// Right
// --- Layer 1 (Outer Square - Gap Top) --- approx 200px path
{
x: 300,
y: 400,
width: 700,
height: 50
},
// Top Left
{
x: 1150,
y: 400,
width: 700,
height: 50
},
// Top Right (Gap: 1000-1150)
{
x: 300,
y: 2282,
width: 1600,
height: 50
},
// Bottom Full
{
x: 300,
y: 450,
width: 50,
height: 1832
},
// Left Full
// --- Layer 2 (Gap Left) --- approx 200px path
{
x: 500,
y: 600,
width: 1300,
height: 50
},
// Top Full
{
x: 500,
y: 2082,
width: 1300,
height: 50
},
// Bottom Full
{
x: 500,
y: 650,
width: 50,
height: 650
},
// Left Top (Gap: 1300-1432)
{
//{4B} // Reused identifier
x: 500,
y: 1432,
width: 50,
height: 650
},
// Left Bottom
// --- Layer 3 (Gap Bottom) --- approx 200px path
{
x: 700,
y: 800,
width: 900,
height: 50
},
// Top Full
{
x: 700,
y: 1882,
width: 400,
height: 50
},
// Bottom Left (Gap: 1100-1250)
{
//{4G} // Reused identifier
x: 1250,
y: 1882,
width: 350,
height: 50
},
// Bottom Right
{
x: 700,
y: 850,
width: 50,
height: 1032
},
// Left Full
{
x: 1598,
// Adjusted right wall x
y: 850,
width: 50,
height: 1032
},
// Right Full
// --- Layer 4 (Gap Right) --- approx 200px path
{
//{5A} // Reused identifier
x: 900,
y: 1000,
width: 500,
height: 50
},
// Top Full
{
//{5G} // Reused identifier
x: 900,
y: 1682,
width: 500,
height: 50
},
// Bottom Full
{
//{5L} // Reused identifier
x: 900,
y: 1050,
width: 50,
height: 632
},
// Left Full
{
//{5Q} // Reused identifier
x: 1398,
y: 1050,
width: 50,
height: 250 // Right Top (Gap: 1300-1432)
}, {
//{5U} // Reused identifier
x: 1398,
y: 1432,
width: 50,
height: 250 // Right Bottom
},
// --- Central Area (Solid Block) ---
{
//{5F} // Reused identifier
x: 1100,
y: 1200,
width: 200,
height: 282 // Adjusted to be smaller central block
},
// --- Added Dead Ends ---
// Removed small horizontal wall in bottom right
{}],
//{6D} // End walls array
coins: [
// Place coins in accessible locations for the harder maze
{
x: 1800,
y: 2400
},
// Near start
{
x: 400,
y: 2182
},
// Path Layer 1 Bottom Left
{
x: 400,
y: 500
},
// Path Layer 2 Top Left
{
x: 600,
y: 1366 // Near Layer 2 Left Gap
}, {
x: 1700,
y: 700
},
// Path Layer 2 Top Right
{
x: 1700,
y: 1982
},
// Path Layer 3 Bottom Right (near dead end)
{
x: 800,
y: 1982 // Near Layer 3 Bottom Gap
}, {
x: 800,
y: 900
},
// Path Layer 3 Top Left
{
x: 1000,
y: 1100
},
// Path Layer 4 Top Left
{
x: 1300,
y: 1366 // Center Area (final coin)
}],
// End coins
startPos: {
x: 1800,
// Bottom right x
y: 2400 // Bottom right y
} // Start bottom right corner //{83} //{84}
},
// Level 3: S-Shape Path with Doors
{
walls: [{
x: 100,
y: 200,
width: 1848,
height: 50
},
// Top
{
x: 100,
y: 2482,
width: 1848,
height: 50
},
// Bottom
{
x: 100,
y: 250,
width: 50,
height: 2232
},
// Left
{
x: 1898,
y: 250,
width: 50,
height: 2232
},
// Right
// S-shape walls with gaps (doors)
// Top H segment (split for door)
{
x: 100,
y: 700,
width: 650,
// Ends at 750
height: 50
}, {
x: 900,
// Starts at 900 (gap 750-900)
y: 700,
width: 650,
// Ends at 1550
height: 50
},
// Middle H segment (split for door)
{
x: 500,
y: 1200,
width: 650,
// Ends at 1150
height: 50
}, {
x: 1300,
// Starts at 1300 (gap 1150-1300)
y: 1200,
width: 648,
// Ends at 1948
height: 50
},
// Bottom H segment (split for door)
{
x: 100,
y: 1700,
width: 650,
// Ends at 750
height: 50
}, {
x: 900,
// Starts at 900 (gap 750-900)
y: 1700,
width: 650,
// Ends at 1550
height: 50
},
// V segment 1 (top right) - Keep solid for now, rely on H gap
{
x: 1500,
y: 250,
width: 50,
height: 450
},
// V segment 2 (middle left - split for door)
{
x: 500,
y: 750,
width: 50,
height: 200 // Ends at 950
}, {
x: 500,
y: 1050,
// Starts at 1050 (gap 950-1050)
width: 50,
height: 150 // Ends at 1200
},
// V segment 3 (middle right - split for door)
{
x: 1500,
y: 1250,
width: 50,
height: 200 // Ends at 1450
}, {
x: 1500,
y: 1550,
// Starts at 1550 (gap 1450-1550)
width: 50,
height: 150 // Ends at 1700
},
// V segment 4 (bottom left) - Split to create door (gap y:2050-2200)
{
// V segment 4 Top part
x: 500,
y: 1750,
width: 50,
height: 300 // Ends at y=2050
}, {
// V segment 4 Bottom part
x: 500,
y: 2200,
// Starts after gap
width: 50,
height: 282 // Ends at y=2482
}],
coins: [{
x: 300,
y: 300
}, {
x: 1700,
y: 450
}, {
x: 300,
y: 950
}, {
x: 1700,
y: 950
}, {
x: 300,
y: 1450
}, {
x: 1700,
y: 1450
}, {
x: 300,
y: 2000
}, {
x: 1700,
y: 2000
}, {
x: 1000,
y: 1450
}, {
x: 1000,
y: 950
}],
startPos: {
x: 200,
y: 300
}
},
// Level 4: Asymmetric Cross (Modified Doors)
{
walls: [
// --- Outer Borders ---
{
x: 100,
y: 200,
width: 1848,
height: 50
},
// Top
{
x: 100,
y: 2482,
width: 1848,
height: 50
},
// Bottom
{
x: 100,
y: 250,
width: 50,
height: 2232
},
// Left
{
x: 1898,
y: 250,
width: 50,
height: 2232
},
// Right
// --- Internal Walls ---
// Horizontal dividers
// Top H (Gap left: 100-600 -> Wider Door 1)
{
x: 600,
//{9o} // Increased start X
y: 800,
width: 1348,
//{9q} // Adjusted width
height: 50
},
// Bottom H (Gap right: 1448-1898 -> Wider Door 2, split for new door)
// Part 1 (Ends at x=500)
{
x: 100,
y: 1900,
width: 400,
//{9w} // Shorter width
height: 50
},
// Part 2 (Starts at x=700, ends at 1448 -> Gap x:500-700 = New Door Bottom-Left)
{
x: 700,
y: 1900,
width: 748,
// Covers remaining section leaving gap
height: 50
},
// Vertical dividers
// Left V (Gap top: 250-950 -> Wider Door 3)
{
x: 600,
y: 950,
//{9C} // Increased start Y
width: 50,
height: 1532 // Adjusted height
},
// Right V (Gap bottom: 1750-2482 -> Wider Door 4, split for new door)
// Part 1 (Ends at y=700)
{
x: 1400,
y: 250,
width: 50,
height: 450 // Shorter height
},
// Part 2 (Starts at y=900, ends at 1750 -> Gap y:700-900 = New Door Top-Right)
{
x: 1400,
y: 900,
width: 50,
height: 850 // Covers remaining section leaving gap
},
// Twist/Blocking walls (Unchanged)
{
x: 150,
y: 1000,
width: 400,
height: 50
},
// Twist block top-left section
{
x: 1450,
y: 1000,
width: 400,
height: 50
},
// Twist block top-right section
{
x: 150,
y: 1700,
width: 400,
height: 50
},
// Twist block bottom-left section
{
x: 1450,
y: 1700,
width: 400,
height: 50
} // Twist block bottom-right section
],
coins: [{
x: 300,
y: 500
},
// Top Left area (before twist)
{
x: 300,
y: 1300
},
// Top Left area (after twist)
{
x: 1700,
y: 500
},
// Top Right area (before twist)
{
x: 1700,
y: 1300
},
// Top Right area (after twist)
{
x: 300,
y: 2200
},
// Bottom Left area (near corner)
{
x: 300,
y: 1800
},
// Bottom Left area (after twist)
{
x: 1700,
y: 2200
},
// Bottom Right area (near corner)
{
x: 1700,
y: 1800
},
// Bottom Right area (after twist)
{
x: 1024,
y: 1000
},
// Center Top area
{
x: 1024,
y: 1500
} // Center Bottom area
],
startPos: {
x: 200,
y: 300
} // Start Top Left
},
// Level 5: Central Hub Branching
{
walls: [{
x: 100,
y: 200,
width: 1848,
height: 50
},
// Top
{
x: 100,
y: 2482,
width: 1848,
height: 50
},
// Bottom
{
x: 100,
y: 250,
width: 50,
height: 2232
},
// Left
{
x: 1898,
y: 250,
width: 50,
height: 2232
},
// Right
// Central Hub Walls (creating a box ~800x800 in center)
{
x: 624,
y: 966,
width: 800,
height: 50
},
// Hub Top
{
x: 624,
y: 1716,
width: 800,
height: 50
},
// Hub Bottom
{
x: 624,
y: 1016,
width: 50,
height: 700
},
// Hub Left
{
x: 1374,
y: 1016,
width: 50,
height: 700
},
// Hub Right
// Branch Walls (connecting hub/borders to create paths)
{
x: 150,
y: 600,
width: 1700,
height: 50
},
// Top Branch Divider
{
x: 150,
y: 2066,
width: 1700,
height: 50
},
// Bottom Branch Divider
{
x: 350,
y: 650,
width: 50,
height: 1416
},
// Left Branch Divider (Inner)
{
x: 1648,
y: 650,
width: 50,
height: 1416
},
// Right Branch Divider (Inner)
// Add some dead ends or twists within branches
{
x: 150,
y: 1341,
width: 150,
height: 50
},
// Left branch block
{
x: 1748,
y: 1341,
width: 150,
height: 50
},
// Right branch block
{
x: 1000,
y: 250,
width: 50,
height: 300
},
// Top branch block
{
x: 1000,
y: 2116,
width: 50,
height: 300
} // Bottom branch block
],
coins: [{
x: 1024,
y: 1341
},
// Center Hub
{
x: 250,
y: 400
},
// Top Left Branch
{
x: 1798,
y: 400
},
// Top Right Branch
{
x: 250,
y: 2250
},
// Bottom Left Branch
{
x: 1798,
y: 2250
},
// Bottom Right Branch
{
x: 250,
y: 1341
},
// Mid Left Branch end
{
x: 1798,
y: 1341
},
// Mid Right Branch end
{
x: 1024,
y: 350
},
// Mid Top Branch end
{
x: 1024,
y: 2300
},
// Mid Bottom Branch end
{
x: 800,
y: 1500
} // Inside Hub offset
],
startPos: {
x: 1024,
y: 1341
} // Start in Center Hub
},
// Level 6: More complex branching/connections
{
walls: [{
x: 100,
y: 200,
width: 1848,
height: 50
}, {
x: 100,
y: 2482,
width: 1848,
height: 50
}, {
x: 100,
y: 250,
width: 50,
height: 2232
}, {
x: 1898,
y: 250,
width: 50,
height: 2232
},
// Horizontal dividers
{
x: 100,
y: 800,
width: 800,
height: 50
}, {
x: 1148,
y: 800,
width: 800,
height: 50
}, {
x: 100,
y: 1300,
width: 1848,
height: 50
}, {
x: 100,
y: 1800,
width: 800,
height: 50
}, {
x: 1148,
y: 1800,
width: 800,
height: 50
},
// Vertical dividers
{
x: 500,
y: 250,
width: 50,
height: 500
}, {
x: 500,
y: 850,
width: 50,
height: 400
}, {
x: 500,
y: 1350,
width: 50,
height: 400
}, {
x: 500,
y: 1850,
width: 50,
height: 632
}, {
x: 1500,
y: 250,
width: 50,
height: 500
}, {
x: 1500,
y: 850,
width: 50,
height: 400
}, {
x: 1500,
y: 1350,
width: 50,
height: 400
}, {
x: 1500,
y: 1850,
width: 50,
height: 632
}],
coins: [{
x: 300,
y: 300
}, {
x: 1024,
y: 300
}, {
x: 1748,
y: 300
}, {
x: 300,
y: 1050
}, {
x: 1748,
y: 1050
}, {
x: 1024,
y: 1550
},
// Center area coin
{
x: 300,
y: 2100
}, {
x: 1748,
y: 2100
}, {
x: 800,
y: 500
}, {
x: 1248,
y: 2000
}],
startPos: {
x: 1024,
y: 1550
} // Start near center
},
// Level 7: Grid with missing segments
{
walls: [{
x: 100,
y: 200,
width: 1848,
height: 50
}, {
x: 100,
y: 2482,
width: 1848,
height: 50
}, {
x: 100,
y: 250,
width: 50,
height: 2232
}, {
x: 1898,
y: 250,
width: 50,
height: 2232
},
// Grid lines (approx every 450px)
{
x: 100,
y: 650,
width: 1848,
height: 50
}, {
x: 100,
y: 1100,
width: 1848,
height: 50
}, {
x: 100,
y: 1550,
width: 1848,
height: 50
}, {
x: 100,
y: 2000,
width: 1848,
height: 50
}, {
x: 550,
y: 250,
width: 50,
height: 2232
}, {
x: 1000,
y: 250,
width: 50,
height: 2232
}, {
x: 1450,
y: 250,
width: 50,
height: 2232
}
// Remove segments to create path
// (For simplicity, just adding coins in grid cells)
],
coins: [
// Place coins in different grid cells
{
x: 325,
y: 425
}, {
x: 775,
y: 425
}, {
x: 1225,
y: 425
}, {
x: 1675,
y: 425
}, {
x: 325,
y: 875
}, {
x: 1675,
y: 875
},
// Skip middle cells
{
x: 325,
y: 1325
}, {
x: 1675,
y: 1325
}, {
x: 775,
y: 1775
}, {
x: 1225,
y: 2225
}],
startPos: {
x: 325,
y: 2225
} // Start bottom left
},
// Level 8: Spiral (Approximation with Rectangles)
{
walls: [{
x: 100,
y: 200,
width: 1848,
height: 50
}, {
x: 100,
y: 2482,
width: 1848,
height: 50
}, {
x: 100,
y: 250,
width: 50,
height: 2232
}, {
x: 1898,
y: 250,
width: 50,
height: 2232
},
// Outer spiral path
{
x: 100,
y: 700,
width: 1600,
height: 50
},
// H1
{
x: 1650,
y: 250,
width: 50,
height: 900
},
// V1
{
x: 300,
y: 1150,
width: 1400,
height: 50
},
// H2
{
x: 250,
y: 750,
width: 50,
height: 850
},
// V2
{
x: 250,
y: 1600,
width: 1200,
height: 50
},
// H3
{
x: 1450,
y: 1150,
width: 50,
height: 900
},
// V3
{
x: 450,
y: 2050,
width: 1050,
height: 50
},
// H4 (inner end)
{
x: 400,
y: 1650,
width: 50,
height: 400
} // V4 (inner end)
],
coins: [
// Place along the spiral path
{
x: 200,
y: 400
}, {
x: 1000,
y: 400
}, {
x: 1800,
y: 400
},
// Outer Top
{
x: 1800,
y: 900
}, {
x: 1800,
y: 1400
},
// Right Down
{
x: 1000,
y: 900
},
// Inner loop 1
{
x: 400,
y: 900
}, {
x: 400,
y: 1400
},
// Left Down/Up
{
x: 400,
y: 1800
}, {
x: 1000,
y: 1800
} // Inner end area
],
startPos: {
x: 200,
y: 300
} // Start near entrance
},
// Level 9: Dense Obstacles
{
walls: [{
x: 100,
y: 200,
width: 1848,
height: 50
}, {
x: 100,
y: 2482,
width: 1848,
height: 50
}, {
x: 100,
y: 250,
width: 50,
height: 2232
}, {
x: 1898,
y: 250,
width: 50,
height: 2232
},
// Many small walls creating tight paths
{
x: 200,
y: 400,
width: 200,
height: 50
}, {
x: 500,
y: 400,
width: 200,
height: 50
}, {
x: 800,
y: 400,
width: 200,
height: 50
}, {
x: 1100,
y: 400,
width: 200,
height: 50
}, {
x: 1400,
y: 400,
width: 200,
height: 50
}, {
x: 1700,
y: 400,
width: 200,
height: 50
}, {
x: 300,
y: 600,
width: 50,
height: 200
}, {
x: 600,
y: 600,
width: 50,
height: 200
}, {
x: 900,
y: 600,
width: 50,
height: 200
}, {
x: 1200,
y: 600,
width: 50,
height: 200
}, {
x: 1500,
y: 600,
width: 50,
height: 200
}, {
x: 1800,
y: 600,
width: 50,
height: 200
},
// ... Continue pattern downwards ...
{
x: 200,
y: 900,
width: 200,
height: 50
}, {
x: 500,
y: 900,
width: 200,
height: 50
},
// etc.
{
x: 300,
y: 1100,
width: 50,
height: 200
}, {
x: 600,
y: 1100,
width: 50,
height: 200
},
// etc.
{
x: 200,
y: 1400,
width: 200,
height: 50
}, {
x: 500,
y: 1400,
width: 200,
height: 50
},
// etc.
{
x: 300,
y: 1600,
width: 50,
height: 200
}, {
x: 600,
y: 1600,
width: 50,
height: 200
},
// etc.
{
x: 200,
y: 1900,
width: 200,
height: 50
}, {
x: 500,
y: 1900,
width: 200,
height: 50
},
// etc.
{
x: 300,
y: 2100,
width: 50,
height: 200
}, {
x: 600,
y: 2100,
width: 50,
height: 200
},
// etc.
// Mirror pattern on right side
{
x: 1100,
y: 900,
width: 200,
height: 50
}, {
x: 1400,
y: 900,
width: 200,
height: 50
}, {
x: 1700,
y: 900,
width: 200,
height: 50
}, {
x: 1200,
y: 1100,
width: 50,
height: 200
}, {
x: 1500,
y: 1100,
width: 50,
height: 200
}, {
x: 1800,
y: 1100,
width: 50,
height: 200
}, {
x: 1100,
y: 1400,
width: 200,
height: 50
}, {
x: 1400,
y: 1400,
width: 200,
height: 50
}, {
x: 1700,
y: 1400,
width: 200,
height: 50
}, {
x: 1200,
y: 1600,
width: 50,
height: 200
}, {
x: 1500,
y: 1600,
width: 50,
height: 200
}, {
x: 1800,
y: 1600,
width: 50,
height: 200
}, {
x: 1100,
y: 1900,
width: 200,
height: 50
}, {
x: 1400,
y: 1900,
width: 200,
height: 50
}, {
x: 1700,
y: 1900,
width: 200,
height: 50
}, {
x: 1200,
y: 2100,
width: 50,
height: 200
}, {
x: 1500,
y: 2100,
width: 50,
height: 200
}, {
x: 1800,
y: 2100,
width: 50,
height: 200
}],
coins: [
// Place in tricky spots within the dense grid
{
x: 250,
y: 300
}, {
x: 1800,
y: 300
}, {
x: 400,
y: 700
}, {
x: 1650,
y: 700
}, {
x: 750,
y: 1000
}, {
x: 1300,
y: 1000
}, {
x: 250,
y: 1500
}, {
x: 1800,
y: 1500
}, {
x: 400,
y: 2200
}, {
x: 1650,
y: 2200
}],
startPos: {
x: 1024,
y: 300
} // Start top center
},
// Level 10: Final Challenge - Combination
{
walls: [{
x: 100,
y: 200,
width: 1848,
height: 50
}, {
x: 100,
y: 2482,
width: 1848,
height: 50
}, {
x: 100,
y: 250,
width: 50,
height: 2232
}, {
x: 1898,
y: 250,
width: 50,
height: 2232
},
// Mix of grid, long paths, tight spots
// Section 1 (Top Grid)
{
x: 100,
y: 700,
width: 1848,
height: 50
}, {
x: 550,
y: 250,
width: 50,
height: 450
}, {
x: 1000,
y: 250,
width: 50,
height: 450
}, {
x: 1450,
y: 250,
width: 50,
height: 450
},
// Section 2 (Middle Maze)
{
x: 100,
y: 1200,
width: 800,
height: 50
}, {
x: 1148,
y: 1200,
width: 800,
height: 50
}, {
x: 500,
y: 750,
width: 50,
height: 900
}, {
x: 1500,
y: 750,
width: 50,
height: 900
}, {
x: 100,
y: 1650,
width: 1848,
height: 50
},
// Section 3 (Bottom Spiral-like element)
{
x: 300,
y: 2100,
width: 1448,
height: 50
}, {
x: 1748,
y: 1700,
width: 50,
height: 400
}, {
x: 300,
y: 1700,
width: 50,
height: 400
}, {
x: 500,
y: 1900,
width: 1048,
height: 50
} // Inner H
],
coins: [{
x: 325,
y: 400
}, {
x: 1675,
y: 400
},
// Top Grid
{
x: 200,
y: 900
}, {
x: 1800,
y: 900
},
// Mid sides
{
x: 1024,
y: 1400
},
// Mid center
{
x: 400,
y: 1800
}, {
x: 1600,
y: 1800
},
// Lower Mid
{
x: 200,
y: 2300
}, {
x: 1800,
y: 2300
},
// Bottom Corners
{
x: 1024,
y: 2000
} // Bottom Center (inner)
],
startPos: {
x: 1024,
y: 300
} // Start Top Center
}
// NOTE: Circular mazes are hard to represent with rectangular walls.
// This implementation uses only rectangular walls.
];
// Game state variables
var currentLevelIndex = 0;
var walls = [];
var coins = [];
var player = null;
var score = 0;
var timeLeft = levelTimeLimit;
var timerInterval = null;
var scoreTxt = null;
var timerTxt = null;
var levelTxt = null;
var levelComplete = false;
var isDragging = false;
var dragOffset = {
x: 0,
y: 0
}; // Offset between touch point and player center
// --- Helper Functions ---
// Clears current level elements
function clearLevel() {
walls.forEach(function (wall) {
wall.destroy();
});
walls = [];
coins.forEach(function (coin) {
coin.destroy();
});
coins = [];
if (player) {
player.destroy();
player = null;
}
levelComplete = false;
isDragging = false;
}
// Loads a level by index
function loadLevel(levelIndex) {
clearLevel();
if (levelIndex >= mazeLevels.length) {
// Handle case where we run out of predefined levels but haven't reached totalLevels
console.log("Warning: Ran out of predefined maze data, repeating last level.");
levelIndex = mazeLevels.length - 1; // Repeat the last available level
// Ideally, generate levels procedurally or add more data.
if (levelIndex < 0) {
// Should not happen if mazeLevels has data
LK.showGameOver(); // No levels defined at all
return;
}
}
currentLevelIndex = levelIndex; // Update the global index tracker
var levelData = mazeLevels[levelIndex];
// Create walls
levelData.walls.forEach(function (wallData) {
var wall = new Wall(wallData.width, wallData.height);
wall.x = wallData.x;
wall.y = wallData.y;
game.addChild(wall);
walls.push(wall);
});
// Create coins
levelData.coins.forEach(function (coinData) {
var coin = new Coin();
coin.x = coinData.x;
coin.y = coinData.y;
game.addChild(coin);
coins.push(coin);
});
// Create player
player = new Player();
player.x = levelData.startPos.x;
player.y = levelData.startPos.y;
game.addChild(player);
// Reset score and timer for the new level
score = 0;
timeLeft = levelTimeLimit;
updateScoreDisplay();
updateTimerDisplay();
updateLevelDisplay(); // Update level text
startTimer();
}
// Checks collision between player (at potential new position) and all walls
function checkWallCollision(targetX, targetY) {
if (!player) {
return true;
} // No player, collision is effectively true
var playerRadius = player.width / 2; // Assuming player is a circle
// Create a hypothetical bounding box for the player at the target position
var playerBounds = {
left: targetX - playerRadius,
right: targetX + playerRadius,
top: targetY - playerRadius,
bottom: targetY + playerRadius
};
for (var i = 0; i < walls.length; i++) {
var wall = walls[i];
var wallBounds = {
left: wall.x,
right: wall.x + wall.wallWidth,
// Use stored dimensions
top: wall.y,
bottom: wall.y + wall.wallHeight // Use stored dimensions
};
// Basic AABB collision check
if (playerBounds.right > wallBounds.left && playerBounds.left < wallBounds.right && playerBounds.bottom > wallBounds.top && playerBounds.top < wallBounds.bottom) {
return true; // Collision detected
}
}
return false; // No collision
}
// Updates the score display
function updateScoreDisplay() {
scoreTxt.setText("Coins: " + score + "/" + requiredCoinsPerLevel);
}
// Updates the timer display
function updateTimerDisplay() {
timerTxt.setText("Time: " + timeLeft);
}
// Updates the level display
function updateLevelDisplay() {
levelTxt.setText("Level: " + (currentLevelIndex + 1));
}
// Timer tick function
function handleTimerTick() {
if (levelComplete) {
return;
} // Stop timer if level is done
timeLeft--;
updateTimerDisplay();
if (timeLeft <= 0) {
LK.clearInterval(timerInterval);
timerInterval = null;
LK.showGameOver(); // LK handles the game over state
}
}
// Starts the level timer
function startTimer() {
if (timerInterval) {
LK.clearInterval(timerInterval);
}
timeLeft = levelTimeLimit; // Reset time
updateTimerDisplay();
timerInterval = LK.setInterval(handleTimerTick, 1000);
}
// Stops the level timer
function stopTimer() {
if (timerInterval) {
LK.clearInterval(timerInterval);
timerInterval = null;
}
}
// --- GUI Setup ---
// Score display
scoreTxt = new Text2('Coins: 0/' + requiredCoinsPerLevel, {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0); // Anchor middle-top
LK.gui.top.addChild(scoreTxt); // Add to top-center GUI area
scoreTxt.y = 20; // Add some padding from the top edge
// Timer display
timerTxt = new Text2('Time: ' + levelTimeLimit, {
size: 60,
fill: 0xFFFFFF
});
timerTxt.anchor.set(1, 0); // Anchor top-right
LK.gui.topRight.addChild(timerTxt); // Add to top-right GUI area
timerTxt.y = 20; // Match score padding
timerTxt.x = -20; // Add some padding from the right edge
// --- Event Handlers ---
// Level display
levelTxt = new Text2('Level: 1', {
size: 60,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0, 0); // Anchor top-left
LK.gui.topLeft.addChild(levelTxt); // Add to top-left GUI area (near pause)
levelTxt.x = 110; // Offset from the very corner (reserved area)
levelTxt.y = 20; // Match other UI padding
game.down = function (x, y, obj) {
if (levelComplete) {
// If level is complete, tap anywhere to proceed
currentLevelIndex++;
if (currentLevelIndex < totalLevels) {
loadLevel(currentLevelIndex);
} else {
LK.showYouWin(); // Completed all levels
}
return; // Don't process drag start
}
// Check if the press is on the player
var playerPos = player.position;
var dx = x - playerPos.x;
var dy = y - playerPos.y;
var distSq = dx * dx + dy * dy;
var playerRadius = player.width / 2;
// Allow starting drag if touching the player
// Add a small tolerance for easier touch
if (distSq < playerRadius * playerRadius * 1.5 * 1.5) {
isDragging = true;
// Calculate offset from player center to touch point
// The x, y arguments are already in the game's local coordinate system
dragOffset.x = player.x - x;
dragOffset.y = player.y - y;
} else {
isDragging = false;
}
};
game.move = function (x, y, obj) {
if (!isDragging || levelComplete || !player) {
return;
}
// The x, y arguments are already in the game's local coordinate system
var targetX = x + dragOffset.x;
var targetY = y + dragOffset.y;
// Check for collisions *before* moving
if (!checkWallCollision(targetX, targetY)) {
player.x = targetX;
player.y = targetY;
} else {
// Optional: Try moving only horizontally or vertically if diagonal move fails
// Check X movement only
if (!checkWallCollision(targetX, player.y)) {
player.x = targetX;
}
// Check Y movement only
if (!checkWallCollision(player.x, targetY)) {
player.y = targetY;
}
// If both fail, the player stays put
}
};
game.up = function (x, y, obj) {
isDragging = false;
};
// --- Game Update Loop ---
game.update = function () {
if (levelComplete || !player) {
return;
} // Don't update if level is done or no player
// Check for coin collection
var playerRadius = player.width / 2;
for (var i = coins.length - 1; i >= 0; i--) {
var coin = coins[i];
var dx = player.x - coin.x;
var dy = player.y - coin.y;
var distance = Math.sqrt(dx * dx + dy * dy);
var coinRadius = coin.width / 2;
if (distance < playerRadius + coinRadius) {
// Collision detected - collect coin
score++;
updateScoreDisplay();
coin.destroy();
coins.splice(i, 1);
// Check for level completion
if (score >= requiredCoinsPerLevel) {
levelComplete = true;
stopTimer();
// Optionally show a "Level Clear! Tap to continue" message
// For now, just rely on the game.down handler checking levelComplete
console.log("Level " + (currentLevelIndex + 1) + " Complete!");
// Note: LK engine might handle transitions, but the prompt asks for tap to continue.
if (currentLevelIndex + 1 >= totalLevels) {
// If this was the last level according to totalLevels count
LK.showYouWin();
}
// Otherwise, wait for tap in game.down
break; // Exit loop after collecting a coin and potentially completing level
}
}
}
};
// --- Start Game ---
loadLevel(currentLevelIndex);