/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Gem = Container.expand(function (type) { var self = Container.call(this); // Define gem types var gemTypes = ['red', 'blue', 'green', 'yellow', 'purple']; self.type = type || gemTypes[Math.floor(Math.random() * gemTypes.length)]; var graphics = self.attachAsset('gem_' + self.type, { anchorX: 0.5, anchorY: 0.5 }); self.velocity = { x: 0, y: 0 }; self.matched = false; self.special = false; self.mass = 1; // Make special gems 25% larger and add a pulse effect self.makeSpecial = function () { if (self.special) { return; } self.special = true; self.mass = 1.5; graphics.scaleX = 1.25; graphics.scaleY = 1.25; // Add a white glow using tint pulses function pulseTint() { tween(graphics, { alpha: 0.7 }, { duration: 700, easing: tween.sinceOut, onFinish: function onFinish() { tween(graphics, { alpha: 1 }, { duration: 700, easing: tween.sinceIn, onFinish: pulseTint }); } }); } pulseTint(); }; // Apply physics movement self.update = function () { // Apply velocity to position self.x += self.velocity.x; self.y += self.velocity.y; // Apply friction self.velocity.x *= 0.98; self.velocity.y *= 0.98; // Rotate gem based on movement graphics.rotation += Math.sqrt(Math.pow(self.velocity.x, 2) + Math.pow(self.velocity.y, 2)) * 0.01; // Bounce off edges if (self.x < graphics.width / 2) { self.x = graphics.width / 2; self.velocity.x *= -0.8; } else if (self.x > 2048 - graphics.width / 2) { self.x = 2048 - graphics.width / 2; self.velocity.x *= -0.8; } if (self.y < graphics.height / 2) { self.y = graphics.height / 2; self.velocity.y *= -0.8; } else if (self.y > 2732 - graphics.height / 2) { self.y = 2732 - graphics.height / 2; self.velocity.y *= -0.8; } }; return self; }); var GravityWell = Container.expand(function () { var self = Container.call(this); var rangeGraphics = self.attachAsset('gravity_range', { anchorX: 0.5, anchorY: 0.5, alpha: 0.1 }); var wellGraphics = self.attachAsset('gravity_well', { anchorX: 0.5, anchorY: 0.5 }); self.strength = 5; self.range = rangeGraphics.width / 2; self.active = true; // Pulse animation for the gravity well function startPulseAnimation() { tween(wellGraphics, { scaleX: 1.2, scaleY: 1.2 }, { duration: 1000, easing: tween.easeInOut, onFinish: function onFinish() { tween(wellGraphics, { scaleX: 1.0, scaleY: 1.0 }, { duration: 1000, easing: tween.easeInOut, onFinish: startPulseAnimation }); } }); } startPulseAnimation(); return self; }); var Particle = Container.expand(function (color) { var self = Container.call(this); var graphics = self.attachAsset('particle', { anchorX: 0.5, anchorY: 0.5 }); if (color) { graphics.tint = color; } self.velocity = { x: (Math.random() - 0.5) * 20, y: (Math.random() - 0.5) * 20 }; self.lifetime = 30 + Math.random() * 30; self.age = 0; self.update = function () { self.x += self.velocity.x; self.y += self.velocity.y; self.velocity.x *= 0.92; self.velocity.y *= 0.92; self.age++; // Fade out as the particle ages self.alpha = 1 - self.age / self.lifetime; return self.age < self.lifetime; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x111122 }); /**** * Game Code ****/ // Game state variables var gems = []; var gravityWells = []; var particles = []; var maxGems = 25; var maxGravityWells = 3; var score = 0; var level = storage.level || 1; var gemTypesInPlay = ['red', 'blue', 'green']; var gemsToMatch = 3; // UI elements var scoreTxt = new Text2('Score: 0', { size: 60, fill: 0xFFFFFF }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); var levelTxt = new Text2('Level: ' + level, { size: 60, fill: 0xFFFFFF }); levelTxt.anchor.set(0.5, 0); levelTxt.y = 70; LK.gui.top.addChild(levelTxt); var wellsLeftTxt = new Text2('Gravity Wells: ' + maxGravityWells, { size: 50, fill: 0xFFFFFF }); wellsLeftTxt.anchor.set(0, 0); wellsLeftTxt.x = 120; wellsLeftTxt.y = 20; LK.gui.topLeft.addChild(wellsLeftTxt); // Initialize game function initLevel() { // Clear existing objects for (var i = gems.length - 1; i >= 0; i--) { gems[i].destroy(); } gems = []; for (var i = gravityWells.length - 1; i >= 0; i--) { gravityWells[i].destroy(); } gravityWells = []; for (var i = particles.length - 1; i >= 0; i--) { particles[i].destroy(); } particles = []; // Update level if (level > 1) { // Unlock yellow gems at level 2 if (level >= 2 && gemTypesInPlay.indexOf('yellow') === -1) { gemTypesInPlay.push('yellow'); } // Unlock purple gems at level 4 if (level >= 4 && gemTypesInPlay.indexOf('purple') === -1) { gemTypesInPlay.push('purple'); } // Increase gem count as level increases maxGems = 25 + (level - 1) * 5; if (maxGems > 50) { maxGems = 50; } // Cap at 50 gems // Increase gravity wells with level maxGravityWells = 3 + Math.floor((level - 1) / 2); if (maxGravityWells > 7) { maxGravityWells = 7; } // Cap at 7 wells } // Update UI levelTxt.setText('Level: ' + level); wellsLeftTxt.setText('Gravity Wells: ' + maxGravityWells); scoreTxt.setText('Score: ' + score); // Create initial gems for (var i = 0; i < Math.min(10, maxGems); i++) { createGem(); } // Play background music LK.playMusic('bgmusic', { fade: { start: 0, end: 0.4, duration: 1000 } }); } // Create a new gem at a random position function createGem() { if (gems.length >= maxGems) { return; } var gem = new Gem(gemTypesInPlay[Math.floor(Math.random() * gemTypesInPlay.length)]); // Position randomly but not too close to the edges gem.x = 200 + Math.random() * (2048 - 400); gem.y = 200 + Math.random() * (2732 - 400); // Give a small random velocity gem.velocity.x = (Math.random() - 0.5) * 3; gem.velocity.y = (Math.random() - 0.5) * 3; // Make some gems special (about 10% chance) if (Math.random() < 0.1) { gem.makeSpecial(); } game.addChild(gem); gems.push(gem); return gem; } // Create particles at the specified position function createParticleExplosion(x, y, color, count) { for (var i = 0; i < count; i++) { var particle = new Particle(color); particle.x = x; particle.y = y; game.addChild(particle); particles.push(particle); } } // Check for gem matches function checkMatches() { // Group gems by type var gemGroups = {}; for (var i = 0; i < gems.length; i++) { var gem = gems[i]; if (gem.matched) { continue; } var type = gem.type; if (!gemGroups[type]) { gemGroups[type] = []; } gemGroups[type].push(gem); } // Check each group for matches var matchFound = false; for (var type in gemGroups) { var group = gemGroups[type]; for (var i = 0; i < group.length; i++) { var gem1 = group[i]; var matchingGems = [gem1]; for (var j = i + 1; j < group.length; j++) { var gem2 = group[j]; // Check distance between gems var dx = gem1.x - gem2.x; var dy = gem1.y - gem2.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 100) { matchingGems.push(gem2); } } // If we have enough matching gems if (matchingGems.length >= gemsToMatch) { matchFound = true; // Calculate center point of the match var centerX = 0; var centerY = 0; var specialGemFound = false; for (var k = 0; k < matchingGems.length; k++) { var gem = matchingGems[k]; centerX += gem.x; centerY += gem.y; gem.matched = true; if (gem.special) { specialGemFound = true; } } centerX /= matchingGems.length; centerY /= matchingGems.length; // Create particle explosion var color; switch (type) { case 'red': color = 0xff3333; break; case 'blue': color = 0x3333ff; break; case 'green': color = 0x33ff33; break; case 'yellow': color = 0xffff33; break; case 'purple': color = 0x9933ff; break; default: color = 0xffffff; } var particleCount = 20 + matchingGems.length * 5; if (specialGemFound) { particleCount *= 2; LK.getSound('special_gem').play(); } else { LK.getSound('match').play(); } createParticleExplosion(centerX, centerY, color, particleCount); // Award points var matchPoints = matchingGems.length * 10; if (specialGemFound) { matchPoints *= 3; } score += matchPoints; scoreTxt.setText('Score: ' + score); // Flash score text tween(scoreTxt, { alpha: 0.5 }, { duration: 100, easing: tween.linear, onFinish: function onFinish() { tween(scoreTxt, { alpha: 1 }, { duration: 100, easing: tween.linear }); } }); // Check level progression if (score >= level * 1000) { level++; storage.level = level; initLevel(); return; } // Create a new special gem if the match is large enough if (matchingGems.length >= 5 && !specialGemFound) { var specialGem = new Gem(type); specialGem.x = centerX; specialGem.y = centerY; specialGem.makeSpecial(); game.addChild(specialGem); gems.push(specialGem); } } } } // Remove matched gems for (var i = gems.length - 1; i >= 0; i--) { if (gems[i].matched) { gems[i].destroy(); gems.splice(i, 1); } } // Create new gems if needed and we had a match if (matchFound) { var neededGems = Math.min(maxGems - gems.length, 3); for (var i = 0; i < neededGems; i++) { // Delay the creation of new gems LK.setTimeout(createGem, 300 + i * 200); } } return matchFound; } // Calculate gravity influence on a gem function applyGravity() { for (var i = 0; i < gems.length; i++) { var gem = gems[i]; for (var j = 0; j < gravityWells.length; j++) { var well = gravityWells[j]; if (!well.active) { continue; } var dx = well.x - gem.x; var dy = well.y - gem.y; var distance = Math.sqrt(dx * dx + dy * dy); // Only apply gravity within the well's range if (distance < well.range) { // Calculate force based on distance (inverse square law) var force = well.strength / Math.max(50, distance); force = Math.min(force, 1); // Cap the maximum force // Apply force to gem's velocity gem.velocity.x += dx / distance * force * gem.mass; gem.velocity.y += dy / distance * force * gem.mass; } } } } // Handle player input var placingWell = false; var tempWell = null; function handleDown(x, y) { if (gravityWells.length < maxGravityWells && !placingWell) { placingWell = true; tempWell = new GravityWell(); tempWell.x = x; tempWell.y = y; tempWell.alpha = 0.5; // Semi-transparent until placed game.addChild(tempWell); } } function handleMove(x, y) { if (placingWell && tempWell) { tempWell.x = x; tempWell.y = y; } } function handleUp(x, y) { if (placingWell && tempWell) { // Finalize well placement tempWell.alpha = 1; gravityWells.push(tempWell); // Update UI wellsLeftTxt.setText('Gravity Wells: ' + (maxGravityWells - gravityWells.length)); // Play sound LK.getSound('place_well').play(); // Reset placement state placingWell = false; tempWell = null; } } // Game event handlers game.down = function (x, y) { handleDown(x, y); }; game.move = function (x, y) { handleMove(x, y); }; game.up = function (x, y) { handleUp(x, y); }; // Main game update loop game.update = function () { // Process particles for (var i = particles.length - 1; i >= 0; i--) { var alive = particles[i].update(); if (!alive) { particles[i].destroy(); particles.splice(i, 1); } } // Apply gravity to gems applyGravity(); // Update gems for (var i = 0; i < gems.length; i++) { gems[i].update(); } // Check for gem collisions and matches var matchFound = checkMatches(); // Create new gems occasionally if we have space and didn't just match if (!matchFound && gems.length < maxGems && LK.ticks % 60 === 0) { createGem(); } // If no gravity wells left and no gems in motion, game over if (gravityWells.length === maxGravityWells) { var gemsInMotion = false; for (var i = 0; i < gems.length; i++) { var gem = gems[i]; if (Math.abs(gem.velocity.x) > 0.1 || Math.abs(gem.velocity.y) > 0.1) { gemsInMotion = true; break; } } if (!gemsInMotion && !checkMatches()) { // Check if we have any available matches var possible = false; // Simplified check - just see if any gems are close enough to match for (var type in gemTypesInPlay) { var gemsByType = gems.filter(function (gem) { return gem.type === gemTypesInPlay[type]; }); if (gemsByType.length >= gemsToMatch) { possible = true; break; } } if (!possible) { // Game over, no possible moves LK.showGameOver(); } } } }; // Initialize the game initLevel();
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Gem = Container.expand(function (type) {
var self = Container.call(this);
// Define gem types
var gemTypes = ['red', 'blue', 'green', 'yellow', 'purple'];
self.type = type || gemTypes[Math.floor(Math.random() * gemTypes.length)];
var graphics = self.attachAsset('gem_' + self.type, {
anchorX: 0.5,
anchorY: 0.5
});
self.velocity = {
x: 0,
y: 0
};
self.matched = false;
self.special = false;
self.mass = 1;
// Make special gems 25% larger and add a pulse effect
self.makeSpecial = function () {
if (self.special) {
return;
}
self.special = true;
self.mass = 1.5;
graphics.scaleX = 1.25;
graphics.scaleY = 1.25;
// Add a white glow using tint pulses
function pulseTint() {
tween(graphics, {
alpha: 0.7
}, {
duration: 700,
easing: tween.sinceOut,
onFinish: function onFinish() {
tween(graphics, {
alpha: 1
}, {
duration: 700,
easing: tween.sinceIn,
onFinish: pulseTint
});
}
});
}
pulseTint();
};
// Apply physics movement
self.update = function () {
// Apply velocity to position
self.x += self.velocity.x;
self.y += self.velocity.y;
// Apply friction
self.velocity.x *= 0.98;
self.velocity.y *= 0.98;
// Rotate gem based on movement
graphics.rotation += Math.sqrt(Math.pow(self.velocity.x, 2) + Math.pow(self.velocity.y, 2)) * 0.01;
// Bounce off edges
if (self.x < graphics.width / 2) {
self.x = graphics.width / 2;
self.velocity.x *= -0.8;
} else if (self.x > 2048 - graphics.width / 2) {
self.x = 2048 - graphics.width / 2;
self.velocity.x *= -0.8;
}
if (self.y < graphics.height / 2) {
self.y = graphics.height / 2;
self.velocity.y *= -0.8;
} else if (self.y > 2732 - graphics.height / 2) {
self.y = 2732 - graphics.height / 2;
self.velocity.y *= -0.8;
}
};
return self;
});
var GravityWell = Container.expand(function () {
var self = Container.call(this);
var rangeGraphics = self.attachAsset('gravity_range', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.1
});
var wellGraphics = self.attachAsset('gravity_well', {
anchorX: 0.5,
anchorY: 0.5
});
self.strength = 5;
self.range = rangeGraphics.width / 2;
self.active = true;
// Pulse animation for the gravity well
function startPulseAnimation() {
tween(wellGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: function onFinish() {
tween(wellGraphics, {
scaleX: 1.0,
scaleY: 1.0
}, {
duration: 1000,
easing: tween.easeInOut,
onFinish: startPulseAnimation
});
}
});
}
startPulseAnimation();
return self;
});
var Particle = Container.expand(function (color) {
var self = Container.call(this);
var graphics = self.attachAsset('particle', {
anchorX: 0.5,
anchorY: 0.5
});
if (color) {
graphics.tint = color;
}
self.velocity = {
x: (Math.random() - 0.5) * 20,
y: (Math.random() - 0.5) * 20
};
self.lifetime = 30 + Math.random() * 30;
self.age = 0;
self.update = function () {
self.x += self.velocity.x;
self.y += self.velocity.y;
self.velocity.x *= 0.92;
self.velocity.y *= 0.92;
self.age++;
// Fade out as the particle ages
self.alpha = 1 - self.age / self.lifetime;
return self.age < self.lifetime;
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x111122
});
/****
* Game Code
****/
// Game state variables
var gems = [];
var gravityWells = [];
var particles = [];
var maxGems = 25;
var maxGravityWells = 3;
var score = 0;
var level = storage.level || 1;
var gemTypesInPlay = ['red', 'blue', 'green'];
var gemsToMatch = 3;
// UI elements
var scoreTxt = new Text2('Score: 0', {
size: 60,
fill: 0xFFFFFF
});
scoreTxt.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreTxt);
var levelTxt = new Text2('Level: ' + level, {
size: 60,
fill: 0xFFFFFF
});
levelTxt.anchor.set(0.5, 0);
levelTxt.y = 70;
LK.gui.top.addChild(levelTxt);
var wellsLeftTxt = new Text2('Gravity Wells: ' + maxGravityWells, {
size: 50,
fill: 0xFFFFFF
});
wellsLeftTxt.anchor.set(0, 0);
wellsLeftTxt.x = 120;
wellsLeftTxt.y = 20;
LK.gui.topLeft.addChild(wellsLeftTxt);
// Initialize game
function initLevel() {
// Clear existing objects
for (var i = gems.length - 1; i >= 0; i--) {
gems[i].destroy();
}
gems = [];
for (var i = gravityWells.length - 1; i >= 0; i--) {
gravityWells[i].destroy();
}
gravityWells = [];
for (var i = particles.length - 1; i >= 0; i--) {
particles[i].destroy();
}
particles = [];
// Update level
if (level > 1) {
// Unlock yellow gems at level 2
if (level >= 2 && gemTypesInPlay.indexOf('yellow') === -1) {
gemTypesInPlay.push('yellow');
}
// Unlock purple gems at level 4
if (level >= 4 && gemTypesInPlay.indexOf('purple') === -1) {
gemTypesInPlay.push('purple');
}
// Increase gem count as level increases
maxGems = 25 + (level - 1) * 5;
if (maxGems > 50) {
maxGems = 50;
} // Cap at 50 gems
// Increase gravity wells with level
maxGravityWells = 3 + Math.floor((level - 1) / 2);
if (maxGravityWells > 7) {
maxGravityWells = 7;
} // Cap at 7 wells
}
// Update UI
levelTxt.setText('Level: ' + level);
wellsLeftTxt.setText('Gravity Wells: ' + maxGravityWells);
scoreTxt.setText('Score: ' + score);
// Create initial gems
for (var i = 0; i < Math.min(10, maxGems); i++) {
createGem();
}
// Play background music
LK.playMusic('bgmusic', {
fade: {
start: 0,
end: 0.4,
duration: 1000
}
});
}
// Create a new gem at a random position
function createGem() {
if (gems.length >= maxGems) {
return;
}
var gem = new Gem(gemTypesInPlay[Math.floor(Math.random() * gemTypesInPlay.length)]);
// Position randomly but not too close to the edges
gem.x = 200 + Math.random() * (2048 - 400);
gem.y = 200 + Math.random() * (2732 - 400);
// Give a small random velocity
gem.velocity.x = (Math.random() - 0.5) * 3;
gem.velocity.y = (Math.random() - 0.5) * 3;
// Make some gems special (about 10% chance)
if (Math.random() < 0.1) {
gem.makeSpecial();
}
game.addChild(gem);
gems.push(gem);
return gem;
}
// Create particles at the specified position
function createParticleExplosion(x, y, color, count) {
for (var i = 0; i < count; i++) {
var particle = new Particle(color);
particle.x = x;
particle.y = y;
game.addChild(particle);
particles.push(particle);
}
}
// Check for gem matches
function checkMatches() {
// Group gems by type
var gemGroups = {};
for (var i = 0; i < gems.length; i++) {
var gem = gems[i];
if (gem.matched) {
continue;
}
var type = gem.type;
if (!gemGroups[type]) {
gemGroups[type] = [];
}
gemGroups[type].push(gem);
}
// Check each group for matches
var matchFound = false;
for (var type in gemGroups) {
var group = gemGroups[type];
for (var i = 0; i < group.length; i++) {
var gem1 = group[i];
var matchingGems = [gem1];
for (var j = i + 1; j < group.length; j++) {
var gem2 = group[j];
// Check distance between gems
var dx = gem1.x - gem2.x;
var dy = gem1.y - gem2.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < 100) {
matchingGems.push(gem2);
}
}
// If we have enough matching gems
if (matchingGems.length >= gemsToMatch) {
matchFound = true;
// Calculate center point of the match
var centerX = 0;
var centerY = 0;
var specialGemFound = false;
for (var k = 0; k < matchingGems.length; k++) {
var gem = matchingGems[k];
centerX += gem.x;
centerY += gem.y;
gem.matched = true;
if (gem.special) {
specialGemFound = true;
}
}
centerX /= matchingGems.length;
centerY /= matchingGems.length;
// Create particle explosion
var color;
switch (type) {
case 'red':
color = 0xff3333;
break;
case 'blue':
color = 0x3333ff;
break;
case 'green':
color = 0x33ff33;
break;
case 'yellow':
color = 0xffff33;
break;
case 'purple':
color = 0x9933ff;
break;
default:
color = 0xffffff;
}
var particleCount = 20 + matchingGems.length * 5;
if (specialGemFound) {
particleCount *= 2;
LK.getSound('special_gem').play();
} else {
LK.getSound('match').play();
}
createParticleExplosion(centerX, centerY, color, particleCount);
// Award points
var matchPoints = matchingGems.length * 10;
if (specialGemFound) {
matchPoints *= 3;
}
score += matchPoints;
scoreTxt.setText('Score: ' + score);
// Flash score text
tween(scoreTxt, {
alpha: 0.5
}, {
duration: 100,
easing: tween.linear,
onFinish: function onFinish() {
tween(scoreTxt, {
alpha: 1
}, {
duration: 100,
easing: tween.linear
});
}
});
// Check level progression
if (score >= level * 1000) {
level++;
storage.level = level;
initLevel();
return;
}
// Create a new special gem if the match is large enough
if (matchingGems.length >= 5 && !specialGemFound) {
var specialGem = new Gem(type);
specialGem.x = centerX;
specialGem.y = centerY;
specialGem.makeSpecial();
game.addChild(specialGem);
gems.push(specialGem);
}
}
}
}
// Remove matched gems
for (var i = gems.length - 1; i >= 0; i--) {
if (gems[i].matched) {
gems[i].destroy();
gems.splice(i, 1);
}
}
// Create new gems if needed and we had a match
if (matchFound) {
var neededGems = Math.min(maxGems - gems.length, 3);
for (var i = 0; i < neededGems; i++) {
// Delay the creation of new gems
LK.setTimeout(createGem, 300 + i * 200);
}
}
return matchFound;
}
// Calculate gravity influence on a gem
function applyGravity() {
for (var i = 0; i < gems.length; i++) {
var gem = gems[i];
for (var j = 0; j < gravityWells.length; j++) {
var well = gravityWells[j];
if (!well.active) {
continue;
}
var dx = well.x - gem.x;
var dy = well.y - gem.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Only apply gravity within the well's range
if (distance < well.range) {
// Calculate force based on distance (inverse square law)
var force = well.strength / Math.max(50, distance);
force = Math.min(force, 1); // Cap the maximum force
// Apply force to gem's velocity
gem.velocity.x += dx / distance * force * gem.mass;
gem.velocity.y += dy / distance * force * gem.mass;
}
}
}
}
// Handle player input
var placingWell = false;
var tempWell = null;
function handleDown(x, y) {
if (gravityWells.length < maxGravityWells && !placingWell) {
placingWell = true;
tempWell = new GravityWell();
tempWell.x = x;
tempWell.y = y;
tempWell.alpha = 0.5; // Semi-transparent until placed
game.addChild(tempWell);
}
}
function handleMove(x, y) {
if (placingWell && tempWell) {
tempWell.x = x;
tempWell.y = y;
}
}
function handleUp(x, y) {
if (placingWell && tempWell) {
// Finalize well placement
tempWell.alpha = 1;
gravityWells.push(tempWell);
// Update UI
wellsLeftTxt.setText('Gravity Wells: ' + (maxGravityWells - gravityWells.length));
// Play sound
LK.getSound('place_well').play();
// Reset placement state
placingWell = false;
tempWell = null;
}
}
// Game event handlers
game.down = function (x, y) {
handleDown(x, y);
};
game.move = function (x, y) {
handleMove(x, y);
};
game.up = function (x, y) {
handleUp(x, y);
};
// Main game update loop
game.update = function () {
// Process particles
for (var i = particles.length - 1; i >= 0; i--) {
var alive = particles[i].update();
if (!alive) {
particles[i].destroy();
particles.splice(i, 1);
}
}
// Apply gravity to gems
applyGravity();
// Update gems
for (var i = 0; i < gems.length; i++) {
gems[i].update();
}
// Check for gem collisions and matches
var matchFound = checkMatches();
// Create new gems occasionally if we have space and didn't just match
if (!matchFound && gems.length < maxGems && LK.ticks % 60 === 0) {
createGem();
}
// If no gravity wells left and no gems in motion, game over
if (gravityWells.length === maxGravityWells) {
var gemsInMotion = false;
for (var i = 0; i < gems.length; i++) {
var gem = gems[i];
if (Math.abs(gem.velocity.x) > 0.1 || Math.abs(gem.velocity.y) > 0.1) {
gemsInMotion = true;
break;
}
}
if (!gemsInMotion && !checkMatches()) {
// Check if we have any available matches
var possible = false;
// Simplified check - just see if any gems are close enough to match
for (var type in gemTypesInPlay) {
var gemsByType = gems.filter(function (gem) {
return gem.type === gemTypesInPlay[type];
});
if (gemsByType.length >= gemsToMatch) {
possible = true;
break;
}
}
if (!possible) {
// Game over, no possible moves
LK.showGameOver();
}
}
}
};
// Initialize the game
initLevel();