User prompt
Add the TurtleShell tower to the tower selector.
User prompt
Code TurtleShell to throw TurtleShellAttack at the enemies, the PlasticBag enemy stuns it.
User prompt
Please fix the bug: 'RangeError: Maximum call stack size exceeded.' in or related to this line: 'localOriginalUpdate.call(p.target);' Line Number: 1282
User prompt
Make it so the megalodon can make you unable to spawn towers if it chooses to.
User prompt
Make it so the megalodon can stun the player, making you unable to put down any towers on the megalodon wave, if the megalodon chooses to stun you.
User prompt
Finish it please
User prompt
Make it so the Plastic Bag enemy spawns at the start of the track(the opposite side of the coral)
User prompt
Please fix the bug: 'RangeError: Maximum call stack size exceeded.' in or related to this line: 'originalUpdate.call(p.target);' Line Number: 1110
User prompt
Do it.
User prompt
Get rid of the code that allows you to upgrade your towers
User prompt
Please fix the bug: 'RangeError: Maximum call stack size exceeded.' in or related to this line: 'originalUpdate.call(p.target);' Line Number: 1089
User prompt
Make it so you can upgrade your towers by simply tapping on them
User prompt
I just added a new tower called the Jelly Stinger, i want you to make it produce electricity that stuns the enemies for 30 seconds. βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
User prompt
Make it so there are 100 waves and not just 10.
User prompt
Make it so the megalodon spawns at wave 20
User prompt
I said to start with 100 pearls.
User prompt
Make it so you start with 100 pearls
User prompt
Make it so the enemies spawn near your towers.
User prompt
Finish the code please
User prompt
Add the code please
User prompt
Now the sharks wonβt spawn.
User prompt
Make them appear on every wave right after you place a tower or 2
User prompt
Make the enemies able to appear from somewhere on the track
User prompt
Make the seaweed trap stare at the enemies, stunning them for a minute βͺπ‘ Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1", {
highScore: 0,
pearls: 0,
currentLevel: 1
});
/****
* Classes
****/
var BuildSpot = Container.expand(function () {
var self = Container.call(this);
var buildGraphics = self.attachAsset('buildPlaceholder', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.5
});
self.occupied = false;
self.tower = null;
self.down = function (x, y, obj) {
if (!self.occupied && gameState.pearls >= 10) {
// Check if any Megalodon is blocking tower building
var towerBlocked = false;
for (var i = 0; i < enemies.length; i++) {
if (enemies[i].constructor.name === "Megalodon" && enemies[i].blockingTowerBuilding) {
towerBlocked = true;
break;
}
}
if (towerBlocked) {
// Show warning message
var blockedText = new Text2("TOWER BUILDING BLOCKED BY MEGALODON!", {
size: 40,
fill: 0xFF0000
});
blockedText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(blockedText);
// Remove warning after 2 seconds
LK.setTimeout(function () {
blockedText.destroy();
}, 2000);
} else {
openBuildMenu(self);
}
}
};
return self;
});
var CoralReef = Container.expand(function () {
var self = Container.call(this);
var coralGraphics = self.attachAsset('coral', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.takeDamage = function (damage) {
self.health -= damage;
LK.effects.flashObject(self, 0xff0000, 500);
if (self.health <= 0) {
LK.effects.flashScreen(0xff0000, 1000);
LK.showGameOver();
}
};
return self;
});
var Enemy = Container.expand(function () {
var self = Container.call(this);
var enemyGraphics = self.attachAsset('enemy', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.speed = 1.5;
self.damage = 10;
self.pathIndex = 0;
self.pearls = 5;
self.boss = false; // Default enemies are not bosses
// Health bar
var healthBar = LK.getAsset('buildPlaceholder', {
anchorX: 0.5,
anchorY: 0.5,
width: 60,
height: 10,
scaleX: 1,
scaleY: 0.2,
y: -40,
tint: 0x00ff00
});
self.addChild(healthBar);
self.update = function () {
// Initialize stunned property if not set
if (self.stunned === undefined) {
self.stunned = false;
}
if (self.pathIndex < gamePath.length && !self.stunned) {
var targetPos = gamePath[self.pathIndex];
var dx = targetPos.x - self.x;
var dy = targetPos.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > self.speed) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
// Face the direction of movement
if (dx < 0) {
enemyGraphics.scaleX = -1;
} else {
enemyGraphics.scaleX = 1;
}
} else {
self.pathIndex++;
}
} else if (self.pathIndex >= gamePath.length) {
// Reached the end, damage coral
coralReef.takeDamage(self.damage);
self.destroy();
var index = enemies.indexOf(self);
if (index !== -1) {
enemies.splice(index, 1);
}
}
healthBar.scaleX = self.health / self.maxHealth;
// Visual indicator for stunned state
if (self.stunned) {
// Pulsate alpha when stunned
if (LK.ticks % 15 === 0) {
enemyGraphics.alpha = enemyGraphics.alpha === 1 ? 0.5 : 1;
}
} else {
enemyGraphics.alpha = 1;
}
};
self.takeDamage = function (damage) {
self.health -= damage;
LK.getSound('enemyHit').play();
if (self.health <= 0) {
gameState.pearls += self.pearls;
pearlText.setText("Pearls: " + gameState.pearls);
LK.getSound('enemyDefeat').play();
// Add to score
LK.setScore(LK.getScore() + 10);
scoreText.setText("Score: " + LK.getScore());
self.destroy();
var index = enemies.indexOf(self);
if (index !== -1) {
enemies.splice(index, 1);
}
}
};
return self;
});
var PlasticBag = Enemy.expand(function () {
var self = Enemy.call(this);
// Replace with plastic bag graphic
self.removeChild(self.children[1]); // Remove regular enemy graphic
var plasticBagGraphics = self.attachAsset('PlasticBag', {
anchorX: 0.5,
anchorY: 0.5
});
// Plastic bag properties
self.health = 80;
self.maxHealth = 80;
self.speed = 2.0; // Faster than regular enemies
self.damage = 5; // Less damage
self.pearls = 3;
// Special ability: Sometimes changes direction randomly
self.update = function () {
// Call parent's update logic
Enemy.prototype.update.call(self);
// 2% chance per frame to temporarily speed up
if (Math.random() < 0.02 && !self.speedBoost) {
self.speedBoost = true;
self.originalSpeed = self.speed;
self.speed *= 1.5;
// Reset speed after a short duration
LK.setTimeout(function () {
if (self && self.parent) {
self.speed = self.originalSpeed;
self.speedBoost = false;
}
}, 1000);
}
};
return self;
});
var Megalodon = Enemy.expand(function () {
var self = Enemy.call(this);
// Replace with megalodon graphic
self.removeChild(self.children[1]); // Remove regular enemy graphic
var megalGraphics = self.attachAsset('Megalodon', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2,
scaleY: 2
});
// Megalodon properties
self.health = 500;
self.maxHealth = 500;
self.speed = 1.0;
self.damage = 30;
self.pearls = 25;
self.boss = true;
// Special ability: regenerate health
self.lastRegenTick = 0;
self.lastSpecialAttackTick = 0;
self.blockingTowerBuilding = false;
// Override update method to add regeneration
var parentUpdate = self.update;
self.update = function () {
// Call parent update first
parentUpdate.call(self);
// Regenerate health every 3 seconds (180 frames)
if (LK.ticks - self.lastRegenTick > 180) {
self.lastRegenTick = LK.ticks;
if (self.health < self.maxHealth) {
self.health = Math.min(self.health + 20, self.maxHealth);
// Show healing effect
LK.effects.flashObject(self, 0x00ff00, 500);
}
}
// Special attack: block tower building every 15 seconds (900 frames)
if (LK.ticks - self.lastSpecialAttackTick > 900) {
// 30% chance to activate special attack when time is ready
if (Math.random() < 0.3) {
self.lastSpecialAttackTick = LK.ticks;
self.blockingTowerBuilding = true;
// Create warning text
var warningText = new Text2("MEGALODON BLOCKS TOWERS!", {
size: 60,
fill: 0xFF0000
});
warningText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(warningText);
// Flash megalodon in red
LK.effects.flashObject(self, 0xff0000, 1000);
// Duration of blocking: 10 seconds (600 frames)
LK.setTimeout(function () {
self.blockingTowerBuilding = false;
// Create end warning text
var endWarningText = new Text2("TOWER BLOCKING ENDED", {
size: 50,
fill: 0x00FF00
});
endWarningText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(endWarningText);
// Remove text after 2 seconds
LK.setTimeout(function () {
endWarningText.destroy();
}, 2000);
}, 10000);
// Remove warning text after 3 seconds
LK.setTimeout(function () {
warningText.destroy();
}, 3000);
}
}
};
return self;
});
var FloppyFish = Container.expand(function () {
var self = Container.call(this);
var fishGraphics = self.attachAsset('floppyFish', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 6;
self.targetX = null;
self.targetY = null;
self.update = function () {
if (self.targetX !== null && self.targetY !== null) {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > self.speed) {
self.x += dx / distance * self.speed;
self.y += dy / distance * self.speed;
// Face the direction of movement
if (dx < 0) {
fishGraphics.scaleX = -1;
} else {
fishGraphics.scaleX = 1;
}
} else {
self.x = self.targetX;
self.y = self.targetY;
self.targetX = null;
self.targetY = null;
}
}
};
return self;
});
var PathTile = Container.expand(function () {
var self = Container.call(this);
var pathGraphics = self.attachAsset('path', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.7
});
self.buildable = false;
return self;
});
var Tower = Container.expand(function (type) {
var self = Container.call(this);
self.type = type || 'bubbleCannon';
self.level = 1;
self.range = 250;
self.damage = 25;
self.fireRate = 60; // frames between shots
self.fireCounter = 0;
self.cost = 10;
self.upgradesCost = 15;
var towerGraphics = self.attachAsset(self.type, {
anchorX: 0.5,
anchorY: 0.5
});
// Range indicator (only shown when selected)
var rangeIndicator = LK.getAsset('buildPlaceholder', {
anchorX: 0.5,
anchorY: 0.5,
alpha: 0.2,
tint: 0x3498db
});
// Scale the range indicator to match the tower's range
rangeIndicator.scaleX = self.range / 40;
rangeIndicator.scaleY = self.range / 40;
rangeIndicator.visible = false;
self.addChild(rangeIndicator);
self.down = function (x, y, obj) {
// Deselect previous tower
if (selectedTower && selectedTower !== self) {
selectedTower.deselect();
}
selectedTower = self;
self.select();
// Show upgrade UI with cost
upgradeButton.visible = true;
stopUpgradeButton.visible = true;
upgradeText.setText("Upgrade: " + self.upgradesCost + " pearls");
};
self.select = function () {
rangeIndicator.visible = true;
towerGraphics.tint = 0xffff00;
};
self.deselect = function () {
rangeIndicator.visible = false;
towerGraphics.tint = 0xffffff;
};
self.upgrade = function () {
if (gameState.pearls >= self.upgradesCost) {
gameState.pearls -= self.upgradesCost;
pearlText.setText("Pearls: " + gameState.pearls);
self.level++;
self.damage += 15;
self.range += 50;
self.fireRate = Math.max(30, self.fireRate - 10);
self.upgradesCost = Math.floor(self.upgradesCost * 1.5);
// Update range indicator
rangeIndicator.scaleX = self.range / 40;
rangeIndicator.scaleY = self.range / 40;
// Update upgrade button text
upgradeText.setText("Upgrade: " + self.upgradesCost + " pearls");
// Scale up tower slightly to show upgrade
tween(towerGraphics, {
scaleX: 1.2,
scaleY: 1.2
}, {
duration: 300,
onFinish: function onFinish() {
tween(towerGraphics, {
scaleX: 1,
scaleY: 1
}, {
duration: 300
});
}
});
}
};
self.findTarget = function () {
var closestEnemy = null;
var closestDistance = self.range;
for (var i = 0; i < enemies.length; i++) {
var enemy = enemies[i];
var dx = enemy.x - self.x;
var dy = enemy.y - self.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < closestDistance) {
closestEnemy = enemy;
closestDistance = distance;
}
}
return closestEnemy;
};
self.shoot = function (target) {
var bubble = LK.getAsset('bubble', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
});
game.addChild(bubble);
projectiles.push({
sprite: bubble,
target: target,
damage: self.damage,
speed: 7
});
LK.getSound('bubble').play();
};
self.update = function () {
self.fireCounter++;
if (self.fireCounter >= self.fireRate) {
var target = self.findTarget();
if (target) {
self.shoot(target);
self.fireCounter = 0;
}
}
};
return self;
});
var TurtleShell = Tower.expand(function () {
var self = Tower.call(this, 'TurtleShell');
// Override properties for turtle shell
self.range = 300;
self.damage = 30;
self.fireRate = 60;
// Override shoot method for attack
self.shoot = function (target) {
// Create turtle shell projectile
var shell = LK.getAsset('TurtleShellAttack', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
});
game.addChild(shell);
// Create special projectile that will do damage and can be stunned by PlasticBag
projectiles.push({
sprite: shell,
target: target,
damage: self.damage,
speed: 8,
isTurtleShell: true // Custom flag for TurtleShellAttack
});
// Play sound (using bubble sound for now)
LK.getSound('bubble').play();
};
return self;
});
var SharkTower = Tower.expand(function () {
var self = Tower.call(this, 'Sharky');
// Override properties for shark tower
self.range = 250;
self.damage = 100;
self.fireRate = 90; // Slower than bubble cannon but high damage
// Override shoot method for instant kill functionality
self.shoot = function (target) {
// Create bite effect (using the bubble as a placeholder for the bite animation)
var bite = LK.getAsset('bubble', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
});
game.addChild(bite);
// Move bite effect to target
projectiles.push({
sprite: bite,
target: target,
damage: target.boss ? self.damage : target.health,
// Instant kill for non-boss enemies
speed: 10 // Faster projectile to simulate bite
});
// Flash target red to indicate it's being bitten
LK.effects.flashObject(target, 0xff0000, 200);
// Play bite sound (using bubble sound for now)
LK.getSound('bubble').play();
};
return self;
});
var SeaweedTrap = Tower.expand(function () {
var self = Tower.call(this, 'seaweedTrap');
// Override properties for seaweed trap
self.range = 200;
self.damage = 5;
self.fireRate = 60;
self.stunDuration = 60; // 1 second stun (60 frames)
// Override shoot method for stun functionality
self.shoot = function (target) {
// Apply stun effect to the target
if (!target.stunned) {
target.stunned = true;
target.originalSpeed = target.speed;
target.speed = 0;
// Make the seaweed trap "stare" at the enemy by rotating to face it
var dx = target.x - self.x;
var dy = target.y - self.y;
var angle = Math.atan2(dy, dx);
// Use tween to rotate the tower to face the enemy
tween(self.children[0], {
rotation: angle
}, {
duration: 300,
easing: tween.easeOut
});
// Flash the target to indicate stun
LK.effects.flashObject(target, 0x00ff00, 300);
// Set a timer to remove the stun after duration
LK.setTimeout(function () {
if (target && target.parent) {
target.speed = target.originalSpeed;
target.stunned = false;
}
}, self.stunDuration * 16.67); // Convert frames to milliseconds (60fps β 16.67ms per frame)
}
target.takeDamage(self.damage);
LK.getSound('bubble').play();
};
return self;
});
var JellyStinger = Tower.expand(function () {
var self = Tower.call(this, 'JellyStinger');
// Override properties for jelly stinger
self.range = 230;
self.damage = 15;
self.fireRate = 90;
self.stunDuration = 1800; // 30 seconds (60fps * 30)
// Override shoot method for electricity stunning
self.shoot = function (target) {
// Create electricity effect
var electricity = LK.getAsset('Electricity', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y,
alpha: 0.8
});
game.addChild(electricity);
// Create special projectile that will apply electricity stun
projectiles.push({
sprite: electricity,
target: target,
damage: self.damage,
speed: 8,
isElectric: true,
stunDuration: self.stunDuration
});
// Play sound (using bubble sound for now)
LK.getSound('bubble').play();
};
return self;
});
var ClamMine = Tower.expand(function () {
var self = Tower.call(this, 'clamMine');
// Override properties for clam mine
self.range = 200;
self.damage = 40;
self.fireRate = 120; // Slower fire rate but area damage
self.areaRadius = 150; // Radius of explosion
// Override shoot method for area damage
self.shoot = function (target) {
// Create pearl projectile
var pearl = LK.getAsset('pearl', {
anchorX: 0.5,
anchorY: 0.5,
x: self.x,
y: self.y
});
game.addChild(pearl);
// Create special projectile that will explode
projectiles.push({
sprite: pearl,
target: target,
damage: self.damage,
speed: 5,
isAreaDamage: true,
areaRadius: self.areaRadius
});
LK.getSound('bubble').play();
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x1a5276
});
/****
* Game Code
****/
// Play music
// Base characters
// Towers and defenses
// Items and effects
// Sounds
// Music
LK.playMusic('underwaterTheme');
// Game variables
var gameState = {
pearls: 100,
currentWave: 1,
maxWaves: 100,
waveInProgress: false,
enemiesLeft: 0
};
var selectedTower = null;
var buildMenuOpen = false;
var gamePath = [];
var buildSpots = [];
var enemies = [];
var towers = [];
var projectiles = [];
var coralReef;
var floppyFish;
// UI Elements
var scoreText = new Text2("Score: 0", {
size: 50,
fill: 0xFFFFFF
});
scoreText.anchor.set(0, 0);
LK.gui.topRight.addChild(scoreText);
scoreText.x = -scoreText.width - 20;
scoreText.y = 20;
var pearlText = new Text2("Pearls: " + gameState.pearls, {
size: 50,
fill: 0xFFFFFF
});
pearlText.anchor.set(0, 0);
LK.gui.topRight.addChild(pearlText);
pearlText.x = -pearlText.width - 20;
pearlText.y = 80;
var waveText = new Text2("Wave: " + gameState.currentWave + "/" + gameState.maxWaves, {
size: 50,
fill: 0xFFFFFF
});
waveText.anchor.set(0.5, 0);
LK.gui.top.addChild(waveText);
waveText.y = 20;
// Upgrade button (hidden initially)
var upgradeButton = LK.getAsset('buildPlaceholder', {
anchorX: 0.5,
anchorY: 0.5,
width: 300,
height: 80,
tint: 0x2ecc71
});
upgradeButton.visible = false;
LK.gui.bottom.addChild(upgradeButton);
upgradeButton.y = -100;
var upgradeText = new Text2("Upgrade: 15 pearls", {
size: 40,
fill: 0xFFFFFF
});
upgradeText.anchor.set(0.5, 0.5);
upgradeButton.addChild(upgradeText);
// Stop upgrading button (hidden initially)
var stopUpgradeButton = LK.getAsset('Stopupgrade', {
anchorX: 0.5,
anchorY: 0.5,
width: 80,
height: 80,
tint: 0xff0000
});
stopUpgradeButton.visible = false;
LK.gui.bottom.addChild(stopUpgradeButton);
stopUpgradeButton.x = 200;
stopUpgradeButton.y = -100;
// Start wave button
var startWaveButton = LK.getAsset('buildPlaceholder', {
anchorX: 0.5,
anchorY: 0.5,
width: 300,
height: 80,
tint: 0x3498db
});
LK.gui.bottom.addChild(startWaveButton);
startWaveButton.y = -200;
var startWaveText = new Text2("Start Wave", {
size: 40,
fill: 0xFFFFFF
});
startWaveText.anchor.set(0.5, 0.5);
startWaveButton.addChild(startWaveText);
// Build menu (hidden initially)
var buildMenu = new Container();
buildMenu.visible = false;
LK.gui.center.addChild(buildMenu);
var buildMenuBackground = LK.getAsset('buildPlaceholder', {
anchorX: 0.5,
anchorY: 0.5,
width: 400,
height: 500,
tint: 0x34495e,
alpha: 0.9
});
buildMenu.addChild(buildMenuBackground);
var buildMenuTitle = new Text2("Build Tower", {
size: 50,
fill: 0xFFFFFF
});
buildMenuTitle.anchor.set(0.5, 0);
buildMenuTitle.y = -200;
buildMenu.addChild(buildMenuTitle);
// Tower options in build menu
var bubbleOption = LK.getAsset('bubbleCannon', {
anchorX: 0.5,
anchorY: 0.5,
x: -100,
y: -100
});
buildMenu.addChild(bubbleOption);
var bubbleText = new Text2("Bubble\nCannon\n10 pearls", {
size: 30,
fill: 0xFFFFFF
});
bubbleText.anchor.set(0.5, 0);
bubbleText.y = 50;
bubbleOption.addChild(bubbleText);
var seaweedOption = LK.getAsset('seaweedTrap', {
anchorX: 0.5,
anchorY: 0.5,
x: 100,
y: -100
});
buildMenu.addChild(seaweedOption);
var seaweedText = new Text2("Seaweed\nTrap\n10 pearls", {
size: 30,
fill: 0xFFFFFF
});
seaweedText.anchor.set(0.5, 0);
seaweedText.y = 50;
seaweedOption.addChild(seaweedText);
var clamOption = LK.getAsset('clamMine', {
anchorX: 0.5,
anchorY: 0.5,
x: -100,
y: 50
});
buildMenu.addChild(clamOption);
var clamText = new Text2("Clam\nMine\n10 pearls", {
size: 30,
fill: 0xFFFFFF
});
clamText.anchor.set(0.5, 0);
clamText.y = 50;
clamOption.addChild(clamText);
// TurtleShell tower option
var turtleShellOption = LK.getAsset('TurtleShell', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 50
});
buildMenu.addChild(turtleShellOption);
var turtleShellText = new Text2("Turtle\nShell\n10 pearls", {
size: 30,
fill: 0xFFFFFF
});
turtleShellText.anchor.set(0.5, 0);
turtleShellText.y = 50;
turtleShellOption.addChild(turtleShellText);
var sharkyOption = LK.getAsset('Sharky', {
anchorX: 0.5,
anchorY: 0.5,
x: 100,
y: 50
});
buildMenu.addChild(sharkyOption);
var sharkyText = new Text2("Sharky\nBiter\n10 pearls", {
size: 30,
fill: 0xFFFFFF
});
sharkyText.anchor.set(0.5, 0);
sharkyText.y = 50;
sharkyOption.addChild(sharkyText);
var jellyStingerOption = LK.getAsset('JellyStinger', {
anchorX: 0.5,
anchorY: 0.5,
x: 0,
y: 150
});
buildMenu.addChild(jellyStingerOption);
var jellyStingerText = new Text2("Jelly\nStinger\n10 pearls", {
size: 30,
fill: 0xFFFFFF
});
jellyStingerText.anchor.set(0.5, 0);
jellyStingerText.y = 50;
jellyStingerOption.addChild(jellyStingerText);
var cancelBuildButton = LK.getAsset('buildPlaceholder', {
anchorX: 0.5,
anchorY: 0.5,
width: 200,
height: 60,
y: 150,
tint: 0xe74c3c
});
buildMenu.addChild(cancelBuildButton);
var cancelText = new Text2("Cancel", {
size: 40,
fill: 0xFFFFFF
});
cancelText.anchor.set(0.5, 0.5);
cancelBuildButton.addChild(cancelText);
// Initialize the map
function initializeMap() {
// Create path
var pathPoints = [{
x: 0,
y: 600
}, {
x: 400,
y: 600
}, {
x: 400,
y: 1000
}, {
x: 800,
y: 1000
}, {
x: 800,
y: 500
}, {
x: 1200,
y: 500
}, {
x: 1200,
y: 1200
}, {
x: 1600,
y: 1200
}, {
x: 1600,
y: 800
}, {
x: 2000,
y: 800
}];
// Create path tiles
for (var i = 0; i < pathPoints.length; i++) {
gamePath.push(pathPoints[i]);
var pathTile = new PathTile();
pathTile.x = pathPoints[i].x;
pathTile.y = pathPoints[i].y;
game.addChild(pathTile);
// Create build spots along the path (offset from path)
if (i < pathPoints.length - 1) {
var dx = pathPoints[i + 1].x - pathPoints[i].x;
var dy = pathPoints[i + 1].y - pathPoints[i].y;
var len = Math.sqrt(dx * dx + dy * dy);
// Normalize and rotate 90 degrees for perpendicular
var perpX = -dy / len;
var perpY = dx / len;
// Create build spots along segments
var segments = Math.floor(len / 200);
for (var j = 0; j <= segments; j++) {
var segX = pathPoints[i].x + dx * j / segments;
var segY = pathPoints[i].y + dy * j / segments;
// Create build spots on both sides of path
var buildSpot1 = new BuildSpot();
buildSpot1.x = segX + perpX * 120;
buildSpot1.y = segY + perpY * 120;
game.addChild(buildSpot1);
buildSpots.push(buildSpot1);
var buildSpot2 = new BuildSpot();
buildSpot2.x = segX - perpX * 120;
buildSpot2.y = segY - perpY * 120;
game.addChild(buildSpot2);
buildSpots.push(buildSpot2);
}
}
}
// Add coral reef at the end of the path
coralReef = new CoralReef();
coralReef.x = 2048 - 100;
coralReef.y = 800;
game.addChild(coralReef);
// Add floppy fish
floppyFish = new FloppyFish();
floppyFish.x = 1024;
floppyFish.y = 1366;
game.addChild(floppyFish);
}
// Initialize the game
initializeMap();
// Functions
function openBuildMenu(buildSpot) {
buildMenuOpen = true;
buildMenu.visible = true;
buildMenu.currentBuildSpot = buildSpot;
// If the player has insufficient pearls, disable options
bubbleOption.alpha = gameState.pearls >= 10 ? 1 : 0.5;
seaweedOption.alpha = gameState.pearls >= 10 ? 1 : 0.5;
clamOption.alpha = gameState.pearls >= 10 ? 1 : 0.5;
turtleShellOption.alpha = gameState.pearls >= 10 ? 1 : 0.5;
sharkyOption.alpha = gameState.pearls >= 10 ? 1 : 0.5;
jellyStingerOption.alpha = gameState.pearls >= 10 ? 1 : 0.5;
}
function closeBuildMenu() {
buildMenuOpen = false;
buildMenu.visible = false;
buildMenu.currentBuildSpot = null;
}
function buildTower(type) {
if (gameState.pearls < 10) {
return;
}
var buildSpot = buildMenu.currentBuildSpot;
if (!buildSpot || buildSpot.occupied) {
return;
}
// Check if any Megalodon is blocking tower building
var towerBlocked = false;
for (var i = 0; i < enemies.length; i++) {
if (enemies[i].constructor.name === "Megalodon" && enemies[i].blockingTowerBuilding) {
towerBlocked = true;
break;
}
}
if (towerBlocked) {
// Show warning message
var blockedText = new Text2("TOWER BUILDING BLOCKED BY MEGALODON!", {
size: 40,
fill: 0xFF0000
});
blockedText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(blockedText);
// Remove warning after 2 seconds
LK.setTimeout(function () {
blockedText.destroy();
}, 2000);
// Close build menu without building tower
closeBuildMenu();
return;
}
gameState.pearls -= 10;
pearlText.setText("Pearls: " + gameState.pearls);
var tower;
if (type === 'seaweedTrap') {
tower = new SeaweedTrap();
} else if (type === 'Sharky') {
tower = new SharkTower();
} else if (type === 'clamMine') {
tower = new ClamMine();
} else if (type === 'JellyStinger') {
tower = new JellyStinger();
} else if (type === 'TurtleShell') {
tower = new TurtleShell();
} else {
tower = new Tower(type);
}
tower.x = buildSpot.x;
tower.y = buildSpot.y;
game.addChild(tower);
buildSpot.occupied = true;
buildSpot.tower = tower;
towers.push(tower);
LK.getSound('buildTower').play();
// Spawn an enemy if a wave is in progress
if (gameState.waveInProgress) {
// Immediate spawn of enemy after tower placement
spawnEnemy();
}
closeBuildMenu();
}
function spawnEnemy() {
if (gameState.enemiesLeft <= 0) {
return;
}
// Determine if this should be a Megalodon boss (at wave 20 or every 3rd wave, and last enemy of the wave)
var isMegalodon = (gameState.currentWave === 20 || gameState.currentWave % 3 === 0) && gameState.enemiesLeft === 1 && enemies.length === 0;
var enemy;
if (isMegalodon) {
// Create Megalodon
enemy = new Megalodon();
// Announce Megalodon arrival
var bossText = new Text2("MEGALODON INCOMING!", {
size: 80,
fill: 0xFF0000
});
bossText.anchor.set(0.5, 0.5);
LK.gui.center.addChild(bossText);
// Flash screen and remove announcement after delay
LK.effects.flashScreen(0xFF0000, 500);
LK.setTimeout(function () {
bossText.destroy();
}, 2000);
} else {
// Create regular enemy
// 25% chance to create a plastic bag instead of regular enemy
if (Math.random() < 0.25) {
enemy = new Enemy();
// Override with plastic bag graphics
enemy.removeChild(enemy.children[1]); // Remove regular enemy graphic
var plasticBagGraphics = enemy.attachAsset('PlasticBag', {
anchorX: 0.5,
anchorY: 0.5
});
} else {
enemy = new Enemy();
}
// Randomly make some regular enemies bosses
var isBoss = Math.random() < 0.2; // 20% chance for boss
enemy.boss = isBoss;
// Increase enemy stats based on wave
enemy.health = 100 + (gameState.currentWave - 1) * 20;
// Boss enemies have double health
if (enemy.boss) {
enemy.health *= 2;
enemy.scaleX = 1.5;
enemy.scaleY = 1.5;
enemy.pearls *= 2;
}
}
// Check if this is a plastic bag (has PlasticBag as child)
var isPlasticBag = enemy.children.some(function (child) {
return child.assetId === 'PlasticBag';
});
var spawnX, spawnY, pathIndex;
// Always spawn at start of track (first path point)
spawnX = gamePath[0].x;
spawnY = gamePath[0].y;
pathIndex = 0;
// If not a plastic bag, we can spawn at different locations
if (!isPlasticBag) {
// Spawn near a tower if there are towers (for non-plastic bag enemies)
var spawnNearTower = towers.length > 0;
if (spawnNearTower) {
// Choose a random tower to spawn near
var randomTower = towers[Math.floor(Math.random() * towers.length)];
// Find the closest path segment to this tower
var minDistance = Infinity;
var closestSegment = 0;
for (var i = 0; i < gamePath.length - 1; i++) {
var dx = gamePath[i].x - randomTower.x;
var dy = gamePath[i].y - randomTower.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance < minDistance) {
minDistance = distance;
closestSegment = i;
}
}
// Use the closest path segment
spawnX = gamePath[closestSegment].x;
spawnY = gamePath[closestSegment].y;
pathIndex = closestSegment;
} else {
// No towers, use random path segment as before
var randomPathSegment = Math.floor(Math.random() * (gamePath.length - 1));
spawnX = gamePath[randomPathSegment].x;
spawnY = gamePath[randomPathSegment].y;
pathIndex = randomPathSegment;
}
}
enemy.x = spawnX;
enemy.y = spawnY;
enemy.pathIndex = pathIndex; // Set the path index to start from the chosen segment
// Adjust regular enemy stats based on wave
if (!isMegalodon) {
enemy.maxHealth = enemy.health;
enemy.speed = 1.5 + (gameState.currentWave - 1) * 0.1;
enemy.pearls = 5 + Math.floor((gameState.currentWave - 1) / 2);
}
game.addChild(enemy);
enemies.push(enemy);
gameState.enemiesLeft--;
// Schedule next enemy
if (gameState.enemiesLeft > 0) {
LK.setTimeout(spawnEnemy, 1000);
}
}
function startWave() {
if (gameState.waveInProgress) {
return;
}
gameState.waveInProgress = true;
gameState.enemiesLeft = 5 + gameState.currentWave * 2;
startWaveButton.tint = 0x95a5a6; // Gray out button
// Update wave text
waveText.setText("Wave: " + gameState.currentWave + "/" + gameState.maxWaves);
// Start spawning enemies
spawnEnemy();
}
function endWave() {
gameState.waveInProgress = false;
gameState.currentWave++;
if (gameState.currentWave > gameState.maxWaves) {
// Game won
LK.showYouWin();
return;
}
// Update wave text
waveText.setText("Wave: " + gameState.currentWave + "/" + gameState.maxWaves);
startWaveButton.tint = 0x3498db; // Blue button
}
// Event handlers
startWaveButton.down = function () {
if (!gameState.waveInProgress) {
startWave();
}
};
upgradeButton.down = function () {
if (selectedTower) {
selectedTower.upgrade();
}
};
stopUpgradeButton.down = function () {
if (selectedTower) {
selectedTower.deselect();
selectedTower = null;
upgradeButton.visible = false;
stopUpgradeButton.visible = false;
}
};
cancelBuildButton.down = function () {
closeBuildMenu();
};
bubbleOption.down = function () {
buildTower('bubbleCannon');
};
seaweedOption.down = function () {
buildTower('seaweedTrap');
};
clamOption.down = function () {
buildTower('clamMine');
};
sharkyOption.down = function () {
buildTower('Sharky');
};
jellyStingerOption.down = function () {
buildTower('JellyStinger');
};
turtleShellOption.down = function () {
buildTower('TurtleShell');
};
game.down = function (x, y, obj) {
// Deselect tower when clicking elsewhere
if (selectedTower && !upgradeButton.visible) {
selectedTower.deselect();
selectedTower = null;
upgradeButton.visible = false;
stopUpgradeButton.visible = false;
}
if (!buildMenuOpen) {
floppyFish.targetX = x;
floppyFish.targetY = y;
}
};
game.up = function (x, y, obj) {
// Nothing specific needed for up events
};
// Update logic
game.update = function () {
// Update floppy fish
if (floppyFish) {
// Check if floppy fish is colliding with any enemies
for (var i = 0; i < enemies.length; i++) {
if (floppyFish.intersects(enemies[i])) {
enemies[i].takeDamage(5);
LK.effects.flashObject(floppyFish, 0xff0000, 200);
break;
}
}
}
// Update towers
for (var i = 0; i < towers.length; i++) {
towers[i].update();
}
// Update enemies
for (var i = enemies.length - 1; i >= 0; i--) {
enemies[i].update();
}
// Update projectiles
for (var i = projectiles.length - 1; i >= 0; i--) {
var p = projectiles[i];
// Check if target still exists
if (!p.target || !p.target.parent) {
p.sprite.destroy();
projectiles.splice(i, 1);
continue;
}
// TurtleShellAttack can be stunned by PlasticBag
if (p.isTurtleShell) {
// Check for collision with any PlasticBag enemy
var stunned = false;
for (var j = 0; j < enemies.length; j++) {
var enemy = enemies[j];
// Only PlasticBag can stun
if (enemy.constructor && enemy.constructor.name === "PlasticBag") {
// Check intersection
if (p.sprite.intersects(enemy)) {
// Stun the TurtleShell projectile (destroy it and show effect)
stunned = true;
// Optional: show a visual effect for stun
LK.effects.flashObject(p.sprite, 0x888888, 400);
// Remove projectile after short delay for effect
(function (sprite, idx) {
LK.setTimeout(function () {
if (sprite && sprite.parent) sprite.destroy();
// Remove from projectiles array if still present
for (var k = 0; k < projectiles.length; k++) {
if (projectiles[k] && projectiles[k].sprite === sprite) {
projectiles.splice(k, 1);
break;
}
}
}, 200);
})(p.sprite, i);
break;
}
}
}
if (stunned) {
continue; // Skip further processing for this projectile
}
}
// Move towards target
var dx = p.target.x - p.sprite.x;
var dy = p.target.y - p.sprite.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > p.speed) {
p.sprite.x += dx / distance * p.speed;
p.sprite.y += dy / distance * p.speed;
} else {
// Hit target
if (p.isAreaDamage) {
// Create explosion effect
var explosion = LK.getAsset('pearl', {
anchorX: 0.5,
anchorY: 0.5,
x: p.target.x,
y: p.target.y,
scaleX: 0.5,
scaleY: 0.5
});
game.addChild(explosion);
// Scale up and fade out for explosion effect
tween(explosion, {
scaleX: 5,
scaleY: 5,
alpha: 0
}, {
duration: 500,
onFinish: function onFinish() {
explosion.destroy();
}
});
// Deal damage to all enemies in area
for (var j = 0; j < enemies.length; j++) {
var enemy = enemies[j];
var dxe = enemy.x - p.target.x;
var dye = enemy.y - p.target.y;
var dist = Math.sqrt(dxe * dxe + dye * dye);
if (dist <= p.areaRadius) {
// Deal damage based on distance (more damage closer to center)
var damageFactor = 1 - dist / p.areaRadius;
var actualDamage = Math.floor(p.damage * damageFactor);
enemy.takeDamage(actualDamage);
}
}
} else if (p.isElectric) {
// Apply electricity stun effect to the target
p.target.takeDamage(p.damage);
// Apply stunning effect
p.target.stunned = true;
p.target.originalSpeed = p.target.speed;
p.target.speed = 0;
// Create electricity visual effect around the target
var electricEffect = LK.getAsset('Electricity', {
anchorX: 0.5,
anchorY: 0.5,
x: p.target.x,
y: p.target.y,
scaleX: 0.8,
scaleY: 0.8,
alpha: 0.7
});
game.addChild(electricEffect);
p.target.electricEffect = electricEffect;
// Make the electricity follow the target
var updateElectricity = function updateElectricity() {
if (p.target && p.target.parent && electricEffect && electricEffect.parent) {
electricEffect.x = p.target.x;
electricEffect.y = p.target.y;
// Pulsate the effect
if (LK.ticks % 10 === 0) {
tween(electricEffect, {
alpha: electricEffect.alpha > 0.4 ? 0.4 : 0.7
}, {
duration: 300
});
}
}
};
// Store the original update function safely before reassigning it
var originalUpdate = p.target.update;
// Create a new update function that doesn't cause recursion
p.target.update = function () {
// Call the original update ONLY if it's not the current function (avoid recursion)
if (typeof originalUpdate === 'function' && originalUpdate !== this.update) {
originalUpdate.call(this);
}
updateElectricity();
};
// Apply a tint to show the enemy is electrified
tween(p.target, {
tint: 0x00ffff
}, {
duration: 300
});
// Set a timer to remove the stun after duration
LK.setTimeout(function () {
if (p.target && p.target.parent) {
p.target.speed = p.target.originalSpeed;
p.target.stunned = false;
// Remove the electricity effect
if (electricEffect && electricEffect.parent) {
electricEffect.destroy();
}
// Reset the tint
tween(p.target, {
tint: 0xffffff
}, {
duration: 300
});
// Safely restore original update function
if (typeof originalUpdate === 'function') {
p.target.update = originalUpdate;
} else {
// Create a fallback update if original was not available
p.target.update = function () {
// Basic enemy update logic
if (p.target.pathIndex < gamePath.length && !p.target.stunned) {
var targetPos = gamePath[p.target.pathIndex];
var dx = targetPos.x - p.target.x;
var dy = targetPos.y - p.target.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > p.target.speed) {
p.target.x += dx / distance * p.target.speed;
p.target.y += dy / distance * p.target.speed;
} else {
p.target.pathIndex++;
}
}
};
}
}
}, p.stunDuration * 16.67); // Convert frames to milliseconds
} else {
// Regular projectile just hits target
p.target.takeDamage(p.damage);
}
p.sprite.destroy();
projectiles.splice(i, 1);
}
}
// Check if wave is over
if (gameState.waveInProgress && gameState.enemiesLeft <= 0 && enemies.length === 0) {
endWave();
}
// Update storage data
storage.pearls = gameState.pearls;
if (gameState.currentWave > storage.currentLevel) {
storage.currentLevel = gameState.currentWave;
}
if (LK.getScore() > storage.highScore) {
storage.highScore = LK.getScore();
}
}; ===================================================================
--- original.js
+++ change.js
@@ -751,8 +751,23 @@
});
clamText.anchor.set(0.5, 0);
clamText.y = 50;
clamOption.addChild(clamText);
+// TurtleShell tower option
+var turtleShellOption = LK.getAsset('TurtleShell', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ x: 0,
+ y: 50
+});
+buildMenu.addChild(turtleShellOption);
+var turtleShellText = new Text2("Turtle\nShell\n10 pearls", {
+ size: 30,
+ fill: 0xFFFFFF
+});
+turtleShellText.anchor.set(0.5, 0);
+turtleShellText.y = 50;
+turtleShellOption.addChild(turtleShellText);
var sharkyOption = LK.getAsset('Sharky', {
anchorX: 0.5,
anchorY: 0.5,
x: 100,
@@ -884,8 +899,9 @@
// If the player has insufficient pearls, disable options
bubbleOption.alpha = gameState.pearls >= 10 ? 1 : 0.5;
seaweedOption.alpha = gameState.pearls >= 10 ? 1 : 0.5;
clamOption.alpha = gameState.pearls >= 10 ? 1 : 0.5;
+ turtleShellOption.alpha = gameState.pearls >= 10 ? 1 : 0.5;
sharkyOption.alpha = gameState.pearls >= 10 ? 1 : 0.5;
jellyStingerOption.alpha = gameState.pearls >= 10 ? 1 : 0.5;
}
function closeBuildMenu() {
@@ -935,8 +951,10 @@
} else if (type === 'clamMine') {
tower = new ClamMine();
} else if (type === 'JellyStinger') {
tower = new JellyStinger();
+ } else if (type === 'TurtleShell') {
+ tower = new TurtleShell();
} else {
tower = new Tower(type);
}
tower.x = buildSpot.x;
@@ -1119,8 +1137,11 @@
};
jellyStingerOption.down = function () {
buildTower('JellyStinger');
};
+turtleShellOption.down = function () {
+ buildTower('TurtleShell');
+};
game.down = function (x, y, obj) {
// Deselect tower when clicking elsewhere
if (selectedTower && !upgradeButton.visible) {
selectedTower.deselect();
Floppy Fish the fish. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows. Floppy Fish
Coral with seeweed inside of it. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Seeweed with eyes. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Bubbles. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A cannon made out of a bubble. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A clam. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A shark. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A pearl. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A sleeping fish. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Friendly shark. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Megalodon shark. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Jellyfish. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Electricity. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Button that says, stop upgrading. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Plastic bag. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Turtle. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
Turtle shell. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows