/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
/****
* Classes
****/
// Drawn Line class (user drawn)
var DrawnLine = Container.expand(function () {
var self = Container.call(this);
// Line endpoints in game coordinates
self.x1 = 0;
self.y1 = 0;
self.x2 = 0;
self.y2 = 0;
// For rendering, we use a box asset and stretch/rotate it
var lineSprite = self.attachAsset('drawnLine', {
anchorX: 0,
anchorY: 0.5
});
// Set endpoints and update visual
self.setEndpoints = function (x1, y1, x2, y2) {
self.x1 = x1;
self.y1 = y1;
self.x2 = x2;
self.y2 = y2;
// Position at (x1, y1)
self.x = x1;
self.y = y1;
// Calculate length and angle
var dx = x2 - x1;
var dy = y2 - y1;
var len = Math.sqrt(dx * dx + dy * dy);
var angle = Math.atan2(dy, dx);
lineSprite.width = len;
lineSprite.height = 16;
self.rotation = angle;
};
// Collision detection with droplet (circle-line segment)
self.collidesWithDroplet = function (droplet) {
// Closest point on line segment to droplet center
var x1 = self.x1,
y1 = self.y1,
x2 = self.x2,
y2 = self.y2;
var px = droplet.x,
py = droplet.y;
var dx = x2 - x1,
dy = y2 - y1;
var lengthSq = dx * dx + dy * dy;
var t = ((px - x1) * dx + (py - y1) * dy) / (lengthSq || 1);
t = Math.max(0, Math.min(1, t));
var closestX = x1 + t * dx;
var closestY = y1 + t * dy;
var distSq = (px - closestX) * (px - closestX) + (py - closestY) * (py - closestY);
return distSq <= (droplet.radius + 8) * (droplet.radius + 8);
};
// Get normal vector of the line (unit vector)
self.getNormal = function () {
var dx = self.x2 - self.x1;
var dy = self.y2 - self.y1;
var len = Math.sqrt(dx * dx + dy * dy) || 1;
// Perpendicular (normal) vector
return {
x: -dy / len,
y: dx / len
};
};
return self;
});
// Water Droplet class
var Droplet = Container.expand(function () {
var self = Container.call(this);
var dropletSprite = self.attachAsset('droplet', {
anchorX: 0.5,
anchorY: 0.5
});
// Physics properties
self.vx = 0;
self.vy = 0;
self.radius = dropletSprite.width / 2;
self.caught = false; // If already scored
// Update method called every tick
self.update = function () {
// Gravity
self.vy += 0.4 * dropletGravityMultiplier;
self.x += self.vx;
self.y += self.vy;
// Collide with drawn lines
for (var i = 0; i < drawnLines.length; i++) {
var line = drawnLines[i];
if (line.collidesWithDroplet(self)) {
// Reflect velocity based on line angle
var normal = line.getNormal();
// Project velocity onto normal
var dot = self.vx * normal.x + self.vy * normal.y;
self.vx = self.vx - 2 * dot * normal.x;
self.vy = self.vy - 2 * dot * normal.y;
// Dampen velocity a bit
self.vx *= 0.7;
self.vy *= 0.7;
// Move droplet slightly away from line to prevent sticking
self.x += normal.x * 4;
self.y += normal.y * 4;
}
}
// Collide with obstacles
for (var i = 0; i < obstacles.length; i++) {
var obs = obstacles[i];
if (self.intersects(obs)) {
// Simple bounce: reverse vy, dampen
self.vy = -Math.abs(self.vy) * 0.6;
// Nudge out of obstacle
if (self.y < obs.y) {
self.y = obs.y - obs.height / 2 - self.radius - 2;
} else {
self.y = obs.y + obs.height / 2 + self.radius + 2;
}
}
}
// Collide with bucket
if (!self.caught && self.intersects(bucket)) {
// Check if inside bucket opening (not just touching sides)
var bucketTop = bucket.y - bucket.height / 2;
if (self.y + self.radius > bucketTop) {
self.caught = true;
LK.setScore(LK.getScore() + 1);
scoreText.setText(LK.getScore());
// Animate droplet into bucket
tween(self, {
alpha: 0,
y: bucket.y + bucket.height / 2
}, {
duration: 400,
easing: tween.easeIn,
onFinish: function onFinish() {
self.destroy();
removeDroplet(self);
}
});
return;
}
}
// Out of bounds (left/right/bottom)
if (self.y - self.radius > 2732 || self.x + self.radius < 0 || self.x - self.radius > 2048) {
if (!self.caught) {
lostDroplets++;
updateLostText();
if (lostDroplets >= maxLostDroplets) {
LK.showGameOver();
}
}
self.destroy();
removeDroplet(self);
}
};
return self;
});
// Obstacle class
var Obstacle = Container.expand(function () {
var self = Container.call(this);
var obsSprite = self.attachAsset('obstacle', {
anchorX: 0.5,
anchorY: 0.5
});
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x222244
});
/****
* Game Code
****/
// Line: blue box (for drawn lines)
// Obstacle: dark box
// Bucket: gray box
// Droplet: blue ellipse
// Game variables
var droplets = [];
var drawnLines = [];
var obstacles = [];
var dropletSpawnTimer = 0;
var dropletSpawnInterval = 60; // ticks
var dropletGravityMultiplier = 1;
var lostDroplets = 0;
var maxLostDroplets = 10;
var currentLevel = 1;
var isDrawing = false;
var drawStartX = 0,
drawStartY = 0;
var drawLinePreview = null;
var bucket = null;
// Score display
var scoreText = new Text2('0', {
size: 120,
fill: 0xFFFFFF
});
scoreText.anchor.set(0.5, 0);
LK.gui.top.addChild(scoreText);
// Lost droplets display
var lostText = new Text2('Lost: 0/10', {
size: 60,
fill: 0xFF6666
});
lostText.anchor.set(1, 0);
LK.gui.topRight.addChild(lostText);
function updateLostText() {
lostText.setText('Lost: ' + lostDroplets + '/' + maxLostDroplets);
}
// Bucket setup
bucket = new Container();
var bucketSprite = bucket.attachAsset('bucket', {
anchorX: 0.5,
anchorY: 0.5
});
bucket.width = bucketSprite.width;
bucket.height = bucketSprite.height;
bucket.x = 2048 / 2;
bucket.y = 2732 - 180;
game.addChild(bucket);
// Obstacles setup (for level 1, one obstacle; more in higher levels)
function setupObstacles(level) {
// Remove old
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].destroy();
}
obstacles = [];
if (level === 1) {
var obs = new Obstacle();
obs.x = 2048 / 2;
obs.y = 1200;
game.addChild(obs);
obstacles.push(obs);
} else if (level === 2) {
var obs1 = new Obstacle();
obs1.x = 2048 / 2 - 300;
obs1.y = 1100;
game.addChild(obs1);
obstacles.push(obs1);
var obs2 = new Obstacle();
obs2.x = 2048 / 2 + 300;
obs2.y = 1500;
game.addChild(obs2);
obstacles.push(obs2);
} else if (level >= 3) {
for (var i = 0; i < 3; i++) {
var obs = new Obstacle();
obs.x = 600 + i * 400;
obs.y = 900 + i * 400;
game.addChild(obs);
obstacles.push(obs);
}
}
}
setupObstacles(currentLevel);
// Remove droplet from array
function removeDroplet(droplet) {
for (var i = droplets.length - 1; i >= 0; i--) {
if (droplets[i] === droplet) {
droplets.splice(i, 1);
break;
}
}
}
// Draw line preview (while dragging)
function showDrawPreview(x1, y1, x2, y2) {
if (!drawLinePreview) {
drawLinePreview = new DrawnLine();
game.addChild(drawLinePreview);
}
drawLinePreview.setEndpoints(x1, y1, x2, y2);
drawLinePreview.alpha = 0.5;
}
function hideDrawPreview() {
if (drawLinePreview) {
drawLinePreview.destroy();
drawLinePreview = null;
}
}
// Drawing lines: only allow a max number of lines at once
var maxDrawnLines = 4;
function addDrawnLine(x1, y1, x2, y2) {
if (drawnLines.length >= maxDrawnLines) {
// Remove oldest
var old = drawnLines.shift();
old.destroy();
}
var line = new DrawnLine();
line.setEndpoints(x1, y1, x2, y2);
game.addChild(line);
drawnLines.push(line);
}
// Touch/mouse events for drawing lines
game.down = function (x, y, obj) {
// Only allow drawing in lower 90% of screen (avoid top menu)
if (y < 120) return;
isDrawing = true;
drawStartX = x;
drawStartY = y;
showDrawPreview(drawStartX, drawStartY, x, y);
};
game.move = function (x, y, obj) {
if (isDrawing) {
showDrawPreview(drawStartX, drawStartY, x, y);
}
};
game.up = function (x, y, obj) {
if (isDrawing) {
// Only draw if line is long enough
var dx = x - drawStartX,
dy = y - drawStartY;
var len = Math.sqrt(dx * dx + dy * dy);
if (len > 80) {
addDrawnLine(drawStartX, drawStartY, x, y);
}
hideDrawPreview();
isDrawing = false;
}
};
// Droplet spawning
function spawnDroplet() {
var droplet = new Droplet();
// Random x, avoid spawning at extreme edges
droplet.x = 180 + Math.random() * (2048 - 360);
droplet.y = -40;
// Small random initial vx
droplet.vx = (Math.random() - 0.5) * 2;
droplet.vy = 2 + Math.random() * 2 + dropletGravityMultiplier;
droplets.push(droplet);
game.addChild(droplet);
}
// Level progression
function nextLevel() {
currentLevel++;
if (currentLevel > 5) currentLevel = 5;
dropletGravityMultiplier = 1 + 0.2 * (currentLevel - 1);
dropletSpawnInterval = Math.max(30, 60 - 8 * (currentLevel - 1));
setupObstacles(currentLevel);
}
// Main game update
game.update = function () {
// Spawn droplets
dropletSpawnTimer++;
if (dropletSpawnTimer >= dropletSpawnInterval) {
dropletSpawnTimer = 0;
spawnDroplet();
}
// Update all droplets
for (var i = 0; i < droplets.length; i++) {
if (droplets[i].update) droplets[i].update();
}
// Level up every 10 points
var score = LK.getScore();
if (score > 0 && score % 10 === 0 && currentLevel < 5) {
nextLevel();
}
};
// Reset game state on new game
LK.on('gameStart', function () {
// Remove all droplets
for (var i = 0; i < droplets.length; i++) {
droplets[i].destroy();
}
droplets = [];
// Remove all lines
for (var i = 0; i < drawnLines.length; i++) {
drawnLines[i].destroy();
}
drawnLines = [];
// Remove obstacles
for (var i = 0; i < obstacles.length; i++) {
obstacles[i].destroy();
}
obstacles = [];
// Reset variables
dropletSpawnTimer = 0;
dropletSpawnInterval = 60;
dropletGravityMultiplier = 1;
lostDroplets = 0;
maxLostDroplets = 10;
currentLevel = 1;
updateLostText();
scoreText.setText('0');
setupObstacles(currentLevel);
}); ===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,391 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+
+/****
+* Classes
+****/
+// Drawn Line class (user drawn)
+var DrawnLine = Container.expand(function () {
+ var self = Container.call(this);
+ // Line endpoints in game coordinates
+ self.x1 = 0;
+ self.y1 = 0;
+ self.x2 = 0;
+ self.y2 = 0;
+ // For rendering, we use a box asset and stretch/rotate it
+ var lineSprite = self.attachAsset('drawnLine', {
+ anchorX: 0,
+ anchorY: 0.5
+ });
+ // Set endpoints and update visual
+ self.setEndpoints = function (x1, y1, x2, y2) {
+ self.x1 = x1;
+ self.y1 = y1;
+ self.x2 = x2;
+ self.y2 = y2;
+ // Position at (x1, y1)
+ self.x = x1;
+ self.y = y1;
+ // Calculate length and angle
+ var dx = x2 - x1;
+ var dy = y2 - y1;
+ var len = Math.sqrt(dx * dx + dy * dy);
+ var angle = Math.atan2(dy, dx);
+ lineSprite.width = len;
+ lineSprite.height = 16;
+ self.rotation = angle;
+ };
+ // Collision detection with droplet (circle-line segment)
+ self.collidesWithDroplet = function (droplet) {
+ // Closest point on line segment to droplet center
+ var x1 = self.x1,
+ y1 = self.y1,
+ x2 = self.x2,
+ y2 = self.y2;
+ var px = droplet.x,
+ py = droplet.y;
+ var dx = x2 - x1,
+ dy = y2 - y1;
+ var lengthSq = dx * dx + dy * dy;
+ var t = ((px - x1) * dx + (py - y1) * dy) / (lengthSq || 1);
+ t = Math.max(0, Math.min(1, t));
+ var closestX = x1 + t * dx;
+ var closestY = y1 + t * dy;
+ var distSq = (px - closestX) * (px - closestX) + (py - closestY) * (py - closestY);
+ return distSq <= (droplet.radius + 8) * (droplet.radius + 8);
+ };
+ // Get normal vector of the line (unit vector)
+ self.getNormal = function () {
+ var dx = self.x2 - self.x1;
+ var dy = self.y2 - self.y1;
+ var len = Math.sqrt(dx * dx + dy * dy) || 1;
+ // Perpendicular (normal) vector
+ return {
+ x: -dy / len,
+ y: dx / len
+ };
+ };
+ return self;
+});
+// Water Droplet class
+var Droplet = Container.expand(function () {
+ var self = Container.call(this);
+ var dropletSprite = self.attachAsset('droplet', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ // Physics properties
+ self.vx = 0;
+ self.vy = 0;
+ self.radius = dropletSprite.width / 2;
+ self.caught = false; // If already scored
+ // Update method called every tick
+ self.update = function () {
+ // Gravity
+ self.vy += 0.4 * dropletGravityMultiplier;
+ self.x += self.vx;
+ self.y += self.vy;
+ // Collide with drawn lines
+ for (var i = 0; i < drawnLines.length; i++) {
+ var line = drawnLines[i];
+ if (line.collidesWithDroplet(self)) {
+ // Reflect velocity based on line angle
+ var normal = line.getNormal();
+ // Project velocity onto normal
+ var dot = self.vx * normal.x + self.vy * normal.y;
+ self.vx = self.vx - 2 * dot * normal.x;
+ self.vy = self.vy - 2 * dot * normal.y;
+ // Dampen velocity a bit
+ self.vx *= 0.7;
+ self.vy *= 0.7;
+ // Move droplet slightly away from line to prevent sticking
+ self.x += normal.x * 4;
+ self.y += normal.y * 4;
+ }
+ }
+ // Collide with obstacles
+ for (var i = 0; i < obstacles.length; i++) {
+ var obs = obstacles[i];
+ if (self.intersects(obs)) {
+ // Simple bounce: reverse vy, dampen
+ self.vy = -Math.abs(self.vy) * 0.6;
+ // Nudge out of obstacle
+ if (self.y < obs.y) {
+ self.y = obs.y - obs.height / 2 - self.radius - 2;
+ } else {
+ self.y = obs.y + obs.height / 2 + self.radius + 2;
+ }
+ }
+ }
+ // Collide with bucket
+ if (!self.caught && self.intersects(bucket)) {
+ // Check if inside bucket opening (not just touching sides)
+ var bucketTop = bucket.y - bucket.height / 2;
+ if (self.y + self.radius > bucketTop) {
+ self.caught = true;
+ LK.setScore(LK.getScore() + 1);
+ scoreText.setText(LK.getScore());
+ // Animate droplet into bucket
+ tween(self, {
+ alpha: 0,
+ y: bucket.y + bucket.height / 2
+ }, {
+ duration: 400,
+ easing: tween.easeIn,
+ onFinish: function onFinish() {
+ self.destroy();
+ removeDroplet(self);
+ }
+ });
+ return;
+ }
+ }
+ // Out of bounds (left/right/bottom)
+ if (self.y - self.radius > 2732 || self.x + self.radius < 0 || self.x - self.radius > 2048) {
+ if (!self.caught) {
+ lostDroplets++;
+ updateLostText();
+ if (lostDroplets >= maxLostDroplets) {
+ LK.showGameOver();
+ }
+ }
+ self.destroy();
+ removeDroplet(self);
+ }
+ };
+ return self;
+});
+// Obstacle class
+var Obstacle = Container.expand(function () {
+ var self = Container.call(this);
+ var obsSprite = self.attachAsset('obstacle', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
+ backgroundColor: 0x222244
+});
+
+/****
+* Game Code
+****/
+// Line: blue box (for drawn lines)
+// Obstacle: dark box
+// Bucket: gray box
+// Droplet: blue ellipse
+// Game variables
+var droplets = [];
+var drawnLines = [];
+var obstacles = [];
+var dropletSpawnTimer = 0;
+var dropletSpawnInterval = 60; // ticks
+var dropletGravityMultiplier = 1;
+var lostDroplets = 0;
+var maxLostDroplets = 10;
+var currentLevel = 1;
+var isDrawing = false;
+var drawStartX = 0,
+ drawStartY = 0;
+var drawLinePreview = null;
+var bucket = null;
+// Score display
+var scoreText = new Text2('0', {
+ size: 120,
+ fill: 0xFFFFFF
+});
+scoreText.anchor.set(0.5, 0);
+LK.gui.top.addChild(scoreText);
+// Lost droplets display
+var lostText = new Text2('Lost: 0/10', {
+ size: 60,
+ fill: 0xFF6666
+});
+lostText.anchor.set(1, 0);
+LK.gui.topRight.addChild(lostText);
+function updateLostText() {
+ lostText.setText('Lost: ' + lostDroplets + '/' + maxLostDroplets);
+}
+// Bucket setup
+bucket = new Container();
+var bucketSprite = bucket.attachAsset('bucket', {
+ anchorX: 0.5,
+ anchorY: 0.5
+});
+bucket.width = bucketSprite.width;
+bucket.height = bucketSprite.height;
+bucket.x = 2048 / 2;
+bucket.y = 2732 - 180;
+game.addChild(bucket);
+// Obstacles setup (for level 1, one obstacle; more in higher levels)
+function setupObstacles(level) {
+ // Remove old
+ for (var i = 0; i < obstacles.length; i++) {
+ obstacles[i].destroy();
+ }
+ obstacles = [];
+ if (level === 1) {
+ var obs = new Obstacle();
+ obs.x = 2048 / 2;
+ obs.y = 1200;
+ game.addChild(obs);
+ obstacles.push(obs);
+ } else if (level === 2) {
+ var obs1 = new Obstacle();
+ obs1.x = 2048 / 2 - 300;
+ obs1.y = 1100;
+ game.addChild(obs1);
+ obstacles.push(obs1);
+ var obs2 = new Obstacle();
+ obs2.x = 2048 / 2 + 300;
+ obs2.y = 1500;
+ game.addChild(obs2);
+ obstacles.push(obs2);
+ } else if (level >= 3) {
+ for (var i = 0; i < 3; i++) {
+ var obs = new Obstacle();
+ obs.x = 600 + i * 400;
+ obs.y = 900 + i * 400;
+ game.addChild(obs);
+ obstacles.push(obs);
+ }
+ }
+}
+setupObstacles(currentLevel);
+// Remove droplet from array
+function removeDroplet(droplet) {
+ for (var i = droplets.length - 1; i >= 0; i--) {
+ if (droplets[i] === droplet) {
+ droplets.splice(i, 1);
+ break;
+ }
+ }
+}
+// Draw line preview (while dragging)
+function showDrawPreview(x1, y1, x2, y2) {
+ if (!drawLinePreview) {
+ drawLinePreview = new DrawnLine();
+ game.addChild(drawLinePreview);
+ }
+ drawLinePreview.setEndpoints(x1, y1, x2, y2);
+ drawLinePreview.alpha = 0.5;
+}
+function hideDrawPreview() {
+ if (drawLinePreview) {
+ drawLinePreview.destroy();
+ drawLinePreview = null;
+ }
+}
+// Drawing lines: only allow a max number of lines at once
+var maxDrawnLines = 4;
+function addDrawnLine(x1, y1, x2, y2) {
+ if (drawnLines.length >= maxDrawnLines) {
+ // Remove oldest
+ var old = drawnLines.shift();
+ old.destroy();
+ }
+ var line = new DrawnLine();
+ line.setEndpoints(x1, y1, x2, y2);
+ game.addChild(line);
+ drawnLines.push(line);
+}
+// Touch/mouse events for drawing lines
+game.down = function (x, y, obj) {
+ // Only allow drawing in lower 90% of screen (avoid top menu)
+ if (y < 120) return;
+ isDrawing = true;
+ drawStartX = x;
+ drawStartY = y;
+ showDrawPreview(drawStartX, drawStartY, x, y);
+};
+game.move = function (x, y, obj) {
+ if (isDrawing) {
+ showDrawPreview(drawStartX, drawStartY, x, y);
+ }
+};
+game.up = function (x, y, obj) {
+ if (isDrawing) {
+ // Only draw if line is long enough
+ var dx = x - drawStartX,
+ dy = y - drawStartY;
+ var len = Math.sqrt(dx * dx + dy * dy);
+ if (len > 80) {
+ addDrawnLine(drawStartX, drawStartY, x, y);
+ }
+ hideDrawPreview();
+ isDrawing = false;
+ }
+};
+// Droplet spawning
+function spawnDroplet() {
+ var droplet = new Droplet();
+ // Random x, avoid spawning at extreme edges
+ droplet.x = 180 + Math.random() * (2048 - 360);
+ droplet.y = -40;
+ // Small random initial vx
+ droplet.vx = (Math.random() - 0.5) * 2;
+ droplet.vy = 2 + Math.random() * 2 + dropletGravityMultiplier;
+ droplets.push(droplet);
+ game.addChild(droplet);
+}
+// Level progression
+function nextLevel() {
+ currentLevel++;
+ if (currentLevel > 5) currentLevel = 5;
+ dropletGravityMultiplier = 1 + 0.2 * (currentLevel - 1);
+ dropletSpawnInterval = Math.max(30, 60 - 8 * (currentLevel - 1));
+ setupObstacles(currentLevel);
+}
+// Main game update
+game.update = function () {
+ // Spawn droplets
+ dropletSpawnTimer++;
+ if (dropletSpawnTimer >= dropletSpawnInterval) {
+ dropletSpawnTimer = 0;
+ spawnDroplet();
+ }
+ // Update all droplets
+ for (var i = 0; i < droplets.length; i++) {
+ if (droplets[i].update) droplets[i].update();
+ }
+ // Level up every 10 points
+ var score = LK.getScore();
+ if (score > 0 && score % 10 === 0 && currentLevel < 5) {
+ nextLevel();
+ }
+};
+// Reset game state on new game
+LK.on('gameStart', function () {
+ // Remove all droplets
+ for (var i = 0; i < droplets.length; i++) {
+ droplets[i].destroy();
+ }
+ droplets = [];
+ // Remove all lines
+ for (var i = 0; i < drawnLines.length; i++) {
+ drawnLines[i].destroy();
+ }
+ drawnLines = [];
+ // Remove obstacles
+ for (var i = 0; i < obstacles.length; i++) {
+ obstacles[i].destroy();
+ }
+ obstacles = [];
+ // Reset variables
+ dropletSpawnTimer = 0;
+ dropletSpawnInterval = 60;
+ dropletGravityMultiplier = 1;
+ lostDroplets = 0;
+ maxLostDroplets = 10;
+ currentLevel = 1;
+ updateLostText();
+ scoreText.setText('0');
+ setupObstacles(currentLevel);
});
\ No newline at end of file