User prompt
The Knock Knock tower can’t see the enemies...
User prompt
Finish it.
User prompt
Now the Knock Knock tower won’t become a wall...
User prompt
Make it so the enemies can’t go through the wall of Knock Knock towers.
User prompt
Make the Knock Knock tower become a wall by duplicating, not shooting.
User prompt
Make the Knock Knock tower copy itself until it’s a wall that the enemies can’t cross at all, can be placed on the track
User prompt
Put the start wave button on the map.
User prompt
Code the start wave button to start the next wave when you press it.
User prompt
Make it so the enemies don’t have so much health, also, make all the towers cost 10 laughs.
User prompt
Please fix the bug: 'Script error.' in or related to this line: 'self.detachChildren();' Line Number: 335
User prompt
Make us start with 100 LaughPoints to start an army of jokes.
User prompt
Please fix the bug: 'undefined is not an object (evaluating 'waveText.setText')' in or related to this line: 'waveText.setText("Wave: " + currentWave + "/" + waves.length);' Line Number: 854
Code edit (1 edits merged)
Please save this source code
User prompt
Joke Defense: Laugh or Lose
Initial prompt
A tower defense game where the towers are jokes.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var BuildSpot = Container.expand(function () { var self = Container.call(this); // Properties self.hasTower = false; self.towerType = null; // Create build spot sprite var spotSprite = self.attachAsset('buildSpot', { anchorX: 0.5, anchorY: 0.5 }); spotSprite.alpha = 0.5; self.down = function (x, y, obj) { if (self.hasTower) { return; } // Toggle build menu visibility if (buildMenu.visible && buildMenuSpot === self) { hideBuildMenu(); } else { showBuildMenu(self); } }; self.buildTower = function (type) { if (self.hasTower) { return false; } var towerCost = 10; if (type === 'pun') { towerCost = 10; } if (type === 'slapstick') { towerCost = 10; } if (type === 'knockknock') { towerCost = 10; } if (laughs >= towerCost) { // Deduct cost laughs -= towerCost; laughsText.setText("Laughs: " + laughs); // Create tower var tower; if (type === 'knockknock') { tower = new KnockKnockTower(); } else { tower = new Tower(); } tower.setup(type); tower.x = self.x; tower.y = self.y; game.addChild(tower); towers.push(tower); // Mark spot as used self.hasTower = true; self.towerType = type; spotSprite.alpha = 0.2; // Play build sound LK.getSound('buildTower').play(); // Hide build menu hideBuildMenu(); return true; } return false; }; return self; }); var Enemy = Container.expand(function () { var self = Container.call(this); // Properties self.health = 100; self.maxHealth = 100; self.speed = 2; self.value = 10; // Laughs earned when killed self.currentWaypointIndex = 0; self.reachedEnd = false; self.enemyType = 'normal'; // normal, punHater, slapstickHater self.healthBarWidth = 80; // Create enemy sprite var enemySprite = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); // Create health bar var healthBarBackground = LK.getAsset('path', { width: self.healthBarWidth, height: 10, color: 0x333333, anchorX: 0.5, anchorY: 0.5 }); healthBarBackground.y = -50; self.addChild(healthBarBackground); var healthBar = LK.getAsset('path', { width: self.healthBarWidth, height: 10, color: 0x00FF00, anchorX: 0, anchorY: 0.5 }); healthBar.x = -self.healthBarWidth / 2; healthBar.y = -50; self.addChild(healthBar); self.healthBar = healthBar; // Methods self.setup = function (type, difficulty) { self.enemyType = type; // Adjust properties based on type if (type === 'punHater') { enemySprite.tint = 0x3355FF; self.health = 70 * difficulty; self.maxHealth = 70 * difficulty; self.speed = 1.8; self.value = 15; } else if (type === 'slapstickHater') { enemySprite.tint = 0xFF5533; self.health = 60 * difficulty; self.maxHealth = 60 * difficulty; self.speed = 2.2; self.value = 12; } else { // Normal type self.health = 50 * difficulty; self.maxHealth = 50 * difficulty; } // Update health bar self.updateHealthBar(); }; self.takeDamage = function (amount, damageType) { // Apply damage modifiers based on enemy type and damage type var actualDamage = amount; if (self.enemyType === 'punHater' && damageType === 'pun') { actualDamage *= 0.5; // Pun haters take 50% damage from puns } else if (self.enemyType === 'slapstickHater' && damageType === 'slapstick') { actualDamage *= 0.5; // Slapstick haters take 50% damage from slapstick } else if (self.enemyType === 'punHater' && damageType === 'slapstick') { actualDamage *= 1.5; // Pun haters take extra damage from slapstick } else if (self.enemyType === 'slapstickHater' && damageType === 'pun') { actualDamage *= 1.5; // Slapstick haters take extra damage from puns } self.health -= actualDamage; // Update health bar self.updateHealthBar(); // Flash the enemy when taking damage LK.effects.flashObject(enemySprite, 0xFFFFFF, 200); return self.health <= 0; }; self.updateHealthBar = function () { var healthPercentage = Math.max(0, self.health / self.maxHealth); self.healthBar.width = self.healthBarWidth * healthPercentage; // Change color based on health if (healthPercentage > 0.6) { self.healthBar.tint = 0x00FF00; } else if (healthPercentage > 0.3) { self.healthBar.tint = 0xFFFF00; } else { self.healthBar.tint = 0xFF0000; } }; self.update = function () { if (self.reachedEnd || !gamePath || !gamePath.waypoints) { return; } // Get current and next waypoint var currentWaypoint = gamePath.waypoints[self.currentWaypointIndex]; var nextWaypointData = gamePath.getNextWaypoint(self.currentWaypointIndex, { x: self.x, y: self.y }); // If reached end of path if (!nextWaypointData) { self.reachedEnd = true; playerHealth -= 1; healthText.setText("Health: " + playerHealth); if (playerHealth <= 0) { LK.effects.flashScreen(0xFF0000, 1000); LK.showGameOver(); } return; } // Move towards next waypoint var nextWaypoint = nextWaypointData.position; var dx = nextWaypoint.x - self.x; var dy = nextWaypoint.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < self.speed) { // Reached waypoint, move to next self.currentWaypointIndex++; } else { // Calculate new position var newX = self.x + dx / distance * self.speed; var newY = self.y + dy / distance * self.speed; // Check for collisions with KnockKnock towers var canMove = true; for (var i = 0; i < towers.length; i++) { var tower = towers[i]; // Only check KnockKnock towers if (tower.towerType === 'knockknock') { var tdx = tower.x - newX; var tdy = tower.y - newY; var towerDistance = Math.sqrt(tdx * tdx + tdy * tdy); // If too close to a KnockKnock tower, can't move if (towerDistance < 50) { canMove = false; break; } } } // Only move if not blocked by KnockKnock towers if (canMove) { self.x = newX; self.y = newY; } else { // Try to find an alternative path by slightly changing direction // This creates a more natural "flowing around obstacles" behavior var angles = [Math.PI / 4, -Math.PI / 4, Math.PI / 2, -Math.PI / 2]; for (var i = 0; i < angles.length; i++) { var angle = Math.atan2(dy, dx) + angles[i]; var altX = self.x + Math.cos(angle) * self.speed * 0.5; var altY = self.y + Math.sin(angle) * self.speed * 0.5; // Check if this alternative path is clear var altPathClear = true; for (var j = 0; j < towers.length; j++) { var tower = towers[j]; if (tower.towerType === 'knockknock') { var altDx = tower.x - altX; var altDy = tower.y - altY; var altDist = Math.sqrt(altDx * altDx + altDy * altDy); if (altDist < 50) { altPathClear = false; break; } } } // If found clear path, take it if (altPathClear) { self.x = altX; self.y = altY; break; } } } } }; return self; }); var Path = Container.expand(function () { var self = Container.call(this); self.waypoints = []; self.addWaypoint = function (x, y) { self.waypoints.push({ x: x, y: y }); }; self.getNextWaypoint = function (currentWaypoint, currentPosition) { if (currentWaypoint < self.waypoints.length - 1) { return { index: currentWaypoint + 1, position: self.waypoints[currentWaypoint + 1] }; } return null; }; self.render = function () { if (self.waypoints.length < 2) { return; } for (var i = 0; i < self.waypoints.length - 1; i++) { var start = self.waypoints[i]; var end = self.waypoints[i + 1]; var distance = Math.sqrt(Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2)); var angle = Math.atan2(end.y - start.y, end.x - start.x); var pathSegment = self.attachAsset('path', { anchorX: 0, anchorY: 0.5, width: distance, height: 80 }); pathSegment.x = start.x; pathSegment.y = start.y; pathSegment.rotation = angle; pathSegment.alpha = 0.5; } }; return self; }); var Projectile = Container.expand(function () { var self = Container.call(this); // Properties self.speed = 10; self.damage = 20; self.target = null; self.damageType = 'normal'; // Create projectile sprite var projectileSprite = self.attachAsset('projectile', { anchorX: 0.5, anchorY: 0.5 }); // Adapt color based on joke type if (self.damageType === 'pun') { projectileSprite.tint = 0x44AADD; } else if (self.damageType === 'slapstick') { projectileSprite.tint = 0xFF6600; } self.update = function () { if (!self.target || !self.target.parent || self.target.health <= 0) { self.destroy(); return; } // Move towards target var dx = self.target.x - self.x; var dy = self.target.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= self.speed) { // Hit target var killed = self.target.takeDamage(self.damage, self.damageType); if (killed) { // Grant laughs for kill laughs += self.target.value; laughsText.setText("Laughs: " + laughs); // Play death sound LK.getSound('enemyDie').play(); // Remove from enemies array var index = enemies.indexOf(self.target); if (index > -1) { enemies.splice(index, 1); } // Destroy the enemy self.target.destroy(); } // Destroy projectile after hit self.destroy(); } else { // Move towards target self.x += dx / distance * self.speed; self.y += dy / distance * self.speed; // Rotate to face movement direction self.rotation = Math.atan2(dy, dx); } }; return self; }); var Tower = Container.expand(function () { var self = Container.call(this); // Properties self.range = 200; self.damage = 20; self.attackSpeed = 1; // Attacks per second self.attackCooldown = 0; self.level = 1; self.cost = 50; self.upgradeMultiplier = 1.5; self.target = null; self.towerType = 'normal'; // normal, pun, slapstick self.rangeIndicator = null; self.showingRange = false; // Create tower sprite var towerSprite = self.attachAsset('tower', { anchorX: 0.5, anchorY: 0.5 }); // Methods self.setup = function (type) { self.towerType = type; // Adjust properties based on type if (type === 'pun') { self.removeChildren(); towerSprite = self.attachAsset('punTower', { anchorX: 0.5, anchorY: 0.5 }); self.damage = 15; self.attackSpeed = 1.2; self.range = 250; self.cost = 60; } else if (type === 'slapstick') { self.removeChildren(); towerSprite = self.attachAsset('slapstickTower', { anchorX: 0.5, anchorY: 0.5 }); self.damage = 25; self.attackSpeed = 0.8; self.range = 180; self.cost = 70; } // Create level indicator text var levelText = new Text2("1", { size: 30, fill: 0xFFFFFF }); levelText.anchor.set(0.5, 0.5); levelText.y = -60; self.addChild(levelText); self.levelText = levelText; }; self.toggleRangeIndicator = function () { if (self.showingRange) { if (self.rangeIndicator) { self.removeChild(self.rangeIndicator); self.rangeIndicator = null; } self.showingRange = false; } else { self.rangeIndicator = LK.getAsset('rangeIndicator', { anchorX: 0.5, anchorY: 0.5, width: self.range * 2, height: self.range * 2 }); self.rangeIndicator.alpha = 0.2; self.addChild(self.rangeIndicator); self.showingRange = true; } }; self.upgrade = function () { if (laughs >= self.getUpgradeCost()) { laughs -= self.getUpgradeCost(); laughsText.setText("Laughs: " + laughs); self.level++; self.damage = Math.floor(self.damage * 1.5); self.range *= 1.2; self.attackSpeed *= 1.1; // Update level indicator self.levelText.setText(self.level); // Update range indicator if visible if (self.showingRange) { self.toggleRangeIndicator(); self.toggleRangeIndicator(); } return true; } return false; }; self.getUpgradeCost = function () { return Math.floor(self.cost * Math.pow(self.upgradeMultiplier, self.level)); }; self.findTarget = function (enemies) { // Clear current target if it's not valid if (self.target && (!self.target.parent || self.target.health <= 0 || self.distanceTo(self.target) > self.range)) { self.target = null; } // Find new target if needed if (!self.target) { var closestEnemy = null; var closestDistance = self.range; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy.health > 0) { var distance = self.distanceTo(enemy); if (distance <= self.range && (!closestEnemy || distance < closestDistance)) { closestEnemy = enemy; closestDistance = distance; } } } self.target = closestEnemy; } return self.target; }; self.distanceTo = function (object) { var dx = self.x - object.x; var dy = self.y - object.y; return Math.sqrt(dx * dx + dy * dy); }; self.attack = function () { if (!self.target) { return; } // Create projectile var projectile = new Projectile(); projectile.x = self.x; projectile.y = self.y; projectile.target = self.target; projectile.damage = self.damage; projectile.damageType = self.towerType; game.addChild(projectile); projectiles.push(projectile); // Play attack sound LK.getSound('joke').play(); // Visual feedback tween(towerSprite, { scaleX: 1.2, scaleY: 1.2 }, { duration: 100, onFinish: function onFinish() { tween(towerSprite, { scaleX: 1, scaleY: 1 }, { duration: 100 }); } }); }; self.down = function (x, y, obj) { // Show range and upgrade info self.toggleRangeIndicator(); // Set as selected tower if (selectedTower && selectedTower !== self) { selectedTower.toggleRangeIndicator(); } selectedTower = self; // Update upgrade button var upgradeCost = self.getUpgradeCost(); upgradeButton.setText("Upgrade: " + upgradeCost); upgradeButton.visible = true; // Can upgrade if enough laughs upgradeButton.tint = laughs >= upgradeCost ? 0xFFFFFF : 0xFF0000; }; self.update = function () { // Handle attack cooldown if (self.attackCooldown > 0) { self.attackCooldown -= 1 / 60; // 60 fps } // Only find and attack if cooldown is ready if (self.attackCooldown <= 0) { if (self.findTarget(enemies)) { self.attack(); self.attackCooldown = 1 / self.attackSpeed; } } }; return self; }); var KnockKnockTower = Tower.expand(function () { var self = Tower.call(this); // Override setup method to customize KnockKnock tower var parentSetup = self.setup; self.setup = function (type) { self.towerType = 'knockknock'; self.removeChildren(); towerSprite = self.attachAsset('KnockKnockTower', { anchorX: 0.5, anchorY: 0.5 }); // Special properties for KnockKnock tower self.damage = 5; self.attackSpeed = 0.5; self.range = 150; self.cost = 10; self.spreadRadius = 40; // Reduced from 60 to make a tighter wall self.duplicateCount = 0; // Track how many times this tower has duplicated self.maxDuplicates = 30; // Increased from 20 to create a bigger wall self.duplicateInterval = 1; // Reduced from 2 to build wall faster self.duplicateTimer = 0; // Create level indicator text var levelText = new Text2("1", { size: 30, fill: 0xFFFFFF }); levelText.anchor.set(0.5, 0.5); levelText.y = -60; self.addChild(levelText); self.levelText = levelText; }; // Override attack method to do nothing self.attack = function () { // Intentionally empty - KnockKnock towers don't attack }; // Override update to add duplication logic - don't shoot, just duplicate self.update = function () { // Do NOT call parent update (attack logic) - this tower doesn't shoot // Only handle duplication if (self.duplicateCount < self.maxDuplicates) { self.duplicateTimer += 1 / 60; // Assuming 60 FPS if (self.duplicateTimer >= self.duplicateInterval) { self.duplicateTimer = 0; self.duplicate(); } } // Handle attack cooldown (needed to maintain parent behavior) if (self.attackCooldown > 0) { self.attackCooldown -= 1 / 60; // 60 fps } // But don't call attack method }; // Create duplicate towers in line to form a wall self.duplicate = function () { if (self.duplicateCount >= self.maxDuplicates) { return; } // Try to duplicate in all four directions to create a solid wall var directions = [{ dx: self.spreadRadius, dy: 0 }, // right { dx: -self.spreadRadius, dy: 0 }, // left { dx: 0, dy: self.spreadRadius }, // down { dx: 0, dy: -self.spreadRadius } // up ]; for (var i = 0; i < directions.length; i++) { var direction = directions[i]; var newX = self.x + direction.dx; var newY = self.y + direction.dy; // Check if position is valid (not on the path and not occupied) if (self.isValidPosition(newX, newY)) { // Create new tower var newTower = new KnockKnockTower(); newTower.setup('knockknock'); newTower.x = newX; newTower.y = newY; // Set duplicate count high so it doesn't duplicate too much newTower.duplicateCount = self.duplicateCount + 1; game.addChild(newTower); towers.push(newTower); self.duplicateCount++; // Play build sound LK.getSound('buildTower').play(); break; // Only place one tower per duplication } } }; // Check if position is valid for tower placement self.isValidPosition = function (x, y) { // KnockKnock towers can be placed on paths, so we don't check for path collision // Check if tower already exists at position for (var i = 0; i < towers.length; i++) { var tower = towers[i]; var dx = tower.x - x; var dy = tower.y - y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 50) { // Minimum distance between towers return false; } } // Check for enemies - don't place tower on top of enemies for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var dx = enemy.x - x; var dy = enemy.y - y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < 70) { // Don't block too close to enemies that are already on the path return false; } } return true; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x4B6F44 }); /**** * Game Code ****/ // Game variables var gamePath; var waves = []; var currentWave = 0; var enemies = []; var towers = []; var projectiles = []; var buildSpots = []; var laughs = 100; // Starting currency var playerHealth = 10; var waveInProgress = false; var nextWaveCountdown = 10; // Seconds until next wave var selectedTower = null; var buildMenu; var buildMenuSpot = null; var spawnInterval; // UI Elements var laughsText; var healthText; var waveText = new Text2("Wave: 0/0", { size: 40, fill: 0xFFFFFF }); var nextWaveText; var startWaveButton; var upgradeButton; var normalTowerButton; var punTowerButton; var slapstickTowerButton; var knockKnockTowerButton; // Initialize UI function initUI() { // Create score text laughsText = new Text2("Laughs: " + laughs, { size: 40, fill: 0xFFFFFF }); laughsText.anchor.set(0, 0); laughsText.x = 120; laughsText.y = 20; LK.gui.addChild(laughsText); // Create health text healthText = new Text2("Health: " + playerHealth, { size: 40, fill: 0xFFFFFF }); healthText.anchor.set(0, 0); healthText.x = 120; healthText.y = 70; LK.gui.addChild(healthText); // Create wave text waveText = new Text2("Wave: 0/" + waves.length, { size: 40, fill: 0xFFFFFF }); waveText.anchor.set(0, 0); waveText.x = 120; waveText.y = 120; LK.gui.addChild(waveText); // Create next wave text nextWaveText = new Text2("Next wave: " + nextWaveCountdown, { size: 40, fill: 0xFFFFFF }); nextWaveText.anchor.set(0.5, 0); nextWaveText.x = 1024; nextWaveText.y = 20; LK.gui.addChild(nextWaveText); // Create start wave button var buttonAsset = LK.getAsset('StartWaveButton', { anchorX: 0.5, anchorY: 0.5 }); startWaveButton = new Container(); startWaveButton.addChild(buttonAsset); // Add text to button var buttonText = new Text2("Start Wave", { size: 40, fill: 0xFFFFFF }); buttonText.anchor.set(0.5, 0.5); startWaveButton.addChild(buttonText); // Position button on the map (not in the GUI) startWaveButton.x = 1900; startWaveButton.y = 300; startWaveButton.interactive = true; game.addChild(startWaveButton); startWaveButton.down = function () { if (!waveInProgress && currentWave < waves.length) { startWave(); nextWaveCountdown = 0; // Reset countdown to avoid timer interference } }; // Create upgrade button (hidden initially) upgradeButton = new Text2("Upgrade: 50", { size: 40, fill: 0xFFFFFF }); upgradeButton.anchor.set(0.5, 0); upgradeButton.x = 1024; upgradeButton.y = 2650; upgradeButton.visible = false; upgradeButton.interactive = true; LK.gui.addChild(upgradeButton); upgradeButton.down = function () { if (selectedTower) { if (selectedTower.upgrade()) { var upgradeCost = selectedTower.getUpgradeCost(); upgradeButton.setText("Upgrade: " + upgradeCost); upgradeButton.tint = laughs >= upgradeCost ? 0xFFFFFF : 0xFF0000; } } }; // Create build menu (hidden initially) buildMenu = new Container(); buildMenu.visible = false; game.addChild(buildMenu); // Normal tower button normalTowerButton = new Text2("Normal Tower (10)", { size: 30, fill: 0xFFFFFF }); normalTowerButton.anchor.set(0.5, 0.5); normalTowerButton.y = -120; normalTowerButton.interactive = true; buildMenu.addChild(normalTowerButton); normalTowerButton.down = function () { if (buildMenuSpot) { buildMenuSpot.buildTower('normal'); } }; // Pun tower button punTowerButton = new Text2("Pun Tower (10)", { size: 30, fill: 0xFFFFFF }); punTowerButton.anchor.set(0.5, 0.5); punTowerButton.y = -60; punTowerButton.interactive = true; buildMenu.addChild(punTowerButton); punTowerButton.down = function () { if (buildMenuSpot) { buildMenuSpot.buildTower('pun'); } }; // Slapstick tower button slapstickTowerButton = new Text2("Slapstick Tower (10)", { size: 30, fill: 0xFFFFFF }); slapstickTowerButton.anchor.set(0.5, 0.5); slapstickTowerButton.y = 0; slapstickTowerButton.interactive = true; buildMenu.addChild(slapstickTowerButton); slapstickTowerButton.down = function () { if (buildMenuSpot) { buildMenuSpot.buildTower('slapstick'); } }; // KnockKnock tower button knockKnockTowerButton = new Text2("KnockKnock Tower (10)", { size: 30, fill: 0xFFFFFF }); knockKnockTowerButton.anchor.set(0.5, 0.5); knockKnockTowerButton.y = 60; knockKnockTowerButton.interactive = true; buildMenu.addChild(knockKnockTowerButton); knockKnockTowerButton.down = function () { if (buildMenuSpot) { buildMenuSpot.buildTower('knockknock'); } }; } function showBuildMenu(spot) { if (buildMenu.visible && buildMenuSpot === spot) { hideBuildMenu(); return; } buildMenuSpot = spot; buildMenu.x = spot.x; buildMenu.y = spot.y; buildMenu.visible = true; // Update button colors based on affordability normalTowerButton.tint = laughs >= 10 ? 0xFFFFFF : 0xFF0000; punTowerButton.tint = laughs >= 10 ? 0xFFFFFF : 0xFF0000; slapstickTowerButton.tint = laughs >= 10 ? 0xFFFFFF : 0xFF0000; knockKnockTowerButton.tint = laughs >= 10 ? 0xFFFFFF : 0xFF0000; } function hideBuildMenu() { buildMenu.visible = false; buildMenuSpot = null; } // Create game path function createPath() { gamePath = new Path(); // Add waypoints (customize these for your map) gamePath.addWaypoint(0, 700); gamePath.addWaypoint(400, 700); gamePath.addWaypoint(400, 1100); gamePath.addWaypoint(1100, 1100); gamePath.addWaypoint(1100, 1500); gamePath.addWaypoint(400, 1500); gamePath.addWaypoint(400, 1900); gamePath.addWaypoint(1600, 1900); gamePath.addWaypoint(1600, 700); gamePath.addWaypoint(2048, 700); // Render path gamePath.render(); game.addChild(gamePath); // Create Punchline (the end point to defend) var punchline = LK.getAsset('punchline', { anchorX: 0.5, anchorY: 0.5 }); punchline.x = 2048 - 100; punchline.y = 700; game.addChild(punchline); // Create build spots along the path createBuildSpots(); } function createBuildSpots() { // Create build spots strategically around the path var spots = [{ x: 300, y: 550 }, { x: 300, y: 850 }, { x: 550, y: 1000 }, { x: 550, y: 1200 }, { x: 950, y: 1000 }, { x: 950, y: 1200 }, { x: 950, y: 1350 }, { x: 950, y: 1650 }, { x: 550, y: 1400 }, { x: 550, y: 1600 }, { x: 300, y: 1750 }, { x: 300, y: 2050 }, { x: 550, y: 1900 }, { x: 800, y: 1750 }, { x: 800, y: 2050 }, { x: 1400, y: 1750 }, { x: 1400, y: 2050 }, { x: 1800, y: 1900 }, { x: 1450, y: 850 }, { x: 1450, y: 550 }, { x: 1800, y: 700 }]; for (var i = 0; i < spots.length; i++) { var spot = new BuildSpot(); spot.x = spots[i].x; spot.y = spots[i].y; buildSpots.push(spot); game.addChild(spot); } } // Define waves function createWaves() { // Wave 1: 10 normal enemies waves.push({ enemies: [{ type: 'normal', count: 10, delay: 1.5, difficulty: 1 }] }); // Wave 2: Mix of normal and pun haters waves.push({ enemies: [{ type: 'normal', count: 8, delay: 1.2, difficulty: 1 }, { type: 'punHater', count: 5, delay: 2, difficulty: 1 }] }); // Wave 3: Mix of all types waves.push({ enemies: [{ type: 'normal', count: 10, delay: 1, difficulty: 1.2 }, { type: 'punHater', count: 7, delay: 1.5, difficulty: 1.2 }, { type: 'slapstickHater', count: 7, delay: 1.5, difficulty: 1.2 }] }); // Wave 4: More enemies, tougher waves.push({ enemies: [{ type: 'normal', count: 15, delay: 0.8, difficulty: 1.5 }, { type: 'punHater', count: 10, delay: 1.2, difficulty: 1.5 }, { type: 'slapstickHater', count: 10, delay: 1.2, difficulty: 1.5 }] }); // Wave 5: Final wave, very tough waves.push({ enemies: [{ type: 'normal', count: 20, delay: 0.7, difficulty: 2 }, { type: 'punHater', count: 15, delay: 1, difficulty: 2 }, { type: 'slapstickHater', count: 15, delay: 1, difficulty: 2 }] }); // Only update waveText if it has been initialized if (waveText) { waveText.setText("Wave: " + currentWave + "/" + waves.length); } } // Start a wave function startWave() { if (waveInProgress || currentWave >= waves.length) { return; } waveInProgress = true; currentWave++; waveText.setText("Wave: " + currentWave + "/" + waves.length); nextWaveText.visible = false; startWaveButton.visible = false; var wave = waves[currentWave - 1]; var enemyGroups = wave.enemies; var enemyTypesProcessed = 0; function spawnEnemyGroup(groupIndex) { if (groupIndex >= enemyGroups.length) { enemyTypesProcessed++; if (enemyTypesProcessed >= enemyGroups.length) { // All enemy types processed, clear interval LK.clearInterval(spawnInterval); // Check for wave completion periodically var checkInterval = LK.setInterval(function () { if (enemies.length === 0) { waveInProgress = false; LK.clearInterval(checkInterval); // Last wave completed if (currentWave >= waves.length) { // Player wins LK.showYouWin(); } else { // Prepare for next wave nextWaveCountdown = 15; nextWaveText.setText("Next wave: " + nextWaveCountdown); nextWaveText.visible = true; startWaveButton.visible = true; // Bonus laughs for completing the wave laughs += 50 + currentWave * 10; laughsText.setText("Laughs: " + laughs); } } }, 1000); return; } // Move to next enemy type groupIndex = 0; } var group = enemyGroups[groupIndex]; if (group.count > 0) { // Spawn enemy var enemy = new Enemy(); enemy.setup(group.type, group.difficulty); enemy.x = gamePath.waypoints[0].x; enemy.y = gamePath.waypoints[0].y; game.addChild(enemy); enemies.push(enemy); // Decrease count group.count--; // Schedule next spawn for this group LK.setTimeout(function () { spawnEnemyGroup(groupIndex); }, group.delay * 1000); } else { // Move to next group spawnEnemyGroup(groupIndex + 1); } } // Start spawning enemies spawnInterval = LK.setInterval(function () { spawnEnemyGroup(0); }, 100); } // Initialize game function initGame() { // Create path createPath(); // Create waves createWaves(); // Init UI initUI(); // Start countdown timer for first wave var countdownInterval = LK.setInterval(function () { if (!waveInProgress) { nextWaveCountdown--; nextWaveText.setText("Next wave: " + nextWaveCountdown); if (nextWaveCountdown <= 0) { LK.clearInterval(countdownInterval); startWave(); } } }, 1000); // Play background music LK.playMusic('bgMusic', { fade: { start: 0, end: 0.3, duration: 1000 } }); } // Initialize game initGame(); // Global event handlers game.down = function (x, y, obj) { // Hide build menu if clicking outside if (buildMenu.visible) { var clickedInMenu = false; // Check if clicked on the menu if (x >= buildMenu.x - 150 && x <= buildMenu.x + 150 && y >= buildMenu.y - 150 && y <= buildMenu.y + 150) { clickedInMenu = true; } if (!clickedInMenu) { hideBuildMenu(); } } // Hide tower info if clicking outside if (selectedTower) { var clickedOnTower = false; for (var i = 0; i < towers.length; i++) { if (towers[i] === obj) { clickedOnTower = true; break; } } if (!clickedOnTower) { if (selectedTower.showingRange) { selectedTower.toggleRangeIndicator(); } selectedTower = null; upgradeButton.visible = false; } } }; // Main game update game.update = function () { // Update all enemies for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; // If enemy is dead but not yet removed if (enemy.health <= 0) { enemies.splice(i, 1); enemy.destroy(); continue; } // If enemy reached the end if (enemy.reachedEnd) { enemies.splice(i, 1); enemy.destroy(); continue; } } // Update all projectiles for (var j = projectiles.length - 1; j >= 0; j--) { var projectile = projectiles[j]; // If projectile is missing if (!projectile.parent) { projectiles.splice(j, 1); } } // Update UI button states if (upgradeButton.visible && selectedTower) { var upgradeCost = selectedTower.getUpgradeCost(); upgradeButton.tint = laughs >= upgradeCost ? 0xFFFFFF : 0xFF0000; } if (buildMenu.visible) { normalTowerButton.tint = laughs >= 10 ? 0xFFFFFF : 0xFF0000; punTowerButton.tint = laughs >= 10 ? 0xFFFFFF : 0xFF0000; slapstickTowerButton.tint = laughs >= 10 ? 0xFFFFFF : 0xFF0000; knockKnockTowerButton.tint = laughs >= 10 ? 0xFFFFFF : 0xFF0000; } };
===================================================================
--- original.js
+++ change.js
@@ -578,8 +578,13 @@
self.duplicateTimer = 0;
self.duplicate();
}
}
+ // Handle attack cooldown (needed to maintain parent behavior)
+ if (self.attackCooldown > 0) {
+ self.attackCooldown -= 1 / 60; // 60 fps
+ }
+ // But don't call attack method
};
// Create duplicate towers in line to form a wall
self.duplicate = function () {
if (self.duplicateCount >= self.maxDuplicates) {
Happy person. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A path. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A speech bubble saying Haha!. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A door with the words Joking Around on it. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A boxing glove. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A bat holding a sign that says Jokes 4 Ever!. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A glove on a stick. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A button that says Start Wave. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A door. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows