User prompt
Decrease the gold increase for generating dice by 30%
User prompt
Compress the top and bottom lanes towards the center by 10% and adjust the building areas to match
User prompt
Compress the lanes in toward the center by 10% and move the building areas to match.
User prompt
All merged dice are becoming Warrior dice. They need to retain their class
User prompt
The merged dice still needs to be the same class as the combined dice and it needs to gain a level.
User prompt
When dice are combined, instead of spawning the new dice in another area, the new dice should be spawned in the cell where the dice where combined.
User prompt
The top building area should spawn dice in the top left corner while the bottom building area should spawn dice in the bottom left corner.
User prompt
But the top building area still spawns in the top left.
User prompt
Lower dice start spawning in the bottom left corner of the build area instead of the top left.
User prompt
Lower dice start spawning in the bottom left corner of the build area instead of the top left.
Code edit (1 edits merged)
Please save this source code
User prompt
Dice should not get bigger as they are merged.
User prompt
Update with: self.createPips = function (level) { var pipPositions = self.getPipPositions(level); for (var i = 0; i < pipPositions.length; i++) { var pip = self.attachAsset('dicePip', { anchorX: 0.5, anchorY: 0.5 }); pip.x = pipPositions[i].x; pip.y = pipPositions[i].y; pip.width = pip.height = 50; // Doubled from 25 pips.push(pip); } }; self.getPipPositions = function (level) { var positions = []; var offset = 80; // Increased from 60 to use more of the dice face switch (level) { case 1: positions.push({x: 0, y: 0}); break; case 2: positions.push({x: -offset, y: -offset}); positions.push({x: offset, y: offset}); break; case 3: positions.push({x: -offset, y: -offset}); positions.push({x: 0, y: 0}); positions.push({x: offset, y: offset}); break; case 4: positions.push({x: -offset, y: -offset}); positions.push({x: offset, y: -offset}); positions.push({x: -offset, y: offset}); positions.push({x: offset, y: offset}); break; case 5: positions.push({x: -offset, y: -offset}); positions.push({x: offset, y: -offset}); positions.push({x: 0, y: 0}); positions.push({x: -offset, y: offset}); positions.push({x: offset, y: offset}); break; case 6: positions.push({x: -offset, y: -offset*0.8}); positions.push({x: offset, y: -offset*0.8}); positions.push({x: -offset, y: 0}); positions.push({x: offset, y: 0}); positions.push({x: -offset, y: offset*0.8}); positions.push({x: offset, y: offset*0.8}); break; } return positions; };
User prompt
Update with: self.updateVisuals = function () { var baseSize = 250 + self.level * 30; // Increased from 150 + self.level * 20 diceGraphics.width = diceGraphics.height = baseSize; for (var i = 0; i < pips.length; i++) { self.removeChild(pips[i]); } pips = []; self.createPips(self.level); var pulseScale = 1 + self.level * 0.02; tween.stop(diceGraphics, { scaleX: true, scaleY: true }); tween(diceGraphics, { scaleX: pulseScale, scaleY: pulseScale }, { duration: 1000 + self.level * 200, easing: tween.sineInOut, repeat: -1, yoyo: true }); }; self.createPips = function (level) { var pipPositions = self.getPipPositions(level); for (var i = 0; i < pipPositions.length; i++) { var pip = self.attachAsset('dicePip', { anchorX: 0.5, anchorY: 0.5 }); pip.x = pipPositions[i].x; pip.y = pipPositions[i].y; pip.width = pip.height = 25; // Increased from 15 pips.push(pip); } }; self.getPipPositions = function (level) { var positions = []; var offset = 60; // Increased from 25 to spread pips out more switch (level) { case 1: positions.push({x: 0, y: 0}); break; case 2: positions.push({x: -offset/2, y: -offset/2}); positions.push({x: offset/2, y: offset/2}); break; case 3: positions.push({x: -offset, y: -offset}); positions.push({x: 0, y: 0}); positions.push({x: offset, y: offset}); break; case 4: positions.push({x: -offset/2, y: -offset/2}); positions.push({x: offset/2, y: -offset/2}); positions.push({x: -offset/2, y: offset/2}); positions.push({x: offset/2, y: offset/2}); break; case 5: positions.push({x: -offset/2, y: -offset/2}); positions.push({x: offset/2, y: -offset/2}); positions.push({x: 0, y: 0}); positions.push({x: -offset/2, y: offset/2}); positions.push({x: offset/2, y: offset/2}); break; case 6: positions.push({x: -offset/2, y: -offset}); positions.push({x: offset/2, y: -offset}); positions.push({x: -offset/2, y: 0}); positions.push({x: offset/2, y: 0}); positions.push({x: -offset/2, y: offset}); positions.push({x: offset/2, y: offset}); break; } return positions; }; ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Change the energyorb class to this dice class and update as needed with: var ClassDice = Container.expand(function () { var self = Container.call(this); self.active = false; self.diceType = 'warrior'; self.level = 1; self.maxLevel = 6; self.gridX = 0; self.gridY = 0; self.area = 0; self.range = 3 * CELL_SIZE; self.damage = 10; self.fireRate = 60; self.bulletSpeed = 5; self.lastFired = 0; self.targetEnemy = null; self.isDragging = false; self.magneticTarget = null; var diceGraphics = self.attachAsset('warriorDice', { anchorX: 0.5, anchorY: 0.5 }); var pips = []; self.activate = function (diceType, gridX, gridY, area) { self.active = true; self.visible = true; self.diceType = diceType || 'warrior'; self.level = 1; self.gridX = gridX; self.gridY = gridY; self.area = area; self.lastFired = 0; self.targetEnemy = null; self.setDiceType(self.diceType); self.updateVisuals(); self.updatePosition(); }; self.setDiceType = function (type) { self.diceType = type; if (diceGraphics) { self.removeChild(diceGraphics); } switch (type) { case 'knifethrower': self.fireRate = 30; self.damage = 5; self.range = 2.5 * CELL_SIZE; self.bulletSpeed = 7; diceGraphics = self.attachAsset('knifethrowerDice', { anchorX: 0.5, anchorY: 0.5 }); break; case 'archer': self.fireRate = 90; self.damage = 25; self.range = 5 * CELL_SIZE; self.bulletSpeed = 25; diceGraphics = self.attachAsset('archerDice', { anchorX: 0.5, anchorY: 0.5 }); break; case 'engineer': self.fireRate = 75; self.damage = 15; self.range = 2 * CELL_SIZE; self.bulletSpeed = 4; diceGraphics = self.attachAsset('engineerDice', { anchorX: 0.5, anchorY: 0.5 }); break; case 'icemage': self.fireRate = 50; self.damage = 8; self.range = 3.5 * CELL_SIZE; self.bulletSpeed = 5; diceGraphics = self.attachAsset('iceMageDice', { anchorX: 0.5, anchorY: 0.5 }); break; case 'assassin': self.fireRate = 70; self.damage = 12; self.range = 3.2 * CELL_SIZE; self.bulletSpeed = 5; diceGraphics = self.attachAsset('assassinDice', { anchorX: 0.5, anchorY: 0.5 }); break; default: self.fireRate = 60; self.damage = 10; self.range = 3 * CELL_SIZE; self.bulletSpeed = 5; diceGraphics = self.attachAsset('warriorDice', { anchorX: 0.5, anchorY: 0.5 }); } }; self.updateVisuals = function () { var baseSize = 150 + self.level * 20; diceGraphics.width = diceGraphics.height = baseSize; for (var i = 0; i < pips.length; i++) { self.removeChild(pips[i]); } pips = []; self.createPips(self.level); var pulseScale = 1 + self.level * 0.02; tween.stop(diceGraphics, { scaleX: true, scaleY: true }); tween(diceGraphics, { scaleX: pulseScale, scaleY: pulseScale }, { duration: 1000 + self.level * 200, easing: tween.sineInOut, repeat: -1, yoyo: true }); }; self.createPips = function (level) { var pipPositions = self.getPipPositions(level); for (var i = 0; i < pipPositions.length; i++) { var pip = self.attachAsset('dicePip', { anchorX: 0.5, anchorY: 0.5 }); pip.x = pipPositions[i].x; pip.y = pipPositions[i].y; pip.width = pip.height = 15; pips.push(pip); } }; self.getPipPositions = function (level) { var positions = []; var offset = 25; switch (level) { case 1: positions.push({x: 0, y: 0}); break; case 2: positions.push({x: -offset/2, y: -offset/2}); positions.push({x: offset/2, y: offset/2}); break; case 3: positions.push({x: -offset, y: -offset}); positions.push({x: 0, y: 0}); positions.push({x: offset, y: offset}); break; case 4: positions.push({x: -offset/2, y: -offset/2}); positions.push({x: offset/2, y: -offset/2}); positions.push({x: -offset/2, y: offset/2}); positions.push({x: offset/2, y: offset/2}); break; case 5: positions.push({x: -offset/2, y: -offset/2}); positions.push({x: offset/2, y: -offset/2}); positions.push({x: 0, y: 0}); positions.push({x: -offset/2, y: offset/2}); positions.push({x: offset/2, y: offset/2}); break; case 6: positions.push({x: -offset/2, y: -offset}); positions.push({x: offset/2, y: -offset}); positions.push({x: -offset/2, y: 0}); positions.push({x: offset/2, y: 0}); positions.push({x: -offset/2, y: offset}); positions.push({x: offset/2, y: offset}); break; } return positions; }; // Copy all the other methods from EnergyOrb but change references: // - self.orbType becomes self.diceType // - bulletType mapping: knifethrower→rapid, archer→sniper, engineer→splash, icemage→slow, assassin→poison, warrior→default // - self.canMergeWith checks diceType instead of orbType // - GameManager.removeDice instead of removeOrb // - PoolManager.getClassDice instead of getEnergyOrb return self; }); ↪💡 Consider importing and using the following plugins: @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
Update with: self.landJump = function () { self.jumpState = 'landing'; self.baseY = self.targetPosition.y; // Update path progress using the same increment var pathLength = PathSystem.getPathLength(self.pathIndex); var progressIncrement = self.jumpDistance / pathLength; self.pathProgress = Math.min(1, self.pathProgress + progressIncrement); // Landing squish tween.stop(blobGraphics, { scaleX: true, scaleY: true }); tween(blobGraphics, { scaleX: 1.3, scaleY: 0.7 }, { duration: 120, easing: tween.quadOut, onFinish: function onFinish() { // Return to normal tween(blobGraphics, { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.elasticOut, onFinish: function onFinish() { self.jumpState = 'paused'; self.jumpTimer = self.pauseDuration; } }); } }); }; ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Update with: self.startJumpPreparation = function () { self.jumpState = 'preparing'; // Calculate next position along path with consistent progress regardless of direction var pathLength = PathSystem.getPathLength(self.pathIndex); var progressIncrement = self.jumpDistance / pathLength; var nextProgress = Math.min(1, self.pathProgress + progressIncrement); var nextPos = PathSystem.getPositionAlongPath(nextProgress, self.pathIndex); self.startPosition.x = self.x; self.startPosition.y = self.baseY; self.targetPosition.x = nextPos.x; self.targetPosition.y = nextPos.y; // Pre-jump squish (getting ready) tween.stop(blobGraphics, { scaleX: true, scaleY: true }); tween(blobGraphics, { scaleX: 1.2, scaleY: 0.8 }, { duration: 150, easing: tween.quadOut, onFinish: function onFinish() { self.executeJump(); } }); }; ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Change the lanes to resemble a backwards 3 shape instead of a backwards 5. Split the spawns in half between the top and the bottom lane with the middle lane end as the new objective for enemies.
User prompt
Reduce buying cost increase amount.
User prompt
Change the grids to be 3x5, increase the size of the cells and orbs and use more of the space between lanes. Use from one lane to the other with a slight buffer between.
User prompt
Reduce the pause for the blobs jumping ↪💡 Consider importing and using the following plugins: @upit/tween.v1
User prompt
Increase the amount of gold gained per enemy by 25%
User prompt
Widen lanes so that slimes can travel two wide and spawn them one two different planes
User prompt
Fix the slime enemy animation so that they jump in the following stages: getting ready = vertical squish, jumping = smooth arc in the direction they are traveling, combined with a horizontal stretch, landing = vertical squish again, then return to normal, brief pause and back to ready again. ↪💡 Consider importing and using the following plugins: @upit/tween.v1
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var BlobEnemy = Container.expand(function () { var self = Container.call(this); self.active = false; self.material = 'slime'; self.health = 40; self.maxHealth = 40; self.speed = 1.2; self.pathProgress = 0; self.pathIndex = 0; self.waveNumber = 1; // Jump states self.jumpState = 'paused'; self.jumpTimer = 0; self.pauseDuration = 30; self.jumpDuration = 30; self.startPosition = { x: 0, y: 0 }; self.targetPosition = { x: 0, y: 0 }; self.jumpDistance = 80; self.jumpHeight = 40; self.baseY = 0; // Status effects self.slowed = false; self.poisoned = false; self.slowDuration = 0; self.poisonDuration = 0; self.poisonDamage = 0; self.originalSpeed = self.speed; var blobGraphics = self.attachAsset('blob', { anchorX: 0.5, anchorY: 0.5 }); var healthBarOutline = self.attachAsset('healthBarOutline', { anchorX: 0, anchorY: 0.5 }); var healthBar = self.attachAsset('healthBar', { anchorX: 0, anchorY: 0.5 }); // Position health bars healthBarOutline.y = healthBar.y = -30; healthBarOutline.x = -36; healthBar.x = -35; healthBar.tint = 0x00ff00; self.activate = function (material, waveNumber, pathIndex) { self.active = true; self.visible = true; self.material = material || 'slime'; self.waveNumber = waveNumber || 1; self.pathIndex = pathIndex || 0; self.pathProgress = 0; self.jumpState = 'paused'; self.jumpTimer = self.pauseDuration; // Reset status effects self.slowed = false; self.poisoned = false; self.slowDuration = 0; self.poisonDuration = 0; // Set material properties self.setMaterial(self.material); // Scale health with wave number var healthMultiplier = Math.pow(1.15, self.waveNumber); self.maxHealth = Math.round(self.maxHealth * healthMultiplier); self.health = self.maxHealth; // Start at beginning of path var startPos = PathSystem.getPositionAlongPath(0, self.pathIndex); self.x = startPos.x; self.y = startPos.y; self.baseY = self.y; // Reset blob scale blobGraphics.scaleX = 1; blobGraphics.scaleY = 1; }; self.setMaterial = function (material) { switch (material) { case 'slime': blobGraphics.tint = 0x00FF00; self.speed = 0.8; self.pauseDuration = 30; self.jumpDistance = 80; self.jumpHeight = 40; self.maxHealth = 40; break; case 'metal': blobGraphics.tint = 0xC0C0C0; self.speed = 0.6; self.pauseDuration = 40; self.jumpDistance = 60; self.jumpHeight = 25; self.maxHealth = 60; break; case 'lava': blobGraphics.tint = 0xFF4500; self.speed = 1.0; self.pauseDuration = 22; self.jumpDistance = 90; self.jumpHeight = 50; self.maxHealth = 48; break; case 'ice': blobGraphics.tint = 0x87CEEB; self.speed = 0.5; self.pauseDuration = 45; self.jumpDistance = 50; self.jumpHeight = 30; self.maxHealth = 32; break; case 'shadow': blobGraphics.tint = 0x4B0082; self.speed = 1.2; self.pauseDuration = 15; self.jumpDistance = 100; self.jumpHeight = 60; self.maxHealth = 80; break; } self.originalSpeed = self.speed; }; self.takeDamage = function (damage, damageType, towerLevel) { self.health -= damage; healthBar.width = Math.max(0, self.health / self.maxHealth * 70); // Apply special effects if (damageType === 'splash') { var effect = PoolManager.getEffect(); if (effect) { effect.activate(self.x, self.y, 'splash'); gameLayer.addChild(effect); } } else if (damageType === 'slow' && self.material !== 'ice') { // Ice is immune to slow if (!self.slowed) { var slowPct = 0.5 + (towerLevel - 1) * 0.05; // 50% to 75% slow self.speed = self.originalSpeed * (1 - slowPct); self.slowed = true; self.slowDuration = 180; var effect = PoolManager.getEffect(); if (effect) { effect.activate(self.x, self.y, 'slow'); gameLayer.addChild(effect); } } else { self.slowDuration = 180; // Reset duration } } else if (damageType === 'poison' && self.material !== 'metal') { // Metal is immune to poison if (!self.poisoned) { self.poisoned = true; self.poisonDamage = damage * 0.2; self.poisonDuration = 300; var effect = PoolManager.getEffect(); if (effect) { effect.activate(self.x, self.y, 'poison'); gameLayer.addChild(effect); } } } else if (damageType === 'sniper') { var effect = PoolManager.getEffect(); if (effect) { effect.activate(self.x, self.y, 'sniper'); gameLayer.addChild(effect); } } if (self.health <= 0) { self.die(); } }; self.die = function () { // Create death particles for (var i = 0; i < 8; i++) { var particle = PoolManager.getParticle(); if (particle) { particle.activate(self.x, self.y, self.material); gameLayer.addChild(particle); } } // Give gold based on enemy type and wave var goldEarned = Math.floor(5 + self.waveNumber * 0.5); if (self.material === 'shadow') { goldEarned *= 2; } // Shadow enemies give double gold goldEarned = Math.floor(goldEarned * 1.25); // Increase gold by 25% setGold(gold + goldEarned); score += 10 + self.waveNumber; PoolManager.returnBlob(self); }; self.startJumpPreparation = function () { self.jumpState = 'preparing'; // Calculate next position along path with consistent progress regardless of direction var pathLength = PathSystem.getPathLength(self.pathIndex); var progressIncrement = self.jumpDistance / pathLength; var nextProgress = Math.min(1, self.pathProgress + progressIncrement); var nextPos = PathSystem.getPositionAlongPath(nextProgress, self.pathIndex); self.startPosition.x = self.x; self.startPosition.y = self.baseY; self.targetPosition.x = nextPos.x; self.targetPosition.y = nextPos.y; // Pre-jump squish (getting ready) tween.stop(blobGraphics, { scaleX: true, scaleY: true }); tween(blobGraphics, { scaleX: 1.2, scaleY: 0.8 }, { duration: 150, easing: tween.quadOut, onFinish: function onFinish() { self.executeJump(); } }); }; self.executeJump = function () { self.jumpState = 'jumping'; self.jumpTimer = self.jumpDuration; // Stretch animation for jump take-off tween.stop(blobGraphics, { scaleX: true, scaleY: true }); tween(blobGraphics, { scaleX: 0.8, scaleY: 1.2 }, { duration: 200, easing: tween.quadOut }); }; self.landJump = function () { self.jumpState = 'landing'; self.baseY = self.targetPosition.y; // Update path progress using the same increment var pathLength = PathSystem.getPathLength(self.pathIndex); var progressIncrement = self.jumpDistance / pathLength; self.pathProgress = Math.min(1, self.pathProgress + progressIncrement); // Landing squish tween.stop(blobGraphics, { scaleX: true, scaleY: true }); tween(blobGraphics, { scaleX: 1.3, scaleY: 0.7 }, { duration: 120, easing: tween.quadOut, onFinish: function onFinish() { // Return to normal tween(blobGraphics, { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.elasticOut, onFinish: function onFinish() { self.jumpState = 'paused'; self.jumpTimer = self.pauseDuration; } }); } }); }; self.getArcPosition = function (progress) { var x = self.startPosition.x + (self.targetPosition.x - self.startPosition.x) * progress; var y_base = self.startPosition.y + (self.targetPosition.y - self.startPosition.y) * progress; // The arc is a parabola on top of the base Y, peaking at progress = 0.5 var arc_y = self.jumpHeight * 4 * progress * (1 - progress); var y = y_base - arc_y; // Subtract because Y is inverted (0 is top) return { x: x, y: y }; }; self.update = function () { if (!self.active) { return; } // Handle status effects if (self.slowed) { self.slowDuration--; if (self.slowDuration <= 0) { self.speed = self.originalSpeed; self.slowed = false; } } if (self.poisoned) { if (LK.ticks % 30 === 0) { // Poison damage every 30 frames self.health -= self.poisonDamage; healthBar.width = Math.max(0, self.health / self.maxHealth * 70); if (self.health <= 0) { self.die(); return; } } self.poisonDuration--; if (self.poisonDuration <= 0) { self.poisoned = false; } } // Check if reached end of path if (self.pathProgress >= 1) { lives--; updateUI(); PoolManager.returnBlob(self); if (lives <= 0) { LK.showGameOver(); } return; } // Jump state machine switch (self.jumpState) { case 'paused': self.jumpTimer--; if (self.jumpTimer <= 0) { self.startJumpPreparation(); } break; case 'preparing': // Animation handles this break; case 'jumping': self.jumpTimer--; var progress = (self.jumpDuration - self.jumpTimer) / self.jumpDuration; var arcPos = self.getArcPosition(progress); self.x = arcPos.x; self.y = arcPos.y; if (self.jumpTimer <= 0) { self.x = self.targetPosition.x; self.y = self.targetPosition.y; self.landJump(); } break; case 'landing': // Animation handles this break; } }; self.reset = function () { self.material = 'slime'; self.health = 40; self.maxHealth = 40; self.speed = 1.2; self.pathProgress = 0; self.pathIndex = 0; self.waveNumber = 1; self.jumpState = 'paused'; self.jumpTimer = 0; self.pauseDuration = 30; self.slowed = false; self.poisoned = false; self.slowDuration = 0; self.poisonDuration = 0; tween.stop(blobGraphics); blobGraphics.scaleX = 1; blobGraphics.scaleY = 1; blobGraphics.tint = 0x00FF00; }; return self; }); var Bullet = Container.expand(function () { var self = Container.call(this); self.active = false; self.targetEnemy = null; self.damage = 10; self.speed = 5; self.type = 'default'; self.sourceTowerLevel = 1; var bulletGraphics = self.attachAsset('particle', { anchorX: 0.5, anchorY: 0.5 }); bulletGraphics.width = 15; bulletGraphics.height = 15; self.activate = function (startX, startY, targetEnemy, damage, speed, type, sourceTowerLevel) { self.active = true; self.visible = true; self.x = startX; self.y = startY; self.targetEnemy = targetEnemy; self.damage = damage || 10; self.speed = speed || 5; self.type = type || 'default'; self.sourceTowerLevel = sourceTowerLevel || 1; // Customize appearance based on type switch (self.type) { case 'rapid': bulletGraphics.tint = 0x00AAFF; bulletGraphics.width = bulletGraphics.height = 12; break; case 'sniper': bulletGraphics.tint = 0xFF5500; bulletGraphics.width = bulletGraphics.height = 8; break; case 'splash': bulletGraphics.tint = 0x33CC00; bulletGraphics.width = bulletGraphics.height = 20; break; case 'slow': bulletGraphics.tint = 0x9900FF; bulletGraphics.width = bulletGraphics.height = 18; break; case 'poison': bulletGraphics.tint = 0x00FFAA; bulletGraphics.width = bulletGraphics.height = 18; break; default: bulletGraphics.tint = 0xFFFFFF; bulletGraphics.width = bulletGraphics.height = 15; } }; self.update = function () { if (!self.active || !self.targetEnemy || !self.targetEnemy.active) { PoolManager.returnBullet(self); return; } var dx = self.targetEnemy.x - self.x; var dy = self.targetEnemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < self.speed) { // Hit target self.targetEnemy.takeDamage(self.damage, self.type, self.sourceTowerLevel); PoolManager.returnBullet(self); } else { var angle = Math.atan2(dy, dx); self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; } }; self.reset = function () { self.targetEnemy = null; self.damage = 10; self.speed = 5; self.type = 'default'; self.sourceTowerLevel = 1; }; return self; }); var ClassDice = Container.expand(function () { var self = Container.call(this); self.active = false; self.diceType = 'warrior'; self.level = 1; self.maxLevel = 6; self.gridX = 0; self.gridY = 0; self.area = 0; self.range = 3 * CELL_SIZE; self.damage = 10; self.fireRate = 60; self.bulletSpeed = 5; self.lastFired = 0; self.targetEnemy = null; self.isDragging = false; self.magneticTarget = null; var diceGraphics = self.attachAsset('warriorDice', { anchorX: 0.5, anchorY: 0.5 }); var pips = []; self.activate = function (diceType, gridX, gridY, area, level) { self.active = true; self.visible = true; self.diceType = diceType || 'warrior'; self.level = level || 1; self.gridX = gridX; self.gridY = gridY; self.area = area; self.lastFired = 0; self.targetEnemy = null; self.setDiceType(self.diceType); self.updateVisuals(); self.updatePosition(); }; self.setDiceType = function (type) { self.diceType = type; if (diceGraphics) { self.removeChild(diceGraphics); } switch (type) { case 'knifethrower': self.fireRate = 30; self.damage = 5; self.range = 2.5 * CELL_SIZE; self.bulletSpeed = 7; diceGraphics = self.attachAsset('knifethrowerDice', { anchorX: 0.5, anchorY: 0.5 }); break; case 'archer': self.fireRate = 90; self.damage = 25; self.range = 5 * CELL_SIZE; self.bulletSpeed = 25; diceGraphics = self.attachAsset('archerDice', { anchorX: 0.5, anchorY: 0.5 }); break; case 'engineer': self.fireRate = 75; self.damage = 15; self.range = 2 * CELL_SIZE; self.bulletSpeed = 4; diceGraphics = self.attachAsset('engineerDice', { anchorX: 0.5, anchorY: 0.5 }); break; case 'icemage': self.fireRate = 50; self.damage = 8; self.range = 3.5 * CELL_SIZE; self.bulletSpeed = 5; diceGraphics = self.attachAsset('iceMageDice', { anchorX: 0.5, anchorY: 0.5 }); break; case 'assassin': self.fireRate = 70; self.damage = 12; self.range = 3.2 * CELL_SIZE; self.bulletSpeed = 5; diceGraphics = self.attachAsset('assassinDice', { anchorX: 0.5, anchorY: 0.5 }); break; default: self.fireRate = 60; self.damage = 10; self.range = 3 * CELL_SIZE; self.bulletSpeed = 5; diceGraphics = self.attachAsset('warriorDice', { anchorX: 0.5, anchorY: 0.5 }); } }; self.updateVisuals = function () { var baseSize = 300; // Fixed size, no longer increases with level diceGraphics.width = diceGraphics.height = baseSize; for (var i = 0; i < pips.length; i++) { self.removeChild(pips[i]); } pips = []; self.createPips(self.level); var pulseScale = 1.02; // Fixed pulse scale, no longer increases with level tween.stop(diceGraphics, { scaleX: true, scaleY: true }); tween(diceGraphics, { scaleX: pulseScale, scaleY: pulseScale }, { duration: 1000, easing: tween.sineInOut, repeat: -1, yoyo: true }); }; self.createPips = function (level) { var pipPositions = self.getPipPositions(level); for (var i = 0; i < pipPositions.length; i++) { var pip = self.attachAsset('dicePip', { anchorX: 0.5, anchorY: 0.5 }); pip.x = pipPositions[i].x; pip.y = pipPositions[i].y; pip.width = pip.height = 50; // Doubled from 25 pips.push(pip); } }; self.getPipPositions = function (level) { var positions = []; var offset = 80; // Increased from 60 to use more of the dice face switch (level) { case 1: positions.push({ x: 0, y: 0 }); break; case 2: positions.push({ x: -offset, y: -offset }); positions.push({ x: offset, y: offset }); break; case 3: positions.push({ x: -offset, y: -offset }); positions.push({ x: 0, y: 0 }); positions.push({ x: offset, y: offset }); break; case 4: positions.push({ x: -offset, y: -offset }); positions.push({ x: offset, y: -offset }); positions.push({ x: -offset, y: offset }); positions.push({ x: offset, y: offset }); break; case 5: positions.push({ x: -offset, y: -offset }); positions.push({ x: offset, y: -offset }); positions.push({ x: 0, y: 0 }); positions.push({ x: -offset, y: offset }); positions.push({ x: offset, y: offset }); break; case 6: positions.push({ x: -offset, y: -offset * 0.8 }); positions.push({ x: offset, y: -offset * 0.8 }); positions.push({ x: -offset, y: 0 }); positions.push({ x: offset, y: 0 }); positions.push({ x: -offset, y: offset * 0.8 }); positions.push({ x: offset, y: offset * 0.8 }); break; } return positions; }; self.updatePosition = function () { var startX = self.area === 0 ? TOP_AREA_X : BOTTOM_AREA_X; var startY = self.area === 0 ? TOP_AREA_Y : BOTTOM_AREA_Y; self.x = startX + self.gridX * CELL_SIZE + CELL_SIZE / 2; self.y = startY + self.gridY * CELL_SIZE + CELL_SIZE / 2; }; self.getRange = function () { var baseRange = self.range; var rangeMultiplier = 1 + (self.level - 1) * 0.15; if (self.diceType === 'archer' && self.level === self.maxLevel) { rangeMultiplier *= 2; } return baseRange * rangeMultiplier; }; self.getCurrentDamage = function () { var damageMultiplier = 1 + (self.level - 1) * 0.5; if (self.level === self.maxLevel) { damageMultiplier *= 2; } return Math.floor(self.damage * damageMultiplier); }; self.getCurrentFireRate = function () { var rateMultiplier = 1 + (self.level - 1) * 0.3; return Math.max(10, Math.floor(self.fireRate / rateMultiplier)); }; self.findTarget = function () { var closestEnemy = null; var closestDistance = Infinity; var diceRange = self.getRange(); for (var i = 0; i < activeBlobs.length; i++) { var blob = activeBlobs[i]; if (!blob.active) { continue; } var dx = blob.x - self.x; var dy = blob.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance <= diceRange && distance < closestDistance) { closestDistance = distance; closestEnemy = blob; } } return closestEnemy; }; self.getBulletType = function () { switch (self.diceType) { case 'knifethrower': return 'rapid'; case 'archer': return 'sniper'; case 'engineer': return 'splash'; case 'icemage': return 'slow'; case 'assassin': return 'poison'; case 'warrior': default: return 'default'; } }; self.fire = function () { if (!self.targetEnemy) { return; } var bullet = PoolManager.getBullet(); if (bullet) { var bulletStartX = self.x; var bulletStartY = self.y; bullet.activate(bulletStartX, bulletStartY, self.targetEnemy, self.getCurrentDamage(), self.bulletSpeed + self.level, self.getBulletType(), self.level); gameLayer.addChild(bullet); activeBullets.push(bullet); tween.stop(diceGraphics, { scaleX: true, scaleY: true }); tween(diceGraphics, { scaleX: 0.9, scaleY: 0.9 }, { duration: 50, easing: tween.quadOut, onFinish: function onFinish() { tween(diceGraphics, { scaleX: 1, scaleY: 1 }, { duration: 100, easing: tween.elasticOut }); } }); } }; self.canMergeWith = function (otherDice) { return otherDice && otherDice !== self && otherDice.active && otherDice.diceType === self.diceType && otherDice.level === self.level && self.level < self.maxLevel; }; self.checkMagneticAttraction = function (otherDice) { if (!self.canMergeWith(otherDice)) { self.magneticTarget = null; return; } var dx = otherDice.x - self.x; var dy = otherDice.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance < CELL_SIZE * 1.5) { self.magneticTarget = otherDice; tween.stop(self, { x: true, y: true }); tween(self, { x: self.x + dx * 0.1, y: self.y + dy * 0.1 }, { duration: 100, easing: tween.quadOut }); } else { self.magneticTarget = null; } }; self.mergeWith = function (otherDice) { if (!self.canMergeWith(otherDice)) { return false; } // Check for an available dice in the pool before removing the existing ones. var newDice = PoolManager.getClassDice(); if (!newDice) { return false; } var newDiceGridX = otherDice.gridX; var newDiceGridY = otherDice.gridY; var newDiceArea = otherDice.area; var newDiceType = self.diceType; var newLevel = self.level + 1; // Remove old dice first to free up the grid spot. GameManager.removeDice(self); GameManager.removeDice(otherDice); // Activate and place the new dice. newDice.activate(newDiceType, newDiceGridX, newDiceGridY, newDiceArea, newLevel); if (newDiceArea === 0) { topAreaLayer.addChild(newDice); topAreaDice[newDiceGridX][newDiceGridY] = newDice; } else { bottomAreaLayer.addChild(newDice); bottomAreaDice[newDiceGridX][newDiceGridY] = newDice; } tween(newDice, { scaleX: 1.5, scaleY: 1.5 }, { duration: 200, easing: tween.elasticOut, onFinish: function onFinish() { tween(newDice, { scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.elasticOut }); } }); return true; }; self.update = function () { if (!self.active || self.isDragging) { return; } self.targetEnemy = self.findTarget(); if (self.targetEnemy) { if (LK.ticks - self.lastFired >= self.getCurrentFireRate()) { self.fire(); self.lastFired = LK.ticks; } } }; self.reset = function () { self.diceType = 'warrior'; self.level = 1; self.gridX = 0; self.gridY = 0; self.area = 0; self.lastFired = 0; self.targetEnemy = null; self.isDragging = false; self.magneticTarget = null; tween.stop(self); tween.stop(diceGraphics); for (var i = 0; i < pips.length; i++) { self.removeChild(pips[i]); } pips = []; diceGraphics.scaleX = diceGraphics.scaleY = 1; }; return self; }); var DeathParticle = Container.expand(function () { var self = Container.call(this); self.active = false; self.velocity = { x: 0, y: 0 }; self.life = 0; self.maxLife = 60; var particleGraphics = self.attachAsset('particle', { anchorX: 0.5, anchorY: 0.5 }); self.activate = function (x, y, material) { self.active = true; self.visible = true; self.x = x; self.y = y; self.life = self.maxLife; // Random velocity var angle = Math.random() * Math.PI * 2; var speed = 2 + Math.random() * 4; self.velocity.x = Math.cos(angle) * speed; self.velocity.y = Math.sin(angle) * speed; // Set color based on material switch (material) { case 'slime': particleGraphics.tint = 0x00FF00; break; case 'metal': particleGraphics.tint = 0xC0C0C0; break; case 'lava': particleGraphics.tint = 0xFF4500; break; case 'ice': particleGraphics.tint = 0x87CEEB; break; case 'shadow': particleGraphics.tint = 0x4B0082; break; default: particleGraphics.tint = 0xFFFFFF; } particleGraphics.width = particleGraphics.height = 4 + Math.random() * 8; self.alpha = 1; }; self.update = function () { if (!self.active) { return; } self.x += self.velocity.x; self.y += self.velocity.y; self.velocity.y += 0.2; // Gravity self.life--; self.alpha = self.life / self.maxLife; if (self.life <= 0) { PoolManager.returnParticle(self); } }; self.reset = function () { self.velocity.x = 0; self.velocity.y = 0; self.life = 0; }; return self; }); var EffectIndicator = Container.expand(function () { var self = Container.call(this); self.active = false; self.effectType = 'splash'; var effectGraphics = self.attachAsset('rangeCircle', { anchorX: 0.5, anchorY: 0.5 }); effectGraphics.blendMode = 1; self.activate = function (x, y, type) { self.active = true; self.visible = true; self.x = x; self.y = y; self.effectType = type; switch (type) { case 'splash': effectGraphics.tint = 0x33CC00; effectGraphics.width = effectGraphics.height = CELL_SIZE * 1.5; break; case 'slow': effectGraphics.tint = 0x9900FF; effectGraphics.width = effectGraphics.height = CELL_SIZE; break; case 'poison': effectGraphics.tint = 0x00FFAA; effectGraphics.width = effectGraphics.height = CELL_SIZE; break; case 'sniper': effectGraphics.tint = 0xFF5500; effectGraphics.width = effectGraphics.height = CELL_SIZE; break; } effectGraphics.alpha = 0.7; self.alpha = 0; self.scaleX = 0.5; self.scaleY = 0.5; // Animate effect tween(self, { alpha: 0.8, scaleX: 1.5, scaleY: 1.5 }, { duration: 200, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { alpha: 0, scaleX: 2, scaleY: 2 }, { duration: 300, easing: tween.easeIn, onFinish: function onFinish() { PoolManager.returnEffect(self); } }); } }); }; self.reset = function () { self.effectType = 'splash'; tween.stop(self); }; return self; }); var GenerateButton = Container.expand(function (area) { var self = Container.call(this); self.area = area; // 0 for top, 1 for bottom self.cost = 10; var buttonGraphics = self.attachAsset('generateButton', { anchorX: 0.5, anchorY: 0.5 }); var costText = new Text2('Generate: ' + self.cost, { size: 30, fill: 0xFFFFFF, weight: 800 }); costText.anchor.set(0.5, 0.5); self.addChild(costText); self.updateCost = function () { // Fibonacci-like progression var newCost = Math.floor(10 * Math.pow(1.315, GameManager.getGenerateCount(self.area))); self.cost = newCost; costText.setText('Generate: ' + self.cost); // Update button color based on affordability if (gold >= self.cost) { buttonGraphics.tint = 0x00AA00; } else { buttonGraphics.tint = 0x888888; } }; self.generate = function () { if (gold < self.cost) { return false; } var emptySpot = GameManager.findEmptySpot(self.area); if (!emptySpot) { return false; } // Random dice type var types = ['warrior', 'knifethrower', 'archer', 'engineer', 'icemage', 'assassin']; var randomType = types[Math.floor(Math.random() * types.length)]; var newDice = PoolManager.getClassDice(); if (newDice) { newDice.activate(randomType, emptySpot.x, emptySpot.y, self.area); if (self.area === 0) { topAreaLayer.addChild(newDice); topAreaDice[emptySpot.x][emptySpot.y] = newDice; } else { bottomAreaLayer.addChild(newDice); bottomAreaDice[emptySpot.x][emptySpot.y] = newDice; } setGold(gold - self.cost); GameManager.incrementGenerateCount(self.area); self.updateCost(); // Spawn animation newDice.alpha = 0; newDice.scaleX = newDice.scaleY = 0.5; tween(newDice, { alpha: 1, scaleX: 1, scaleY: 1 }, { duration: 300, easing: tween.elasticOut }); return true; } return false; }; self.down = function () { self.generate(); }; self.update = function () { self.updateCost(); }; return self; }); var Notification = Container.expand(function (message) { var self = Container.call(this); var notificationGraphics = self.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5 }); var notificationText = new Text2(message, { size: 50, fill: 0x000000, weight: 800 }); notificationText.anchor.set(0.5, 0.5); notificationGraphics.width = notificationText.width + 30; self.addChild(notificationText); self.alpha = 1; var fadeOutTime = 180; // 3 seconds self.update = function () { if (fadeOutTime > 0) { fadeOutTime--; self.alpha = Math.min(fadeOutTime / 60, 1); } else { self.destroy(); } }; return self; }); /**** * Game Manager ****/ var WaveManager = Container.expand(function () { var self = Container.call(this); self.currentWave = 0; self.maxWaves = 50; self.waveTimer = 0; self.waveDelay = 300; // 5 seconds at 60fps self.waveActive = false; self.enemiesSpawned = 0; self.enemiesToSpawn = 0; self.spawnTimer = 0; self.spawnDelay = 30; // 0.5 seconds between spawns var waveText = new Text2('Wave: 1', { size: 60, fill: 0xFFFF00, weight: 800 }); waveText.anchor.set(0.5, 0.5); self.addChild(waveText); self.startNextWave = function () { self.currentWave++; self.waveActive = true; self.enemiesSpawned = 0; self.spawnTimer = 0; // Determine wave composition self.enemiesToSpawn = 10 + Math.floor(self.currentWave * 1.5); waveText.setText('Wave: ' + self.currentWave); // Show wave start notification var notification = new Notification('Wave ' + self.currentWave + ' incoming!'); notification.x = SCREEN_WIDTH / 2; notification.y = SCREEN_HEIGHT / 2; gameLayer.addChild(notification); }; self.getWaveMaterial = function () { // Introduce new materials over time if (self.currentWave <= 5) { return 'slime'; } if (self.currentWave <= 10) { return Math.random() < 0.7 ? 'slime' : 'metal'; } if (self.currentWave <= 15) { var materials = ['slime', 'metal', 'ice']; return materials[Math.floor(Math.random() * materials.length)]; } if (self.currentWave <= 25) { var materials = ['slime', 'metal', 'ice', 'lava']; return materials[Math.floor(Math.random() * materials.length)]; } // All materials available var materials = ['slime', 'metal', 'ice', 'lava', 'shadow']; return materials[Math.floor(Math.random() * materials.length)]; }; self.spawnEnemy = function () { var blob = PoolManager.getBlob(); if (blob) { var material = self.getWaveMaterial(); var pathIndex = self.enemiesSpawned % 2; blob.activate(material, self.currentWave, pathIndex); gameLayer.addChild(blob); activeBlobs.push(blob); self.enemiesSpawned++; } }; self.update = function () { if (!self.waveActive) { self.waveTimer++; if (self.waveTimer >= self.waveDelay) { self.waveTimer = 0; self.startNextWave(); } } else { // Spawn enemies if (self.enemiesSpawned < self.enemiesToSpawn) { self.spawnTimer++; if (self.spawnTimer >= self.spawnDelay) { self.spawnTimer = 0; self.spawnEnemy(); } } else { // Check if wave is complete var activeEnemies = 0; for (var i = 0; i < activeBlobs.length; i++) { if (activeBlobs[i].active) { activeEnemies++; } } if (activeEnemies === 0) { self.waveActive = false; if (self.currentWave >= self.maxWaves) { LK.showYouWin(); } } } } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x1a1a2e }); /**** * Game Code ****/ /**** * Object Pools ****/ /**** * Game Constants ****/ var PoolManager = { classDicePool: [], blobPool: [], bulletPool: [], particlePool: [], effectPool: [], // Pool sizes CLASS_DICE_POOL_SIZE: 30, BLOB_POOL_SIZE: 100, BULLET_POOL_SIZE: 200, PARTICLE_POOL_SIZE: 500, EFFECT_POOL_SIZE: 100, init: function init() { // Initialize dice pool for (var i = 0; i < this.CLASS_DICE_POOL_SIZE; i++) { var dice = new ClassDice(); dice.active = false; dice.visible = false; this.classDicePool.push(dice); } // Initialize blob pool for (var i = 0; i < this.BLOB_POOL_SIZE; i++) { var blob = new BlobEnemy(); blob.active = false; blob.visible = false; this.blobPool.push(blob); } // Initialize bullet pool for (var i = 0; i < this.BULLET_POOL_SIZE; i++) { var bullet = new Bullet(); bullet.active = false; bullet.visible = false; this.bulletPool.push(bullet); } // Initialize particle pool for (var i = 0; i < this.PARTICLE_POOL_SIZE; i++) { var particle = new DeathParticle(); particle.active = false; particle.visible = false; this.particlePool.push(particle); } // Initialize effect pool for (var i = 0; i < this.EFFECT_POOL_SIZE; i++) { var effect = new EffectIndicator(); effect.active = false; effect.visible = false; this.effectPool.push(effect); } }, getClassDice: function getClassDice() { for (var i = 0; i < this.classDicePool.length; i++) { if (!this.classDicePool[i].active) { return this.classDicePool[i]; } } return null; // Pool exhausted }, getBlob: function getBlob() { for (var i = 0; i < this.blobPool.length; i++) { if (!this.blobPool[i].active) { return this.blobPool[i]; } } return null; }, getBullet: function getBullet() { for (var i = 0; i < this.bulletPool.length; i++) { if (!this.bulletPool[i].active) { return this.bulletPool[i]; } } return null; }, getParticle: function getParticle() { for (var i = 0; i < this.particlePool.length; i++) { if (!this.particlePool[i].active) { return this.particlePool[i]; } } return null; }, getEffect: function getEffect() { for (var i = 0; i < this.effectPool.length; i++) { if (!this.effectPool[i].active) { return this.effectPool[i]; } } return null; }, returnClassDice: function returnClassDice(dice) { dice.active = false; dice.visible = false; dice.reset(); }, returnBlob: function returnBlob(blob) { blob.active = false; blob.visible = false; blob.reset(); if (blob.shadow) { blob.shadow.visible = false; } }, returnBullet: function returnBullet(bullet) { bullet.active = false; bullet.visible = false; bullet.reset(); }, returnParticle: function returnParticle(particle) { particle.active = false; particle.visible = false; particle.reset(); }, returnEffect: function returnEffect(effect) { effect.active = false; effect.visible = false; effect.reset(); } }; /**** * Path System - WRAPPED AROUND BUILDING AREAS ****/ var PathSystem = { pathPoints: [], init: function init() { // Define centerline for path var pathLeft = 150; // Start near left edge of screen var pathRight = SCREEN_WIDTH - 150; // Go to near right edge of screen var pathTop = 274; // Compressed towards center var pathMiddle = TOP_AREA_Y + BUILDING_AREA_HEIGHT + 77; // Center path in the new ~153px gap var pathBottom = BOTTOM_AREA_Y + BUILDING_AREA_HEIGHT + 100; // Position path 100px below the build area var laneWidth = 40; // Half the distance between the two lanes var L = pathLeft, R = pathRight, T = pathTop, M = pathMiddle, B = pathBottom; var d = laneWidth; // Paths for top and bottom spawns, meeting in the middle. // Top path this.pathPoints = [{ x: L, y: T }, // Start top-left { x: R, y: T }, // Top-right { x: R, y: M - d }, // Mid-right (top part) { x: L, y: M - d } // End in middle (top part) ]; // Bottom path this.pathPoints2 = [{ x: L, y: B }, // Start bottom-left { x: R, y: B }, // Bottom-right { x: R, y: M + d }, // Mid-right (bottom part) { x: L, y: M + d } // End in middle (bottom part) ]; }, getPositionAlongPath: function getPositionAlongPath(progress, pathIndex) { var points = pathIndex === 1 ? this.pathPoints2 : this.pathPoints; if (!points) { points = this.pathPoints; // Fallback } if (progress <= 0) { return { x: points[0].x, y: points[0].y }; } if (progress >= 1) { return { x: points[points.length - 1].x, y: points[points.length - 1].y }; } var totalSegments = points.length - 1; var segmentProgress = progress * totalSegments; var currentSegment = Math.floor(segmentProgress); var segmentRatio = segmentProgress - currentSegment; if (currentSegment >= totalSegments) { return { x: points[points.length - 1].x, y: points[points.length - 1].y }; } var start = points[currentSegment]; var end = points[currentSegment + 1]; return { x: start.x + (end.x - start.x) * segmentRatio, y: start.y + (end.y - start.y) * segmentRatio }; }, getPathLength: function getPathLength(pathIndex) { var points = pathIndex === 1 ? this.pathPoints2 : this.pathPoints; if (!points) { points = this.pathPoints; // Fallback } var totalLength = 0; for (var i = 0; i < points.length - 1; i++) { var dx = points[i + 1].x - points[i].x; var dy = points[i + 1].y - points[i].y; totalLength += Math.sqrt(dx * dx + dy * dy); } return totalLength; } }; /**** * Game Manager ****/ var GameManager = { topAreaGenerateCount: 0, bottomAreaGenerateCount: 0, findEmptySpot: function findEmptySpot(area) { var diceArray = area === 0 ? topAreaDice : bottomAreaDice; if (area === 0) { // Top area: spawn from top-left for (var y = 0; y < GRID_ROWS; y++) { for (var x = 0; x < GRID_COLS; x++) { if (!diceArray[x][y]) { return { x: x, y: y }; } } } } else { // Bottom area: spawn from bottom-left for (var y = GRID_ROWS - 1; y >= 0; y--) { for (var x = 0; x < GRID_COLS; x++) { if (!diceArray[x][y]) { return { x: x, y: y }; } } } } return null; // No empty spot }, removeDice: function removeDice(dice) { var diceArray = dice.area === 0 ? topAreaDice : bottomAreaDice; var layer = dice.area === 0 ? topAreaLayer : bottomAreaLayer; diceArray[dice.gridX][dice.gridY] = null; layer.removeChild(dice); PoolManager.returnClassDice(dice); }, getDiceAt: function getDiceAt(area, gridX, gridY) { var diceArray = area === 0 ? topAreaDice : bottomAreaDice; return diceArray[gridX] && diceArray[gridX][gridY]; }, getGenerateCount: function getGenerateCount(area) { return area === 0 ? this.topAreaGenerateCount : this.bottomAreaGenerateCount; }, incrementGenerateCount: function incrementGenerateCount(area) { if (area === 0) { this.topAreaGenerateCount++; } else { this.bottomAreaGenerateCount++; } } }; /**** * Game Constants - ACTUALLY USE THE FULL SCREEN ****/ var CELL_SIZE = 320; // Increased cell size var GRID_COLS = 5; var GRID_ROWS = 3; var SCREEN_WIDTH = 2048; var SCREEN_HEIGHT = 2732; // Actually use the full screen dimensions var BUILDING_AREA_WIDTH = GRID_COLS * CELL_SIZE; // 1600px wide var BUILDING_AREA_HEIGHT = GRID_ROWS * CELL_SIZE; // 960px tall // Center the building areas in the screen with proper spacing var TOP_AREA_X = (SCREEN_WIDTH - BUILDING_AREA_WIDTH) / 2; // ~224px from left var TOP_AREA_Y = 324; // Repositioned to match compressed lanes var BOTTOM_AREA_X = TOP_AREA_X; // Same X position var BOTTOM_AREA_Y = TOP_AREA_Y + BUILDING_AREA_HEIGHT + 153; // Adjusted gap to match compressed lanes // Path uses much more of the screen var PATH_MARGIN = 200; // Bigger margins for wider paths /**** * Game Variables ****/ var gold = 100; var lives = 20; var score = 0; var activeBlobs = []; var activeBullets = []; var topAreaDice = []; var bottomAreaDice = []; var selectedDice = null; var isDragging = false; var dragStartPos = { x: 0, y: 0 }; /**** * Initialize Arrays ****/ for (var x = 0; x < GRID_COLS; x++) { topAreaDice[x] = []; bottomAreaDice[x] = []; for (var y = 0; y < GRID_ROWS; y++) { topAreaDice[x][y] = null; bottomAreaDice[x][y] = null; } } /**** * UI Elements ****/ var goldText = new Text2('Gold: ' + gold, { size: 60, fill: 0xFFD700, weight: 800 }); goldText.anchor.set(0.5, 0.5); var livesText = new Text2('Lives: ' + lives, { size: 60, fill: 0x00FF00, weight: 800 }); livesText.anchor.set(0.5, 0.5); var scoreText = new Text2('Score: ' + score, { size: 60, fill: 0xFF0000, weight: 800 }); scoreText.anchor.set(0.5, 0.5); LK.gui.top.addChild(goldText); LK.gui.top.addChild(livesText); LK.gui.top.addChild(scoreText); goldText.x = -400; goldText.y = 50; livesText.x = 0; livesText.y = 50; scoreText.x = 400; scoreText.y = 50; function updateUI() { goldText.setText('Gold: ' + gold); livesText.setText('Lives: ' + lives); scoreText.setText('Score: ' + score); } function setGold(value) { gold = value; updateUI(); } /**** * Layer Setup ****/ var gameLayer = new Container(); var topAreaLayer = new Container(); var bottomAreaLayer = new Container(); var effectLayer = new Container(); game.addChild(gameLayer); game.addChild(topAreaLayer); game.addChild(bottomAreaLayer); game.addChild(effectLayer); /**** * Generate Buttons - PROPERLY POSITIONED ****/ var topGenerateButton = new GenerateButton(0); topGenerateButton.x = TOP_AREA_X + BUILDING_AREA_WIDTH + 100; topGenerateButton.y = TOP_AREA_Y + BUILDING_AREA_HEIGHT / 2; game.addChild(topGenerateButton); var bottomGenerateButton = new GenerateButton(1); bottomGenerateButton.x = BOTTOM_AREA_X + BUILDING_AREA_WIDTH + 100; bottomGenerateButton.y = BOTTOM_AREA_Y + BUILDING_AREA_HEIGHT / 2; game.addChild(bottomGenerateButton); /**** * Wave Manager - BOTTOM OF SCREEN ****/ var waveManager = new WaveManager(); waveManager.x = SCREEN_WIDTH / 2; waveManager.y = SCREEN_HEIGHT - 200; // Actually near bottom of screen game.addChild(waveManager); /**** * Input Handling ****/ game.down = function (x, y, obj) { // Check if clicking on a dice var clickedDice = null; var clickedArea = -1; // Check top area if (x >= TOP_AREA_X && x <= TOP_AREA_X + GRID_COLS * CELL_SIZE && y >= TOP_AREA_Y && y <= TOP_AREA_Y + GRID_ROWS * CELL_SIZE) { var gridX = Math.floor((x - TOP_AREA_X) / CELL_SIZE); var gridY = Math.floor((y - TOP_AREA_Y) / CELL_SIZE); clickedDice = GameManager.getDiceAt(0, gridX, gridY); clickedArea = 0; } // Check bottom area if (!clickedDice && x >= BOTTOM_AREA_X && x <= BOTTOM_AREA_X + GRID_COLS * CELL_SIZE && y >= BOTTOM_AREA_Y && y <= BOTTOM_AREA_Y + GRID_ROWS * CELL_SIZE) { var gridX = Math.floor((x - BOTTOM_AREA_X) / CELL_SIZE); var gridY = Math.floor((y - BOTTOM_AREA_Y) / CELL_SIZE); clickedDice = GameManager.getDiceAt(1, gridX, gridY); clickedArea = 1; } if (clickedDice) { selectedDice = clickedDice; isDragging = true; dragStartPos.x = x; dragStartPos.y = y; selectedDice.isDragging = true; // Bring to front var layer = selectedDice.area === 0 ? topAreaLayer : bottomAreaLayer; layer.removeChild(selectedDice); layer.addChild(selectedDice); } }; game.move = function (x, y, obj) { if (isDragging && selectedDice) { selectedDice.x = selectedDice.x + (x - dragStartPos.x); selectedDice.y = selectedDice.y + (y - dragStartPos.y); dragStartPos.x = x; dragStartPos.y = y; // Check for magnetic attraction var diceArray = selectedDice.area === 0 ? topAreaDice : bottomAreaDice; for (var gx = 0; gx < GRID_COLS; gx++) { for (var gy = 0; gy < GRID_ROWS; gy++) { var otherDice = diceArray[gx][gy]; if (otherDice && otherDice !== selectedDice) { selectedDice.checkMagneticAttraction(otherDice); } } } } }; game.up = function (x, y, obj) { if (isDragging && selectedDice) { isDragging = false; selectedDice.isDragging = false; // Check for merge var mergeHappened = false; var targetArea = -1; // Determine which area we're over if (x >= TOP_AREA_X && x <= TOP_AREA_X + GRID_COLS * CELL_SIZE && y >= TOP_AREA_Y && y <= TOP_AREA_Y + GRID_ROWS * CELL_SIZE) { targetArea = 0; } else if (x >= BOTTOM_AREA_X && x <= BOTTOM_AREA_X + GRID_COLS * CELL_SIZE && y >= BOTTOM_AREA_Y && y <= BOTTOM_AREA_Y + GRID_ROWS * CELL_SIZE) { targetArea = 1; } if (targetArea !== -1) { var gridX = Math.floor((x - (targetArea === 0 ? TOP_AREA_X : BOTTOM_AREA_X)) / CELL_SIZE); var gridY = Math.floor((y - (targetArea === 0 ? TOP_AREA_Y : BOTTOM_AREA_Y)) / CELL_SIZE); if (gridX >= 0 && gridX < GRID_COLS && gridY >= 0 && gridY < GRID_ROWS) { var targetDice = GameManager.getDiceAt(targetArea, gridX, gridY); if (targetDice && selectedDice.canMergeWith(targetDice)) { // Merge the dice mergeHappened = selectedDice.mergeWith(targetDice); } } } if (!mergeHappened) { // Return dice to original position selectedDice.updatePosition(); // Smooth return animation tween(selectedDice, { x: selectedDice.area === 0 ? TOP_AREA_X + selectedDice.gridX * CELL_SIZE + CELL_SIZE / 2 : BOTTOM_AREA_X + selectedDice.gridX * CELL_SIZE + CELL_SIZE / 2, y: selectedDice.area === 0 ? TOP_AREA_Y + selectedDice.gridY * CELL_SIZE + CELL_SIZE / 2 : BOTTOM_AREA_Y + selectedDice.gridY * CELL_SIZE + CELL_SIZE / 2 }, { duration: 300, easing: tween.elasticOut }); } selectedDice.magneticTarget = null; selectedDice = null; } }; /**** * Grid Visual Indicators ****/ function drawGridLines() { // Top area grid for (var x = 0; x <= GRID_COLS; x++) { var line = new Container(); var lineGraphics = line.attachAsset('cell', { anchorX: 0, anchorY: 0 }); lineGraphics.width = 2; lineGraphics.height = GRID_ROWS * CELL_SIZE; lineGraphics.tint = 0x444444; lineGraphics.alpha = 0.3; line.x = TOP_AREA_X + x * CELL_SIZE; line.y = TOP_AREA_Y; gameLayer.addChild(line); } for (var y = 0; y <= GRID_ROWS; y++) { var line = new Container(); var lineGraphics = line.attachAsset('cell', { anchorX: 0, anchorY: 0 }); lineGraphics.width = GRID_COLS * CELL_SIZE; lineGraphics.height = 2; lineGraphics.tint = 0x444444; lineGraphics.alpha = 0.3; line.x = TOP_AREA_X; line.y = TOP_AREA_Y + y * CELL_SIZE; gameLayer.addChild(line); } // Bottom area grid for (var x = 0; x <= GRID_COLS; x++) { var line = new Container(); var lineGraphics = line.attachAsset('cell', { anchorX: 0, anchorY: 0 }); lineGraphics.width = 2; lineGraphics.height = GRID_ROWS * CELL_SIZE; lineGraphics.tint = 0x444444; lineGraphics.alpha = 0.3; line.x = BOTTOM_AREA_X + x * CELL_SIZE; line.y = BOTTOM_AREA_Y; gameLayer.addChild(line); } for (var y = 0; y <= GRID_ROWS; y++) { var line = new Container(); var lineGraphics = line.attachAsset('cell', { anchorX: 0, anchorY: 0 }); lineGraphics.width = GRID_COLS * CELL_SIZE; lineGraphics.height = 2; lineGraphics.tint = 0x444444; lineGraphics.alpha = 0.3; line.x = BOTTOM_AREA_X; line.y = BOTTOM_AREA_Y + y * CELL_SIZE; gameLayer.addChild(line); } } /**** * Path Visualization ****/ function drawPath() { var paths = [PathSystem.pathPoints, PathSystem.pathPoints2]; for (var p = 0; p < paths.length; p++) { var currentPath = paths[p]; for (var i = 0; i < currentPath.length - 1; i++) { var start = currentPath[i]; var end = currentPath[i + 1]; var dx = end.x - start.x; var dy = end.y - start.y; var distance = Math.sqrt(dx * dx + dy * dy); var segments = Math.floor(distance / 20); for (var j = 0; j < segments; j++) { var progress = j / segments; var x = start.x + dx * progress; var y = start.y + dy * progress; var pathDot = new Container(); var dotGraphics = pathDot.attachAsset('particle', { anchorX: 0.5, anchorY: 0.5 }); dotGraphics.width = dotGraphics.height = 8; dotGraphics.tint = 0x666666; dotGraphics.alpha = 0.6; pathDot.x = x; pathDot.y = y; gameLayer.addChild(pathDot); } } } } /**** * Initialization ****/ PoolManager.init(); PathSystem.init(); drawGridLines(); drawPath(); // Start first wave after a delay waveManager.waveTimer = 240; // 4 second delay before first wave /**** * Main Game Loop ****/ game.update = function () { // Update active blobs for (var i = activeBlobs.length - 1; i >= 0; i--) { var blob = activeBlobs[i]; if (blob.active) { blob.update(); } else { activeBlobs.splice(i, 1); gameLayer.removeChild(blob); } } // Update active bullets for (var i = activeBullets.length - 1; i >= 0; i--) { var bullet = activeBullets[i]; if (bullet.active) { bullet.update(); } else { activeBullets.splice(i, 1); gameLayer.removeChild(bullet); } } // Update all dice for (var x = 0; x < GRID_COLS; x++) { for (var y = 0; y < GRID_ROWS; y++) { if (topAreaDice[x][y]) { topAreaDice[x][y].update(); } if (bottomAreaDice[x][y]) { bottomAreaDice[x][y].update(); } } } // Update active particles var allParticles = gameLayer.children.filter(function (child) { return child instanceof DeathParticle; }); for (var i = 0; i < allParticles.length; i++) { allParticles[i].update(); } // Update active effects var allEffects = gameLayer.children.filter(function (child) { return child instanceof EffectIndicator; }); for (var i = 0; i < allEffects.length; i++) { if (allEffects[i] && typeof allEffects[i].update === 'function') { allEffects[i].update(); } } // Update notifications var allNotifications = gameLayer.children.filter(function (child) { return child instanceof Notification; }); for (var i = 0; i < allNotifications.length; i++) { allNotifications[i].update(); } // Update wave manager waveManager.update(); // Update generate buttons topGenerateButton.update(); bottomGenerateButton.update(); // Check win condition if (waveManager.currentWave >= waveManager.maxWaves && activeBlobs.length === 0) { LK.showYouWin(); } // Check lose condition if (lives <= 0) { LK.showGameOver(); } updateUI(); }; /**** * Debug Features (Remove in production) ****/ if (LK.debug) { // Add some starting dice for testing setTimeout(function () { var testDice1 = PoolManager.getClassDice(); if (testDice1) { testDice1.activate('knifethrower', 1, 1, 0); topAreaLayer.addChild(testDice1); topAreaDice[1][1] = testDice1; } var testDice2 = PoolManager.getClassDice(); if (testDice2) { testDice2.activate('archer', 3, 1, 1); bottomAreaLayer.addChild(testDice2); bottomAreaDice[3][1] = testDice2; } }, 1000); } /**** * Performance Monitoring (Optional) ****/ var PerformanceMonitor = { frameCount: 0, lastTime: Date.now(), fps: 60, update: function update() { this.frameCount++; var currentTime = Date.now(); if (currentTime - this.lastTime >= 1000) { this.fps = this.frameCount; this.frameCount = 0; this.lastTime = currentTime; // Log performance warnings if (this.fps < 30) { console.warn('Low FPS detected:', this.fps); console.log('Active objects - Blobs:', activeBlobs.length, 'Bullets:', activeBullets.length); } } } }; // Add performance monitoring to game loop if in debug mode if (LK.debug) { var originalUpdate = game.update; game.update = function () { PerformanceMonitor.update(); originalUpdate.call(this); }; } /**** * Utility Functions ****/ function getDistanceBetweenPoints(p1, p2) { var dx = p2.x - p1.x; var dy = p2.y - p1.y; return Math.sqrt(dx * dx + dy * dy); } function clamp(value, min, max) { return Math.max(min, Math.min(max, value)); } function lerp(start, end, factor) { return start + (end - start) * factor; } // Fibonacci sequence for costs function fibonacci(n) { if (n <= 1) { return 1; } var a = 1, b = 1; for (var i = 2; i <= n; i++) { var temp = a + b; a = b; b = temp; } return b; } /**** * Effects and Polish ****/ function createScreenShake(intensity, duration) { var originalX = game.x; var originalY = game.y; var shakeTimer = duration; var _shakeUpdate = function shakeUpdate() { if (shakeTimer > 0) { game.x = originalX + (Math.random() - 0.5) * intensity; game.y = originalY + (Math.random() - 0.5) * intensity; shakeTimer--; requestAnimationFrame(_shakeUpdate); } else { game.x = originalX; game.y = originalY; } }; _shakeUpdate(); } function createFloatingText(text, x, y, color) { var floatingText = new Text2(text, { size: 40, fill: color || 0xFFFFFF, weight: 800 }); floatingText.anchor.set(0.5, 0.5); floatingText.x = x; floatingText.y = y; floatingText.alpha = 1; gameLayer.addChild(floatingText); tween(floatingText, { y: y - 50, alpha: 0 }, { duration: 1000, easing: tween.quadOut, onFinish: function onFinish() { gameLayer.removeChild(floatingText); } }); } /**** * Game Over and Victory Screens ****/ LK.showGameOver = function () { var overlay = new Container(); var bg = overlay.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5 }); bg.width = SCREEN_WIDTH; bg.height = SCREEN_HEIGHT; bg.tint = 0x000000; bg.alpha = 0.8; var gameOverText = new Text2('GAME OVER', { size: 100, fill: 0xFF0000, weight: 800 }); gameOverText.anchor.set(0.5, 0.5); gameOverText.y = -100; overlay.addChild(gameOverText); var finalScoreText = new Text2('Final Score: ' + score, { size: 60, fill: 0xFFFFFF, weight: 600 }); finalScoreText.anchor.set(0.5, 0.5); finalScoreText.y = 0; overlay.addChild(finalScoreText); var restartText = new Text2('Tap to Restart', { size: 50, fill: 0x00FF00, weight: 600 }); restartText.anchor.set(0.5, 0.5); restartText.y = 100; overlay.addChild(restartText); overlay.x = SCREEN_WIDTH / 2; overlay.y = SCREEN_HEIGHT / 2; game.addChild(overlay); overlay.down = function () { location.reload(); // Simple restart }; }; LK.showYouWin = function () { var overlay = new Container(); var bg = overlay.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5 }); bg.width = SCREEN_WIDTH; bg.height = SCREEN_HEIGHT; bg.tint = 0x000000; bg.alpha = 0.8; var victoryText = new Text2('VICTORY!', { size: 100, fill: 0x00FF00, weight: 800 }); victoryText.anchor.set(0.5, 0.5); victoryText.y = -100; overlay.addChild(victoryText); var finalScoreText = new Text2('Final Score: ' + score, { size: 60, fill: 0xFFFFFF, weight: 600 }); finalScoreText.anchor.set(0.5, 0.5); finalScoreText.y = 0; overlay.addChild(finalScoreText); var restartText = new Text2('Tap to Play Again', { size: 50, fill: 0x00FF00, weight: 600 }); restartText.anchor.set(0.5, 0.5); restartText.y = 100; overlay.addChild(restartText); overlay.x = SCREEN_WIDTH / 2; overlay.y = SCREEN_HEIGHT / 2; game.addChild(overlay); overlay.down = function () { location.reload(); // Simple restart }; // Victory animation tween(victoryText, { scaleX: 1.2, scaleY: 1.2 }, { duration: 500, easing: tween.elasticOut, repeat: -1, yoyo: true }); };
===================================================================
--- original.js
+++ change.js
@@ -1039,9 +1039,9 @@
costText.anchor.set(0.5, 0.5);
self.addChild(costText);
self.updateCost = function () {
// Fibonacci-like progression
- var newCost = Math.floor(10 * Math.pow(1.45, GameManager.getGenerateCount(self.area)));
+ var newCost = Math.floor(10 * Math.pow(1.315, GameManager.getGenerateCount(self.area)));
self.cost = newCost;
costText.setText('Generate: ' + self.cost);
// Update button color based on affordability
if (gold >= self.cost) {
A long rack of different colored poker chips seen from above. Anime style.. In-Game asset. 2d. High contrast. No shadows
A graphic for the center of a joker card.
a 2:3 format thin black border with nothing in the center. In-Game asset. 2d. High contrast. No shadows
A small white explosion particle.. In-Game asset. 2d. High contrast. No shadows
Make the blue a lighter blue.
Make this in a white instead of blue. Keep everything else the same.
A couple different sized stacks of these chips beside each other.
Just the spade from this picture with a blue snowflake in the middle of it.
Just the heart from this picture with a flame in the cent t of it.
Just the club from this picture with 1. **Fan/Spray Symbol** - Three or more lines radiating outward from a central point, yellow in color, in the center of the club.
Just the diamond from this picture with a dollar sign in the center
A white circle with a lightening gradient towards the edge.. Single Game Texture. In-Game asset. 2d. Blank background. High contrast. No shadows
A simple golden line break.. In-Game asset. 2d. High contrast. No shadows
A fanned card hand that shows a royal flush in spades. Anime style. In-Game asset. 2d. High contrast. No shadows
An SVG of the word 'Battle'. text in yellow with a black outline. In-Game asset. 2d. High contrast. No shadows
change the text to say "Mods"
The four card suits arranged in 2x2 grid layout, no lines. Anime style. In-Game asset. 2d. High contrast. No shadows
A single ice crystal. anime style. In-Game asset. 2d. High contrast. No shadows
Change the text to say ‘Refund’. Change the cards to a trash can.
A completely blank playing card with textured surface. Slightly used edges with a couple nicks out of it. Black background. In-Game asset. 2d. High contrast. No shadows
A 3:2 ratio rectangular green button that says “PvP” using this yellow font.
Change the text to say ‘Co-op’
Change the font to say ‘Victory!’
Change the text to say ‘Defeat!’
A 2:3 ratio rectangular picture that shows two card playing cats in a casino very close face to face with teeth bared and fists clenched as if they’re about to fight. Each cat has a different card suit pattern on the fur of their forehead. One is wearing a suit and the other is wearing tan leather jacket with a striped tank top underneath. Anime style.. In-Game asset. 2d. High contrast. No shadows
Show these same cats smiling and instead of clenched fists they’re grasping hands because they’re friends.
Incorporate these two cats heads into a game logo for a poker based tower defense that includes the name “Double Down Defense”. Put their heads offset on either side with eyes open and looking at the logo.
A small treasure chest with poker themed graphics on it. Anime style. In-Game asset. 2d. High contrast. No shadows
The hearts card suit symbol with two linked hearts in the center of it. Anime style.. In-Game asset. 2d. High contrast. No shadows
The diamond card suit with a coin in the center. The coin has a ‘2X’ in the center. Anime style.. In-Game asset. 2d. High contrast. No shadows
Just the club from this picture with a clock in the center.
Just the spade from this image with a land mine in the center of it.
Just the mine from this image.
Just the heart from this image with a piggy bank in the center.
Just the diamond from this picture with a sword with a small arrow pointing up in the center of the diamond.
Just the club from this picture with an icon in the center of it that represents a projectile bouncing at an angle off of a surface.
Just the spade with a skull in the center of it. Anime style.
This chest with the top open and nothing inside.
Change the text to say Shop
An old style cash register. The numeric read out says 7.77. Anime style.. In-Game asset. 2d. High contrast. No shadows
A giant question mark. Anime style.. In-Game asset. 2d. High contrast. No shadows
A shield with a spade and heart card suit coat of arms on it with a sword crossed downwards, behind it. icon. Anime style.. In-Game asset. 2d. High contrast. No shadows
Change the text to say ‘Draw’
The back of a playing card. Blue pattern. Anime style.. In-Game asset. 2d. High contrast. No shadows
The back of a playing card. Red pattern with a heart in the center. Anime style.. In-Game asset. 2d. High contrast. No shadows