/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ /**** * Classes ****/ var Bullet = Container.expand(function (startX, startY, targetEnemy, damage, speed) { var self = Container.call(this); self.targetEnemy = targetEnemy; self.damage = damage || 10; self.speed = speed || 5; self.x = startX; self.y = startY; var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { if (!self.targetEnemy || !self.targetEnemy.parent) { self.destroy(); 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) { self.targetEnemy.health -= self.damage; if (self.targetEnemy.health <= 0) { self.targetEnemy.health = 0; } else { self.targetEnemy.healthBar.width = self.targetEnemy.health / self.targetEnemy.maxHealth * 70; } if (self.type === 'splash') { var splashEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'splash'); game.addChild(splashEffect); var splashRadius = CELL_SIZE * 1.5; for (var i = 0; i < enemies.length; i++) { var otherEnemy = enemies[i]; if (otherEnemy !== self.targetEnemy) { var splashDx = otherEnemy.x - self.targetEnemy.x; var splashDy = otherEnemy.y - self.targetEnemy.y; var splashDistance = Math.sqrt(splashDx * splashDx + splashDy * splashDy); if (splashDistance <= splashRadius) { otherEnemy.health -= self.damage * 0.5; if (otherEnemy.health <= 0) { otherEnemy.health = 0; } else { otherEnemy.healthBar.width = otherEnemy.health / otherEnemy.maxHealth * 70; } } } } } else if (self.type === 'slow') { if (!self.targetEnemy.isImmune) { var slowEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'slow'); game.addChild(slowEffect); var slowPct = 0.5; if (self.sourceTowerLevel !== undefined) { var slowLevels = [0.5, 0.6, 0.65, 0.7, 0.75, 0.8]; var idx = Math.max(0, Math.min(5, self.sourceTowerLevel - 1)); slowPct = slowLevels[idx]; } if (!self.targetEnemy.slowed) { self.targetEnemy.originalSpeed = self.targetEnemy.speed; self.targetEnemy.speed *= 1 - slowPct; self.targetEnemy.slowed = true; self.targetEnemy.slowDuration = 180; } else { self.targetEnemy.slowDuration = 180; } } } else if (self.type === 'poison') { if (!self.targetEnemy.isImmune) { var poisonEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'poison'); game.addChild(poisonEffect); self.targetEnemy.poisoned = true; self.targetEnemy.poisonDamage = self.damage * 0.2; self.targetEnemy.poisonDuration = 300; } } else if (self.type === 'sniper') { var sniperEffect = new EffectIndicator(self.targetEnemy.x, self.targetEnemy.y, 'sniper'); game.addChild(sniperEffect); } self.destroy(); } else { var angle = Math.atan2(dy, dx); self.x += Math.cos(angle) * self.speed; self.y += Math.sin(angle) * self.speed; } }; return self; }); var DebugCell = Container.expand(function () { var self = Container.call(this); var cellGraphics = self.attachAsset('cell', { anchorX: 0.5, anchorY: 0.5 }); cellGraphics.tint = Math.random() * 0xffffff; var debugArrows = []; var numberLabel = new Text2('0', { size: 30, fill: 0xFFFFFF, weight: 800 }); numberLabel.anchor.set(.5, .5); self.addChild(numberLabel); self.update = function () {}; self.down = function () { return; if (self.cell.type == 0 || self.cell.type == 1) { self.cell.type = self.cell.type == 1 ? 0 : 1; if (grid.pathFind()) { self.cell.type = self.cell.type == 1 ? 0 : 1; grid.pathFind(); var notification = game.addChild(new Notification("Path is blocked!")); notification.x = 2048 / 2; notification.y = grid.height - 50; } grid.renderDebug(); } }; self.removeArrows = function () { while (debugArrows.length) { self.removeChild(debugArrows.pop()); } }; self.render = function (data) { switch (data.type) { case 0: case 2: { if (data.pathId != pathId) { self.removeArrows(); numberLabel.setText("-"); cellGraphics.tint = 0x880000; return; } numberLabel.visible = true; var tint = Math.floor(data.score / maxScore * 0x88); var towerInRangeHighlight = false; if (selectedTower && data.towersInRange && data.towersInRange.indexOf(selectedTower) !== -1) { towerInRangeHighlight = true; cellGraphics.tint = 0x0088ff; } else { cellGraphics.tint = 0x88 - tint << 8 | tint; } while (debugArrows.length > data.targets.length) { self.removeChild(debugArrows.pop()); } for (var a = 0; a < data.targets.length; a++) { var destination = data.targets[a]; var ox = destination.x - data.x; var oy = destination.y - data.y; var angle = Math.atan2(oy, ox); if (!debugArrows[a]) { debugArrows[a] = LK.getAsset('arrow', { anchorX: -.5, anchorY: 0.5 }); debugArrows[a].alpha = .5; self.addChildAt(debugArrows[a], 1); } debugArrows[a].rotation = angle; } break; } case 1: { self.removeArrows(); cellGraphics.tint = 0xaaaaaa; numberLabel.visible = false; break; } case 3: { self.removeArrows(); cellGraphics.tint = 0x008800; numberLabel.visible = false; break; } } numberLabel.setText(Math.floor(data.score / 1000) / 10); }; }); var EffectIndicator = Container.expand(function (x, y, type) { var self = Container.call(this); self.x = x; self.y = y; var effectGraphics = self.attachAsset('rangeCircle', { anchorX: 0.5, anchorY: 0.5 }); effectGraphics.blendMode = 1; 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; 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() { self.destroy(); } }); } }); return self; }); var Enemy = Container.expand(function (type) { var self = Container.call(this); self.type = type || 'normal'; self.speed = .01; self.cellX = 0; self.cellY = 0; self.currentCellX = 0; self.currentCellY = 0; self.currentTarget = undefined; self.maxHealth = 100; self.health = self.maxHealth; self.bulletsTargetingThis = []; self.waveNumber = currentWave; self.isFlying = false; self.isImmune = false; self.isBoss = false; switch (self.type) { case 'fast': self.speed *= 2; self.maxHealth = 100; break; case 'immune': self.isImmune = true; self.maxHealth = 80; break; case 'flying': self.isFlying = true; self.maxHealth = 80; break; case 'swarm': self.maxHealth = 50; break; case 'normal': default: break; } if (currentWave % 10 === 0 && currentWave > 0 && type !== 'swarm') { self.isBoss = true; self.maxHealth *= 20; self.speed = self.speed * 0.7; } self.health = self.maxHealth; var assetId = 'enemy'; if (self.type !== 'normal') { assetId = 'enemy_' + self.type; } var enemyGraphics = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); if (self.isBoss) { enemyGraphics.scaleX = 1.8; enemyGraphics.scaleY = 1.8; } if (self.isFlying) { self.shadow = new Container(); var shadowGraphics = self.shadow.attachAsset(assetId || 'enemy', { anchorX: 0.5, anchorY: 0.5 }); shadowGraphics.tint = 0x000000; shadowGraphics.alpha = 0.4; if (self.isBoss) { shadowGraphics.scaleX = 1.8; shadowGraphics.scaleY = 1.8; } self.shadow.x = 20; self.shadow.y = 20; shadowGraphics.rotation = enemyGraphics.rotation; } var healthBarOutline = self.attachAsset('healthBarOutline', { anchorX: 0, anchorY: 0.5 }); var healthBarBG = self.attachAsset('healthBar', { anchorX: 0, anchorY: 0.5 }); var healthBar = self.attachAsset('healthBar', { anchorX: 0, anchorY: 0.5 }); healthBarBG.y = healthBarOutline.y = healthBar.y = -enemyGraphics.height / 2 - 10; healthBarOutline.x = -healthBarOutline.width / 2; healthBarBG.x = healthBar.x = -healthBar.width / 2 - .5; healthBar.tint = 0x00ff00; healthBarBG.tint = 0xff0000; self.healthBar = healthBar; self.update = function () { if (self.health <= 0) { self.health = 0; self.healthBar.width = 0; } if (self.isImmune) { self.slowed = false; self.slowEffect = false; self.poisoned = false; self.poisonEffect = false; if (self.originalSpeed !== undefined) { self.speed = self.originalSpeed; } } else { if (self.slowed) { if (!self.slowEffect) { self.slowEffect = true; } self.slowDuration--; if (self.slowDuration <= 0) { self.speed = self.originalSpeed; self.slowed = false; self.slowEffect = false; if (!self.poisoned) { enemyGraphics.tint = 0xFFFFFF; } } } if (self.poisoned) { if (!self.poisonEffect) { self.poisonEffect = true; } if (LK.ticks % 30 === 0) { self.health -= self.poisonDamage; if (self.health <= 0) { self.health = 0; } self.healthBar.width = self.health / self.maxHealth * 70; } self.poisonDuration--; if (self.poisonDuration <= 0) { self.poisoned = false; self.poisonEffect = false; if (!self.slowed) { enemyGraphics.tint = 0xFFFFFF; } } } } if (self.isImmune) { enemyGraphics.tint = 0xFFFFFF; } else if (self.poisoned && self.slowed) { enemyGraphics.tint = 0x4C7FD4; } else if (self.poisoned) { enemyGraphics.tint = 0x00FFAA; } else if (self.slowed) { enemyGraphics.tint = 0x9900FF; } else { enemyGraphics.tint = 0xFFFFFF; } if (self.currentTarget) { var ox = self.currentTarget.x - self.currentCellX; var oy = self.currentTarget.y - self.currentCellY; if (ox !== 0 || oy !== 0) { var angle = Math.atan2(oy, ox); if (enemyGraphics.targetRotation === undefined) { enemyGraphics.targetRotation = angle; enemyGraphics.rotation = angle; } else { if (Math.abs(angle - enemyGraphics.targetRotation) > 0.05) { tween.stop(enemyGraphics, { rotation: true }); var currentRotation = enemyGraphics.rotation; var angleDiff = angle - currentRotation; while (angleDiff > Math.PI) { angleDiff -= Math.PI * 2; } while (angleDiff < -Math.PI) { angleDiff += Math.PI * 2; } enemyGraphics.targetRotation = angle; tween(enemyGraphics, { rotation: currentRotation + angleDiff }, { duration: 250, easing: tween.easeOut }); } } } } healthBarOutline.y = healthBarBG.y = healthBar.y = -enemyGraphics.height / 2 - 10; }; return self; }); var GoldIndicator = Container.expand(function (value, x, y) { var self = Container.call(this); var shadowText = new Text2("+" + value, { size: 45, fill: 0x000000, weight: 800 }); shadowText.anchor.set(0.5, 0.5); shadowText.x = 2; shadowText.y = 2; self.addChild(shadowText); var goldText = new Text2("+" + value, { size: 45, fill: 0xFFD700, weight: 800 }); goldText.anchor.set(0.5, 0.5); self.addChild(goldText); self.x = x; self.y = y; self.alpha = 0; self.scaleX = 0.5; self.scaleY = 0.5; tween(self, { alpha: 1, scaleX: 1.2, scaleY: 1.2, y: y - 40 }, { duration: 50, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { alpha: 0, scaleX: 1.5, scaleY: 1.5, y: y - 80 }, { duration: 600, easing: tween.easeIn, delay: 800, onFinish: function onFinish() { self.destroy(); } }); } }); return self; }); var Grid = Container.expand(function (gridWidth, gridHeight) { var self = Container.call(this); self.cells = []; self.spawns = []; self.goals = []; for (var i = 0; i < gridWidth; i++) { self.cells[i] = []; for (var j = 0; j < gridHeight; j++) { self.cells[i][j] = { score: 0, pathId: 0, towersInRange: [], specialEffect: null }; } } for (var i = 0; i < gridWidth; i++) { for (var j = 0; j < gridHeight; j++) { var cell = self.cells[i][j]; cell.x = i; cell.y = j; cell.upLeft = self.cells[i - 1] && self.cells[i - 1][j - 1]; cell.up = self.cells[i - 1] && self.cells[i - 1][j]; cell.upRight = self.cells[i - 1] && self.cells[i - 1][j + 1]; cell.left = self.cells[i][j - 1]; cell.right = self.cells[i][j + 1]; cell.downLeft = self.cells[i + 1] && self.cells[i + 1][j - 1]; cell.down = self.cells[i + 1] && self.cells[i + 1][j]; cell.downRight = self.cells[i + 1] && self.cells[i + 1][j + 1]; cell.neighbors = [cell.upLeft, cell.up, cell.upRight, cell.right, cell.downRight, cell.down, cell.downLeft, cell.left]; cell.targets = []; } } self.getCell = function (x, y) { return self.cells[x] && self.cells[x][y]; }; self.pathFind = function () { var before = new Date().getTime(); // Znajdź wszystkie komórki końcowe (goals) self.goals = []; for (var i = 0; i < gridWidth; i++) { for (var j = 0; j < gridHeight; j++) { if (self.cells[i][j].type === 3) { self.goals.push(self.cells[i][j]); } } } var toProcess = self.goals.concat([]); maxScore = 0; pathId += 1; for (var a = 0; a < toProcess.length; a++) { toProcess[a].pathId = pathId; toProcess[a].score = 0; // Reset score for pathfinding } function processNode(node, targetValue, targetNode) { if (node && node.type != 1) { // Typ 1 to ściana if (node.pathId < pathId || targetValue < node.score) { node.targets = [targetNode]; } else if (node.pathId == pathId && targetValue == node.score) { node.targets.push(targetNode); } if (node.pathId < pathId || targetValue < node.score) { node.score = targetValue; if (node.pathId != pathId) { toProcess.push(node); } node.pathId = pathId; if (targetValue > maxScore) { maxScore = targetValue; } } } } while (toProcess.length) { var nodes = toProcess; toProcess = []; for (var a = 0; a < nodes.length; a++) { var node = nodes[a]; var targetScore = node.score + 14142; // Diagonal cost if (node.up && node.left && node.up.type != 1 && node.left.type != 1) { processNode(node.upLeft, targetScore, node); } if (node.up && node.right && node.up.type != 1 && node.right.type != 1) { processNode(node.upRight, targetScore, node); } if (node.down && node.right && node.down.type != 1 && node.right.type != 1) { processNode(node.downRight, targetScore, node); } if (node.down && node.left && node.down.type != 1 && node.left.type != 1) { processNode(node.downLeft, targetScore, node); } targetScore = node.score + 10000; // Straight cost processNode(node.up, targetScore, node); processNode(node.right, targetScore, node); processNode(node.down, targetScore, node); processNode(node.left, targetScore, node); } } self.spawns = []; for (var i = 0; i < gridWidth; i++) { for (var j = 0; j < gridHeight; j++) { if (self.cells[i][j].type === 2) { self.spawns.push(self.cells[i][j]); } } } for (var a = 0; a < self.spawns.length; a++) { if (self.spawns[a].pathId != pathId) { console.warn("Spawn blocked"); return true; } } for (var a = 0; a < enemies.length; a++) { var enemy = enemies[a]; if (enemy.isFlying) { continue; } var target = self.getCell(enemy.cellX, enemy.cellY); if (enemy.currentTarget) { if (enemy.currentTarget.pathId != pathId) { if (!target || target.pathId != pathId) { console.warn("Enemy blocked 1 "); return true; } } } else if (!target || target.pathId != pathId) { console.warn("Enemy blocked 2"); return true; } } console.log("Pathfinding complete in:", new Date().getTime() - before, "ms"); }; self.renderDebug = function () { // Ta funkcja może pozostać pusta lub zostać usunięta, // bo nie używamy już starego debugowania siatki }; self.updateEnemy = function (enemy) { var cell = grid.getCell(enemy.cellX, enemy.cellY); if (cell && cell.type == 3) { return true; } if (enemy.isFlying && enemy.shadow) { enemy.shadow.x = enemy.x + 20; enemy.shadow.y = enemy.y + 20; if (enemy.children[0] && enemy.shadow.children[0]) { enemy.shadow.children[0].rotation = enemy.children[0].rotation; } } // Prosta logika startu wroga - idzie prosto w dół do pierwszej komórki ścieżki if (enemy.currentCellY < 5) { enemy.currentCellY += enemy.speed; enemy.x = grid.x + enemy.currentCellX * CELL_SIZE; enemy.y = grid.y + enemy.currentCellY * CELL_SIZE; if (enemy.currentCellY >= 5) { enemy.cellX = Math.round(enemy.currentCellX); enemy.cellY = Math.round(enemy.currentCellY); } return false; } if (enemy.isFlying) { if (!enemy.flyingTarget) { enemy.flyingTarget = self.goals[Math.floor(Math.random() * self.goals.length)]; } var ox = enemy.flyingTarget.x - enemy.currentCellX; var oy = enemy.flyingTarget.y - enemy.currentCellY; var dist = Math.sqrt(ox * ox + oy * oy); if (dist < enemy.speed) { return true; } var angle = Math.atan2(oy, ox); enemy.currentCellX += Math.cos(angle) * enemy.speed; enemy.currentCellY += Math.sin(angle) * enemy.speed; enemy.x = grid.x + enemy.currentCellX * CELL_SIZE; enemy.y = grid.y + enemy.currentCellY * CELL_SIZE; enemy.cellX = Math.round(enemy.currentCellX); enemy.cellY = Math.round(enemy.currentCellY); return false; } if (!enemy.currentTarget) { if (cell) { enemy.currentTarget = cell.targets[Math.floor(Math.random() * cell.targets.length)]; } } if (enemy.currentTarget) { var ox = enemy.currentTarget.x - enemy.currentCellX; var oy = enemy.currentTarget.y - enemy.currentCellY; var dist = Math.sqrt(ox * ox + oy * oy); if (dist < enemy.speed) { enemy.currentCellX = enemy.currentTarget.x; enemy.currentCellY = enemy.currentTarget.y; enemy.cellX = Math.round(enemy.currentCellX); enemy.cellY = Math.round(enemy.currentCellY); enemy.currentTarget = undefined; return; } var angle = Math.atan2(oy, ox); enemy.currentCellX += Math.cos(angle) * enemy.speed; enemy.currentCellY += Math.sin(angle) * enemy.speed; } enemy.x = grid.x + enemy.currentCellX * CELL_SIZE; enemy.y = grid.y + enemy.currentCellY * CELL_SIZE; }; }); 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 = 120; self.update = function () { if (fadeOutTime > 0) { fadeOutTime--; self.alpha = Math.min(fadeOutTime / 120 * 2, 1); } else { self.destroy(); } }; return self; }); var SourceTower = Container.expand(function (towerType) { var self = Container.call(this); self.towerType = towerType || 'default'; var baseGraphics = self.attachAsset('tower', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.3, scaleY: 1.3 }); switch (self.towerType) { case 'rapid': baseGraphics.tint = 0x00AAFF; break; case 'sniper': baseGraphics.tint = 0xFF5500; break; case 'splash': baseGraphics.tint = 0x33CC00; break; case 'slow': baseGraphics.tint = 0x9900FF; break; case 'poison': baseGraphics.tint = 0x00FFAA; break; default: baseGraphics.tint = 0xAAAAAA; } var towerCost = getTowerCost(self.towerType); var typeLabelShadow = new Text2(self.towerType.charAt(0).toUpperCase() + self.towerType.slice(1), { size: 50, fill: 0x000000, weight: 800 }); typeLabelShadow.anchor.set(0.5, 0.5); typeLabelShadow.x = 4; typeLabelShadow.y = -20 + 4; self.addChild(typeLabelShadow); var typeLabel = new Text2(self.towerType.charAt(0).toUpperCase() + self.towerType.slice(1), { size: 50, fill: 0xFFFFFF, weight: 800 }); typeLabel.anchor.set(0.5, 0.5); typeLabel.y = -20; self.addChild(typeLabel); var costLabelShadow = new Text2(towerCost, { size: 50, fill: 0x000000, weight: 800 }); costLabelShadow.anchor.set(0.5, 0.5); costLabelShadow.x = 4; costLabelShadow.y = 24 + 12; self.addChild(costLabelShadow); var costLabel = new Text2(towerCost, { size: 50, fill: 0xFFD700, weight: 800 }); costLabel.anchor.set(0.5, 0.5); costLabel.y = 20 + 12; self.addChild(costLabel); self.update = function () { var canAfford = gold >= getTowerCost(self.towerType); self.alpha = canAfford ? 1 : 0.5; }; return self; }); var Tower = Container.expand(function (id) { var self = Container.call(this); self.id = id || 'default'; self.level = 1; self.maxLevel = 6; self.gridX = 0; self.gridY = 0; self.range = 3 * CELL_SIZE; self.getRange = function () { switch (self.id) { case 'sniper': if (self.level === self.maxLevel) { return 12 * CELL_SIZE; } return (5 + (self.level - 1) * 0.8) * CELL_SIZE; case 'splash': return (2 + (self.level - 1) * 0.2) * CELL_SIZE; case 'rapid': return (2.5 + (self.level - 1) * 0.5) * CELL_SIZE; case 'slow': return (3.5 + (self.level - 1) * 0.5) * CELL_SIZE; case 'poison': return (3.2 + (self.level - 1) * 0.5) * CELL_SIZE; default: return (3 + (self.level - 1) * 0.5) * CELL_SIZE; } }; self.cellsInRange = []; self.fireRate = 60; self.bulletSpeed = 5; self.damage = 10; self.lastFired = 0; self.targetEnemy = null; switch (self.id) { case 'rapid': self.fireRate = 30; self.damage = 5; self.range = 2.5 * CELL_SIZE; self.bulletSpeed = 7; break; case 'sniper': self.fireRate = 90; self.damage = 25; self.range = 5 * CELL_SIZE; self.bulletSpeed = 25; break; case 'splash': self.fireRate = 75; self.damage = 15; self.range = 2 * CELL_SIZE; self.bulletSpeed = 4; break; case 'slow': self.fireRate = 50; self.damage = 8; self.range = 3.5 * CELL_SIZE; self.bulletSpeed = 5; break; case 'poison': self.fireRate = 70; self.damage = 12; self.range = 3.2 * CELL_SIZE; self.bulletSpeed = 5; break; } var baseGraphics = self.attachAsset('tower', { anchorX: 0.5, anchorY: 0.5 }); switch (self.id) { case 'rapid': baseGraphics.tint = 0x00AAFF; break; case 'sniper': baseGraphics.tint = 0xFF5500; break; case 'splash': baseGraphics.tint = 0x33CC00; break; case 'slow': baseGraphics.tint = 0x9900FF; break; case 'poison': baseGraphics.tint = 0x00FFAA; break; default: baseGraphics.tint = 0xAAAAAA; } var levelIndicators = []; var maxDots = self.maxLevel; var dotSpacing = baseGraphics.width / (maxDots + 1); var dotSize = CELL_SIZE / 6; for (var i = 0; i < maxDots; i++) { var dot = new Container(); var outlineCircle = dot.attachAsset('towerLevelIndicator', { anchorX: 0.5, anchorY: 0.5 }); outlineCircle.width = dotSize + 4; outlineCircle.height = dotSize + 4; outlineCircle.tint = 0x000000; var towerLevelIndicator = dot.attachAsset('towerLevelIndicator', { anchorX: 0.5, anchorY: 0.5 }); towerLevelIndicator.width = dotSize; towerLevelIndicator.height = dotSize; towerLevelIndicator.tint = 0xCCCCCC; dot.x = -CELL_SIZE + dotSpacing * (i + 1); dot.y = CELL_SIZE * 0.7; self.addChild(dot); levelIndicators.push(dot); } var gunContainer = new Container(); self.addChild(gunContainer); var gunGraphics = gunContainer.attachAsset('defense', { anchorX: 0.5, anchorY: 0.5 }); self.updateLevelIndicators = function () { for (var i = 0; i < maxDots; i++) { var dot = levelIndicators[i]; var towerLevelIndicator = dot.children[1]; if (i < self.level) { towerLevelIndicator.tint = 0xFFFFFF; } else { switch (self.id) { case 'rapid': towerLevelIndicator.tint = 0x00AAFF; break; case 'sniper': towerLevelIndicator.tint = 0xFF5500; break; case 'splash': towerLevelIndicator.tint = 0x33CC00; break; case 'slow': towerLevelIndicator.tint = 0x9900FF; break; case 'poison': towerLevelIndicator.tint = 0x00FFAA; break; default: towerLevelIndicator.tint = 0xAAAAAA; } } } }; self.updateLevelIndicators(); self.refreshCellsInRange = function () { for (var i = 0; i < self.cellsInRange.length; i++) { var cell = self.cellsInRange[i]; var towerIndex = cell.towersInRange.indexOf(self); if (towerIndex !== -1) { cell.towersInRange.splice(towerIndex, 1); } } self.cellsInRange = []; var rangeRadius = self.getRange() / CELL_SIZE; var centerX = self.gridX + 1; var centerY = self.gridY + 1; var minI = Math.floor(centerX - rangeRadius - 0.5); var maxI = Math.ceil(centerX + rangeRadius + 0.5); var minJ = Math.floor(centerY - rangeRadius - 0.5); var maxJ = Math.ceil(centerY + rangeRadius + 0.5); for (var i = minI; i <= maxI; i++) { for (var j = minJ; j <= maxJ; j++) { var closestX = Math.max(i, Math.min(centerX, i + 1)); var closestY = Math.max(j, Math.min(centerY, j + 1)); var deltaX = closestX - centerX; var deltaY = closestY - centerY; var distanceSquared = deltaX * deltaX + deltaY * deltaY; if (distanceSquared <= rangeRadius * rangeRadius) { var cell = grid.getCell(i, j); if (cell) { self.cellsInRange.push(cell); cell.towersInRange.push(self); } } } } grid.renderDebug(); }; self.getTotalValue = function () { var baseTowerCost = getTowerCost(self.id); var totalInvestment = baseTowerCost; var baseUpgradeCost = baseTowerCost; for (var i = 1; i < self.level; i++) { totalInvestment += Math.floor(baseUpgradeCost * Math.pow(2, i - 1)); } return totalInvestment; }; self.upgrade = function () { if (self.level < self.maxLevel) { var baseUpgradeCost = getTowerCost(self.id); var upgradeCost; if (self.level === self.maxLevel - 1) { upgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.level - 1) * 3.5 / 2); } else { upgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.level - 1)); } if (gold >= upgradeCost) { setGold(gold - upgradeCost); self.level++; if (self.id === 'rapid') { if (self.level === self.maxLevel) { self.fireRate = Math.max(4, 30 - self.level * 9); self.damage = 5 + self.level * 10; self.bulletSpeed = 7 + self.level * 2.4; } else { self.fireRate = Math.max(15, 30 - self.level * 3); self.damage = 5 + self.level * 3; self.bulletSpeed = 7 + self.level * 0.7; } } else { if (self.level === self.maxLevel) { self.fireRate = Math.max(5, 60 - self.level * 24); self.damage = 10 + self.level * 20; self.bulletSpeed = 5 + self.level * 2.4; } else { self.fireRate = Math.max(20, 60 - self.level * 8); self.damage = 10 + self.level * 5; self.bulletSpeed = 5 + self.level * 0.5; } } self.refreshCellsInRange(); self.updateLevelIndicators(); if (self.level > 1) { var levelDot = levelIndicators[self.level - 1].children[1]; tween(levelDot, { scaleX: 1.5, scaleY: 1.5 }, { duration: 300, easing: tween.elasticOut, onFinish: function onFinish() { tween(levelDot, { scaleX: 1, scaleY: 1 }, { duration: 200, easing: tween.easeOut }); } }); } return true; } else { var notification = game.addChild(new Notification("Not enough gold to upgrade!")); notification.x = 2048 / 2; notification.y = grid.height - 50; return false; } } return false; }; self.findTarget = function () { var closestEnemy = null; var closestScore = Infinity; 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 <= self.getRange()) { if (enemy.isFlying) { if (enemy.flyingTarget) { var goalX = enemy.flyingTarget.x; var goalY = enemy.flyingTarget.y; var distToGoal = Math.sqrt((goalX - enemy.cellX) * (goalX - enemy.cellX) + (goalY - enemy.cellY) * (goalY - enemy.cellY)); if (distToGoal < closestScore) { closestScore = distToGoal; closestEnemy = enemy; } } else { if (distance < closestScore) { closestScore = distance; closestEnemy = enemy; } } } else { var cell = grid.getCell(enemy.cellX, enemy.cellY); if (cell && cell.pathId === pathId) { if (cell.score < closestScore) { closestScore = cell.score; closestEnemy = enemy; } } } } } if (!closestEnemy) { self.targetEnemy = null; } return closestEnemy; }; self.update = function () { self.targetEnemy = self.findTarget(); if (self.targetEnemy) { var dx = self.targetEnemy.x - self.x; var dy = self.targetEnemy.y - self.y; var angle = Math.atan2(dy, dx); gunContainer.rotation = angle; if (LK.ticks - self.lastFired >= self.fireRate) { self.fire(); self.lastFired = LK.ticks; } } }; self.down = function (x, y, obj) { var existingMenus = game.children.filter(function (child) { return child instanceof UpgradeMenu; }); var hasOwnMenu = false; var rangeCircle = null; for (var i = 0; i < game.children.length; i++) { if (game.children[i].isTowerRange && game.children[i].tower === self) { rangeCircle = game.children[i]; break; } } for (var i = 0; i < existingMenus.length; i++) { if (existingMenus[i].tower === self) { hasOwnMenu = true; break; } } if (hasOwnMenu) { for (var i = 0; i < existingMenus.length; i++) { if (existingMenus[i].tower === self) { hideUpgradeMenu(existingMenus[i]); } } if (rangeCircle) { game.removeChild(rangeCircle); } selectedTower = null; grid.renderDebug(); return; } for (var i = 0; i < existingMenus.length; i++) { existingMenus[i].destroy(); } for (var i = game.children.length - 1; i >= 0; i--) { if (game.children[i].isTowerRange) { game.removeChild(game.children[i]); } } selectedTower = self; var rangeIndicator = new Container(); rangeIndicator.isTowerRange = true; rangeIndicator.tower = self; game.addChild(rangeIndicator); rangeIndicator.x = self.x; rangeIndicator.y = self.y; var rangeGraphics = rangeIndicator.attachAsset('rangeCircle', { anchorX: 0.5, anchorY: 0.5 }); rangeGraphics.width = rangeGraphics.height = self.getRange() * 2; rangeGraphics.alpha = 0.3; var upgradeMenu = new UpgradeMenu(self); game.addChild(upgradeMenu); upgradeMenu.x = 2048 / 2; tween(upgradeMenu, { y: 2732 - 225 }, { duration: 200, easing: tween.backOut }); grid.renderDebug(); }; self.isInRange = function (enemy) { if (!enemy) { return false; } var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); return distance <= self.getRange(); }; self.fire = function () { if (self.targetEnemy) { var potentialDamage = 0; for (var i = 0; i < self.targetEnemy.bulletsTargetingThis.length; i++) { potentialDamage += self.targetEnemy.bulletsTargetingThis[i].damage; } if (self.targetEnemy.health > potentialDamage) { var bulletX = self.x + Math.cos(gunContainer.rotation) * 40; var bulletY = self.y + Math.sin(gunContainer.rotation) * 40; var bullet = new Bullet(bulletX, bulletY, self.targetEnemy, self.damage, self.bulletSpeed); bullet.type = self.id; if (self.id === 'slow') { bullet.sourceTowerLevel = self.level; } switch (self.id) { case 'rapid': bullet.children[0].tint = 0x00AAFF; bullet.children[0].width = 20; bullet.children[0].height = 20; break; case 'sniper': bullet.children[0].tint = 0xFF5500; bullet.children[0].width = 15; bullet.children[0].height = 15; break; case 'splash': bullet.children[0].tint = 0x33CC00; bullet.children[0].width = 40; bullet.children[0].height = 40; break; case 'slow': bullet.children[0].tint = 0x9900FF; bullet.children[0].width = 35; bullet.children[0].height = 35; break; case 'poison': bullet.children[0].tint = 0x00FFAA; bullet.children[0].width = 35; bullet.children[0].height = 35; break; } game.addChild(bullet); bullets.push(bullet); self.targetEnemy.bulletsTargetingThis.push(bullet); tween.stop(gunContainer, { x: true, y: true, scaleX: true, scaleY: true }); if (gunContainer._restX === undefined) { gunContainer._restX = 0; } if (gunContainer._restY === undefined) { gunContainer._restY = 0; } if (gunContainer._restScaleX === undefined) { gunContainer._restScaleX = 1; } if (gunContainer._restScaleY === undefined) { gunContainer._restScaleY = 1; } gunContainer.x = gunContainer._restX; gunContainer.y = gunContainer._restY; gunContainer.scaleX = gunContainer._restScaleX; gunContainer.scaleY = gunContainer._restScaleY; var recoilDistance = 8; var recoilX = -Math.cos(gunContainer.rotation) * recoilDistance; var recoilY = -Math.sin(gunContainer.rotation) * recoilDistance; tween(gunContainer, { x: gunContainer._restX + recoilX, y: gunContainer._restY + recoilY }, { duration: 60, easing: tween.cubicOut, onFinish: function onFinish() { tween(gunContainer, { x: gunContainer._restX, y: gunContainer._restY }, { duration: 90, easing: tween.cubicIn }); } }); } } }; self.placeOnGrid = function (gridX, gridY) { self.gridX = gridX; self.gridY = gridY; self.x = grid.x + gridX * CELL_SIZE + CELL_SIZE / 2; self.y = grid.y + gridY * CELL_SIZE + CELL_SIZE / 2; for (var i = 0; i < 2; i++) { for (var j = 0; j < 2; j++) { var cell = grid.getCell(gridX + i, gridY + j); if (cell) { cell.type = 1; } } } self.refreshCellsInRange(); }; return self; }); var TowerPreview = Container.expand(function () { var self = Container.call(this); var towerRange = 3; var rangeInPixels = towerRange * CELL_SIZE; self.towerType = 'default'; self.hasEnoughGold = true; var rangeIndicator = new Container(); self.addChild(rangeIndicator); var rangeGraphics = rangeIndicator.attachAsset('rangeCircle', { anchorX: 0.5, anchorY: 0.5 }); rangeGraphics.alpha = 0.3; var previewGraphics = self.attachAsset('towerpreview', { anchorX: 0.5, anchorY: 0.5 }); previewGraphics.width = CELL_SIZE * 2; previewGraphics.height = CELL_SIZE * 2; self.canPlace = false; self.gridX = 0; self.gridY = 0; self.blockedByEnemy = false; self.update = function () { var previousHasEnoughGold = self.hasEnoughGold; self.hasEnoughGold = gold >= getTowerCost(self.towerType); if (previousHasEnoughGold !== self.hasEnoughGold) { self.updateAppearance(); } }; self.updateAppearance = function () { var tempTower = new Tower(self.towerType); var previewRange = tempTower.getRange(); if (tempTower && tempTower.destroy) { tempTower.destroy(); } rangeGraphics.width = rangeGraphics.height = previewRange * 2; switch (self.towerType) { case 'rapid': previewGraphics.tint = 0x00AAFF; break; case 'sniper': previewGraphics.tint = 0xFF5500; break; case 'splash': previewGraphics.tint = 0x33CC00; break; case 'slow': previewGraphics.tint = 0x9900FF; break; case 'poison': previewGraphics.tint = 0x00FFAA; break; default: previewGraphics.tint = 0xAAAAAA; } if (!self.canPlace || !self.hasEnoughGold) { previewGraphics.tint = 0xFF0000; } }; self.updatePlacementStatus = function () { var validGridPlacement = true; if (self.gridY <= 4 || self.gridY + 1 >= grid.cells[0].length - 4) { validGridPlacement = false; } else { for (var i = 0; i < 2; i++) { for (var j = 0; j < 2; j++) { var cell = grid.getCell(self.gridX + i, self.gridY + j); if (!cell || cell.type !== 0) { validGridPlacement = false; break; } } if (!validGridPlacement) { break; } } } self.blockedByEnemy = false; if (validGridPlacement) { for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy.currentCellY < 4) { continue; } if (!enemy.isFlying) { if (enemy.cellX >= self.gridX && enemy.cellX < self.gridX + 2 && enemy.cellY >= self.gridY && enemy.cellY < self.gridY + 2) { self.blockedByEnemy = true; break; } if (enemy.currentTarget) { var targetX = enemy.currentTarget.x; var targetY = enemy.currentTarget.y; if (targetX >= self.gridX && targetX < self.gridX + 2 && targetY >= self.gridY && targetY < self.gridY + 2) { self.blockedByEnemy = true; break; } } } } } self.canPlace = validGridPlacement && !self.blockedByEnemy; self.hasEnoughGold = gold >= getTowerCost(self.towerType); self.updateAppearance(); }; self.checkPlacement = function () { self.updatePlacementStatus(); }; self.snapToGrid = function (x, y) { var gridPosX = x - grid.x; var gridPosY = y - grid.y; self.gridX = Math.floor(gridPosX / CELL_SIZE); self.gridY = Math.floor(gridPosY / CELL_SIZE); self.x = grid.x + self.gridX * CELL_SIZE + CELL_SIZE / 2; self.y = grid.y + self.gridY * CELL_SIZE + CELL_SIZE / 2; self.checkPlacement(); }; return self; }); var UpgradeMenu = Container.expand(function (tower) { var self = Container.call(this); self.tower = tower; self.y = 2732 + 225; var menuBackground = self.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5 }); menuBackground.width = 2048; menuBackground.height = 500; menuBackground.tint = 0x444444; menuBackground.alpha = 0.9; var towerTypeText = new Text2(self.tower.id.charAt(0).toUpperCase() + self.tower.id.slice(1) + ' Tower', { size: 80, fill: 0xFFFFFF, weight: 800 }); towerTypeText.anchor.set(0, 0); towerTypeText.x = -840; towerTypeText.y = -160; self.addChild(towerTypeText); var statsText = new Text2('Level: ' + self.tower.level + '/' + self.tower.maxLevel + '\nDamage: ' + self.tower.damage + '\nFire Rate: ' + (60 / self.tower.fireRate).toFixed(1) + '/s', { size: 70, fill: 0xFFFFFF, weight: 400 }); statsText.anchor.set(0, 0.5); statsText.x = -840; statsText.y = 50; self.addChild(statsText); var buttonsContainer = new Container(); buttonsContainer.x = 500; self.addChild(buttonsContainer); var upgradeButton = new Container(); buttonsContainer.addChild(upgradeButton); var buttonBackground = upgradeButton.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5 }); buttonBackground.width = 500; buttonBackground.height = 150; var isMaxLevel = self.tower.level >= self.tower.maxLevel; var baseUpgradeCost = getTowerCost(self.tower.id); var upgradeCost; if (isMaxLevel) { upgradeCost = 0; } else if (self.tower.level === self.tower.maxLevel - 1) { upgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.tower.level - 1) * 3.5 / 2); } else { upgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.tower.level - 1)); } buttonBackground.tint = isMaxLevel ? 0x888888 : gold >= upgradeCost ? 0x00AA00 : 0x888888; var buttonText = new Text2(isMaxLevel ? 'Max Level' : 'Upgrade: ' + upgradeCost + ' gold', { size: 60, fill: 0xFFFFFF, weight: 800 }); buttonText.anchor.set(0.5, 0.5); upgradeButton.addChild(buttonText); var sellButton = new Container(); buttonsContainer.addChild(sellButton); var sellButtonBackground = sellButton.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5 }); sellButtonBackground.width = 500; sellButtonBackground.height = 150; sellButtonBackground.tint = 0xCC0000; var totalInvestment = self.tower.getTotalValue ? self.tower.getTotalValue() : 0; var sellValue = getTowerSellValue(totalInvestment); var sellButtonText = new Text2('Sell: +' + sellValue + ' gold', { size: 60, fill: 0xFFFFFF, weight: 800 }); sellButtonText.anchor.set(0.5, 0.5); sellButton.addChild(sellButtonText); upgradeButton.y = -85; sellButton.y = 85; var closeButton = new Container(); self.addChild(closeButton); var closeBackground = closeButton.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5 }); closeBackground.width = 90; closeBackground.height = 90; closeBackground.tint = 0xAA0000; var closeText = new Text2('X', { size: 68, fill: 0xFFFFFF, weight: 800 }); closeText.anchor.set(0.5, 0.5); closeButton.addChild(closeText); closeButton.x = menuBackground.width / 2 - 57; closeButton.y = -menuBackground.height / 2 + 57; upgradeButton.down = function (x, y, obj) { if (self.tower.level >= self.tower.maxLevel) { var notification = game.addChild(new Notification("Tower is already at max level!")); notification.x = 2048 / 2; notification.y = grid.height - 50; return; } if (self.tower.upgrade()) { var baseUpgradeCost = getTowerCost(self.tower.id); if (self.tower.level >= self.tower.maxLevel) { upgradeCost = 0; } else if (self.tower.level === self.tower.maxLevel - 1) { upgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.tower.level - 1) * 3.5 / 2); } else { upgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.tower.level - 1)); } statsText.setText('Level: ' + self.tower.level + '/' + self.tower.maxLevel + '\nDamage: ' + self.tower.damage + '\nFire Rate: ' + (60 / self.tower.fireRate).toFixed(1) + '/s'); buttonText.setText('Upgrade: ' + upgradeCost + ' gold'); var totalInvestment = self.tower.getTotalValue ? self.tower.getTotalValue() : 0; var sellValue = Math.floor(totalInvestment * 0.6); sellButtonText.setText('Sell: +' + sellValue + ' gold'); if (self.tower.level >= self.tower.maxLevel) { buttonBackground.tint = 0x888888; buttonText.setText('Max Level'); } var rangeCircle = null; for (var i = 0; i < game.children.length; i++) { if (game.children[i].isTowerRange && game.children[i].tower === self.tower) { rangeCircle = game.children[i]; break; } } if (rangeCircle) { var rangeGraphics = rangeCircle.children[0]; rangeGraphics.width = rangeGraphics.height = self.tower.getRange() * 2; } else { var newRangeIndicator = new Container(); newRangeIndicator.isTowerRange = true; newRangeIndicator.tower = self.tower; game.addChildAt(newRangeIndicator, 0); newRangeIndicator.x = self.tower.x; newRangeIndicator.y = self.tower.y; var rangeGraphics = newRangeIndicator.attachAsset('rangeCircle', { anchorX: 0.5, anchorY: 0.5 }); rangeGraphics.width = rangeGraphics.height = self.tower.getRange() * 2; rangeGraphics.alpha = 0.3; } tween(self, { scaleX: 1.05, scaleY: 1.05 }, { duration: 100, easing: tween.easeOut, onFinish: function onFinish() { tween(self, { scaleX: 1, scaleY: 1 }, { duration: 100, easing: tween.easeIn }); } }); } }; sellButton.down = function (x, y, obj) { var totalInvestment = self.tower.getTotalValue ? self.tower.getTotalValue() : 0; var sellValue = getTowerSellValue(totalInvestment); setGold(gold + sellValue); var notification = game.addChild(new Notification("Tower sold for " + sellValue + " gold!")); notification.x = 2048 / 2; notification.y = grid.height - 50; var gridX = self.tower.gridX; var gridY = self.tower.gridY; for (var i = 0; i < 2; i++) { for (var j = 0; j < 2; j++) { var cell = grid.getCell(gridX + i, gridY + j); if (cell) { cell.type = 0; var towerIndex = cell.towersInRange.indexOf(self.tower); if (towerIndex !== -1) { cell.towersInRange.splice(towerIndex, 1); } } } } if (selectedTower === self.tower) { selectedTower = null; } var towerIndex = towers.indexOf(self.tower); if (towerIndex !== -1) { towers.splice(towerIndex, 1); } towerLayer.removeChild(self.tower); grid.pathFind(); grid.renderDebug(); self.destroy(); for (var i = 0; i < game.children.length; i++) { if (game.children[i].isTowerRange && game.children[i].tower === self.tower) { game.removeChild(game.children[i]); break; } } }; closeButton.down = function (x, y, obj) { hideUpgradeMenu(self); selectedTower = null; grid.renderDebug(); }; self.update = function () { if (self.tower.level >= self.tower.maxLevel) { if (buttonText.text !== 'Max Level') { buttonText.setText('Max Level'); buttonBackground.tint = 0x888888; } return; } var baseUpgradeCost = getTowerCost(self.tower.id); var currentUpgradeCost; if (self.tower.level >= self.tower.maxLevel) { currentUpgradeCost = 0; } else if (self.tower.level === self.tower.maxLevel - 1) { currentUpgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.tower.level - 1) * 3.5 / 2); } else { currentUpgradeCost = Math.floor(baseUpgradeCost * Math.pow(2, self.tower.level - 1)); } var canAfford = gold >= currentUpgradeCost; buttonBackground.tint = canAfford ? 0x00AA00 : 0x888888; var newText = 'Upgrade: ' + currentUpgradeCost + ' gold'; if (buttonText.text !== newText) { buttonText.setText(newText); } }; return self; }); /**** * Initialize Game ****/ /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x333333 }); /**** * Game Code ****/ /**** * Game Code ****/ var CELL_SIZE = 76; // --- GŁÓWNE ZMIENNE STANU GRY --- var pathId; var maxScore; var enemies; var towers; var bullets; var selectedTower; var gold; var lives; var enemiesKilled; var enemiesInCurrentWave; var levelData; var currentWave; var totalWaves; var waveTimer; var waveInProgress; var waveSpawned; var nextWaveTime; var sourceTowers; var grid; var wavesText; var towerLayer; var enemyLayer; var debugLayer; var specialTilesLayer; var enemyLayerBottom; var enemyLayerMiddle; var enemyLayerTop; var towerPreview; var buildPanelContainer; var buildButton; var isBuildPanelOpen; // --- GŁÓWNE ELEMENTY UI --- var goldText = new Text2('Gold: ' + 80, { size: 60, fill: 0xFFD700, weight: 800 }); goldText.anchor.set(0.5, 0.5); var livesText = new Text2('Lives: ' + 20, { size: 60, fill: 0x00FF00, weight: 800 }); livesText.anchor.set(0.5, 0.5); var enemiesText = new Text2('Enemies: 0/0', { size: 60, fill: 0xFF0000, weight: 800 }); enemiesText.anchor.set(0.5, 0.5); var topMargin = 50; var centerX = 2048 / 2; var spacing = 400; LK.gui.top.addChild(goldText); LK.gui.top.addChild(livesText); LK.gui.top.addChild(enemiesText); livesText.x = 0; livesText.y = topMargin; goldText.x = -spacing; goldText.y = topMargin; enemiesText.x = spacing; enemiesText.y = topMargin; // --- ZARZĄDZANIE STANAMI GRY --- var currentScreenState = ''; var titleScreenContainer = null; var levelSelectContainer = null; // --- GLOBALNE FUNKCJE POMOCNICZE --- var isHidingUpgradeMenu = false; function hideUpgradeMenu(menu) { if (isHidingUpgradeMenu) { return; } isHidingUpgradeMenu = true; tween(menu, { y: 2732 + 225 }, { duration: 150, easing: tween.easeIn, onFinish: function onFinish() { menu.destroy(); isHidingUpgradeMenu = false; } }); } function updateUI() { goldText.setText('Gold: ' + gold); livesText.setText('Lives: ' + lives); enemiesText.setText('Enemies: ' + enemiesKilled + '/' + enemiesInCurrentWave); if (wavesText) { wavesText.setText('Wave: ' + currentWave + '/' + totalWaves); } } function setGold(value) { gold = value; updateUI(); } function getTowerCost(towerType) { var cost = 5; switch (towerType) { case 'rapid': cost = 15; break; case 'sniper': cost = 25; break; case 'splash': cost = 35; break; case 'slow': cost = 45; break; case 'poison': cost = 55; break; } return cost; } function getTowerSellValue(totalValue) { return waveIndicator && waveIndicator.gameStarted ? Math.floor(totalValue * 0.6) : totalValue; } // --- FUNKCJE EKRANÓW --- function showTitleScreen() { if (titleScreenContainer) { titleScreenContainer.destroy(); } titleScreenContainer = new Container(); game.addChild(titleScreenContainer); currentScreenState = 'title'; if (LK.gui.top) { LK.gui.top.visible = false; } var gameTitle = new Text2("NASZA FANTASY TD", { size: 120, fill: 0xFFD700, weight: 800, align: 'center', stroke: 0x000000, strokeThickness: 8 }); gameTitle.anchor.set(0.5, 0.5); gameTitle.x = 2048 / 2; gameTitle.y = 2732 / 2 - 300; titleScreenContainer.addChild(gameTitle); var startButton = new Container(); startButton.x = 2048 / 2; startButton.y = 2732 / 2 + 100; titleScreenContainer.addChild(startButton); var startButtonBg = startButton.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5, width: 400, height: 150, tint: 0x00AA00 }); var startButtonText = new Text2("START", { size: 80, fill: 0xFFFFFF, weight: 'bold' }); startButtonText.anchor.set(0.5, 0.5); startButton.addChild(startButtonText); startButton.interactive = true; startButton.down = function () { console.log("Przycisk START wciśnięty. Przechodzę do wyboru poziomu."); if (titleScreenContainer) { titleScreenContainer.destroy(); } showLevelSelectScreen(); }; } function showLevelSelectScreen() { if (levelSelectContainer) { levelSelectContainer.destroy(); } levelSelectContainer = new Container(); game.addChild(levelSelectContainer); currentScreenState = 'levelSelect'; if (LK.gui.top) { LK.gui.top.visible = false; } var selectTitle = new Text2("WYBIERZ POZIOM", { size: 100, fill: 0xFFFFFF, weight: 700 }); selectTitle.anchor.set(0.5, 0.5); selectTitle.x = 2048 / 2; selectTitle.y = 400; levelSelectContainer.addChild(selectTitle); var levelButton1 = new Container(); levelButton1.x = 2048 / 2; levelButton1.y = 800; levelSelectContainer.addChild(levelButton1); var levelButton1Bg = levelButton1.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5, width: 600, height: 200, tint: 0x0088CC }); var levelButton1Text = new Text2("Poziom 1: Mroczny Las", { size: 60, fill: 0xFFFFFF }); levelButton1Text.anchor.set(0.5, 0.5); levelButton1.addChild(levelButton1Text); levelButton1.interactive = true; levelButton1.down = function () { console.log("Wybrano Poziom 1. Rozpoczynam grę..."); startGame(1); }; var backButton = new Container(); backButton.x = 2048 / 2; backButton.y = 2732 - 300; levelSelectContainer.addChild(backButton); var backButtonBg = backButton.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5, width: 300, height: 120, tint: 0xAA0000 }); var backButtonText = new Text2("Powrót", { size: 60, fill: 0xFFFFFF }); backButtonText.anchor.set(0.5, 0.5); backButton.addChild(backButtonText); backButton.interactive = true; backButton.down = function () { console.log("Powrót do menu głównego."); if (levelSelectContainer) { levelSelectContainer.destroy(); } showTitleScreen(); }; } function toggleBuildPanel(forceState) { var targetY; var shouldBeOpen; if (forceState === 'open') { shouldBeOpen = true; } else if (forceState === 'close') { shouldBeOpen = false; } else { shouldBeOpen = !isBuildPanelOpen; } if (shouldBeOpen) { targetY = 2732 - 250; // Pozycja panelu gdy jest otwarty isBuildPanelOpen = true; } else { targetY = 2732 + 100; // Pozycja panelu gdy jest zamknięty (schowany) isBuildPanelOpen = false; } tween(buildPanelContainer, { y: targetY }, { duration: 300, easing: tween.backOut }); } function startGame(levelNumber) { // --- CZYSZCZENIE EKRANU I INICJALIZACJA --- console.log("Rozpoczynam ładowanie poziomu: " + levelNumber); if (titleScreenContainer) { titleScreenContainer.destroy(); titleScreenContainer = null; } if (levelSelectContainer) { levelSelectContainer.destroy(); levelSelectContainer = null; } currentScreenState = 'gameplay'; if (LK.gui.top) { LK.gui.top.visible = true; } // --- WCZYTANIE DANYCH POZIOMU DO ZMIENNEJ GLOBALNEJ --- levelData = { levelName: "Mroczny Las (Test)", initialGold: 100, initialLives: 20, mapLayout: ["11111111111S111111111111", "111111111110111111111111", "111111111110111111111111", "111111111110111111111111", "111111111110111111111111", "111111111110111111111111", "111111111110111111111111", "111111111110111111111111", "111111111110111111111111", "111111111110111111111111", "111111111110111111111111", "111111111110111111111111", "111111111110111111111111", "111111111110111111111111", "11111111111E111111111111"], waves: [{ wave: 1, type: 'normal', count: 10 }, { wave: 2, type: 'normal', count: 15 }, { wave: 3, type: 'fast', count: 10 }, { wave: 4, type: 'normal', count: 20 }, { wave: 5, type: 'swarm', count: 30 }, { wave: 6, type: 'normal', count: 10 }, { wave: 7, type: 'fast', count: 15 }, { wave: 8, type: 'immune', count: 10 }, { wave: 9, type: 'flying', count: 10 }, { wave: 10, type: 'normal', isBoss: true, count: 1 }], specialTiles: [{ type: 'amplify', x: 10, y: 8 }, { type: 'synergy', x: 12, y: 8 }] }; pathId = 1; maxScore = 0; enemies = []; towers = []; bullets = []; selectedTower = null; gold = levelData.initialGold; lives = levelData.initialLives; enemiesKilled = 0; enemiesInCurrentWave = 0; currentWave = 1; totalWaves = levelData.waves.length; waveInProgress = true; waveSpawned = false; waveTimer = 0; nextWaveTime = 1200; sourceTowers = []; isBuildPanelOpen = false; wavesText = new Text2('Wave: 0/' + totalWaves, { size: 60, fill: 0x00FFFF, weight: 800 }); wavesText.anchor.set(0.5, 0.5); LK.gui.top.addChild(wavesText); wavesText.x = spacing; wavesText.y = topMargin + 80; updateUI(); debugLayer = new Container(); specialTilesLayer = new Container(); towerLayer = new Container(); enemyLayerBottom = new Container(); enemyLayerMiddle = new Container(); enemyLayerTop = new Container(); enemyLayer = new Container(); enemyLayer.addChild(enemyLayerBottom); enemyLayer.addChild(enemyLayerMiddle); enemyLayer.addChild(enemyLayerTop); grid = new Grid(24, 29 + 6); grid.x = 150; grid.y = 200 - CELL_SIZE * 4; for (var y = 0; y < levelData.mapLayout.length; y++) { for (var x = 0; x < levelData.mapLayout[y].length; x++) { var tileChar = levelData.mapLayout[y][x]; var cell = grid.getCell(x, y + 5); if (cell) { if (tileChar === '0') { cell.type = 0; } else if (tileChar === 'S') { cell.type = 2; } else if (tileChar === 'E') { cell.type = 3; } else { cell.type = 1; } } } } levelData.specialTiles.forEach(function (tile) { var cell = grid.getCell(tile.x, tile.y); if (cell) { cell.specialEffect = tile.type; var tilePlaceholder = specialTilesLayer.attachAsset('cell', { anchorX: 0.5, anchorY: 0.5 }); tilePlaceholder.x = grid.x + tile.x * CELL_SIZE + CELL_SIZE / 2; tilePlaceholder.y = grid.y + tile.y * CELL_SIZE + CELL_SIZE / 2; tilePlaceholder.alpha = 0.5; if (tile.type === 'amplify') { tilePlaceholder.tint = 0xFFD700; } else if (tile.type === 'synergy') { tilePlaceholder.tint = 0x9932CC; } } }); grid.pathFind(); game.addChild(debugLayer); game.addChild(specialTilesLayer); game.addChild(towerLayer); game.addChild(enemyLayer); towerPreview = new TowerPreview(); game.addChild(towerPreview); towerPreview.visible = false; var isDragging = false; function wouldBlockPath(gridX, gridY) { var cells = []; for (var i = 0; i < 2; i++) { for (var j = 0; j < 2; j++) { var cell = grid.getCell(gridX + i, gridY + j); if (cell) { cells.push({ cell: cell, originalType: cell.type }); cell.type = 1; } } } var blocked = grid.pathFind(); for (var i = 0; i < cells.length; i++) { cells[i].cell.type = cells[i].originalType; } grid.pathFind(); return blocked; } function placeTower(gridX, gridY, towerType) { var towerCost = getTowerCost(towerType); if (gold >= towerCost) { var tower = new Tower(towerType || 'default'); tower.placeOnGrid(gridX, gridY); towerLayer.addChild(tower); towers.push(tower); setGold(gold - towerCost); grid.pathFind(); return true; } else { var notification = game.addChild(new Notification("Not enough gold!")); notification.x = 2048 / 2; notification.y = grid.height - 50; return false; } } buildPanelContainer = new Container(); game.addChild(buildPanelContainer); buildPanelContainer.x = 2048 / 2; buildPanelContainer.y = 2732 + 100; var panelBackground = buildPanelContainer.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5, width: 1800, height: 400 }); panelBackground.tint = 0x222222; panelBackground.alpha = 0.9; var towerTypes = ['default', 'rapid', 'sniper', 'splash', 'slow', 'poison']; var towerIconSpacing = 280; var totalWidth = (towerTypes.length - 1) * towerIconSpacing; var towerStartX = -totalWidth / 2; for (var i = 0; i < towerTypes.length; i++) { var tower = new SourceTower(towerTypes[i]); tower.x = towerStartX + i * towerIconSpacing; tower.y = 0; buildPanelContainer.addChild(tower); sourceTowers.push(tower); } buildButton = new Container(); game.addChild(buildButton); buildButton.x = 2048 / 2; buildButton.y = 2732 - 100; var buildButtonBg = buildButton.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5, width: 300, height: 120 }); buildButtonBg.tint = 0xFFD700; var buildButtonText = new Text2("Buduj", { size: 60, fill: 0x000000, weight: 800 }); buildButtonText.anchor.set(0.5, 0.5); buildButton.addChild(buildButtonText); buildButton.down = function () { toggleBuildPanel(); }; game.down = function (x, y, obj) { var buildButtonBounds = buildButton.getBounds(); if (buildButtonBounds.contains(x, y)) { return; } var upgradeMenuVisible = game.children.some(function (child) { return child instanceof UpgradeMenu; }); if (upgradeMenuVisible) return; if (isBuildPanelOpen) { var localClickPos = buildPanelContainer.toLocal({ x: x, y: y }); var clickedOnSourceTower = false; for (var i = 0; i < sourceTowers.length; i++) { var tower = sourceTowers[i]; if (tower.getBounds().contains(localClickPos.x, localClickPos.y)) { clickedOnSourceTower = true; if (gold < getTowerCost(tower.towerType)) { var notification = game.addChild(new Notification("Not enough gold!")); notification.x = 2048 / 2; notification.y = grid.height - 50; return; } towerPreview.visible = true; isDragging = true; towerPreview.towerType = tower.towerType; towerPreview.updateAppearance(); towerPreview.snapToGrid(x, y - CELL_SIZE * 1.5); return; } } if (!clickedOnSourceTower) { toggleBuildPanel('close'); } } }; game.move = function (x, y, obj) { if (isDragging) { towerPreview.snapToGrid(x, y - CELL_SIZE * 1.5); } }; game.up = function (x, y, obj) { // Ta funkcja jest wywoływana, gdy gracz puści przycisk myszy if (isDragging) { // Jeśli gracz coś przeciągał, to kończymy ten proces isDragging = false; if (towerPreview.canPlace) { if (!wouldBlockPath(towerPreview.gridX, towerPreview.gridY)) { placeTower(towerPreview.gridX, towerPreview.gridY, towerPreview.towerType); } else { var notification = game.addChild(new Notification("Tower would block the path!")); notification.x = 2048 / 2; notification.y = grid.height - 50; } } else if (towerPreview.blockedByEnemy) { var notification = game.addChild(new Notification("Cannot build: Enemy in the way!")); notification.x = 2048 / 2; notification.y = grid.height - 50; } towerPreview.visible = false; // Zamykamy panel budowy dopiero po zakończeniu przeciągania toggleBuildPanel('close'); } else { // Jeśli nic nie przeciągaliśmy, to sprawdzamy czy kliknięto na istniejącą wieżę lub menu var clickedOnTower = false; for (var i = 0; i < towers.length; i++) { var tower = towers[i]; var towerBounds = tower.getBounds(); if (towerBounds.contains(x - tower.x + tower.width / 2, y - tower.y + tower.height / 2)) { clickedOnTower = true; break; } } var upgradeMenus = game.children.filter(function (child) { return child instanceof UpgradeMenu; }); if (upgradeMenus.length > 0 && !clickedOnTower) { var clickedOnMenu = false; for (var i = 0; i < upgradeMenus.length; i++) { var menu = upgradeMenus[i]; var menuBounds = menu.getBounds(); if (menuBounds.contains(x - menu.x + menu.width / 2, y - menu.y + menu.height / 2)) { clickedOnMenu = true; break; } } if (!clickedOnMenu) { for (var i = 0; i < upgradeMenus.length; i++) hideUpgradeMenu(upgradeMenus[i]); for (var i = game.children.length - 1; i >= 0; i--) if (game.children[i].isTowerRange) game.removeChild(game.children[i]); selectedTower = null; } } } }; } game.update = function () { if (currentScreenState === 'gameplay') { // --- NOWA LOGIKA FAL OPARTA NA levelData --- if (currentWave <= totalWaves && enemies.length === 0 && !waveInProgress) { waveTimer++; if (waveTimer >= nextWaveTime) { currentWave++; if (currentWave <= totalWaves) { waveInProgress = true; waveSpawned = false; var notification = game.addChild(new Notification("Wave " + currentWave + " incoming!")); notification.x = 2048 / 2; notification.y = grid.height - 150; } } } if (waveInProgress && !waveSpawned) { waveSpawned = true; enemiesKilled = 0; // Resetuj licznik zabitych dla nowej fali // Znajdź dane dla aktualnej fali var waveData = levelData.waves[currentWave - 1]; if (waveData) { var enemyCount = waveData.count; var waveType = waveData.type; var isBoss = waveData.isBoss || false; enemiesInCurrentWave = enemyCount; // Ustaw liczbę wrogów w fali updateUI(); if (isBoss) { var notification = game.addChild(new Notification("⚠️ BOSS WAVE! ⚠️")); notification.x = 2048 / 2; notification.y = grid.height - 200; } for (var i = 0; i < enemyCount; i++) { // --- POPRAWIONA, BEZPIECZNA LOGIKA SPAWNOWANIA --- var enemy = new Enemy(waveType); var spawnPoints = grid.spawns; var spawnX; if (spawnPoints && spawnPoints.length > 0) { spawnX = spawnPoints[Math.floor(Math.random() * spawnPoints.length)].x; } else { console.warn("Brak punktu startowego 'S' w mapie! Używam domyślnego na środku."); spawnX = Math.floor(grid.cells.length / 2); } var spawnY = -1 - i * 2; // Rozmieszczamy ich lekko w pionie, żeby się nie naкладывали enemy.currentCellX = spawnX; enemy.currentCellY = spawnY; enemy.cellX = spawnX; // Ustawiamy od razu, żeby wiedział dokąd iść enemy.cellY = 5; enemy.waveNumber = currentWave; if (isBoss) { enemy.isBoss = true; } // Upewniamy się, że flaga bossa jest ustawiona var healthMultiplier = Math.pow(1.12, currentWave); enemy.maxHealth = Math.round(enemy.maxHealth * healthMultiplier); enemy.health = enemy.maxHealth; // Dodawanie do warstw if (enemy.isFlying) { enemyLayerTop.addChild(enemy); if (enemy.shadow) { enemyLayerMiddle.addChild(enemy.shadow); } } else { enemyLayerBottom.addChild(enemy); } enemies.push(enemy); } } else { console.warn("Brak danych dla fali numer: " + currentWave); waveInProgress = false; } } if (enemies.length === 0 && waveSpawned) { waveInProgress = false; waveSpawned = false; waveTimer = 0; // Rozpocznij odliczanie do następnej fali } // --- Reszta logiki (aktualizacja wrogów, wież, pocisków) --- for (var a = enemies.length - 1; a >= 0; a--) { var enemy = enemies[a]; if (enemy.health <= 0) { enemiesKilled++; updateUI(); var goldEarned = enemy.isBoss ? Math.floor(50 + (enemy.waveNumber - 1) * 5) : Math.floor(1 + (enemy.waveNumber - 1) * 0.5); var goldIndicator = new GoldIndicator(goldEarned, enemy.x, enemy.y); game.addChild(goldIndicator); setGold(gold + goldEarned); if (enemy.isBoss) { var notification = game.addChild(new Notification("Boss defeated! +" + goldEarned + " gold!")); notification.x = 2048 / 2; notification.y = grid.height - 150; } for (var i = 0; i < enemy.bulletsTargetingThis.length; i++) { enemy.bulletsTargetingThis[i].targetEnemy = null; } if (enemy.isFlying && enemy.shadow) { enemyLayerMiddle.removeChild(enemy.shadow); enemy.shadow = null; } if (enemy.isFlying) { enemyLayerTop.removeChild(enemy); } else { enemyLayerBottom.removeChild(enemy); } enemies.splice(a, 1); continue; } if (grid.updateEnemy(enemy)) { if (enemy.isFlying && enemy.shadow) { enemyLayerMiddle.removeChild(enemy.shadow); enemy.shadow = null; } if (enemy.isFlying) { enemyLayerTop.removeChild(enemy); } else { enemyLayerBottom.removeChild(enemy); } enemies.splice(a, 1); lives = Math.max(0, lives - 1); updateUI(); if (lives <= 0) { LK.showGameOver(); } } } for (var i = bullets.length - 1; i >= 0; i--) { if (!bullets[i].parent) { if (bullets[i].targetEnemy) { var targetEnemy = bullets[i].targetEnemy; var bulletIndex = targetEnemy.bulletsTargetingThis.indexOf(bullets[i]); if (bulletIndex !== -1) { targetEnemy.bulletsTargetingThis.splice(bulletIndex, 1); } } bullets.splice(i, 1); } } if (towerPreview.visible) { towerPreview.checkPlacement(); } if (currentWave >= totalWaves && enemies.length === 0 && !waveInProgress) { LK.showYouWin(); } } }; showTitleScreen();
===================================================================
--- original.js
+++ change.js
@@ -482,30 +482,16 @@
for (var j = 0; j < gridHeight; j++) {
self.cells[i][j] = {
score: 0,
pathId: 0,
- towersInRange: []
+ towersInRange: [],
+ specialEffect: null
};
}
}
for (var i = 0; i < gridWidth; i++) {
for (var j = 0; j < gridHeight; j++) {
var cell = self.cells[i][j];
- var cellType = i === 0 || i === gridWidth - 1 || j <= 4 || j >= gridHeight - 4 ? 1 : 0;
- if (i > 11 - 3 && i <= 11 + 3) {
- if (j === 0) {
- cellType = 2;
- self.spawns.push(cell);
- } else if (j <= 4) {
- cellType = 0;
- } else if (j === gridHeight - 1) {
- cellType = 3;
- self.goals.push(cell);
- } else if (j >= gridHeight - 4) {
- cellType = 0;
- }
- }
- cell.type = cellType;
cell.x = i;
cell.y = j;
cell.upLeft = self.cells[i - 1] && self.cells[i - 1][j - 1];
cell.up = self.cells[i - 1] && self.cells[i - 1][j];
@@ -516,31 +502,34 @@
cell.down = self.cells[i + 1] && self.cells[i + 1][j];
cell.downRight = self.cells[i + 1] && self.cells[i + 1][j + 1];
cell.neighbors = [cell.upLeft, cell.up, cell.upRight, cell.right, cell.downRight, cell.down, cell.downLeft, cell.left];
cell.targets = [];
- if (j > 3 && j <= gridHeight - 4) {
- var debugCell = new DebugCell();
- self.addChild(debugCell);
- debugCell.cell = cell;
- debugCell.x = i * CELL_SIZE;
- debugCell.y = j * CELL_SIZE;
- cell.debugCell = debugCell;
- }
}
}
self.getCell = function (x, y) {
return self.cells[x] && self.cells[x][y];
};
self.pathFind = function () {
var before = new Date().getTime();
+ // Znajdź wszystkie komórki końcowe (goals)
+ self.goals = [];
+ for (var i = 0; i < gridWidth; i++) {
+ for (var j = 0; j < gridHeight; j++) {
+ if (self.cells[i][j].type === 3) {
+ self.goals.push(self.cells[i][j]);
+ }
+ }
+ }
var toProcess = self.goals.concat([]);
maxScore = 0;
pathId += 1;
for (var a = 0; a < toProcess.length; a++) {
toProcess[a].pathId = pathId;
+ toProcess[a].score = 0; // Reset score for pathfinding
}
function processNode(node, targetValue, targetNode) {
if (node && node.type != 1) {
+ // Typ 1 to ściana
if (node.pathId < pathId || targetValue < node.score) {
node.targets = [targetNode];
} else if (node.pathId == pathId && targetValue == node.score) {
node.targets.push(targetNode);
@@ -561,9 +550,9 @@
var nodes = toProcess;
toProcess = [];
for (var a = 0; a < nodes.length; a++) {
var node = nodes[a];
- var targetScore = node.score + 14142;
+ var targetScore = node.score + 14142; // Diagonal cost
if (node.up && node.left && node.up.type != 1 && node.left.type != 1) {
processNode(node.upLeft, targetScore, node);
}
if (node.up && node.right && node.up.type != 1 && node.right.type != 1) {
@@ -574,26 +563,31 @@
}
if (node.down && node.left && node.down.type != 1 && node.left.type != 1) {
processNode(node.downLeft, targetScore, node);
}
- targetScore = node.score + 10000;
+ targetScore = node.score + 10000; // Straight cost
processNode(node.up, targetScore, node);
processNode(node.right, targetScore, node);
processNode(node.down, targetScore, node);
processNode(node.left, targetScore, node);
}
}
+ self.spawns = [];
+ for (var i = 0; i < gridWidth; i++) {
+ for (var j = 0; j < gridHeight; j++) {
+ if (self.cells[i][j].type === 2) {
+ self.spawns.push(self.cells[i][j]);
+ }
+ }
+ }
for (var a = 0; a < self.spawns.length; a++) {
if (self.spawns[a].pathId != pathId) {
console.warn("Spawn blocked");
return true;
}
}
for (var a = 0; a < enemies.length; a++) {
var enemy = enemies[a];
- if (enemy.currentCellY < 4) {
- continue;
- }
if (enemy.isFlying) {
continue;
}
var target = self.getCell(enemy.cellX, enemy.cellY);
@@ -608,23 +602,17 @@
console.warn("Enemy blocked 2");
return true;
}
}
- console.log("Speed", new Date().getTime() - before);
+ console.log("Pathfinding complete in:", new Date().getTime() - before, "ms");
};
self.renderDebug = function () {
- for (var i = 0; i < gridWidth; i++) {
- for (var j = 0; j < gridHeight; j++) {
- var debugCell = self.cells[i][j].debugCell;
- if (debugCell) {
- debugCell.render(self.cells[i][j]);
- }
- }
- }
+ // Ta funkcja może pozostać pusta lub zostać usunięta,
+ // bo nie używamy już starego debugowania siatki
};
self.updateEnemy = function (enemy) {
var cell = grid.getCell(enemy.cellX, enemy.cellY);
- if (cell.type == 3) {
+ if (cell && cell.type == 3) {
return true;
}
if (enemy.isFlying && enemy.shadow) {
enemy.shadow.x = enemy.x + 20;
@@ -632,113 +620,50 @@
if (enemy.children[0] && enemy.shadow.children[0]) {
enemy.shadow.children[0].rotation = enemy.children[0].rotation;
}
}
- var hasReachedEntryArea = enemy.currentCellY >= 4;
- if (!hasReachedEntryArea) {
+ // Prosta logika startu wroga - idzie prosto w dół do pierwszej komórki ścieżki
+ if (enemy.currentCellY < 5) {
enemy.currentCellY += enemy.speed;
- var angle = Math.PI / 2;
- if (enemy.children[0] && enemy.children[0].targetRotation === undefined) {
- enemy.children[0].targetRotation = angle;
- enemy.children[0].rotation = angle;
- } else if (enemy.children[0]) {
- if (Math.abs(angle - enemy.children[0].targetRotation) > 0.05) {
- tween.stop(enemy.children[0], {
- rotation: true
- });
- var currentRotation = enemy.children[0].rotation;
- var angleDiff = angle - currentRotation;
- while (angleDiff > Math.PI) {
- angleDiff -= Math.PI * 2;
- }
- while (angleDiff < -Math.PI) {
- angleDiff += Math.PI * 2;
- }
- enemy.children[0].targetRotation = angle;
- tween(enemy.children[0], {
- rotation: currentRotation + angleDiff
- }, {
- duration: 250,
- easing: tween.easeOut
- });
- }
- }
enemy.x = grid.x + enemy.currentCellX * CELL_SIZE;
enemy.y = grid.y + enemy.currentCellY * CELL_SIZE;
- if (enemy.currentCellY >= 4) {
+ if (enemy.currentCellY >= 5) {
enemy.cellX = Math.round(enemy.currentCellX);
enemy.cellY = Math.round(enemy.currentCellY);
}
return false;
}
if (enemy.isFlying) {
if (!enemy.flyingTarget) {
- enemy.flyingTarget = self.goals[0];
- if (self.goals.length > 1) {
- var closestDist = Infinity;
- for (var i = 0; i < self.goals.length; i++) {
- var goal = self.goals[i];
- var dx = goal.x - enemy.cellX;
- var dy = goal.y - enemy.cellY;
- var dist = dx * dx + dy * dy;
- if (dist < closestDist) {
- closestDist = dist;
- enemy.flyingTarget = goal;
- }
- }
- }
+ enemy.flyingTarget = self.goals[Math.floor(Math.random() * self.goals.length)];
}
var ox = enemy.flyingTarget.x - enemy.currentCellX;
var oy = enemy.flyingTarget.y - enemy.currentCellY;
var dist = Math.sqrt(ox * ox + oy * oy);
if (dist < enemy.speed) {
return true;
}
var angle = Math.atan2(oy, ox);
- if (enemy.children[0] && enemy.children[0].targetRotation === undefined) {
- enemy.children[0].targetRotation = angle;
- enemy.children[0].rotation = angle;
- } else if (enemy.children[0]) {
- if (Math.abs(angle - enemy.children[0].targetRotation) > 0.05) {
- tween.stop(enemy.children[0], {
- rotation: true
- });
- var currentRotation = enemy.children[0].rotation;
- var angleDiff = angle - currentRotation;
- while (angleDiff > Math.PI) {
- angleDiff -= Math.PI * 2;
- }
- while (angleDiff < -Math.PI) {
- angleDiff += Math.PI * 2;
- }
- enemy.children[0].targetRotation = angle;
- tween(enemy.children[0], {
- rotation: currentRotation + angleDiff
- }, {
- duration: 250,
- easing: tween.easeOut
- });
- }
- }
- enemy.cellX = Math.round(enemy.currentCellX);
- enemy.cellY = Math.round(enemy.currentCellY);
enemy.currentCellX += Math.cos(angle) * enemy.speed;
enemy.currentCellY += Math.sin(angle) * enemy.speed;
enemy.x = grid.x + enemy.currentCellX * CELL_SIZE;
enemy.y = grid.y + enemy.currentCellY * CELL_SIZE;
+ enemy.cellX = Math.round(enemy.currentCellX);
+ enemy.cellY = Math.round(enemy.currentCellY);
return false;
}
if (!enemy.currentTarget) {
- enemy.currentTarget = cell.targets[0];
+ if (cell) {
+ enemy.currentTarget = cell.targets[Math.floor(Math.random() * cell.targets.length)];
+ }
}
if (enemy.currentTarget) {
- if (cell.score < enemy.currentTarget.score) {
- enemy.currentTarget = cell;
- }
var ox = enemy.currentTarget.x - enemy.currentCellX;
var oy = enemy.currentTarget.y - enemy.currentCellY;
var dist = Math.sqrt(ox * ox + oy * oy);
if (dist < enemy.speed) {
+ enemy.currentCellX = enemy.currentTarget.x;
+ enemy.currentCellY = enemy.currentTarget.y;
enemy.cellX = Math.round(enemy.currentCellX);
enemy.cellY = Math.round(enemy.currentCellY);
enemy.currentTarget = undefined;
return;
@@ -750,57 +675,8 @@
enemy.x = grid.x + enemy.currentCellX * CELL_SIZE;
enemy.y = grid.y + enemy.currentCellY * CELL_SIZE;
};
});
-var NextWaveButton = Container.expand(function () {
- var self = Container.call(this);
- var buttonBackground = self.attachAsset('notification', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- buttonBackground.width = 300;
- buttonBackground.height = 100;
- buttonBackground.tint = 0x0088FF;
- var buttonText = new Text2("Next Wave", {
- size: 50,
- fill: 0xFFFFFF,
- weight: 800
- });
- buttonText.anchor.set(0.5, 0.5);
- self.addChild(buttonText);
- self.enabled = false;
- self.visible = false;
- self.update = function () {
- if (waveIndicator && waveIndicator.gameStarted && currentWave < totalWaves) {
- self.enabled = true;
- self.visible = true;
- buttonBackground.tint = 0x0088FF;
- self.alpha = 1;
- } else {
- self.enabled = false;
- self.visible = false;
- buttonBackground.tint = 0x888888;
- self.alpha = 0.7;
- }
- };
- self.down = function () {
- if (!self.enabled) {
- return;
- }
- if (waveIndicator.gameStarted && currentWave < totalWaves) {
- currentWave++;
- waveTimer = 0;
- waveInProgress = true;
- waveSpawned = false;
- var waveType = waveIndicator.getWaveTypeName(currentWave);
- var enemyCount = waveIndicator.getEnemyCount(currentWave);
- var notification = game.addChild(new Notification("Wave " + currentWave + " (" + waveType + " - " + enemyCount + " enemies) activated!"));
- notification.x = 2048 / 2;
- notification.y = grid.height - 150;
- }
- };
- return self;
-});
var Notification = Container.expand(function (message) {
var self = Container.call(this);
var notificationGraphics = self.attachAsset('notification', {
anchorX: 0.5,
@@ -1733,319 +1609,8 @@
}
};
return self;
});
-var WaveIndicator = Container.expand(function () {
- var self = Container.call(this);
- self.gameStarted = false;
- self.waveMarkers = [];
- self.waveTypes = [];
- self.enemyCounts = [];
- self.indicatorWidth = 0;
- self.lastBossType = null;
- var blockWidth = 400;
- var totalBlocksWidth = blockWidth * totalWaves;
- var startMarker = new Container();
- var startBlock = startMarker.attachAsset('notification', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- startBlock.width = blockWidth - 10;
- startBlock.height = 70 * 2;
- startBlock.tint = 0x00AA00;
- var startTextShadow = new Text2("Start Game", {
- size: 50,
- fill: 0x000000,
- weight: 800
- });
- startTextShadow.anchor.set(0.5, 0.5);
- startTextShadow.x = 4;
- startTextShadow.y = 4;
- startMarker.addChild(startTextShadow);
- var startText = new Text2("Start Game", {
- size: 50,
- fill: 0xFFFFFF,
- weight: 800
- });
- startText.anchor.set(0.5, 0.5);
- startMarker.addChild(startText);
- startMarker.x = -self.indicatorWidth;
- self.addChild(startMarker);
- self.waveMarkers.push(startMarker);
- startMarker.down = function () {
- if (!self.gameStarted) {
- self.gameStarted = true;
- currentWave = 0;
- waveTimer = nextWaveTime;
- startBlock.tint = 0x00FF00;
- startText.setText("Started!");
- startTextShadow.setText("Started!");
- startTextShadow.x = 4;
- startTextShadow.y = 4;
- var notification = game.addChild(new Notification("Game started! Wave 1 incoming!"));
- notification.x = 2048 / 2;
- notification.y = grid.height - 150;
- }
- };
- for (var i = 0; i < totalWaves; i++) {
- var marker = new Container();
- var block = marker.attachAsset('notification', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- block.width = blockWidth - 10;
- block.height = 70 * 2;
- var waveType = "normal";
- var enemyType = "normal";
- var enemyCount = 10;
- var isBossWave = (i + 1) % 10 === 0;
- if (i === 0) {
- block.tint = 0xAAAAAA;
- waveType = "Normal";
- enemyType = "normal";
- enemyCount = 10;
- } else if (i === 1) {
- block.tint = 0x00AAFF;
- waveType = "Fast";
- enemyType = "fast";
- enemyCount = 10;
- } else if (i === 2) {
- block.tint = 0xAA0000;
- waveType = "Immune";
- enemyType = "immune";
- enemyCount = 10;
- } else if (i === 3) {
- block.tint = 0xFFFF00;
- waveType = "Flying";
- enemyType = "flying";
- enemyCount = 10;
- } else if (i === 4) {
- block.tint = 0xFF00FF;
- waveType = "Swarm";
- enemyType = "swarm";
- enemyCount = 30;
- } else if (isBossWave) {
- var bossTypes = ['normal', 'fast', 'immune', 'flying'];
- var bossTypeIndex = Math.floor((i + 1) / 10) - 1;
- if (i === totalWaves - 1) {
- enemyType = 'flying';
- waveType = "Boss Flying";
- block.tint = 0xFFFF00;
- } else {
- enemyType = bossTypes[bossTypeIndex % bossTypes.length];
- switch (enemyType) {
- case 'normal':
- block.tint = 0xAAAAAA;
- waveType = "Boss Normal";
- break;
- case 'fast':
- block.tint = 0x00AAFF;
- waveType = "Boss Fast";
- break;
- case 'immune':
- block.tint = 0xAA0000;
- waveType = "Boss Immune";
- break;
- case 'flying':
- block.tint = 0xFFFF00;
- waveType = "Boss Flying";
- break;
- }
- }
- enemyCount = 1;
- switch (enemyType) {
- case 'normal':
- block.tint = 0xAAAAAA;
- break;
- case 'fast':
- block.tint = 0x00AAFF;
- break;
- case 'immune':
- block.tint = 0xAA0000;
- break;
- case 'flying':
- block.tint = 0xFFFF00;
- break;
- default:
- block.tint = 0xFF0000;
- break;
- }
- } else if ((i + 1) % 5 === 0) {
- block.tint = 0x00AAFF;
- waveType = "Fast";
- enemyType = "fast";
- enemyCount = 10;
- } else if ((i + 1) % 4 === 0) {
- block.tint = 0xAA0000;
- waveType = "Immune";
- enemyType = "immune";
- enemyCount = 10;
- } else if ((i + 1) % 7 === 0) {
- block.tint = 0xFFFF00;
- waveType = "Flying";
- enemyType = "flying";
- enemyCount = 10;
- } else if ((i + 1) % 3 === 0) {
- block.tint = 0xFF00FF;
- waveType = "Swarm";
- enemyType = "swarm";
- enemyCount = 30;
- } else {
- block.tint = 0xAAAAAA;
- waveType = "Normal";
- enemyType = "normal";
- enemyCount = 10;
- }
- if (isBossWave && enemyType !== 'swarm') {
- var bossIndicator = marker.attachAsset('towerLevelIndicator', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- bossIndicator.width = 30;
- bossIndicator.height = 30;
- bossIndicator.tint = 0xFFD700;
- bossIndicator.y = -block.height / 2 - 15;
- waveType = "BOSS";
- }
- self.waveTypes[i] = enemyType;
- self.enemyCounts[i] = enemyCount;
- var waveTypeShadow = new Text2(waveType, {
- size: 56,
- fill: 0x000000,
- weight: 800
- });
- waveTypeShadow.anchor.set(0.5, 0.5);
- waveTypeShadow.x = 4;
- waveTypeShadow.y = 4;
- marker.addChild(waveTypeShadow);
- var waveTypeText = new Text2(waveType, {
- size: 56,
- fill: 0xFFFFFF,
- weight: 800
- });
- waveTypeText.anchor.set(0.5, 0.5);
- waveTypeText.y = 0;
- marker.addChild(waveTypeText);
- var waveNumShadow = new Text2((i + 1).toString(), {
- size: 48,
- fill: 0x000000,
- weight: 800
- });
- waveNumShadow.anchor.set(1.0, 1.0);
- waveNumShadow.x = blockWidth / 2 - 16 + 5;
- waveNumShadow.y = block.height / 2 - 12 + 5;
- marker.addChild(waveNumShadow);
- var waveNum = new Text2((i + 1).toString(), {
- size: 48,
- fill: 0xFFFFFF,
- weight: 800
- });
- waveNum.anchor.set(1.0, 1.0);
- waveNum.x = blockWidth / 2 - 16;
- waveNum.y = block.height / 2 - 12;
- marker.addChild(waveNum);
- marker.x = -self.indicatorWidth + (i + 1) * blockWidth;
- self.addChild(marker);
- self.waveMarkers.push(marker);
- }
- self.getWaveType = function (waveNumber) {
- if (waveNumber < 1 || waveNumber > totalWaves) {
- return "normal";
- }
- var waveType = self.waveTypes[waveNumber - 1];
- return waveType;
- };
- self.getEnemyCount = function (waveNumber) {
- if (waveNumber < 1 || waveNumber > totalWaves) {
- return 10;
- }
- return self.enemyCounts[waveNumber - 1];
- };
- self.getWaveTypeName = function (waveNumber) {
- var type = self.getWaveType(waveNumber);
- var typeName = type.charAt(0).toUpperCase() + type.slice(1);
- if (waveNumber % 10 === 0 && waveNumber > 0 && type !== 'swarm') {
- typeName = "BOSS";
- }
- return typeName;
- };
- self.positionIndicator = new Container();
- var indicator = self.positionIndicator.attachAsset('towerLevelIndicator', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- indicator.width = blockWidth - 10;
- indicator.height = 16;
- indicator.tint = 0xffad0e;
- indicator.y = -65;
- var indicator2 = self.positionIndicator.attachAsset('towerLevelIndicator', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- indicator2.width = blockWidth - 10;
- indicator2.height = 16;
- indicator2.tint = 0xffad0e;
- indicator2.y = 65;
- var leftWall = self.positionIndicator.attachAsset('towerLevelIndicator', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- leftWall.width = 16;
- leftWall.height = 146;
- leftWall.tint = 0xffad0e;
- leftWall.x = -(blockWidth - 16) / 2;
- var rightWall = self.positionIndicator.attachAsset('towerLevelIndicator', {
- anchorX: 0.5,
- anchorY: 0.5
- });
- rightWall.width = 16;
- rightWall.height = 146;
- rightWall.tint = 0xffad0e;
- rightWall.x = (blockWidth - 16) / 2;
- self.addChild(self.positionIndicator);
- self.update = function () {
- var progress = waveTimer / nextWaveTime;
- var moveAmount = (progress + currentWave) * blockWidth;
- for (var i = 0; i < self.waveMarkers.length; i++) {
- var marker = self.waveMarkers[i];
- marker.x = -moveAmount + i * blockWidth;
- }
- self.positionIndicator.x = 0;
- for (var i = 0; i < totalWaves + 1; i++) {
- var marker = self.waveMarkers[i];
- if (i === 0) {
- continue;
- }
- var block = marker.children[0];
- if (i - 1 < currentWave) {
- block.alpha = .5;
- }
- }
- self.handleWaveProgression = function () {
- if (!self.gameStarted) {
- return;
- }
- if (currentWave < totalWaves) {
- waveTimer++;
- if (waveTimer >= nextWaveTime) {
- waveTimer = 0;
- currentWave++;
- waveInProgress = true;
- waveSpawned = false;
- if (currentWave != 1) {
- var waveType = self.getWaveTypeName(currentWave);
- var enemyCount = self.getEnemyCount(currentWave);
- var notification = game.addChild(new Notification("Wave " + currentWave + " (" + waveType + " - " + enemyCount + " enemies) incoming!"));
- notification.x = 2048 / 2;
- notification.y = grid.height - 150;
- }
- }
- }
- };
- self.handleWaveProgression();
- };
- return self;
-});
/****
* Initialize Game
****/
@@ -2070,22 +1635,30 @@
var selectedTower;
var gold;
var lives;
var enemiesKilled;
-var totalEnemiesInLevel;
+var enemiesInCurrentWave;
+var levelData;
var currentWave;
var totalWaves;
var waveTimer;
var waveInProgress;
var waveSpawned;
var nextWaveTime;
var sourceTowers;
var grid;
-var waveIndicator;
+var wavesText;
var towerLayer;
var enemyLayer;
var debugLayer;
+var specialTilesLayer;
+var enemyLayerBottom;
+var enemyLayerMiddle;
+var enemyLayerTop;
var towerPreview;
+var buildPanelContainer;
+var buildButton;
+var isBuildPanelOpen;
// --- GŁÓWNE ELEMENTY UI ---
var goldText = new Text2('Gold: ' + 80, {
size: 60,
fill: 0xFFD700,
@@ -2140,9 +1713,12 @@
}
function updateUI() {
goldText.setText('Gold: ' + gold);
livesText.setText('Lives: ' + lives);
- enemiesText.setText('Enemies: ' + enemiesKilled + '/' + totalEnemiesInLevel);
+ enemiesText.setText('Enemies: ' + enemiesKilled + '/' + enemiesInCurrentWave);
+ if (wavesText) {
+ wavesText.setText('Wave: ' + currentWave + '/' + totalWaves);
+ }
}
function setGold(value) {
gold = value;
updateUI();
@@ -2287,9 +1863,34 @@
}
showTitleScreen();
};
}
+function toggleBuildPanel(forceState) {
+ var targetY;
+ var shouldBeOpen;
+ if (forceState === 'open') {
+ shouldBeOpen = true;
+ } else if (forceState === 'close') {
+ shouldBeOpen = false;
+ } else {
+ shouldBeOpen = !isBuildPanelOpen;
+ }
+ if (shouldBeOpen) {
+ targetY = 2732 - 250; // Pozycja panelu gdy jest otwarty
+ isBuildPanelOpen = true;
+ } else {
+ targetY = 2732 + 100; // Pozycja panelu gdy jest zamknięty (schowany)
+ isBuildPanelOpen = false;
+ }
+ tween(buildPanelContainer, {
+ y: targetY
+ }, {
+ duration: 300,
+ easing: tween.backOut
+ });
+}
function startGame(levelNumber) {
+ // --- CZYSZCZENIE EKRANU I INICJALIZACJA ---
console.log("Rozpoczynam ładowanie poziomu: " + levelNumber);
if (titleScreenContainer) {
titleScreenContainer.destroy();
titleScreenContainer = null;
@@ -2301,42 +1902,145 @@
currentScreenState = 'gameplay';
if (LK.gui.top) {
LK.gui.top.visible = true;
}
+ // --- WCZYTANIE DANYCH POZIOMU DO ZMIENNEJ GLOBALNEJ ---
+ levelData = {
+ levelName: "Mroczny Las (Test)",
+ initialGold: 100,
+ initialLives: 20,
+ mapLayout: ["11111111111S111111111111", "111111111110111111111111", "111111111110111111111111", "111111111110111111111111", "111111111110111111111111", "111111111110111111111111", "111111111110111111111111", "111111111110111111111111", "111111111110111111111111", "111111111110111111111111", "111111111110111111111111", "111111111110111111111111", "111111111110111111111111", "111111111110111111111111", "11111111111E111111111111"],
+ waves: [{
+ wave: 1,
+ type: 'normal',
+ count: 10
+ }, {
+ wave: 2,
+ type: 'normal',
+ count: 15
+ }, {
+ wave: 3,
+ type: 'fast',
+ count: 10
+ }, {
+ wave: 4,
+ type: 'normal',
+ count: 20
+ }, {
+ wave: 5,
+ type: 'swarm',
+ count: 30
+ }, {
+ wave: 6,
+ type: 'normal',
+ count: 10
+ }, {
+ wave: 7,
+ type: 'fast',
+ count: 15
+ }, {
+ wave: 8,
+ type: 'immune',
+ count: 10
+ }, {
+ wave: 9,
+ type: 'flying',
+ count: 10
+ }, {
+ wave: 10,
+ type: 'normal',
+ isBoss: true,
+ count: 1
+ }],
+ specialTiles: [{
+ type: 'amplify',
+ x: 10,
+ y: 8
+ }, {
+ type: 'synergy',
+ x: 12,
+ y: 8
+ }]
+ };
pathId = 1;
maxScore = 0;
enemies = [];
towers = [];
bullets = [];
selectedTower = null;
- gold = 80;
- lives = 20;
+ gold = levelData.initialGold;
+ lives = levelData.initialLives;
enemiesKilled = 0;
- totalEnemiesInLevel = 50;
- currentWave = 0;
- totalWaves = 50;
- waveTimer = 0;
- waveInProgress = false;
+ enemiesInCurrentWave = 0;
+ currentWave = 1;
+ totalWaves = levelData.waves.length;
+ waveInProgress = true;
waveSpawned = false;
- nextWaveTime = 12000 / 2;
+ waveTimer = 0;
+ nextWaveTime = 1200;
sourceTowers = [];
+ isBuildPanelOpen = false;
+ wavesText = new Text2('Wave: 0/' + totalWaves, {
+ size: 60,
+ fill: 0x00FFFF,
+ weight: 800
+ });
+ wavesText.anchor.set(0.5, 0.5);
+ LK.gui.top.addChild(wavesText);
+ wavesText.x = spacing;
+ wavesText.y = topMargin + 80;
updateUI();
debugLayer = new Container();
+ specialTilesLayer = new Container();
towerLayer = new Container();
- var enemyLayerBottom = new Container();
- var enemyLayerMiddle = new Container();
- var enemyLayerTop = new Container();
+ enemyLayerBottom = new Container();
+ enemyLayerMiddle = new Container();
+ enemyLayerTop = new Container();
enemyLayer = new Container();
enemyLayer.addChild(enemyLayerBottom);
enemyLayer.addChild(enemyLayerMiddle);
enemyLayer.addChild(enemyLayerTop);
grid = new Grid(24, 29 + 6);
grid.x = 150;
grid.y = 200 - CELL_SIZE * 4;
+ for (var y = 0; y < levelData.mapLayout.length; y++) {
+ for (var x = 0; x < levelData.mapLayout[y].length; x++) {
+ var tileChar = levelData.mapLayout[y][x];
+ var cell = grid.getCell(x, y + 5);
+ if (cell) {
+ if (tileChar === '0') {
+ cell.type = 0;
+ } else if (tileChar === 'S') {
+ cell.type = 2;
+ } else if (tileChar === 'E') {
+ cell.type = 3;
+ } else {
+ cell.type = 1;
+ }
+ }
+ }
+ }
+ levelData.specialTiles.forEach(function (tile) {
+ var cell = grid.getCell(tile.x, tile.y);
+ if (cell) {
+ cell.specialEffect = tile.type;
+ var tilePlaceholder = specialTilesLayer.attachAsset('cell', {
+ anchorX: 0.5,
+ anchorY: 0.5
+ });
+ tilePlaceholder.x = grid.x + tile.x * CELL_SIZE + CELL_SIZE / 2;
+ tilePlaceholder.y = grid.y + tile.y * CELL_SIZE + CELL_SIZE / 2;
+ tilePlaceholder.alpha = 0.5;
+ if (tile.type === 'amplify') {
+ tilePlaceholder.tint = 0xFFD700;
+ } else if (tile.type === 'synergy') {
+ tilePlaceholder.tint = 0x9932CC;
+ }
+ }
+ });
grid.pathFind();
- grid.renderDebug();
- debugLayer.addChild(grid);
game.addChild(debugLayer);
+ game.addChild(specialTilesLayer);
game.addChild(towerLayer);
game.addChild(enemyLayer);
towerPreview = new TowerPreview();
game.addChild(towerPreview);
@@ -2360,9 +2064,8 @@
for (var i = 0; i < cells.length; i++) {
cells[i].cell.type = cells[i].originalType;
}
grid.pathFind();
- grid.renderDebug();
return blocked;
}
function placeTower(gridX, gridY, towerType) {
var towerCost = getTowerCost(towerType);
@@ -2372,87 +2075,107 @@
towerLayer.addChild(tower);
towers.push(tower);
setGold(gold - towerCost);
grid.pathFind();
- grid.renderDebug();
return true;
} else {
var notification = game.addChild(new Notification("Not enough gold!"));
notification.x = 2048 / 2;
notification.y = grid.height - 50;
return false;
}
}
+ buildPanelContainer = new Container();
+ game.addChild(buildPanelContainer);
+ buildPanelContainer.x = 2048 / 2;
+ buildPanelContainer.y = 2732 + 100;
+ var panelBackground = buildPanelContainer.attachAsset('notification', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: 1800,
+ height: 400
+ });
+ panelBackground.tint = 0x222222;
+ panelBackground.alpha = 0.9;
+ var towerTypes = ['default', 'rapid', 'sniper', 'splash', 'slow', 'poison'];
+ var towerIconSpacing = 280;
+ var totalWidth = (towerTypes.length - 1) * towerIconSpacing;
+ var towerStartX = -totalWidth / 2;
+ for (var i = 0; i < towerTypes.length; i++) {
+ var tower = new SourceTower(towerTypes[i]);
+ tower.x = towerStartX + i * towerIconSpacing;
+ tower.y = 0;
+ buildPanelContainer.addChild(tower);
+ sourceTowers.push(tower);
+ }
+ buildButton = new Container();
+ game.addChild(buildButton);
+ buildButton.x = 2048 / 2;
+ buildButton.y = 2732 - 100;
+ var buildButtonBg = buildButton.attachAsset('notification', {
+ anchorX: 0.5,
+ anchorY: 0.5,
+ width: 300,
+ height: 120
+ });
+ buildButtonBg.tint = 0xFFD700;
+ var buildButtonText = new Text2("Buduj", {
+ size: 60,
+ fill: 0x000000,
+ weight: 800
+ });
+ buildButtonText.anchor.set(0.5, 0.5);
+ buildButton.addChild(buildButtonText);
+ buildButton.down = function () {
+ toggleBuildPanel();
+ };
game.down = function (x, y, obj) {
+ var buildButtonBounds = buildButton.getBounds();
+ if (buildButtonBounds.contains(x, y)) {
+ return;
+ }
var upgradeMenuVisible = game.children.some(function (child) {
return child instanceof UpgradeMenu;
});
- if (upgradeMenuVisible) {
- return;
- }
- for (var i = 0; i < sourceTowers.length; i++) {
- var tower = sourceTowers[i];
- if (x >= tower.x - tower.width / 2 && x <= tower.x + tower.width / 2 && y >= tower.y - tower.height / 2 && y <= tower.y + tower.height / 2) {
- towerPreview.visible = true;
- isDragging = true;
- towerPreview.towerType = tower.towerType;
- towerPreview.updateAppearance();
- towerPreview.snapToGrid(x, y - CELL_SIZE * 1.5);
- break;
+ if (upgradeMenuVisible) return;
+ if (isBuildPanelOpen) {
+ var localClickPos = buildPanelContainer.toLocal({
+ x: x,
+ y: y
+ });
+ var clickedOnSourceTower = false;
+ for (var i = 0; i < sourceTowers.length; i++) {
+ var tower = sourceTowers[i];
+ if (tower.getBounds().contains(localClickPos.x, localClickPos.y)) {
+ clickedOnSourceTower = true;
+ if (gold < getTowerCost(tower.towerType)) {
+ var notification = game.addChild(new Notification("Not enough gold!"));
+ notification.x = 2048 / 2;
+ notification.y = grid.height - 50;
+ return;
+ }
+ towerPreview.visible = true;
+ isDragging = true;
+ towerPreview.towerType = tower.towerType;
+ towerPreview.updateAppearance();
+ towerPreview.snapToGrid(x, y - CELL_SIZE * 1.5);
+ return;
+ }
}
+ if (!clickedOnSourceTower) {
+ toggleBuildPanel('close');
+ }
}
};
game.move = function (x, y, obj) {
if (isDragging) {
towerPreview.snapToGrid(x, y - CELL_SIZE * 1.5);
}
};
game.up = function (x, y, obj) {
- var clickedOnTower = false;
- for (var i = 0; i < towers.length; i++) {
- var tower = towers[i];
- var towerLeft = tower.x - tower.width / 2;
- var towerRight = tower.x + tower.width / 2;
- var towerTop = tower.y - tower.height / 2;
- var towerBottom = tower.y + tower.height / 2;
- if (x >= towerLeft && x <= towerRight && y >= towerTop && y <= towerBottom) {
- clickedOnTower = true;
- break;
- }
- }
- var upgradeMenus = game.children.filter(function (child) {
- return child instanceof UpgradeMenu;
- });
- if (upgradeMenus.length > 0 && !isDragging && !clickedOnTower) {
- var clickedOnMenu = false;
- for (var i = 0; i < upgradeMenus.length; i++) {
- var menu = upgradeMenus[i];
- var menuWidth = 2048;
- var menuHeight = 450;
- var menuLeft = menu.x - menuWidth / 2;
- var menuRight = menu.x + menuWidth / 2;
- var menuTop = menu.y - menuHeight / 2;
- var menuBottom = menu.y + menuHeight / 2;
- if (x >= menuLeft && x <= menuRight && y >= menuTop && y <= menuBottom) {
- clickedOnMenu = true;
- break;
- }
- }
- if (!clickedOnMenu) {
- for (var i = 0; i < upgradeMenus.length; i++) {
- var menu = upgradeMenus[i];
- hideUpgradeMenu(menu);
- }
- for (var i = game.children.length - 1; i >= 0; i--) {
- if (game.children[i].isTowerRange) {
- game.removeChild(game.children[i]);
- }
- }
- selectedTower = null;
- grid.renderDebug();
- }
- }
+ // Ta funkcja jest wywoływana, gdy gracz puści przycisk myszy
if (isDragging) {
+ // Jeśli gracz coś przeciągał, to kończymy ten proces
isDragging = false;
if (towerPreview.canPlace) {
if (!wouldBlockPath(towerPreview.gridX, towerPreview.gridY)) {
placeTower(towerPreview.gridX, towerPreview.gridY, towerPreview.towerType);
@@ -2464,132 +2187,139 @@
} else if (towerPreview.blockedByEnemy) {
var notification = game.addChild(new Notification("Cannot build: Enemy in the way!"));
notification.x = 2048 / 2;
notification.y = grid.height - 50;
- } else if (towerPreview.visible) {
- var notification = game.addChild(new Notification("Cannot build here!"));
- notification.x = 2048 / 2;
- notification.y = grid.height - 50;
}
towerPreview.visible = false;
- if (isDragging) {
- var upgradeMenus = game.children.filter(function (child) {
- return child instanceof UpgradeMenu;
- });
+ // Zamykamy panel budowy dopiero po zakończeniu przeciągania
+ toggleBuildPanel('close');
+ } else {
+ // Jeśli nic nie przeciągaliśmy, to sprawdzamy czy kliknięto na istniejącą wieżę lub menu
+ var clickedOnTower = false;
+ for (var i = 0; i < towers.length; i++) {
+ var tower = towers[i];
+ var towerBounds = tower.getBounds();
+ if (towerBounds.contains(x - tower.x + tower.width / 2, y - tower.y + tower.height / 2)) {
+ clickedOnTower = true;
+ break;
+ }
+ }
+ var upgradeMenus = game.children.filter(function (child) {
+ return child instanceof UpgradeMenu;
+ });
+ if (upgradeMenus.length > 0 && !clickedOnTower) {
+ var clickedOnMenu = false;
for (var i = 0; i < upgradeMenus.length; i++) {
- upgradeMenus[i].destroy();
+ var menu = upgradeMenus[i];
+ var menuBounds = menu.getBounds();
+ if (menuBounds.contains(x - menu.x + menu.width / 2, y - menu.y + menu.height / 2)) {
+ clickedOnMenu = true;
+ break;
+ }
}
+ if (!clickedOnMenu) {
+ for (var i = 0; i < upgradeMenus.length; i++) hideUpgradeMenu(upgradeMenus[i]);
+ for (var i = game.children.length - 1; i >= 0; i--) if (game.children[i].isTowerRange) game.removeChild(game.children[i]);
+ selectedTower = null;
+ }
}
}
};
- waveIndicator = new WaveIndicator();
- waveIndicator.x = 2048 / 2;
- waveIndicator.y = 2732 - 80;
- game.addChild(waveIndicator);
- var nextWaveButtonContainer = new Container();
- var nextWaveButton = new NextWaveButton();
- nextWaveButton.x = 2048 - 200;
- nextWaveButton.y = 2732 - 100 + 20;
- nextWaveButtonContainer.addChild(nextWaveButton);
- game.addChild(nextWaveButtonContainer);
- var towerTypes = ['default', 'rapid', 'sniper', 'splash', 'slow', 'poison'];
- var startX = 2048 / 2 - towerTypes.length * 300 / 2 + 300 / 2;
- var towerY = 2732 - CELL_SIZE * 3 - 90;
- for (var i = 0; i < towerTypes.length; i++) {
- var tower = new SourceTower(towerTypes[i]);
- tower.x = startX + i * 300;
- tower.y = towerY;
- towerLayer.addChild(tower);
- sourceTowers.push(tower);
- }
}
game.update = function () {
if (currentScreenState === 'gameplay') {
- if (waveInProgress) {
- if (!waveSpawned) {
- waveSpawned = true;
- var waveType = waveIndicator.getWaveType(currentWave);
- var enemyCount = waveIndicator.getEnemyCount(currentWave);
- var isBossWave = currentWave % 10 === 0 && currentWave > 0;
- if (isBossWave && waveType !== 'swarm') {
- enemyCount = 1;
+ // --- NOWA LOGIKA FAL OPARTA NA levelData ---
+ if (currentWave <= totalWaves && enemies.length === 0 && !waveInProgress) {
+ waveTimer++;
+ if (waveTimer >= nextWaveTime) {
+ currentWave++;
+ if (currentWave <= totalWaves) {
+ waveInProgress = true;
+ waveSpawned = false;
+ var notification = game.addChild(new Notification("Wave " + currentWave + " incoming!"));
+ notification.x = 2048 / 2;
+ notification.y = grid.height - 150;
+ }
+ }
+ }
+ if (waveInProgress && !waveSpawned) {
+ waveSpawned = true;
+ enemiesKilled = 0; // Resetuj licznik zabitych dla nowej fali
+ // Znajdź dane dla aktualnej fali
+ var waveData = levelData.waves[currentWave - 1];
+ if (waveData) {
+ var enemyCount = waveData.count;
+ var waveType = waveData.type;
+ var isBoss = waveData.isBoss || false;
+ enemiesInCurrentWave = enemyCount; // Ustaw liczbę wrogów w fali
+ updateUI();
+ if (isBoss) {
var notification = game.addChild(new Notification("⚠️ BOSS WAVE! ⚠️"));
notification.x = 2048 / 2;
notification.y = grid.height - 200;
}
for (var i = 0; i < enemyCount; i++) {
+ // --- POPRAWIONA, BEZPIECZNA LOGIKA SPAWNOWANIA ---
var enemy = new Enemy(waveType);
+ var spawnPoints = grid.spawns;
+ var spawnX;
+ if (spawnPoints && spawnPoints.length > 0) {
+ spawnX = spawnPoints[Math.floor(Math.random() * spawnPoints.length)].x;
+ } else {
+ console.warn("Brak punktu startowego 'S' w mapie! Używam domyślnego na środku.");
+ spawnX = Math.floor(grid.cells.length / 2);
+ }
+ var spawnY = -1 - i * 2; // Rozmieszczamy ich lekko w pionie, żeby się nie naкладывали
+ enemy.currentCellX = spawnX;
+ enemy.currentCellY = spawnY;
+ enemy.cellX = spawnX; // Ustawiamy od razu, żeby wiedział dokąd iść
+ enemy.cellY = 5;
+ enemy.waveNumber = currentWave;
+ if (isBoss) {
+ enemy.isBoss = true;
+ } // Upewniamy się, że flaga bossa jest ustawiona
+ var healthMultiplier = Math.pow(1.12, currentWave);
+ enemy.maxHealth = Math.round(enemy.maxHealth * healthMultiplier);
+ enemy.health = enemy.maxHealth;
+ // Dodawanie do warstw
if (enemy.isFlying) {
enemyLayerTop.addChild(enemy);
if (enemy.shadow) {
enemyLayerMiddle.addChild(enemy.shadow);
}
} else {
enemyLayerBottom.addChild(enemy);
}
- var healthMultiplier = Math.pow(1.12, currentWave);
- enemy.maxHealth = Math.round(enemy.maxHealth * healthMultiplier);
- enemy.health = enemy.maxHealth;
- var gridWidth = 24;
- var midPoint = Math.floor(gridWidth / 2);
- var availableColumns = [];
- for (var col = midPoint - 3; col < midPoint + 3; col++) {
- var columnOccupied = false;
- for (var e = 0; e < enemies.length; e++) {
- if (enemies[e].cellX === col && enemies[e].currentCellY < 4) {
- columnOccupied = true;
- break;
- }
- }
- if (!columnOccupied) {
- availableColumns.push(col);
- }
- }
- var spawnX;
- if (availableColumns.length > 0) {
- spawnX = availableColumns[Math.floor(Math.random() * availableColumns.length)];
- } else {
- spawnX = midPoint - 3 + Math.floor(Math.random() * 6);
- }
- var spawnY = -1 - Math.random() * 5;
- enemy.cellX = spawnX;
- enemy.cellY = 5;
- enemy.currentCellX = spawnX;
- enemy.currentCellY = spawnY;
- enemy.waveNumber = currentWave;
enemies.push(enemy);
}
- }
- var currentWaveEnemiesRemaining = false;
- for (var i = 0; i < enemies.length; i++) {
- if (enemies[i].waveNumber === currentWave) {
- currentWaveEnemiesRemaining = true;
- break;
- }
- }
- if (waveSpawned && !currentWaveEnemiesRemaining) {
+ } else {
+ console.warn("Brak danych dla fali numer: " + currentWave);
waveInProgress = false;
- waveSpawned = false;
}
}
+ if (enemies.length === 0 && waveSpawned) {
+ waveInProgress = false;
+ waveSpawned = false;
+ waveTimer = 0; // Rozpocznij odliczanie do następnej fali
+ }
+ // --- Reszta logiki (aktualizacja wrogów, wież, pocisków) ---
for (var a = enemies.length - 1; a >= 0; a--) {
var enemy = enemies[a];
if (enemy.health <= 0) {
- for (var i = 0; i < enemy.bulletsTargetingThis.length; i++) {
- var bullet = enemy.bulletsTargetingThis[i];
- bullet.targetEnemy = null;
- }
+ enemiesKilled++;
+ updateUI();
var goldEarned = enemy.isBoss ? Math.floor(50 + (enemy.waveNumber - 1) * 5) : Math.floor(1 + (enemy.waveNumber - 1) * 0.5);
var goldIndicator = new GoldIndicator(goldEarned, enemy.x, enemy.y);
game.addChild(goldIndicator);
setGold(gold + goldEarned);
- enemiesKilled++;
if (enemy.isBoss) {
var notification = game.addChild(new Notification("Boss defeated! +" + goldEarned + " gold!"));
notification.x = 2048 / 2;
notification.y = grid.height - 150;
}
- updateUI();
+ for (var i = 0; i < enemy.bulletsTargetingThis.length; i++) {
+ enemy.bulletsTargetingThis[i].targetEnemy = null;
+ }
if (enemy.isFlying && enemy.shadow) {
enemyLayerMiddle.removeChild(enemy.shadow);
enemy.shadow = null;
}