User prompt
Reduce the unit price to 35 gold
User prompt
Make units a little cheaper
User prompt
You know that 0/1000 waves thingy? Well, remove the /1000 thing
User prompt
Remove the wave limit
User prompt
The game now has INFINITE WAVES!
User prompt
Make goblins drop more gold
User prompt
Make goblins a little weaker
User prompt
Make waves a little easier by making goblins be slower and have less health, but they will get 2% more health and speed every wave
User prompt
Put the unit's name below the level of the tower
Code edit (1 edits merged)
Please save this source code
User prompt
Merge Defenders: Evolution
Initial prompt
Merging defense: a game where you merge units to make strongher units (example: rock thrower + rock thrower = slinger)to kill the goblins from reaching your base! You have 3 lives. (Units (from weakest to stronghest): rock thrower, slinger, spear thrower, archer, Fire archer, crossbowman, Mosquettere, cannoneer, rifleman, grenadier, tank); make sure to use ALL of the units i said and make them cost gold that is obtained through killing enemies or making in-app purchases with real money
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1", { highScore: 0 }); /**** * Classes ****/ var Goblin = Container.expand(function (pathPoints, health, speed, value) { var self = Container.call(this); self.pathPoints = pathPoints; self.maxHealth = health; self.health = health; self.speed = speed; self.value = value; self.active = true; self.currentPathIndex = 0; self.pathProgress = 0; var goblinGraphics = self.attachAsset('goblin', { anchorX: 0.5, anchorY: 0.5 }); var healthBar = LK.getAsset('tile', { width: 60, height: 10, color: 0x00FF00, anchorX: 0.5, anchorY: 0.5 }); healthBar.y = -40; self.addChild(healthBar); self.x = pathPoints[0].x; self.y = pathPoints[0].y; self.takeDamage = function (damage) { self.health -= damage; // Update health bar var healthPercent = Math.max(0, self.health / self.maxHealth); healthBar.width = 60 * healthPercent; healthBar.tint = healthPercent > 0.5 ? 0x00FF00 : healthPercent > 0.25 ? 0xFFFF00 : 0xFF0000; if (self.health <= 0) { self.die(); } }; self.die = function () { self.active = false; gold += self.value; goldText.setText("Gold: " + gold); score += self.value; scoreText.setText("Score: " + score); LK.setScore(score); LK.getSound('goblin_death').play(); // Create coin effect var coinText = new Text2("+" + self.value, { size: 40, fill: 0xFFD700 }); coinText.x = self.x; coinText.y = self.y; coinText.anchor.set(0.5, 0.5); game.addChild(coinText); tween(coinText, { y: coinText.y - 100, alpha: 0 }, { duration: 1000, onFinish: function onFinish() { coinText.destroy(); } }); self.destroy(); }; self.reachedBase = function () { self.active = false; lives--; livesText.setText("Lives: " + lives); if (lives <= 0) { endGame(); } self.destroy(); }; self.update = function () { if (!self.active) { return; } if (self.currentPathIndex >= self.pathPoints.length - 1) { self.reachedBase(); return; } var currentPoint = self.pathPoints[self.currentPathIndex]; var nextPoint = self.pathPoints[self.currentPathIndex + 1]; var dx = nextPoint.x - currentPoint.x; var dy = nextPoint.y - currentPoint.y; var dist = Math.sqrt(dx * dx + dy * dy); self.pathProgress = self.currentPathIndex / (self.pathPoints.length - 1); // Calculate how far along this segment we are var currentX = self.x - currentPoint.x; var currentY = self.y - currentPoint.y; var segmentProgress = Math.sqrt(currentX * currentX + currentY * currentY) / dist; // Add segment progress to overall progress self.pathProgress += segmentProgress / (self.pathPoints.length - 1); // Move towards next point var dx = nextPoint.x - self.x; var dy = nextPoint.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < self.speed) { // Reached the next point self.currentPathIndex++; } else { // Move along the path self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; } }; return self; }); var Projectile = Container.expand(function (source, target, damage) { var self = Container.call(this); self.source = source; self.target = target; self.damage = damage; self.speed = 15; self.active = true; var projectileGraphics = self.attachAsset('projectile', { anchorX: 0.5, anchorY: 0.5 }); self.x = source.x; self.y = source.y; self.update = function () { if (!self.active || !self.target || !self.target.active) { self.destroy(); return; } // Calculate direction vector var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 20) { // Hit target self.target.takeDamage(self.damage); LK.getSound('hit').play(); self.active = false; self.destroy(); return; } // Normalize and scale by speed self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; }; return self; }); var Unit = Container.expand(function (tier) { var self = Container.call(this); self.tier = tier || 1; self.damage = Math.pow(2, self.tier - 1); self.range = 300 + self.tier * 50; self.fireRate = 1000 - self.tier * 50; // ms between shots self.lastShot = 0; self.targets = []; self.isDragging = false; self.merging = false; var unitGraphics = self.attachAsset('unit' + self.tier, { anchorX: 0.5, anchorY: 0.5 }); var tierText = new Text2(self.tier.toString(), { size: 40, fill: 0x000000 }); tierText.anchor.set(0.5, 0.5); self.addChild(tierText); self.findTarget = function () { if (self.targets.length === 0) { return null; } // Sort targets by progress self.targets.sort(function (a, b) { return b.pathProgress - a.pathProgress; }); // Return the most progressed goblin return self.targets[0]; }; self.shoot = function (target) { if (!target || !target.active) { return; } var now = Date.now(); if (now - self.lastShot < self.fireRate) { return; } self.lastShot = now; var projectile = new Projectile(self, target, self.damage); projectiles.push(projectile); game.addChild(projectile); LK.getSound('shoot').play(); }; self.update = function () { if (self.merging) { return; } // Clean up destroyed targets self.targets = self.targets.filter(function (target) { return target.active; }); var target = self.findTarget(); if (target) { self.shoot(target); } }; self.down = function (x, y, obj) { if (!placingUnit && !gameOver) { self.isDragging = true; selectedUnit = self; if (self.gridPosition) { // Remove from grid grid[self.gridPosition.y][self.gridPosition.x].unit = null; self.gridPosition = null; } } }; self.checkForMerge = function (gridCell) { if (!gridCell || !gridCell.unit || gridCell.unit === self) { return false; } if (gridCell.unit.tier === self.tier && self.tier < 10) { // Merge units mergeUnits(self, gridCell.unit); return true; } return false; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x558855 }); /**** * Game Code ****/ // Game configuration var gridWidth = 7; var gridHeight = 6; var tileSize = 120; var grid = []; var pathPoints = []; var goblins = []; var projectiles = []; var units = []; var waves = []; var currentWave = 0; var waveInProgress = false; var selectedUnit = null; var placingUnit = null; var gold = 100; var lives = 3; var score = 0; var gameOver = false; var gridOffsetX = (2048 - gridWidth * tileSize) / 2; var gridOffsetY = 400; var highlight = null; var unitCost = 50; // UI elements var waveText; var goldText; var livesText; var scoreText; var nextWaveButton; var unitBuyButton; function initializeGrid() { for (var y = 0; y < gridHeight; y++) { grid[y] = []; for (var x = 0; x < gridWidth; x++) { grid[y][x] = { x: gridOffsetX + x * tileSize + tileSize / 2, y: gridOffsetY + y * tileSize + tileSize / 2, unit: null, isPath: false }; var tile = LK.getAsset('tile', { anchorX: 0.5, anchorY: 0.5 }); tile.x = grid[y][x].x; tile.y = grid[y][x].y; tile.alpha = 0.5; game.addChild(tile); } } } function createPath() { // Define a simple path through the grid var pathCoordinates = [{ x: 0, y: 2 }, { x: 1, y: 2 }, { x: 2, y: 2 }, { x: 3, y: 2 }, { x: 3, y: 3 }, { x: 3, y: 4 }, { x: 4, y: 4 }, { x: 5, y: 4 }, { x: 6, y: 4 }]; for (var i = 0; i < pathCoordinates.length; i++) { var coord = pathCoordinates[i]; var cell = grid[coord.y][coord.x]; cell.isPath = true; var pathTile = LK.getAsset('path', { anchorX: 0.5, anchorY: 0.5 }); pathTile.x = cell.x; pathTile.y = cell.y; pathTile.alpha = 0.7; game.addChild(pathTile); pathPoints.push({ x: cell.x, y: cell.y }); } // Add the base at the end of the path var lastCoord = pathCoordinates[pathCoordinates.length - 1]; var baseCell = grid[lastCoord.y][lastCoord.x]; var base = LK.getAsset('base', { anchorX: 0.5, anchorY: 0.5 }); base.x = baseCell.x + tileSize; base.y = baseCell.y; game.addChild(base); } function initializeWaves() { // Define waves of increasing difficulty waves = [{ count: 5, health: 10, speed: 2, value: 5, delay: 1000 }, { count: 8, health: 15, speed: 2, value: 6, delay: 900 }, { count: 10, health: 20, speed: 2.2, value: 7, delay: 850 }, { count: 12, health: 30, speed: 2.2, value: 8, delay: 800 }, { count: 15, health: 40, speed: 2.3, value: 9, delay: 750 }, { count: 18, health: 50, speed: 2.4, value: 10, delay: 700 }, { count: 20, health: 70, speed: 2.5, value: 12, delay: 650 }, { count: 25, health: 90, speed: 2.6, value: 15, delay: 600 }, { count: 30, health: 120, speed: 2.8, value: 18, delay: 550 }, { count: 35, health: 150, speed: 3, value: 20, delay: 500 }]; } function startWave() { if (waveInProgress || gameOver) { return; } waveInProgress = true; currentWave++; if (currentWave > waves.length) { // Player has completed all waves! LK.showYouWin(); return; } waveText.setText("Wave: " + currentWave + "/" + waves.length); nextWaveButton.visible = false; var wave = waves[currentWave - 1]; var goblinsReleased = 0; var spawnInterval = LK.setInterval(function () { spawnGoblin(wave); goblinsReleased++; if (goblinsReleased >= wave.count) { LK.clearInterval(spawnInterval); // Check every second if wave is complete var checkInterval = LK.setInterval(function () { if (goblins.length === 0) { waveInProgress = false; nextWaveButton.visible = true; LK.clearInterval(checkInterval); } }, 1000); } }, wave.delay); } function spawnGoblin(wave) { var goblin = new Goblin(pathPoints, wave.health, wave.speed, wave.value); goblins.push(goblin); game.addChild(goblin); // Add this goblin as a target for all units in range units.forEach(function (unit) { var dx = unit.x - goblin.x; var dy = unit.y - goblin.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist <= unit.range) { unit.targets.push(goblin); } }); } function createUnit(tier) { var unit = new Unit(tier); units.push(unit); return unit; } function placeUnit(unit, gridX, gridY) { if (gridX < 0 || gridX >= gridWidth || gridY < 0 || gridY >= gridHeight) { return false; } var gridCell = grid[gridY][gridX]; if (gridCell.isPath || gridCell.unit) { return false; } // Place the unit unit.x = gridCell.x; unit.y = gridCell.y; unit.gridPosition = { x: gridX, y: gridY }; gridCell.unit = unit; // Find nearby targets goblins.forEach(function (goblin) { if (goblin.active) { var dx = unit.x - goblin.x; var dy = unit.y - goblin.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist <= unit.range) { unit.targets.push(goblin); } } }); LK.getSound('place').play(); return true; } function mergeUnits(unit1, unit2) { // Create a new unit of the next tier var newUnit = createUnit(unit1.tier + 1); game.addChild(newUnit); // Place at the position of the second unit newUnit.gridPosition = unit2.gridPosition; grid[unit2.gridPosition.y][unit2.gridPosition.x].unit = newUnit; newUnit.x = unit2.x; newUnit.y = unit2.y; // Remove the merged units unit1.merging = true; unit2.merging = true; // Effect tween(unit1, { x: unit2.x, y: unit2.y, alpha: 0 }, { duration: 300, onFinish: function onFinish() { unit1.destroy(); removeFromArray(units, unit1); } }); tween(unit2, { alpha: 0 }, { duration: 300, onFinish: function onFinish() { unit2.destroy(); removeFromArray(units, unit2); } }); // Scale up effect for new unit newUnit.scale.x = 0.5; newUnit.scale.y = 0.5; tween(newUnit.scale, { x: 1, y: 1 }, { duration: 300, easing: tween.elasticOut }); LK.getSound('merge').play(); return newUnit; } function buyUnit() { if (gold < unitCost || placingUnit || gameOver) { return; } gold -= unitCost; goldText.setText("Gold: " + gold); placingUnit = createUnit(1); game.addChild(placingUnit); // Create highlight if it doesn't exist if (!highlight) { highlight = LK.getAsset('highlight', { anchorX: 0.5, anchorY: 0.5 }); highlight.alpha = 0.3; game.addChild(highlight); } highlight.visible = true; } function createUI() { // Wave text waveText = new Text2("Wave: 0/" + waves.length, { size: 50, fill: 0xFFFFFF }); waveText.anchor.set(0.5, 0); LK.gui.top.addChild(waveText); // Gold text goldText = new Text2("Gold: " + gold, { size: 50, fill: 0xFFD700 }); goldText.anchor.set(0, 0); LK.gui.topLeft.addChild(goldText); goldText.x = 120; // Move away from the top left corner // Lives text livesText = new Text2("Lives: " + lives, { size: 50, fill: 0xFF0000 }); livesText.anchor.set(1, 0); LK.gui.topRight.addChild(livesText); // Score text scoreText = new Text2("Score: " + score, { size: 50, fill: 0xFFFFFF }); scoreText.anchor.set(0.5, 0); LK.gui.top.addChild(scoreText); scoreText.y = 80; // Next wave button nextWaveButton = new Container(); var nextWaveButtonBg = LK.getAsset('tile', { width: 300, height: 100, color: 0x336699, anchorX: 0.5, anchorY: 0.5 }); nextWaveButton.addChild(nextWaveButtonBg); var nextWaveButtonText = new Text2("Next Wave", { size: 50, fill: 0xFFFFFF }); nextWaveButtonText.anchor.set(0.5, 0.5); nextWaveButton.addChild(nextWaveButtonText); nextWaveButton.down = function () { startWave(); }; LK.gui.bottom.addChild(nextWaveButton); nextWaveButton.y = -150; // Buy unit button unitBuyButton = new Container(); var unitBuyButtonBg = LK.getAsset('tile', { width: 300, height: 100, color: 0x669933, anchorX: 0.5, anchorY: 0.5 }); unitBuyButton.addChild(unitBuyButtonBg); var unitBuyButtonText = new Text2("Buy Unit: " + unitCost + "g", { size: 40, fill: 0xFFFFFF }); unitBuyButtonText.anchor.set(0.5, 0.5); unitBuyButton.addChild(unitBuyButtonText); unitBuyButton.down = function () { buyUnit(); }; LK.gui.bottomLeft.addChild(unitBuyButton); unitBuyButton.y = -150; unitBuyButton.x = 170; } function endGame() { if (gameOver) { return; } gameOver = true; if (score > storage.highScore) { storage.highScore = score; } LK.getSound('game_over').play(); LK.showGameOver(); } function removeFromArray(array, item) { var index = array.indexOf(item); if (index !== -1) { array.splice(index, 1); } } function initialize() { initializeGrid(); createPath(); initializeWaves(); createUI(); // Play background music LK.playMusic('bg_music'); // Create highlight for placing units (initially hidden) highlight = LK.getAsset('highlight', { anchorX: 0.5, anchorY: 0.5 }); highlight.alpha = 0.3; highlight.visible = false; game.addChild(highlight); // Start with wave 0 waveText.setText("Wave: 0/" + waves.length); } initialize(); game.move = function (x, y) { if (gameOver) { return; } if (selectedUnit) { selectedUnit.x = x; selectedUnit.y = y; } else if (placingUnit) { // Find the closest grid cell var gridX = Math.floor((x - gridOffsetX) / tileSize); var gridY = Math.floor((y - gridOffsetY) / tileSize); if (gridX >= 0 && gridX < gridWidth && gridY >= 0 && gridY < gridHeight) { var cell = grid[gridY][gridX]; placingUnit.x = x; placingUnit.y = y; highlight.x = cell.x; highlight.y = cell.y; highlight.visible = true; // Change highlight color based on valid placement if (cell.isPath || cell.unit) { highlight.tint = 0xFF0000; } else { highlight.tint = 0x00FF00; } } else { highlight.visible = false; } } }; game.down = function (x, y) { if (gameOver) { return; } // If we're placing a new unit if (placingUnit) { var gridX = Math.floor((x - gridOffsetX) / tileSize); var gridY = Math.floor((y - gridOffsetY) / tileSize); if (gridX >= 0 && gridX < gridWidth && gridY >= 0 && gridY < gridHeight) { if (placeUnit(placingUnit, gridX, gridY)) { placingUnit = null; highlight.visible = false; } } } }; game.up = function (x, y) { if (gameOver) { return; } if (selectedUnit && selectedUnit.isDragging) { var gridX = Math.floor((x - gridOffsetX) / tileSize); var gridY = Math.floor((y - gridOffsetY) / tileSize); if (gridX >= 0 && gridX < gridWidth && gridY >= 0 && gridY < gridHeight) { var gridCell = grid[gridY][gridX]; if (selectedUnit.checkForMerge(gridCell)) { // Merged successfully } else if (!gridCell.isPath && !gridCell.unit) { // Place on empty cell placeUnit(selectedUnit, gridX, gridY); } else { // Invalid placement, return to original position if any if (selectedUnit.gridPosition) { selectedUnit.x = grid[selectedUnit.gridPosition.y][selectedUnit.gridPosition.x].x; selectedUnit.y = grid[selectedUnit.gridPosition.y][selectedUnit.gridPosition.x].y; grid[selectedUnit.gridPosition.y][selectedUnit.gridPosition.x].unit = selectedUnit; } else { // If it didn't have a grid position, destroy it selectedUnit.destroy(); removeFromArray(units, selectedUnit); } } } else { // Dragged off grid, return to original position if any if (selectedUnit.gridPosition) { selectedUnit.x = grid[selectedUnit.gridPosition.y][selectedUnit.gridPosition.x].x; selectedUnit.y = grid[selectedUnit.gridPosition.y][selectedUnit.gridPosition.x].y; grid[selectedUnit.gridPosition.y][selectedUnit.gridPosition.x].unit = selectedUnit; } else { // If it didn't have a grid position, destroy it selectedUnit.destroy(); removeFromArray(units, selectedUnit); } } selectedUnit.isDragging = false; selectedUnit = null; } }; game.update = function () { // Update all active goblins for (var i = goblins.length - 1; i >= 0; i--) { if (goblins[i].active) { goblins[i].update(); } else { removeFromArray(goblins, goblins[i]); } } // Update all active projectiles for (var i = projectiles.length - 1; i >= 0; i--) { if (projectiles[i].active) { projectiles[i].update(); } else { removeFromArray(projectiles, projectiles[i]); } } // Update all units for (var i = 0; i < units.length; i++) { if (!units[i].merging) { units[i].update(); } } // Check if units in range of goblins units.forEach(function (unit) { if (unit.merging) { return; } // Reset targets and find new ones unit.targets = []; goblins.forEach(function (goblin) { if (goblin.active) { var dx = unit.x - goblin.x; var dy = unit.y - goblin.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist <= unit.range) { unit.targets.push(goblin); } } }); }); };
===================================================================
--- original.js
+++ change.js
@@ -1,6 +1,804 @@
-/****
+/****
+* Plugins
+****/
+var tween = LK.import("@upit/tween.v1");
+var storage = LK.import("@upit/storage.v1", {
+ highScore: 0
+});
+
+/****
+* Classes
+****/
+var Goblin = Container.expand(function (pathPoints, health, speed, value) {
+ var self = Container.call(this);
+ self.pathPoints = pathPoints;
+ self.maxHealth = health;
+ self.health = health;
+ self.speed = speed;
+ self.value = value;
+ self.active = true;
+ self.currentPathIndex = 0;
+ self.pathProgress = 0;
+ var goblinGraphics = self.attachAsset('goblin', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ var healthBar = LK.getAsset('tile', {
+ width: 60,
+ height: 10,
+ color: 0x00FF00,
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ healthBar.y = -40;
+ self.addChild(healthBar);
+ self.x = pathPoints[0].x;
+ self.y = pathPoints[0].y;
+ self.takeDamage = function (damage) {
+ self.health -= damage;
+ // Update health bar
+ var healthPercent = Math.max(0, self.health / self.maxHealth);
+ healthBar.width = 60 * healthPercent;
+ healthBar.tint = healthPercent > 0.5 ? 0x00FF00 : healthPercent > 0.25 ? 0xFFFF00 : 0xFF0000;
+ if (self.health <= 0) {
+ self.die();
+ }
+ };
+ self.die = function () {
+ self.active = false;
+ gold += self.value;
+ goldText.setText("Gold: " + gold);
+ score += self.value;
+ scoreText.setText("Score: " + score);
+ LK.setScore(score);
+ LK.getSound('goblin_death').play();
+ // Create coin effect
+ var coinText = new Text2("+" + self.value, {
+ size: 40,
+ fill: 0xFFD700
+ });
+ coinText.x = self.x;
+ coinText.y = self.y;
+ coinText.anchor.set(0.5, 0.5);
+ game.addChild(coinText);
+ tween(coinText, {
+ y: coinText.y - 100,
+ alpha: 0
+ }, {
+ duration: 1000,
+ onFinish: function onFinish() {
+ coinText.destroy();
+ }
+ });
+ self.destroy();
+ };
+ self.reachedBase = function () {
+ self.active = false;
+ lives--;
+ livesText.setText("Lives: " + lives);
+ if (lives <= 0) {
+ endGame();
+ }
+ self.destroy();
+ };
+ self.update = function () {
+ if (!self.active) {
+ return;
+ }
+ if (self.currentPathIndex >= self.pathPoints.length - 1) {
+ self.reachedBase();
+ return;
+ }
+ var currentPoint = self.pathPoints[self.currentPathIndex];
+ var nextPoint = self.pathPoints[self.currentPathIndex + 1];
+ var dx = nextPoint.x - currentPoint.x;
+ var dy = nextPoint.y - currentPoint.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ self.pathProgress = self.currentPathIndex / (self.pathPoints.length - 1);
+ // Calculate how far along this segment we are
+ var currentX = self.x - currentPoint.x;
+ var currentY = self.y - currentPoint.y;
+ var segmentProgress = Math.sqrt(currentX * currentX + currentY * currentY) / dist;
+ // Add segment progress to overall progress
+ self.pathProgress += segmentProgress / (self.pathPoints.length - 1);
+ // Move towards next point
+ var dx = nextPoint.x - self.x;
+ var dy = nextPoint.y - self.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist < self.speed) {
+ // Reached the next point
+ self.currentPathIndex++;
+ } else {
+ // Move along the path
+ self.x += dx / dist * self.speed;
+ self.y += dy / dist * self.speed;
+ }
+ };
+ return self;
+});
+var Projectile = Container.expand(function (source, target, damage) {
+ var self = Container.call(this);
+ self.source = source;
+ self.target = target;
+ self.damage = damage;
+ self.speed = 15;
+ self.active = true;
+ var projectileGraphics = self.attachAsset('projectile', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ self.x = source.x;
+ self.y = source.y;
+ self.update = function () {
+ if (!self.active || !self.target || !self.target.active) {
+ self.destroy();
+ return;
+ }
+ // Calculate direction vector
+ var dx = self.target.x - self.x;
+ var dy = self.target.y - self.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist < 20) {
+ // Hit target
+ self.target.takeDamage(self.damage);
+ LK.getSound('hit').play();
+ self.active = false;
+ self.destroy();
+ return;
+ }
+ // Normalize and scale by speed
+ self.x += dx / dist * self.speed;
+ self.y += dy / dist * self.speed;
+ };
+ return self;
+});
+var Unit = Container.expand(function (tier) {
+ var self = Container.call(this);
+ self.tier = tier || 1;
+ self.damage = Math.pow(2, self.tier - 1);
+ self.range = 300 + self.tier * 50;
+ self.fireRate = 1000 - self.tier * 50; // ms between shots
+ self.lastShot = 0;
+ self.targets = [];
+ self.isDragging = false;
+ self.merging = false;
+ var unitGraphics = self.attachAsset('unit' + self.tier, {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ var tierText = new Text2(self.tier.toString(), {
+ size: 40,
+ fill: 0x000000
+ });
+ tierText.anchor.set(0.5, 0.5);
+ self.addChild(tierText);
+ self.findTarget = function () {
+ if (self.targets.length === 0) {
+ return null;
+ }
+ // Sort targets by progress
+ self.targets.sort(function (a, b) {
+ return b.pathProgress - a.pathProgress;
+ });
+ // Return the most progressed goblin
+ return self.targets[0];
+ };
+ self.shoot = function (target) {
+ if (!target || !target.active) {
+ return;
+ }
+ var now = Date.now();
+ if (now - self.lastShot < self.fireRate) {
+ return;
+ }
+ self.lastShot = now;
+ var projectile = new Projectile(self, target, self.damage);
+ projectiles.push(projectile);
+ game.addChild(projectile);
+ LK.getSound('shoot').play();
+ };
+ self.update = function () {
+ if (self.merging) {
+ return;
+ }
+ // Clean up destroyed targets
+ self.targets = self.targets.filter(function (target) {
+ return target.active;
+ });
+ var target = self.findTarget();
+ if (target) {
+ self.shoot(target);
+ }
+ };
+ self.down = function (x, y, obj) {
+ if (!placingUnit && !gameOver) {
+ self.isDragging = true;
+ selectedUnit = self;
+ if (self.gridPosition) {
+ // Remove from grid
+ grid[self.gridPosition.y][self.gridPosition.x].unit = null;
+ self.gridPosition = null;
+ }
+ }
+ };
+ self.checkForMerge = function (gridCell) {
+ if (!gridCell || !gridCell.unit || gridCell.unit === self) {
+ return false;
+ }
+ if (gridCell.unit.tier === self.tier && self.tier < 10) {
+ // Merge units
+ mergeUnits(self, gridCell.unit);
+ return true;
+ }
+ return false;
+ };
+ return self;
+});
+
+/****
* Initialize Game
-****/
+****/
var game = new LK.Game({
- backgroundColor: 0x000000
-});
\ No newline at end of file
+ backgroundColor: 0x558855
+});
+
+/****
+* Game Code
+****/
+// Game configuration
+var gridWidth = 7;
+var gridHeight = 6;
+var tileSize = 120;
+var grid = [];
+var pathPoints = [];
+var goblins = [];
+var projectiles = [];
+var units = [];
+var waves = [];
+var currentWave = 0;
+var waveInProgress = false;
+var selectedUnit = null;
+var placingUnit = null;
+var gold = 100;
+var lives = 3;
+var score = 0;
+var gameOver = false;
+var gridOffsetX = (2048 - gridWidth * tileSize) / 2;
+var gridOffsetY = 400;
+var highlight = null;
+var unitCost = 50;
+// UI elements
+var waveText;
+var goldText;
+var livesText;
+var scoreText;
+var nextWaveButton;
+var unitBuyButton;
+function initializeGrid() {
+ for (var y = 0; y < gridHeight; y++) {
+ grid[y] = [];
+ for (var x = 0; x < gridWidth; x++) {
+ grid[y][x] = {
+ x: gridOffsetX + x * tileSize + tileSize / 2,
+ y: gridOffsetY + y * tileSize + tileSize / 2,
+ unit: null,
+ isPath: false
+ };
+ var tile = LK.getAsset('tile', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ tile.x = grid[y][x].x;
+ tile.y = grid[y][x].y;
+ tile.alpha = 0.5;
+ game.addChild(tile);
+ }
+ }
+}
+function createPath() {
+ // Define a simple path through the grid
+ var pathCoordinates = [{
+ x: 0,
+ y: 2
+ }, {
+ x: 1,
+ y: 2
+ }, {
+ x: 2,
+ y: 2
+ }, {
+ x: 3,
+ y: 2
+ }, {
+ x: 3,
+ y: 3
+ }, {
+ x: 3,
+ y: 4
+ }, {
+ x: 4,
+ y: 4
+ }, {
+ x: 5,
+ y: 4
+ }, {
+ x: 6,
+ y: 4
+ }];
+ for (var i = 0; i < pathCoordinates.length; i++) {
+ var coord = pathCoordinates[i];
+ var cell = grid[coord.y][coord.x];
+ cell.isPath = true;
+ var pathTile = LK.getAsset('path', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ pathTile.x = cell.x;
+ pathTile.y = cell.y;
+ pathTile.alpha = 0.7;
+ game.addChild(pathTile);
+ pathPoints.push({
+ x: cell.x,
+ y: cell.y
+ });
+ }
+ // Add the base at the end of the path
+ var lastCoord = pathCoordinates[pathCoordinates.length - 1];
+ var baseCell = grid[lastCoord.y][lastCoord.x];
+ var base = LK.getAsset('base', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ base.x = baseCell.x + tileSize;
+ base.y = baseCell.y;
+ game.addChild(base);
+}
+function initializeWaves() {
+ // Define waves of increasing difficulty
+ waves = [{
+ count: 5,
+ health: 10,
+ speed: 2,
+ value: 5,
+ delay: 1000
+ }, {
+ count: 8,
+ health: 15,
+ speed: 2,
+ value: 6,
+ delay: 900
+ }, {
+ count: 10,
+ health: 20,
+ speed: 2.2,
+ value: 7,
+ delay: 850
+ }, {
+ count: 12,
+ health: 30,
+ speed: 2.2,
+ value: 8,
+ delay: 800
+ }, {
+ count: 15,
+ health: 40,
+ speed: 2.3,
+ value: 9,
+ delay: 750
+ }, {
+ count: 18,
+ health: 50,
+ speed: 2.4,
+ value: 10,
+ delay: 700
+ }, {
+ count: 20,
+ health: 70,
+ speed: 2.5,
+ value: 12,
+ delay: 650
+ }, {
+ count: 25,
+ health: 90,
+ speed: 2.6,
+ value: 15,
+ delay: 600
+ }, {
+ count: 30,
+ health: 120,
+ speed: 2.8,
+ value: 18,
+ delay: 550
+ }, {
+ count: 35,
+ health: 150,
+ speed: 3,
+ value: 20,
+ delay: 500
+ }];
+}
+function startWave() {
+ if (waveInProgress || gameOver) {
+ return;
+ }
+ waveInProgress = true;
+ currentWave++;
+ if (currentWave > waves.length) {
+ // Player has completed all waves!
+ LK.showYouWin();
+ return;
+ }
+ waveText.setText("Wave: " + currentWave + "/" + waves.length);
+ nextWaveButton.visible = false;
+ var wave = waves[currentWave - 1];
+ var goblinsReleased = 0;
+ var spawnInterval = LK.setInterval(function () {
+ spawnGoblin(wave);
+ goblinsReleased++;
+ if (goblinsReleased >= wave.count) {
+ LK.clearInterval(spawnInterval);
+ // Check every second if wave is complete
+ var checkInterval = LK.setInterval(function () {
+ if (goblins.length === 0) {
+ waveInProgress = false;
+ nextWaveButton.visible = true;
+ LK.clearInterval(checkInterval);
+ }
+ }, 1000);
+ }
+ }, wave.delay);
+}
+function spawnGoblin(wave) {
+ var goblin = new Goblin(pathPoints, wave.health, wave.speed, wave.value);
+ goblins.push(goblin);
+ game.addChild(goblin);
+ // Add this goblin as a target for all units in range
+ units.forEach(function (unit) {
+ var dx = unit.x - goblin.x;
+ var dy = unit.y - goblin.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist <= unit.range) {
+ unit.targets.push(goblin);
+ }
+ });
+}
+function createUnit(tier) {
+ var unit = new Unit(tier);
+ units.push(unit);
+ return unit;
+}
+function placeUnit(unit, gridX, gridY) {
+ if (gridX < 0 || gridX >= gridWidth || gridY < 0 || gridY >= gridHeight) {
+ return false;
+ }
+ var gridCell = grid[gridY][gridX];
+ if (gridCell.isPath || gridCell.unit) {
+ return false;
+ }
+ // Place the unit
+ unit.x = gridCell.x;
+ unit.y = gridCell.y;
+ unit.gridPosition = {
+ x: gridX,
+ y: gridY
+ };
+ gridCell.unit = unit;
+ // Find nearby targets
+ goblins.forEach(function (goblin) {
+ if (goblin.active) {
+ var dx = unit.x - goblin.x;
+ var dy = unit.y - goblin.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist <= unit.range) {
+ unit.targets.push(goblin);
+ }
+ }
+ });
+ LK.getSound('place').play();
+ return true;
+}
+function mergeUnits(unit1, unit2) {
+ // Create a new unit of the next tier
+ var newUnit = createUnit(unit1.tier + 1);
+ game.addChild(newUnit);
+ // Place at the position of the second unit
+ newUnit.gridPosition = unit2.gridPosition;
+ grid[unit2.gridPosition.y][unit2.gridPosition.x].unit = newUnit;
+ newUnit.x = unit2.x;
+ newUnit.y = unit2.y;
+ // Remove the merged units
+ unit1.merging = true;
+ unit2.merging = true;
+ // Effect
+ tween(unit1, {
+ x: unit2.x,
+ y: unit2.y,
+ alpha: 0
+ }, {
+ duration: 300,
+ onFinish: function onFinish() {
+ unit1.destroy();
+ removeFromArray(units, unit1);
+ }
+ });
+ tween(unit2, {
+ alpha: 0
+ }, {
+ duration: 300,
+ onFinish: function onFinish() {
+ unit2.destroy();
+ removeFromArray(units, unit2);
+ }
+ });
+ // Scale up effect for new unit
+ newUnit.scale.x = 0.5;
+ newUnit.scale.y = 0.5;
+ tween(newUnit.scale, {
+ x: 1,
+ y: 1
+ }, {
+ duration: 300,
+ easing: tween.elasticOut
+ });
+ LK.getSound('merge').play();
+ return newUnit;
+}
+function buyUnit() {
+ if (gold < unitCost || placingUnit || gameOver) {
+ return;
+ }
+ gold -= unitCost;
+ goldText.setText("Gold: " + gold);
+ placingUnit = createUnit(1);
+ game.addChild(placingUnit);
+ // Create highlight if it doesn't exist
+ if (!highlight) {
+ highlight = LK.getAsset('highlight', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ highlight.alpha = 0.3;
+ game.addChild(highlight);
+ }
+ highlight.visible = true;
+}
+function createUI() {
+ // Wave text
+ waveText = new Text2("Wave: 0/" + waves.length, {
+ size: 50,
+ fill: 0xFFFFFF
+ });
+ waveText.anchor.set(0.5, 0);
+ LK.gui.top.addChild(waveText);
+ // Gold text
+ goldText = new Text2("Gold: " + gold, {
+ size: 50,
+ fill: 0xFFD700
+ });
+ goldText.anchor.set(0, 0);
+ LK.gui.topLeft.addChild(goldText);
+ goldText.x = 120; // Move away from the top left corner
+ // Lives text
+ livesText = new Text2("Lives: " + lives, {
+ size: 50,
+ fill: 0xFF0000
+ });
+ livesText.anchor.set(1, 0);
+ LK.gui.topRight.addChild(livesText);
+ // Score text
+ scoreText = new Text2("Score: " + score, {
+ size: 50,
+ fill: 0xFFFFFF
+ });
+ scoreText.anchor.set(0.5, 0);
+ LK.gui.top.addChild(scoreText);
+ scoreText.y = 80;
+ // Next wave button
+ nextWaveButton = new Container();
+ var nextWaveButtonBg = LK.getAsset('tile', {
+ width: 300,
+ height: 100,
+ color: 0x336699,
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ nextWaveButton.addChild(nextWaveButtonBg);
+ var nextWaveButtonText = new Text2("Next Wave", {
+ size: 50,
+ fill: 0xFFFFFF
+ });
+ nextWaveButtonText.anchor.set(0.5, 0.5);
+ nextWaveButton.addChild(nextWaveButtonText);
+ nextWaveButton.down = function () {
+ startWave();
+ };
+ LK.gui.bottom.addChild(nextWaveButton);
+ nextWaveButton.y = -150;
+ // Buy unit button
+ unitBuyButton = new Container();
+ var unitBuyButtonBg = LK.getAsset('tile', {
+ width: 300,
+ height: 100,
+ color: 0x669933,
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ unitBuyButton.addChild(unitBuyButtonBg);
+ var unitBuyButtonText = new Text2("Buy Unit: " + unitCost + "g", {
+ size: 40,
+ fill: 0xFFFFFF
+ });
+ unitBuyButtonText.anchor.set(0.5, 0.5);
+ unitBuyButton.addChild(unitBuyButtonText);
+ unitBuyButton.down = function () {
+ buyUnit();
+ };
+ LK.gui.bottomLeft.addChild(unitBuyButton);
+ unitBuyButton.y = -150;
+ unitBuyButton.x = 170;
+}
+function endGame() {
+ if (gameOver) {
+ return;
+ }
+ gameOver = true;
+ if (score > storage.highScore) {
+ storage.highScore = score;
+ }
+ LK.getSound('game_over').play();
+ LK.showGameOver();
+}
+function removeFromArray(array, item) {
+ var index = array.indexOf(item);
+ if (index !== -1) {
+ array.splice(index, 1);
+ }
+}
+function initialize() {
+ initializeGrid();
+ createPath();
+ initializeWaves();
+ createUI();
+ // Play background music
+ LK.playMusic('bg_music');
+ // Create highlight for placing units (initially hidden)
+ highlight = LK.getAsset('highlight', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ highlight.alpha = 0.3;
+ highlight.visible = false;
+ game.addChild(highlight);
+ // Start with wave 0
+ waveText.setText("Wave: 0/" + waves.length);
+}
+initialize();
+game.move = function (x, y) {
+ if (gameOver) {
+ return;
+ }
+ if (selectedUnit) {
+ selectedUnit.x = x;
+ selectedUnit.y = y;
+ } else if (placingUnit) {
+ // Find the closest grid cell
+ var gridX = Math.floor((x - gridOffsetX) / tileSize);
+ var gridY = Math.floor((y - gridOffsetY) / tileSize);
+ if (gridX >= 0 && gridX < gridWidth && gridY >= 0 && gridY < gridHeight) {
+ var cell = grid[gridY][gridX];
+ placingUnit.x = x;
+ placingUnit.y = y;
+ highlight.x = cell.x;
+ highlight.y = cell.y;
+ highlight.visible = true;
+ // Change highlight color based on valid placement
+ if (cell.isPath || cell.unit) {
+ highlight.tint = 0xFF0000;
+ } else {
+ highlight.tint = 0x00FF00;
+ }
+ } else {
+ highlight.visible = false;
+ }
+ }
+};
+game.down = function (x, y) {
+ if (gameOver) {
+ return;
+ }
+ // If we're placing a new unit
+ if (placingUnit) {
+ var gridX = Math.floor((x - gridOffsetX) / tileSize);
+ var gridY = Math.floor((y - gridOffsetY) / tileSize);
+ if (gridX >= 0 && gridX < gridWidth && gridY >= 0 && gridY < gridHeight) {
+ if (placeUnit(placingUnit, gridX, gridY)) {
+ placingUnit = null;
+ highlight.visible = false;
+ }
+ }
+ }
+};
+game.up = function (x, y) {
+ if (gameOver) {
+ return;
+ }
+ if (selectedUnit && selectedUnit.isDragging) {
+ var gridX = Math.floor((x - gridOffsetX) / tileSize);
+ var gridY = Math.floor((y - gridOffsetY) / tileSize);
+ if (gridX >= 0 && gridX < gridWidth && gridY >= 0 && gridY < gridHeight) {
+ var gridCell = grid[gridY][gridX];
+ if (selectedUnit.checkForMerge(gridCell)) {
+ // Merged successfully
+ } else if (!gridCell.isPath && !gridCell.unit) {
+ // Place on empty cell
+ placeUnit(selectedUnit, gridX, gridY);
+ } else {
+ // Invalid placement, return to original position if any
+ if (selectedUnit.gridPosition) {
+ selectedUnit.x = grid[selectedUnit.gridPosition.y][selectedUnit.gridPosition.x].x;
+ selectedUnit.y = grid[selectedUnit.gridPosition.y][selectedUnit.gridPosition.x].y;
+ grid[selectedUnit.gridPosition.y][selectedUnit.gridPosition.x].unit = selectedUnit;
+ } else {
+ // If it didn't have a grid position, destroy it
+ selectedUnit.destroy();
+ removeFromArray(units, selectedUnit);
+ }
+ }
+ } else {
+ // Dragged off grid, return to original position if any
+ if (selectedUnit.gridPosition) {
+ selectedUnit.x = grid[selectedUnit.gridPosition.y][selectedUnit.gridPosition.x].x;
+ selectedUnit.y = grid[selectedUnit.gridPosition.y][selectedUnit.gridPosition.x].y;
+ grid[selectedUnit.gridPosition.y][selectedUnit.gridPosition.x].unit = selectedUnit;
+ } else {
+ // If it didn't have a grid position, destroy it
+ selectedUnit.destroy();
+ removeFromArray(units, selectedUnit);
+ }
+ }
+ selectedUnit.isDragging = false;
+ selectedUnit = null;
+ }
+};
+game.update = function () {
+ // Update all active goblins
+ for (var i = goblins.length - 1; i >= 0; i--) {
+ if (goblins[i].active) {
+ goblins[i].update();
+ } else {
+ removeFromArray(goblins, goblins[i]);
+ }
+ }
+ // Update all active projectiles
+ for (var i = projectiles.length - 1; i >= 0; i--) {
+ if (projectiles[i].active) {
+ projectiles[i].update();
+ } else {
+ removeFromArray(projectiles, projectiles[i]);
+ }
+ }
+ // Update all units
+ for (var i = 0; i < units.length; i++) {
+ if (!units[i].merging) {
+ units[i].update();
+ }
+ }
+ // Check if units in range of goblins
+ units.forEach(function (unit) {
+ if (unit.merging) {
+ return;
+ }
+ // Reset targets and find new ones
+ unit.targets = [];
+ goblins.forEach(function (goblin) {
+ if (goblin.active) {
+ var dx = unit.x - goblin.x;
+ var dy = unit.y - goblin.y;
+ var dist = Math.sqrt(dx * dx + dy * dy);
+ if (dist <= unit.range) {
+ unit.targets.push(goblin);
+ }
+ }
+ });
+ });
+};
\ No newline at end of file