/**** * 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