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