Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Code edit (1 edits merged)
Please save this source code
Remix started
Copy Tower Defense Template
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ var Building = Container.expand(function (id, size) { var self = Container.call(this); self.id = id || 'default'; self.size = size || { w: 2, h: 2 }; var assetId = 'building_' + self.id; var baseGraphics = self.attachAsset(assetId, { anchorX: 0, anchorY: 0 }); baseGraphics.width = CELL_SIZE * self.size.w; baseGraphics.height = CELL_SIZE * self.size.h; baseGraphics.tint = 0xFFFFFF; // Wymuszenie domyślnego koloru (naprawia jasność) baseGraphics.alpha = 1; // Wymuszenie pełnej widoczności return self; }); /**** * Classes ****/ var Bullet = Container.expand(function (startX, startY, targetEnemy, towerProps) { var self = Container.call(this); self.targetEnemy = targetEnemy; self.props = towerProps; // Przekazujemy właściwości wieży (obrażenia, prędkość, typ itp.) self.x = startX; self.y = startY; var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); if (self.props.bulletType === 'splash') { bulletGraphics.tint = 0xFF8C00; } if (self.props.bulletType === 'slow') { bulletGraphics.tint = 0xADD8E6; } if (self.props.bulletType === 'chain') { bulletGraphics.tint = 0xFFFF00; } self.update = function () { if (!self.targetEnemy || !self.targetEnemy.parent || self.targetEnemy.health <= 0) { 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.props.bulletSpeed) { // --- LOGIKA TRAFIENIA --- self.targetEnemy.health -= self.props.damage; self.targetEnemy.healthBar.width = self.targetEnemy.health / self.targetEnemy.maxHealth * 70; switch (self.props.bulletType) { case 'slow': self.targetEnemy.slowDuration = self.props.slowDuration; break; case 'splash': gateContainer.addChild(new EffectIndicator(self.x, self.y, 'splash')); enemies.forEach(function (enemy) { if (enemy !== self.targetEnemy) { var distToSplash = Math.sqrt(Math.pow(self.targetEnemy.x - enemy.x, 2) + Math.pow(self.targetEnemy.y - enemy.y, 2)); if (distToSplash < self.props.splashRadius) { enemy.health -= self.props.splashDamage; enemy.healthBar.width = enemy.health / enemy.maxHealth * 70; } } }); break; case 'chain': var chainedTargets = [self.targetEnemy]; var lastTarget = self.targetEnemy; var damage = self.props.damage; for (var i = 0; i < self.props.chainTargets; i++) { damage -= self.props.chainDamageFalloff; if (damage <= 0) { break; } var nextTarget = null; var minChainDist = Infinity; enemies.forEach(function (enemy) { if (!chainedTargets.includes(enemy)) { var distToChain = Math.sqrt(Math.pow(lastTarget.x - enemy.x, 2) + Math.pow(lastTarget.y - enemy.y, 2)); if (distToChain < self.props.range / 2 && distToChain < minChainDist) { minChainDist = distToChain; nextTarget = enemy; } } }); if (nextTarget) { nextTarget.health -= damage; nextTarget.healthBar.width = nextTarget.health / nextTarget.maxHealth * 70; chainedTargets.push(nextTarget); lastTarget = nextTarget; } else { break; } } break; } self.destroy(); } else { var angle = Math.atan2(dy, dx); self.x += Math.cos(angle) * self.props.bulletSpeed; self.y += Math.sin(angle) * self.props.bulletSpeed; } }; 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 = 0.01; self.originalSpeed = self.speed; self.slowed = false; self.slowDuration = 0; self.isBurning = false; self.burnDuration = 0; self.burnDamage = 0; self.lastBurnTick = 0; self.cellX = 0; self.cellY = 0; self.currentCellX = 0; self.currentCellY = 0; self.currentTarget = undefined; self.maxHealth = 50; self.health = self.maxHealth; self.combatSpeed = self.speed * 75; self.bulletsTargetingThis = []; self.waveNumber = currentWave; self.isFlying = false; self.isImmune = false; self.isBoss = false; self.manaCategory = 'strong'; self.damage = 10; self.attackSpeed = 120; self.lastAttack = 0; self.range = 1.5 * CELL_SIZE; self.targetUnit = null; switch (self.type) { case 'fast': self.speed *= 2; self.maxHealth = 100; self.manaCategory = 'strong'; self.damage = 8; break; case 'immune': self.isImmune = true; self.maxHealth = 80; self.manaCategory = 'special'; self.damage = 12; break; case 'flying': self.isFlying = true; self.speed *= 1.5; self.maxHealth = 80; self.manaCategory = 'special'; self.damage = 10; break; case 'swarm': self.maxHealth = 50; self.manaCategory = 'weak'; self.damage = 5; break; case 'normal': default: self.manaCategory = 'strong'; self.damage = 10; break; } self.originalSpeed = self.speed; self.health = self.maxHealth; var assetId = self.type === 'normal' ? 'enemy' : '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.findTarget = function () { if (self.isFlying) { return null; } var closestUnit = null; var minDistance = self.range * self.range; for (var i = 0; i < friendlyUnits.length; i++) { var unit = friendlyUnits[i]; var dx = unit.x - self.x; var dy = unit.y - self.y; var distanceSq = dx * dx + dy * dy; if (distanceSq < minDistance) { minDistance = distanceSq; closestUnit = unit; } } return closestUnit; }; self.attack = function () { if (self.targetUnit && self.targetUnit.health > 0) { self.targetUnit.health -= self.damage; self.targetUnit.healthBar.width = self.targetUnit.health / self.targetUnit.maxHealth * 70; var angle = Math.atan2(self.targetUnit.y - self.y, self.targetUnit.x - self.x); var recoilDistance = 5; var originalX = self.targetUnit.x; var originalY = self.targetUnit.y; tween(self.targetUnit, { x: originalX + Math.cos(angle) * recoilDistance, y: originalY + Math.sin(angle) * recoilDistance }, { duration: 80, easing: tween.easeOut, onFinish: function onFinish() { if (self.targetUnit) { tween(self.targetUnit, { x: originalX, y: originalY }, { duration: 120, easing: tween.easeIn }); } } }); var unitGraphics = self.targetUnit.children[0]; if (unitGraphics) { var originalTint = unitGraphics.tint; unitGraphics.tint = 0xFF0000; LK.setTimeout(function () { if (unitGraphics && !unitGraphics.destroyed) { unitGraphics.tint = originalTint; } }, 100); } createHitSparks(self.targetUnit.x, self.targetUnit.y); } }; self.update = function () { if (self.health <= 0) { self.health = 0; self.healthBar.width = 0; return false; } self.targetUnit = self.findTarget(); if (self.targetUnit && self.targetUnit.health > 0 && !self.isFlying) { // --- LOGIKA WALKI DLA JEDNOSTEK NAZIEMNYCH --- var dx = self.targetUnit.x - self.x; var dy = self.targetUnit.y - self.y; var distance = Math.sqrt(dx * dx + dy * dy); // Ustawienie rotacji w kierunku celu var angle = Math.atan2(dy, dx); var enemyGraphics = self.children[0]; if (distance < self.range) { // Jesteśmy w zasięgu ataku - zatrzymujemy się i atakujemy if (LK.ticks - self.lastAttack >= self.attackSpeed) { self.attack(); self.lastAttack = LK.ticks; } } else { // Cel jest poza zasięgiem - poruszamy się w jego kierunku z logiką separacji // Wektor w kierunku celu var moveX = dx; var moveY = dy; // --- Logika separacji (przeniesiona z FriendlyUnit) --- var separationX = 0; var separationY = 0; var separationRadius = 40; enemies.forEach(function (otherEnemy) { if (otherEnemy === self || otherEnemy.isFlying) { return; } var otherDx = self.x - otherEnemy.x; var otherDy = self.y - otherEnemy.y; var otherDist = Math.sqrt(otherDx * otherDx + otherDy * otherDy); if (otherDist > 0 && otherDist < separationRadius) { separationX += otherDx / (otherDist * otherDist); separationY += otherDy / (otherDist * otherDist); } }); // Dostosowujemy wagę separacji, aby uniknąć "trzęsienia się" var separationWeight = 15; moveX += separationX * separationWeight; moveY += separationY * separationWeight; // Normalizacja wektora i ruch var moveMagnitude = Math.sqrt(moveX * moveX + moveY * moveY); if (moveMagnitude > 0) { self.x += moveX / moveMagnitude * self.combatSpeed; self.y += moveY / moveMagnitude * self.combatSpeed; } } } else { // --- LOGIKA ŚCIEŻKI (gdy nie ma celu w zasięgu lub jednostka latająca) --- if (grid.updateEnemy(self)) { return true; // Dotarł do końca } // Aktualizacja rotacji na podstawie ścieżki (z grid.updateEnemy) 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); var enemyGraphics = self.children[0]; } } } // Aktualizacja paska zdrowia var enemyGraphics = self.children[0]; healthBarOutline.y = healthBarBG.y = healthBar.y = -enemyGraphics.height / 2 - 10; return false; }; return self; }); var FireWall = Container.expand(function (x, y, spellData) { var self = Container.call(this); self.x = x; self.y = y; self.spellData = spellData; self.duration = self.spellData.duration; self.radiusSq = self.spellData.radius * self.spellData.radius; var effectGraphics = self.attachAsset('rangeCircle', { anchorX: 0.5, anchorY: 0.5 }); effectGraphics.blendMode = 1; effectGraphics.tint = 0xFF4500; effectGraphics.width = effectGraphics.height = self.spellData.radius * 2; effectGraphics.alpha = 0; tween(effectGraphics, { alpha: 0.6 }, { duration: 150 }); self.update = function () { self.duration--; if (self.duration <= 0) { tween(effectGraphics, { alpha: 0 }, { duration: 200, onFinish: function onFinish() { var spellIndex = activeSpells.indexOf(self); if (spellIndex !== -1) { activeSpells.splice(spellIndex, 1); } self.destroy(); } }); return; } enemies.forEach(function (enemy) { if (enemy.isBurning) { return; } var dx = enemy.x - self.x; var dy = enemy.y - self.y; if (dx * dx + dy * dy < self.radiusSq) { enemy.isBurning = true; enemy.burnDuration = self.spellData.dotDuration; enemy.burnDamage = self.spellData.dotDamage; } }); }; return self; }); var FriendlyUnit = Container.expand(function (startX, startY) { var self = Container.call(this); self.x = startX; self.y = startY; self.speed = 1.2; self.maxHealth = 150; self.health = self.maxHealth; self.damage = 5; self.attackSpeed = 90; self.lastAttack = 0; self.range = 1.2 * CELL_SIZE; self.targetEnemy = null; var unitGraphics = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); unitGraphics.tint = 0x0096FF; 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 = -unitGraphics.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.findClosestEnemy = function () { var closestEnemy = null; var minDistanceSq = Infinity; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy.isFlying) { continue; } var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distanceSq = dx * dx + dy * dy; if (distanceSq < minDistanceSq) { minDistanceSq = distanceSq; closestEnemy = enemy; } } return closestEnemy; }; self.attack = function () { if (self.targetEnemy && self.targetEnemy.health > 0) { self.targetEnemy.health -= self.damage; self.targetEnemy.healthBar.width = self.targetEnemy.health / self.targetEnemy.maxHealth * 70; var angle = Math.atan2(self.targetEnemy.y - self.y, self.targetEnemy.x - self.x); var recoilDistance = 5; var originalX = self.targetEnemy.x; var originalY = self.targetEnemy.y; tween(self.targetEnemy, { x: originalX + Math.cos(angle) * recoilDistance, y: originalY + Math.sin(angle) * recoilDistance }, { duration: 80, easing: tween.easeOut, onFinish: function onFinish() { if (self.targetEnemy) { tween(self.targetEnemy, { x: originalX, y: originalY }, { duration: 120, easing: tween.easeIn }); } } }); var enemyGraphics = self.targetEnemy.children[0]; if (enemyGraphics) { var originalTint = enemyGraphics.tint; enemyGraphics.tint = 0xFF0000; LK.setTimeout(function () { if (enemyGraphics && !enemyGraphics.destroyed) { enemyGraphics.tint = originalTint; } }, 100); } createHitSparks(self.targetEnemy.x, self.targetEnemy.y); } }; self.die = function () { peopleRegenQueue.push(LK.ticks); var unitIndex = friendlyUnits.indexOf(self); if (unitIndex !== -1) { friendlyUnits.splice(unitIndex, 1); } self.destroy(); }; self.update = function () { if (self.health <= 0) { self.die(); return; } self.healthBar.width = self.health / self.maxHealth * 70; if (!self.targetEnemy || self.targetEnemy.health <= 0) { self.targetEnemy = self.findClosestEnemy(); } if (self.targetEnemy) { 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.range) { if (LK.ticks - self.lastAttack >= self.attackSpeed) { self.attack(); self.lastAttack = LK.ticks; } } else { var gridX = Math.floor((self.x - grid.x) / CELL_SIZE); var gridY = Math.floor((self.y - grid.y) / CELL_SIZE); var currentCell = grid.getCell(gridX, gridY); var targetCell = null; if (currentCell && currentCell.aggressionTargets && currentCell.aggressionTargets.length > 0) { targetCell = currentCell.aggressionTargets[0]; } var moveX = 0; var moveY = 0; if (targetCell) { var targetX = grid.x + targetCell.x * CELL_SIZE + CELL_SIZE / 2; var targetY = grid.y + targetCell.y * CELL_SIZE + CELL_SIZE / 2; var angle = Math.atan2(targetY - self.y, targetX - self.x); moveX += Math.cos(angle); moveY += Math.sin(angle); } else { moveY -= 1; } var separationX = 0; var separationY = 0; var separationRadius = 40; for (var i = 0; i < friendlyUnits.length; i++) { var otherUnit = friendlyUnits[i]; if (otherUnit === self) { continue; } var otherDx = self.x - otherUnit.x; var otherDy = self.y - otherUnit.y; var otherDist = Math.sqrt(otherDx * otherDx + otherDy * otherDy); if (otherDist < separationRadius) { separationX += otherDx / (otherDist * otherDist); separationY += otherDy / (otherDist * otherDist); } } var separationWeight = 0.5; moveX += separationX * separationWeight; moveY += separationY * separationWeight; var moveMagnitude = Math.sqrt(moveX * moveX + moveY * moveY); if (moveMagnitude > 0) { var finalMoveX = moveX / moveMagnitude * self.speed; var finalMoveY = moveY / moveMagnitude * self.speed; self.x += finalMoveX; self.y += finalMoveY; } } } else { self.y -= self.speed; if (self.y < grid.y) { self.die(); } } }; 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, aggressionScore: 0, aggressionPathId: 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 = []; cell.aggressionTargets = []; } } self.getCell = function (x, y) { return self.cells[x] && self.cells[x][y]; }; self.pathFind = function () { var before = new Date().getTime(); self.goals = []; for (var i = 0; i < gridWidth; i++) { for (var j = 0; j < gridHeight; j++) { if (self.cells[i][j].type === 4) { 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; } function processNode(node, targetValue, targetNode) { if (node && (node.type === 0 || node.type === 3 || node.type === 4)) { 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; if (node.up && node.left && (node.up.type === 0 || node.up.type === 3) && (node.left.type === 0 || node.left.type === 3)) { processNode(node.upLeft, targetScore, node); } if (node.up && node.right && (node.up.type === 0 || node.up.type === 3) && (node.right.type === 0 || node.right.type === 3)) { processNode(node.upRight, targetScore, node); } if (node.down && node.right && (node.down.type === 0 || node.down.type === 3) && (node.right.type === 0 || node.right.type === 3)) { processNode(node.downRight, targetScore, node); } if (node.down && node.left && (node.down.type === 0 || node.down.type === 3) && (node.left.type === 0 || node.left.type === 3)) { processNode(node.downLeft, targetScore, node); } targetScore = node.score + 10000; 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 === 3) { self.spawns.push(self.cells[i][j]); } } } for (var a = 0; a < self.spawns.length; a++) { if (self.spawns[a].pathId != pathId) { 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) { return true; } } } else if (!target || target.pathId != pathId) { return true; } } }; self.calculateAggressionPath = function () { var toProcess = self.spawns.concat([]); var aggressionPathId = pathId + 1; for (var a = 0; a < toProcess.length; a++) { toProcess[a].aggressionPathId = aggressionPathId; toProcess[a].aggressionScore = 0; } function processAggressionNode(node, targetValue, targetNode) { if (node && (node.type === 0 || node.type === 3 || node.type === 4)) { if (node.aggressionPathId < aggressionPathId || targetValue < node.aggressionScore) { node.aggressionTargets = [targetNode]; } else if (node.aggressionPathId == aggressionPathId && targetValue == node.aggressionScore) { node.aggressionTargets.push(targetNode); } if (node.aggressionPathId < aggressionPathId || targetValue < node.aggressionScore) { node.aggressionScore = targetValue; if (node.aggressionPathId != aggressionPathId) { toProcess.push(node); } node.aggressionPathId = aggressionPathId; } } } while (toProcess.length) { var nodes = toProcess; toProcess = []; for (var a = 0; a < nodes.length; a++) { var node = nodes[a]; var targetScore = node.aggressionScore + 10000; processAggressionNode(node.up, targetScore, node); processAggressionNode(node.right, targetScore, node); processAggressionNode(node.down, targetScore, node); processAggressionNode(node.left, targetScore, node); } } }; self.renderDebug = function () {}; self.updateEnemy = function (enemy) { var cell = grid.getCell(enemy.cellX, enemy.cellY); if (cell && cell.type == 4) { 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; } } 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; } else { 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; return false; }; }); 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 PlacementPreview = Container.expand(function () { var self = Container.call(this); self.itemType = 'tower'; self.subType = 'guard'; self.itemSize = { w: 2, h: 2 }; self.hasEnoughGold = true; self.canPlace = false; self.gridX = 0; self.gridY = 0; var rangeIndicator = self.attachAsset('rangeCircle', { anchorX: 0.5, anchorY: 0.5, alpha: 0.3 }); var previewHolder = new Container(); self.addChild(previewHolder); self.updateAppearance = function () { while (previewHolder.children.length) { previewHolder.removeChildAt(0); } var previewGraphics; // Używamy niezawodnego kształtu 'cell' jako bazy dla podglądu previewGraphics = previewHolder.attachAsset('cell', { anchorX: 0.5, anchorY: 0.5 }); // Ustawiamy prawidłowy rozmiar i przezroczystość previewGraphics.width = CELL_SIZE * self.itemSize.w; previewGraphics.height = CELL_SIZE * self.itemSize.h; previewGraphics.alpha = 0.5; // Domyślny, niebieski kolor dla prawidłowego miejsca previewGraphics.tint = 0x00BFFF; rangeIndicator.visible = false; if (self.itemType === 'tower') { var stats = TOWER_DATA[self.subType] ? TOWER_DATA[self.subType][1] : null; if (stats && stats.range) { rangeIndicator.width = rangeIndicator.height = stats.range * 2; rangeIndicator.visible = true; } } // Jeśli miejsce jest nieprawidłowe, zmieniamy kolor na czerwony if (!self.canPlace || !self.hasEnoughGold) { previewGraphics.tint = 0xFF0000; } }; self.updatePlacementStatus = function (targetGrid) { var validGridPlacement = true; var currentGrid = targetGrid || grid; if (self.gridY <= 4 && self.itemType === 'tower') { validGridPlacement = false; } if (self.gridY + self.itemSize.h > currentGrid.cells[0].length && self.itemType === 'building') { validGridPlacement = false; } if (validGridPlacement) { for (var i = 0; i < self.itemSize.w; i++) { for (var j = 0; j < self.itemSize.h; j++) { var cell = currentGrid.getCell(self.gridX + i, self.gridY + j); if (!cell || cell.type !== 1) { validGridPlacement = false; break; } } if (!validGridPlacement) { break; } } } var cost = 0; if (self.itemType === 'tower') { cost = getTowerCost(self.subType); } else { var buildingCost = getBuildingCost(self.subType); cost = buildingCost.gold; } self.hasEnoughGold = gold >= cost; self.canPlace = validGridPlacement; self.updateAppearance(); }; self.snapToGrid = function (x, y, targetGrid) { var currentGrid = targetGrid || grid; var gridPosX = x - currentGrid.x; var gridPosY = y - currentGrid.y; self.gridX = Math.floor(gridPosX / CELL_SIZE); self.gridY = Math.floor(gridPosY / CELL_SIZE); var itemWidth = CELL_SIZE * self.itemSize.w; var itemHeight = CELL_SIZE * self.itemSize.h; self.x = currentGrid.x + self.gridX * CELL_SIZE + itemWidth / 2; self.y = currentGrid.y + self.gridY * CELL_SIZE + itemHeight / 2; self.updatePlacementStatus(currentGrid); }; return self; }); var SourceBuilding = Container.expand(function (buildingType) { var self = Container.call(this); self.buildingType = buildingType || 'default'; var buildingData = BUILDING_DATA[self.buildingType]; var assetId = 'building_' + self.buildingType; var baseGraphics = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); baseGraphics.width = 130; baseGraphics.height = 130; var typeLabelShadow = new Text2(buildingData.name, { 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(buildingData.name, { size: 50, fill: 0xFFFFFF, weight: 800 }); typeLabel.anchor.set(0.5, 0.5); typeLabel.y = -20; self.addChild(typeLabel); var costLabelShadow = new Text2(buildingData.cost, { 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(buildingData.cost, { size: 50, fill: 0xFFD700, weight: 800 }); costLabel.anchor.set(0.5, 0.5); costLabel.y = 20 + 12; self.addChild(costLabel); self.update = function () { var currentCost = getBuildingCost(self.buildingType); if (costLabel.text !== currentCost.gold) { costLabel.setText(currentCost.gold); costLabelShadow.setText(currentCost.gold); } var canAfford = gold >= currentCost.gold; self.alpha = canAfford ? 1 : 0.5; }; return self; }); var SourceSpell = Container.expand(function (spellId) { var self = Container.call(this); self.spellId = spellId; var spellData = SPELL_DATA[self.spellId]; var baseGraphics = self.attachAsset('tower', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.3, scaleY: 1.3 }); switch (self.spellId) { case 'fire_wall': baseGraphics.tint = 0xFF4500; break; case 'healing_field': baseGraphics.tint = 0x32CD32; break; case 'lightning_stun': baseGraphics.tint = 0x1E90FF; break; case 'curse': baseGraphics.tint = 0x9400D3; break; } var nameLabelShadow = new Text2(spellData.name, { size: 40, fill: 0x000000, weight: 800, align: 'center' }); nameLabelShadow.anchor.set(0.5, 0.5); nameLabelShadow.x = 2; nameLabelShadow.y = -20 + 2; self.addChild(nameLabelShadow); var nameLabel = new Text2(spellData.name, { size: 40, fill: 0xFFFFFF, weight: 800, align: 'center' }); nameLabel.anchor.set(0.5, 0.5); nameLabel.y = -20; self.addChild(nameLabel); var costLabelShadow = new Text2(spellData.cost, { 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(spellData.cost, { size: 50, fill: 0x00BFFF, weight: 800 }); costLabel.anchor.set(0.5, 0.5); costLabel.y = 20 + 12; self.addChild(costLabel); self.update = function () { var canAfford = mana >= spellData.cost; self.alpha = canAfford ? 1 : 0.5; }; return self; }); var SourceTower = Container.expand(function (towerType) { var self = Container.call(this); self.towerType = towerType || 'default'; var assetId = 'tower_' + self.towerType; var baseGraphics = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // ZMIANA: Ustawiamy stały, mniejszy rozmiar dla wszystkich ikon w panelu baseGraphics.width = 130; baseGraphics.height = 170; var stats = TOWER_DATA[self.towerType][1]; // Pobierz dane dla poziomu 1 var towerName = stats.name; var initialCost = stats.cost; var typeLabelShadow = new Text2(towerName, { size: 40, fill: 0x000000, weight: 800 }); typeLabelShadow.anchor.set(0.5, 0.5); typeLabelShadow.x = 2; typeLabelShadow.y = -20 + 2; self.addChild(typeLabelShadow); var typeLabel = new Text2(towerName, { size: 40, fill: 0xFFFFFF, weight: 800 }); typeLabel.anchor.set(0.5, 0.5); typeLabel.y = -20; self.addChild(typeLabel); var costLabelShadow = new Text2(initialCost, { 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(initialCost, { size: 50, fill: 0xFFD700, weight: 800 }); costLabel.anchor.set(0.5, 0.5); costLabel.y = 20 + 12; self.addChild(costLabel); self.update = function () { // Ta funkcja będzie aktualizować koszt w przyszłości, gdy wprowadzimy dynamiczne zmiany var currentCost = TOWER_DATA[self.towerType][1].cost; if (!isBuildPhase && currentWave > 0) { currentCost = Math.floor(currentCost * 1.5); } if (costLabel.text !== currentCost) { costLabel.setText(currentCost); costLabelShadow.setText(currentCost); } var canAfford = gold >= currentCost; self.alpha = canAfford ? 1 : 0.5; }; return self; }); var SourceUnit = Container.expand(function (unitId) { var self = Container.call(this); self.unitId = unitId; var unitData = UNIT_DATA[self.unitId]; var baseGraphics = self.attachAsset('tower', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.3, scaleY: 1.3 }); baseGraphics.tint = 0x0096FF; var nameLabelShadow = new Text2(unitData.name, { size: 40, fill: 0x000000, weight: 800, align: 'center' }); nameLabelShadow.anchor.set(0.5, 0.5); nameLabelShadow.x = 2; nameLabelShadow.y = -20 + 2; self.addChild(nameLabelShadow); var nameLabel = new Text2(unitData.name, { size: 40, fill: 0xFFFFFF, weight: 800, align: 'center' }); nameLabel.anchor.set(0.5, 0.5); nameLabel.y = -20; self.addChild(nameLabel); var costLabelShadow = new Text2(unitData.cost, { 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(unitData.cost, { 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 >= unitData.cost && people > 0; self.alpha = canAfford ? 1 : 0.5; }; self.down = function () { if (gold >= unitData.cost && people > 0) { setGold(gold - unitData.cost); people--; updateUI(); panelActionTaken = true; var spawnLaneOffset = 80; var spawnLanes = [2048 / 2 - spawnLaneOffset, 2048 / 2, 2048 / 2 + spawnLaneOffset]; var spawnX = spawnLanes[Math.floor(Math.random() * spawnLanes.length)]; spawnX += Math.random() * 40 - 20; var spawnY = grid.y + (grid.cells[0].length - 3) * CELL_SIZE; var newUnit = new FriendlyUnit(spawnX, spawnY); friendlyUnitLayer.addChild(newUnit); friendlyUnits.push(newUnit); } else { var message = gold < unitData.cost ? "Not enough gold!" : "Not enough people!"; var notification = game.addChild(new Notification(message)); notification.x = 2048 / 2; notification.y = grid.height - 50; } }; return self; }); var SpellPreview = Container.expand(function () { var self = Container.call(this); self.visible = false; var rangeIndicator = self.attachAsset('rangeCircle', { anchorX: 0.5, anchorY: 0.5, alpha: 0.4 }); rangeIndicator.blendMode = 1; self.updateAppearance = function (spellData, canAfford) { rangeIndicator.width = rangeIndicator.height = spellData.radius * 2; rangeIndicator.tint = canAfford ? 0x00BFFF : 0xFF0000; self.visible = true; }; self.updatePosition = function (x, y) { self.x = x; self.y = y; }; return self; }); var Tower = Container.expand(function (towerType) { var self = Container.call(this); self.towerType = towerType; self.level = 1; self.levelPath = ''; self.maxLevel = 3; self.gridX = 0; self.gridY = 0; self.targetEnemy = null; self.lastFired = 0; self.totalValue = 0; self.baseStats = {}; self.buffs = []; self.towersInAura = []; function applyStats(stats) { self.baseStats = Object.assign({}, stats); // Kopiowanie wszystkich właściwości ze stats do self Object.assign(self, stats); self.recalculateStats(); } self.recalculateStats = function () { self.damage = self.baseStats.damage; self.fireRate = self.baseStats.fireRate; self.buffs.forEach(function (buff) { if (buff.effect.damage) { self.damage *= buff.effect.damage; } if (buff.effect.attackSpeed) { self.fireRate /= buff.effect.attackSpeed; } }); }; self.addBuff = function (buff) { if (!self.buffs.find(function (b) { return b.source === buff.source; })) { self.buffs.push(buff); self.recalculateStats(); } }; self.removeBuff = function (buffSource) { self.buffs = self.buffs.filter(function (b) { return b.source !== buffSource; }); self.recalculateStats(); }; var initialStats = TOWER_DATA[self.towerType][self.level]; self.cost = initialStats.cost; self.totalValue = self.cost; applyStats(initialStats); var assetId = 'tower_' + self.towerType; var baseGraphics = self.attachAsset(assetId, { anchorX: 0.5, anchorY: 0.5 }); // Kontrola szerokości i wysokości var towerScaleX = 2.2; // Skala dla szerokości var towerScaleY = 2.8; // Skala dla wysokości baseGraphics.width = CELL_SIZE * towerScaleX; baseGraphics.height = CELL_SIZE * towerScaleY; self.getRange = function () { return self.range; }; self.getTotalValue = function () { return self.totalValue; }; self.upgrade = function (path) { path = path || ''; var nextLevelKey = self.level + 1 + path; var upgradeData = TOWER_DATA[self.towerType][nextLevelKey]; if (!upgradeData) { return; } if (self.aura) { self.removeAuraFromTowers(); } self.level++; self.levelPath = path; self.totalValue += upgradeData.cost; applyStats(upgradeData); if (self.aura) { self.applyAuraToTowers(); } }; self.destroyAura = function () { if (self.aura) { self.removeAuraFromTowers(); } }; self.placeOnGrid = function (gridX, gridY) { self.gridX = gridX; self.gridY = gridY; self.x = grid.x + (gridX + 1) * CELL_SIZE; self.y = grid.y + (gridY + 1) * CELL_SIZE; 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 = 5; } } } if (self.aura) { self.applyAuraToTowers(); } }; self.findTarget = function () { if (self.damage === 0) { return null; } if (self.bulletType === 'slow') { var potentialTargets = []; enemies.forEach(function (enemy) { if (!enemy.slowed) { var dx = enemy.x - self.x; var dy = enemy.y - self.y; if (dx * dx + dy * dy < self.range * self.range) { potentialTargets.push(enemy); } } }); if (potentialTargets.length === 0) { // Jeśli wszyscy są spowolnieni, wybierz dowolnego enemies.forEach(function (enemy) { var dx = enemy.x - self.x; var dy = enemy.y - self.y; if (dx * dx + dy * dy < self.range * self.range) { potentialTargets.push(enemy); } }); } if (potentialTargets.length > 0) { return potentialTargets[Math.floor(Math.random() * potentialTargets.length)]; } } var closestEnemy = null; var minDistance = self.range * self.range; for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var dx = enemy.x - self.x; var dy = enemy.y - self.y; var distanceSq = dx * dx + dy * dy; if (distanceSq < minDistance) { minDistance = distanceSq; closestEnemy = enemy; } } return closestEnemy; }; self.fire = function () { if (self.targetEnemy && self.targetEnemy.health > 0) { var dx = self.targetEnemy.x - self.x; var dy = self.targetEnemy.y - self.y; var angle = Math.atan2(dy, dx); var bulletX = self.x + Math.cos(angle) * 40; var bulletY = self.y + Math.sin(angle) * 40; var bullet = new Bullet(bulletX, bulletY, self.targetEnemy, self); gateContainer.addChild(bullet); bullets.push(bullet); } }; self.applyAuraToTowers = function () { self.removeAuraFromTowers(); towers.forEach(function (tower) { if (tower === self) { return; } var dx = tower.x - self.x; var dy = tower.y - self.y; if (dx * dx + dy * dy < self.range * self.range) { tower.addBuff({ source: self, effect: self.aura }); self.towersInAura.push(tower); } }); }; self.removeAuraFromTowers = function () { self.towersInAura.forEach(function (tower) { tower.removeBuff(self); }); self.towersInAura = []; }; self.update = function () { if (self.towerType === 'banner' || self.aura) { return; } if (self.buffs.length > 0) { baseGraphics.alpha = 0.7 + Math.sin(LK.ticks / 10) * 0.3; } else { baseGraphics.alpha = 1; } self.targetEnemy = self.findTarget(); if (self.targetEnemy) { if (self.fireRate > 0 && LK.ticks - self.lastFired >= self.fireRate) { self.fire(); self.lastFired = LK.ticks; } } }; self.down = function (x, y, obj) { if (isMenuTransitioning) { return; } // Sprawdź blokadę na samym początku if (selectedTower === self) { closeActiveUpgradeMenu(); return; } closeActiveUpgradeMenu(); selectedTower = self; var upgradeMenu = game.addChild(new UpgradeMenu(self)); upgradeMenu.x = 2048 / 2; // Ustawiamy blokadę na czas animacji wysuwania isMenuTransitioning = true; tween(upgradeMenu, { y: 2732 - 250 }, { duration: 200, easing: tween.backOut, onFinish: function onFinish() { isMenuTransitioning = false; // Wyłącz blokadę po zakończeniu } }); var rangeCircle = new Container(); rangeCircle.isTowerRange = true; rangeCircle.tower = self; var graphics = rangeCircle.attachAsset('rangeCircle', { anchorX: 0.5, anchorY: 0.5 }); graphics.width = graphics.height = self.getRange() * 2; graphics.alpha = 0.2; graphics.tint = 0xFFFFFF; rangeCircle.x = self.x; rangeCircle.y = self.y; gateContainer.addChildAt(rangeCircle, gateContainer.children.indexOf(towerLayer)); }; 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.name, { size: 80, fill: 0xFFFFFF, weight: 800 }); towerTypeText.anchor.set(0, 0); towerTypeText.x = -840; towerTypeText.y = -160; self.addChild(towerTypeText); function getTowerFireRate() { if (self.tower.fireRate > 0) { return (60 / self.tower.fireRate).toFixed(1) + '/s'; } return 'N/A'; } var statsText = new Text2('Level: ' + self.tower.level + '/' + self.tower.maxLevel + '\nDamage: ' + self.tower.damage + '\nFire Rate: ' + getTowerFireRate(), { size: 70, fill: 0xFFFFFF, weight: 400 }); statsText.anchor.set(0, 0.5); statsText.x = -840; statsText.y = 50; self.addChild(statsText); var sellButton = new Container(); var sellButtonBackground = sellButton.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5, width: 400, height: 120, tint: 0xCC0000 }); var sellValue = getTowerSellValue(self.tower.getTotalValue()); var sellButtonText = new Text2('Sell: +' + sellValue, { size: 50, fill: 0xFFFFFF, weight: 800 }); sellButtonText.anchor.set(0.5, 0.5); sellButton.addChild(sellButtonText); sellButton.x = 650; sellButton.y = 120; self.addChild(sellButton); sellButton.down = function () { // NOWA LINIA - usuwa aurę przed zniszczeniem wieży if (self.tower.aura) { self.tower.destroyAura(); } setGold(gold + getTowerSellValue(self.tower.getTotalValue())); 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 = 1; } } } grid.pathFind(); if (selectedTower === self.tower) { selectedTower = null; } var towerIndex = towers.indexOf(self.tower); if (towerIndex !== -1) { towers.splice(towerIndex, 1); } towerLayer.removeChild(self.tower); hideUpgradeMenu(self); }; var closeButton = new Container(); var closeBackground = closeButton.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5, width: 90, height: 90, 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; self.addChild(closeButton); closeButton.down = function () { hideUpgradeMenu(self); }; function createUpgradeButton(upgradeKey, position, totalButtons) { var upgradeData = TOWER_DATA[self.tower.towerType][upgradeKey]; if (!upgradeData) { return; } var button = new Container(); var bg = button.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5 }); bg.width = 450; bg.height = 180 / totalButtons + 60 / totalButtons; var buttonTextContent = 'Upgrade: ' + upgradeData.cost + 'g'; if (upgradeData.name) { buttonTextContent = upgradeData.name + '\n' + upgradeData.cost + 'g'; if (upgradeData.description) { buttonTextContent += '\n' + upgradeData.description; } } var text = new Text2(buttonTextContent, { size: 40, fill: 0xFFFFFF, weight: 800, align: 'center' }); text.anchor.set(0.5, 0.5); button.addChild(text); var canAfford = gold >= upgradeData.cost; bg.tint = canAfford ? 0x00AA00 : 0x888888; button.alpha = canAfford ? 1 : 0.7; button.down = function () { if (gold >= upgradeData.cost) { setGold(gold - upgradeData.cost); self.tower.upgrade(upgradeKey.replace((self.tower.level + 1).toString(), '')); // Przekaż ścieżkę 'A' lub 'B' hideUpgradeMenu(self); } else { var notification = game.addChild(new Notification("Not enough gold!")); notification.x = 2048 / 2; notification.y = grid.height - 50; } }; button.x = position.x; button.y = position.y; self.addChild(button); } function populateUpgradeButtons() { if (self.tower.level >= self.tower.maxLevel) { var maxLevelText = new Text2('Max Level', { size: 80, fill: 0xFFD700, weight: 800 }); maxLevelText.anchor.set(0.5, 0.5); maxLevelText.x = 50; self.addChild(maxLevelText); return; } var nextLevel = self.tower.level + 1; var upgradeKeys = Object.keys(TOWER_DATA[self.tower.towerType]).filter(function (key) { return key.startsWith(nextLevel.toString()); }); if (upgradeKeys.length === 1) { createUpgradeButton(upgradeKeys[0], { x: 50, y: 0 }, 1); } else if (upgradeKeys.length > 1) { for (var i = 0; i < upgradeKeys.length; i++) { var yPos = -85 + i * 170; createUpgradeButton(upgradeKeys[i], { x: 50, y: yPos }, upgradeKeys.length); } } } populateUpgradeButtons(); self.update = function () { // Ta funkcja jest teraz pusta, cała logika dzieje się przy tworzeniu. // Można ją rozbudować o dynamiczne odświeżanie, jeśli będzie taka potrzeba. }; // Nadpisanie funkcji, aby poprawnie ukrywała też okrąg zasięgu var originalDestroy = self.destroy; self.destroy = function () { for (var i = game.children.length - 1; i >= 0; i--) { if (game.children[i].isTowerRange && game.children[i].tower === self.tower) { game.removeChild(game.children[i]); } } if (selectedTower === self.tower) { selectedTower = null; } originalDestroy.call(self); }; return self; }); /**** * Initialize Game ****/ /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x333333 }); /**** * Game Code ****/ /**** * Game Code ****/ function createHitSparks(x, y) { var numSparks = 5; for (var i = 0; i < numSparks; i++) { var spark = new Container(); var sparkGraphics = spark.attachAsset('star_particle', { anchorX: 0.5, anchorY: 0.5, tint: 0xFFD700 }); spark.x = x; spark.y = y; gateContainer.addChild(spark); var angle = Math.random() * Math.PI * 2; var distance = Math.random() * 40 + 20; tween(spark, { x: x + Math.cos(angle) * distance, y: y + Math.sin(angle) * distance, alpha: 0 }, { duration: 500, easing: tween.easeOut, onFinish: function onFinish() { spark.destroy(); } }); } } var CELL_SIZE = 76; var TOWER_DATA = { 'guard': { 1: { name: 'Guard Tower', cost: 30, damage: 4, fireRate: 60, range: 4 * CELL_SIZE, bulletSpeed: 7 }, 2: { name: 'Veteran Guard', cost: 60, damage: 8, fireRate: 60, range: 4 * CELL_SIZE, bulletSpeed: 7 } }, 'crossbow': { 1: { name: 'Crossbow Tower', cost: 50, damage: 12, fireRate: 150, range: 7 * CELL_SIZE, bulletSpeed: 15 }, 2: { name: 'Heavy Crossbow', cost: 100, damage: 24, fireRate: 150, range: 7 * CELL_SIZE, bulletSpeed: 15 } }, 'mage': { 1: { name: 'Mage Tower', cost: 40, damage: 6, fireRate: 90, range: 4 * CELL_SIZE, bulletSpeed: 8, bulletType: 'normal' }, '2F': { name: 'Fire Mage', cost: 80, damage: 6, fireRate: 120, range: 5 * CELL_SIZE, bulletSpeed: 8, bulletType: 'splash', splashDamage: 3, splashRadius: 1 * CELL_SIZE }, '2W': { name: 'Water Mage', cost: 80, damage: 4, fireRate: 120, range: 5 * CELL_SIZE, bulletSpeed: 8, bulletType: 'slow', slowAmount: 0.6, slowDuration: 120 }, '2L': { name: 'Lightning Mage', cost: 80, damage: 7, fireRate: 150, range: 6 * CELL_SIZE, bulletSpeed: 20, bulletType: 'chain', chainTargets: 2, chainDamageFalloff: 2 } }, 'banner': { 1: { name: 'War Banner', cost: 50, damage: 0, range: 4 * CELL_SIZE, aura: { 'attackSpeed': 1.15 } }, '2A': { name: 'Banner of Fury', cost: 100, range: 4 * CELL_SIZE, aura: { 'attackSpeed': 1.30 } }, '2B': { name: 'Banner of Command', cost: 100, range: 4 * CELL_SIZE, aura: { 'attackSpeed': 1.15, 'damage': 1.15 } } } }; var BUILDING_DATA = { 'home': { id: 'home', name: 'Home', size: { w: 2, h: 2 }, cost: 100 }, 'workshop': { id: 'workshop', name: 'Workshop', size: { w: 4, h: 2 }, cost: 250 }, 'barracks': { id: 'barracks', name: 'Barracks', size: { w: 6, h: 7 }, cost: 400 }, 'magic_academy': { id: 'magic_academy', name: 'Magic Academy', size: { w: 6, h: 8 }, cost: 300 }, 'tavern': { id: 'tavern', name: 'Tavern', size: { w: 5, h: 5 }, cost: 150 }, 'smithy': { id: 'smithy', name: 'Smithy', size: { w: 5, h: 6 }, cost: 350 } }; var SPELL_DATA = { 'fire_wall': { id: 'fire_wall', name: 'Fire Wall', description: 'Damages enemies in an area.', cost: 0, radius: 2.5 * CELL_SIZE, duration: 360, // 6 sekund (60fps * 6) dotDamage: 5, // Obrażenia na sekundę dotDuration: 180 }, 'healing_field': { id: 'healing_field', name: 'Healing Field', description: 'Heals allied targets in an area.', cost: 35, heal: 25, radius: 3 * CELL_SIZE }, 'lightning_stun': { id: 'lightning_stun', name: 'Thunderstorm', description: 'Stuns enemies in an area.', cost: 25, stunDuration: 2 * 60, radius: 3 * CELL_SIZE }, 'curse': { id: 'curse', name: 'Curse', description: 'Cursed enemies take +20% damage.', cost: 30, damageMultiplier: 1.2, duration: 8 * 60, radius: 3.5 * CELL_SIZE } }; var LEVEL_DATA_ALL = { 1: { levelName: "Level 1: Royal Road", initialGold: 2000, initialLives: 20, mapLayout: ["22222222110S011222222222", "22222222AA00011222222222", "222222221100011222222222", "222222221100011222222222", "2222222211000AA222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "22222222110E011222222222"], castleMapLayout: ["11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111"], waves: [{ wave: 1, type: 'normal', count: 10 }], specialTiles: [] } // Tutaj w przyszłości dodamy level 2, 3 itd. }; var UNIT_DATA = { 'swordsman': { id: 'swordsman', name: 'Swordsman', cost: 50 } }; var isBuildPhase = false; var buildPhaseTimer = 0; var startGameButton; var timerText; var pathId; var buildingDeck; var buildingHand; var castleBuildings; var castleBuildingsLayer; var maxScore; var enemies; var towers; var bullets; var selectedTower; var isDragging; var isDraggingSpell; var panelActionTaken; var gold; var lives; var mana; var isManaUnlocked; 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 buildableTilesLayer; var specialTilesLayer; var enemyLayerBottom; var enemyLayerMiddle; var enemyLayerTop; var placementPreview; var buildPanelContainer; var buildButton; var isBuildPanelOpen; var currentActiveView; var gateContainer; var castleContainer; var viewToggleButton; var castleGrid; var castleBuildPanel; var magicButton; var magicPanelContainer; var isMagicPanelOpen; var armyButton; var armyPanelContainer; var isArmyPanelOpen; var isArmyUnlocked; var panelActionTaken; var isDraggingSpell; var spellPreview; var activeSpells; friendlyUnits = []; var friendlyUnitLayer; var peopleRegenQueue; 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 timerText = new Text2('', { size: 50, fill: 0xFFFFFF, weight: 700 }); timerText.anchor.set(0.5, 0.5); timerText.visible = false; var manaText = new Text2('Mana: 0', { size: 60, fill: 0x00BFFF, weight: 800 }); manaText.anchor.set(0.5, 0.5); manaText.visible = false; 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); LK.gui.top.addChild(timerText); LK.gui.top.addChild(manaText); livesText.x = 0; livesText.y = topMargin; goldText.x = -spacing; goldText.y = topMargin; enemiesText.x = spacing; enemiesText.y = topMargin; timerText.x = -spacing; timerText.y = topMargin + 70; manaText.x = -spacing * 2 - 100; manaText.y = topMargin; var currentScreenState = ''; var titleScreenContainer = null; var levelSelectContainer = null; var isHidingUpgradeMenu = false; var isMenuTransitioning = false; function hideUpgradeMenu(menu) { if (isHidingUpgradeMenu) { return; } isHidingUpgradeMenu = true; isMenuTransitioning = true; tween(menu, { y: 2732 + 225 }, { duration: 150, easing: tween.easeIn, onFinish: function onFinish() { menu.destroy(); isHidingUpgradeMenu = false; isMenuTransitioning = false; } }); } function closeActiveUpgradeMenu() { if (isMenuTransitioning) { return; } game.children.forEach(function (child) { if (child instanceof UpgradeMenu) { hideUpgradeMenu(child); } }); for (var i = gateContainer.children.length - 1; i >= 0; i--) { if (gateContainer.children[i].isTowerRange) { gateContainer.removeChild(gateContainer.children[i]); } } selectedTower = null; } function updateUI() { goldText.setText('Gold: ' + gold); livesText.setText('Lives: ' + lives); enemiesText.setText('Enemies: ' + enemiesKilled + '/' + enemiesInCurrentWave); peopleText.setText('People: ' + people + '/' + maxPeople); if (isManaUnlocked) { manaText.setText('Mana: ' + mana); } if (wavesText) { wavesText.setText('Wave: ' + currentWave + '/' + totalWaves); } } function setGold(value) { gold = value; updateUI(); } function setMana(value) { if (isManaUnlocked) { mana = value; updateUI(); } } function getTowerCost(towerType) { var cost = 0; if (TOWER_DATA[towerType] && TOWER_DATA[towerType][1]) { cost = TOWER_DATA[towerType][1].cost; } if (!isBuildPhase && currentWave > 0) { cost = Math.floor(cost * 1.5); } return cost; } function getTowerSellValue(totalValue) { return Math.floor(totalValue * 0.6); } function getBuildingCost(buildingType) { var count = 0; for (var i = 0; i < castleBuildings.length; i++) { if (castleBuildings[i].id === buildingType) { count++; } } var baseCost = BUILDING_DATA[buildingType] ? BUILDING_DATA[buildingType].cost : 0; var finalCost = { gold: Math.floor(baseCost * Math.pow(1.2, count)), people: 0 // Placeholder, if buildings cost people in the future }; return finalCost; } // --- 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("OUR 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 () { 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("SELECT LEVEL", { 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("Level 1: Royal Road", { size: 60, fill: 0xFFFFFF }); levelButton1Text.anchor.set(0.5, 0.5); levelButton1.addChild(levelButton1Text); levelButton1.interactive = true; levelButton1.down = function () { 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("Back", { size: 60, fill: 0xFFFFFF }); backButtonText.anchor.set(0.5, 0.5); backButton.addChild(backButtonText); backButton.interactive = true; backButton.down = function () { if (levelSelectContainer) { levelSelectContainer.destroy(); } showTitleScreen(); }; } function toggleBuildPanel(panelOrState, stateOrPanel) { var panel, forceState; if (typeof panelOrState === 'string') { forceState = panelOrState; panel = stateOrPanel; } else { panel = panelOrState; forceState = stateOrPanel; } var targetY; var shouldBeOpen; if (!panel) { return; } if (forceState === 'open') { shouldBeOpen = true; } else if (forceState === 'close') { shouldBeOpen = false; } else { shouldBeOpen = !isBuildPanelOpen; } if (shouldBeOpen) { targetY = 2732 - 250; isBuildPanelOpen = true; } else { targetY = 2732 + 100; isBuildPanelOpen = false; } tween(panel, { y: targetY }, { duration: 300, easing: tween.backOut }); } function toggleMagicPanel(panel, forceState) { var targetY; var shouldBeOpen; if (!panel) { return; } if (forceState === 'open') { shouldBeOpen = true; } else if (forceState === 'close') { shouldBeOpen = false; } else { shouldBeOpen = !isMagicPanelOpen; } if (shouldBeOpen) { targetY = 2732 - 250; isMagicPanelOpen = true; } else { targetY = 2732 + 100; isMagicPanelOpen = false; } tween(panel, { y: targetY }, { duration: 300, easing: tween.backOut }); } function toggleArmyPanel(panel, forceState) { var targetY; var shouldBeOpen; if (!panel) { return; } if (forceState === 'open') { shouldBeOpen = true; } else if (forceState === 'close') { shouldBeOpen = false; } else { shouldBeOpen = !isArmyPanelOpen; } if (shouldBeOpen) { targetY = 2732 - 250; isArmyPanelOpen = true; } else { targetY = 2732 + 100; isArmyPanelOpen = false; isDraggingSpell = false; activeSpells = []; isDraggingSpell = false; } tween(panel, { y: targetY }, { duration: 300, easing: tween.backOut }); } function initializeGameState(levelData) { pathId = 1; maxScore = 0; enemies = []; towers = []; bullets = []; friendlyUnits = []; peopleRegenQueue = []; activeSpells = []; selectedTower = null; castleBuildings = []; gold = levelData.initialGold; lives = levelData.initialLives; mana = 0; isManaUnlocked = false; isArmyUnlocked = false; enemiesKilled = 0; enemiesInCurrentWave = 0; currentWave = 1; totalWaves = levelData.waves.length; waveInProgress = false; waveSpawned = false; waveTimer = 0; nextWaveTime = 1200; sourceTowers = []; isBuildPanelOpen = false; isMagicPanelOpen = false; isArmyPanelOpen = false; people = 5; maxPeople = 5; isDragging = false; isDraggingSpell = false; panelActionTaken = false; currentActiveView = 'gate'; } function setupScene() { gateContainer = new Container(); game.addChild(gateContainer); castleContainer = new Container(); castleBuildingsLayer = new Container(); castleContainer.addChild(castleBuildingsLayer); game.addChild(castleContainer); castleContainer.visible = false; var castleMainBg = LK.getAsset('castle_bg_image', {}); castleContainer.addChildAt(castleMainBg, 0); castleMainBg.x = -100; castleMainBg.y = -300; gateContainer.addChild(LK.getAsset('map1', { scaleMode: 'nearest' })); debugLayer = new Container(); specialTilesLayer = new Container(); towerLayer = new Container(); friendlyUnitLayer = new Container(); buildableTilesLayer = new Container(); enemyLayerBottom = new Container(); enemyLayerMiddle = new Container(); enemyLayerTop = new Container(); enemyLayer = new Container(); enemyLayer.addChild(enemyLayerBottom); enemyLayer.addChild(enemyLayerMiddle); enemyLayer.addChild(enemyLayerTop); gateContainer.addChild(buildableTilesLayer); gateContainer.addChild(debugLayer); gateContainer.addChild(specialTilesLayer); gateContainer.addChild(towerLayer); gateContainer.addChild(friendlyUnitLayer); gateContainer.addChild(enemyLayer); buildableTilesLayer.visible = false; } function setupGrid(levelData) { 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) { cell.specialEffect = null; if (tileChar === '0') { cell.type = 0; } else if (tileChar === '1' || tileChar === 'A' || tileChar === 'C') { cell.type = 1; if (tileChar === 'A') { cell.specialEffect = 'amplifier'; } if (tileChar === 'C') { cell.specialEffect = 'catalyst'; } var tilePlaceholder = buildableTilesLayer.attachAsset('cell', { anchorX: 0.5, anchorY: 0.5 }); tilePlaceholder.alpha = 0.25; tilePlaceholder.x = grid.x + x * CELL_SIZE + CELL_SIZE / 2; tilePlaceholder.y = grid.y + (y + 5) * CELL_SIZE + CELL_SIZE / 2; } else if (tileChar === 'S') { cell.type = 3; } else if (tileChar === 'E') { cell.type = 4; } else { cell.type = 2; } } } } grid.pathFind(); grid.calculateAggressionPath(); var castleGridWidth = 17; var castleGridHeight = 22; castleGrid = new Grid(castleGridWidth, castleGridHeight); castleGrid.x = (2048 - castleGridWidth * CELL_SIZE) / 2 - 30; castleGrid.y = (2732 - castleGridHeight * CELL_SIZE) / 2 - 100; var castleBuildableTilesLayer = new Container(); castleContainer.addChild(castleBuildableTilesLayer); for (var y = 0; y < levelData.castleMapLayout.length; y++) { for (var x = 0; x < levelData.castleMapLayout[y].length; x++) { var tileChar = levelData.castleMapLayout[y][x]; var cell = castleGrid.getCell(x, y); if (cell) { if (tileChar === '1') { cell.type = 1; var tilePlaceholder = castleBuildableTilesLayer.attachAsset('cell', { anchorX: 0.5, anchorY: 0.5 }); tilePlaceholder.alpha = 0.25; tilePlaceholder.x = castleGrid.x + x * CELL_SIZE + CELL_SIZE / 2; tilePlaceholder.y = castleGrid.y + y * CELL_SIZE + CELL_SIZE / 2; } else { cell.type = 2; } } } } } function setupUIAndPanels() { 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; peopleText = new Text2('People: 5/5', { size: 60, fill: 0xADD8E6, weight: 800 }); peopleText.anchor.set(0.5, 0.5); LK.gui.top.addChild(peopleText); peopleText.x = -spacing * 2; peopleText.y = topMargin; updateUI(); startGameButton = new Container(); var startBuildingBg = startGameButton.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5, width: 600, height: 150, tint: 0xFFD700 }); var startBuildingText = new Text2("Start Building", { size: 70, fill: 0x000000, weight: 'bold' }); startBuildingText.anchor.set(0.5, 0.5); startGameButton.addChild(startBuildingText); startGameButton.x = 2048 / 2; startGameButton.y = 2732 / 2; gateContainer.addChild(startGameButton); startGameButton.down = function () { isBuildPhase = true; buildPhaseTimer = 60 * 10; timerText.visible = true; startGameButton.destroy(); }; buildPanelContainer = new Container(); gateContainer.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 = ['guard', 'crossbow', 'mage', 'banner']; 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); } castleBuildPanel = new Container(); castleContainer.addChild(castleBuildPanel); castleBuildPanel.x = 2048 / 2; castleBuildPanel.y = 2732 + 100; var castlePanelBackground = castleBuildPanel.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5, width: 1800, height: 400 }); castlePanelBackground.tint = 0x332211; castlePanelBackground.alpha = 0.9; magicPanelContainer = new Container(); gateContainer.addChild(magicPanelContainer); magicPanelContainer.x = 2048 / 2; magicPanelContainer.y = 2732 + 100; var magicPanelBackground = magicPanelContainer.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5, width: 1800, height: 400 }); magicPanelBackground.tint = 0x1A237E; magicPanelBackground.alpha = 0.9; var spellTypes = Object.keys(SPELL_DATA); var spellIconSpacing = 280; var totalSpellsWidth = (spellTypes.length - 1) * spellIconSpacing; var spellStartX = -totalSpellsWidth / 2; for (var i = 0; i < spellTypes.length; i++) { var spell = new SourceSpell(spellTypes[i]); spell.x = spellStartX + i * spellIconSpacing; spell.y = 0; magicPanelContainer.addChild(spell); } armyPanelContainer = new Container(); gateContainer.addChild(armyPanelContainer); armyPanelContainer.x = 2048 / 2; armyPanelContainer.y = 2732 + 100; var armyPanelBackground = armyPanelContainer.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5, width: 1800, height: 400 }); armyPanelBackground.tint = 0x613218; armyPanelBackground.alpha = 0.9; var unitTypes = Object.keys(UNIT_DATA); var unitIconSpacing = 280; var totalUnitsWidth = (unitTypes.length - 1) * unitIconSpacing; var unitStartX = -totalUnitsWidth / 2; for (var i = 0; i < unitTypes.length; i++) { var unit = new SourceUnit(unitTypes[i]); unit.x = unitStartX + i * unitIconSpacing; unit.y = 0; armyPanelContainer.addChild(unit); } buildButton = new Container(); game.addChild(buildButton); buildButton.x = 2048 / 2 - 400; 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("Build", { size: 60, fill: 0x000000, weight: 800 }); buildButtonText.anchor.set(0.5, 0.5); buildButton.addChild(buildButtonText); buildButton.down = function () { if (currentActiveView === 'gate') { toggleBuildPanel(buildPanelContainer); toggleMagicPanel(magicPanelContainer, 'close'); toggleArmyPanel(armyPanelContainer, 'close'); } else { toggleBuildPanel(castleBuildPanel); } }; magicButton = new Container(); game.addChild(magicButton); magicButton.x = 2048 / 2; magicButton.y = 2732 - 100; magicButton.visible = false; var magicButtonBg = magicButton.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5, width: 300, height: 120 }); magicButtonBg.tint = 0x00BFFF; var magicButtonText = new Text2("Magic", { size: 60, fill: 0x000000, weight: 800 }); magicButtonText.anchor.set(0.5, 0.5); magicButton.addChild(magicButtonText); magicButton.down = function () { if (currentActiveView === 'gate') { toggleMagicPanel(magicPanelContainer); toggleBuildPanel(buildPanelContainer, 'close'); toggleArmyPanel(armyPanelContainer, 'close'); } }; armyButton = new Container(); game.addChild(armyButton); armyButton.x = 2048 / 2 + 400; armyButton.y = 2732 - 100; armyButton.visible = false; var armyButtonBg = armyButton.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5, width: 300, height: 120 }); armyButtonBg.tint = 0xFF8C00; var armyButtonText = new Text2("Army", { size: 60, fill: 0x000000, weight: 800 }); armyButtonText.anchor.set(0.5, 0.5); armyButton.addChild(armyButtonText); armyButton.down = function () { if (currentActiveView === 'gate') { toggleArmyPanel(armyPanelContainer); toggleBuildPanel(buildPanelContainer, 'close'); toggleMagicPanel(magicPanelContainer, 'close'); } }; viewToggleButton = new Container(); game.addChild(viewToggleButton); viewToggleButton.x = 250; viewToggleButton.y = 2732 - 100; var viewToggleButtonBg = viewToggleButton.attachAsset('notification', { anchorX: 0.5, anchorY: 0.5, width: 300, height: 120, tint: 0x4466AA }); var viewToggleButtonText = new Text2("Castle", { size: 60, fill: 0xFFFFFF, weight: 800 }); viewToggleButtonText.anchor.set(0.5, 0.5); viewToggleButton.addChild(viewToggleButtonText); viewToggleButton.down = function () { if (currentActiveView === 'gate') { currentActiveView = 'castle'; viewToggleButtonText.setText("Gate"); gateContainer.visible = false; castleContainer.visible = true; } else { currentActiveView = 'gate'; viewToggleButtonText.setText("Castle"); gateContainer.visible = true; castleContainer.visible = false; } }; placementPreview = new PlacementPreview(); game.addChild(placementPreview); placementPreview.visible = false; spellPreview = new SpellPreview(); game.addChild(spellPreview); spellPreview.visible = false; } function startGame(levelNumber) { 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; } var levelData = LEVEL_DATA_ALL[levelNumber]; if (!levelData) { console.error("Nie znaleziono danych dla poziomu: " + levelNumber); return; } initializeGameState(levelData); setupScene(); setupGrid(levelData); setupUIAndPanels(); //... Tutaj będą kolejne wywołania pomocniczych funkcji, np. do handli eventów } function handleUIClick(x, y) { var buttons = [{ btn: buildButton, visible: true }, { btn: magicButton, visible: magicButton.visible }, { btn: armyButton, visible: armyButton.visible }, { btn: viewToggleButton, visible: true }]; for (var i = 0; i < buttons.length; i++) { if (buttons[i].visible && buttons[i].btn.getBounds().contains(x, y)) { buttons[i].btn.down(); return true; } } return false; } function handlePanelClick(x, y) { if (isBuildPanelOpen) { var activePanel = currentActiveView === 'gate' ? buildPanelContainer : castleBuildPanel; var sourceItems = activePanel.children.filter(function (c) { return c instanceof SourceTower || c instanceof SourceBuilding; }); var localClickPos = { x: x - activePanel.x, y: y - activePanel.y }; for (var i = 0; i < sourceItems.length; i++) { var item = sourceItems[i]; var itemLeft = item.x - item.width / 2; var itemRight = item.x + item.width / 2; var itemTop = item.y - item.height / 2; var itemBottom = item.y + item.height / 2; if (localClickPos.x >= itemLeft && localClickPos.x <= itemRight && localClickPos.y >= itemTop && localClickPos.y <= itemBottom) { isDragging = true; placementPreview.visible = true; var activeGrid = currentActiveView === 'gate' ? grid : castleGrid; var itemSubType = item.towerType || item.buildingType; placementPreview.itemType = item.towerType ? 'tower' : 'building'; placementPreview.subType = itemSubType; if (placementPreview.itemType === 'tower') { placementPreview.itemSize = { w: 2, h: 2 }; } else { placementPreview.itemSize = BUILDING_DATA[itemSubType].size; } placementPreview.updateAppearance(); if (currentActiveView === 'gate') { buildableTilesLayer.visible = true; } else { castleBuildableTilesLayer.visible = true; } placementPreview.snapToGrid(x, y, activeGrid); return true; } } } if (isMagicPanelOpen) { var sourceSpells = magicPanelContainer.children.filter(function (c) { return c instanceof SourceSpell; }); var localClickPos = { x: x - magicPanelContainer.x, y: y - magicPanelContainer.y }; for (var i = 0; i < sourceSpells.length; i++) { var spell = sourceSpells[i]; var itemLeft = spell.x - spell.width / 2; var itemRight = spell.x + spell.width / 2; var itemTop = spell.y - spell.height / 2; var itemBottom = spell.y + spell.height / 2; if (localClickPos.x >= itemLeft && localClickPos.x <= itemRight && localClickPos.y >= itemTop && localClickPos.y <= itemBottom) { var spellData = SPELL_DATA[spell.spellId]; if (mana >= spellData.cost) { isDraggingSpell = true; spellPreview.spellId = spell.spellId; spellPreview.updateAppearance(spellData, true); spellPreview.updatePosition(x, y); } else { var notification = game.addChild(new Notification("Not enough mana!")); notification.x = 2048 / 2; notification.y = grid.height - 50; } return true; } } } return false; } function handleClickOnMap(x, y) { if (isMenuTransitioning) { return; } var upgradeMenuVisible = game.children.some(function (child) { return child instanceof UpgradeMenu; }); if (upgradeMenuVisible) { // Kliknięcia na menu są obsługiwane przez samo menu return; } var aPanelIsOpen = isBuildPanelOpen || isMagicPanelOpen || isArmyPanelOpen; if (aPanelIsOpen) { var activePanel; if (isBuildPanelOpen) { activePanel = currentActiveView === 'gate' ? buildPanelContainer : castleBuildPanel; } else if (isMagicPanelOpen) { activePanel = magicPanelContainer; } else if (isArmyPanelOpen) { activePanel = armyPanelContainer; } if (activePanel && !activePanel.getBounds().contains(x, y)) { if (isBuildPanelOpen) { toggleBuildPanel(activePanel, 'close'); } if (isMagicPanelOpen) { toggleMagicPanel(magicPanelContainer, 'close'); } if (isArmyPanelOpen) { toggleArmyPanel(armyPanelContainer, 'close'); } return; } } 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)) { tower.down(x, y); clickedOnTower = true; break; } } if (!clickedOnTower) { closeActiveUpgradeMenu(); } } function handleItemDrop(x, y) { if (isDragging) { isDragging = false; placementPreview.visible = false; if (currentActiveView === 'gate') { buildableTilesLayer.visible = false; if (placementPreview.canPlace) { placeTower(placementPreview.gridX, placementPreview.gridY, placementPreview.subType); } } else { castleBuildableTilesLayer.visible = false; if (placementPreview.canPlace) { placeBuilding(placementPreview.subType, placementPreview.gridX, placementPreview.gridY); } } if (isBuildPanelOpen) { var targetPanel = currentActiveView === 'gate' ? buildPanelContainer : castleBuildPanel; toggleBuildPanel(targetPanel, 'close'); } return true; } return false; } function handleSpellCast(x, y) { if (isDraggingSpell) { isDraggingSpell = false; spellPreview.visible = false; var spellData = SPELL_DATA[spellPreview.spellId]; if (mana >= spellData.cost) { setMana(mana - spellData.cost); var spellEffect = new FireWall(x, y, spellData); // Na razie tylko ten, do rozbudowy game.addChild(spellEffect); activeSpells.push(spellEffect); } if (isMagicPanelOpen) { toggleMagicPanel(magicPanelContainer, 'close'); } return true; } return false; } game.down = function (x, y, obj) { if (currentScreenState !== 'gameplay') { return; } // DODANY STRAŻNIK if (obj && obj.down && obj instanceof SourceUnit) { obj.down(); return; } if (panelActionTaken) { panelActionTaken = false; return; } if (handleUIClick(x, y)) { return; } if (handlePanelClick(x, y)) { return; } handleClickOnMap(x, y); }; game.move = function (x, y, obj) { if (currentScreenState !== 'gameplay') { return; } // DODANY STRAŻNIK if (isDragging) { var activeGrid = currentActiveView === 'gate' ? grid : castleGrid; var yOffset = currentActiveView === 'gate' ? CELL_SIZE * 1.5 : 0; placementPreview.snapToGrid(x, y - yOffset, activeGrid); } else if (isDraggingSpell) { spellPreview.updatePosition(x, y); } }; game.up = function (x, y, obj) { if (currentScreenState !== 'gameplay') { return; } // DODANY STRAŻNIK if (handleItemDrop(x, y)) { return; } if (handleSpellCast(x, y)) { return; } var clickedOnMenu = false; var upgradeMenus = game.children.filter(function (child) { return child instanceof UpgradeMenu; }); if (upgradeMenus.length > 0) { for (var i = 0; i < upgradeMenus.length; i++) { var menu = upgradeMenus[i]; var menuBounds = menu.getBounds(); if (menuBounds.contains(x, y)) { clickedOnMenu = true; break; } } } if (!clickedOnMenu && selectedTower) { closeActiveUpgradeMenu(); } }; function updateGameLogic() { if (isBuildPhase) { if (buildPhaseTimer > 0) { buildPhaseTimer--; timerText.setText("Build Time: " + Math.ceil(buildPhaseTimer / 60)); } else { isBuildPhase = false; timerText.visible = false; waveInProgress = true; waveSpawned = false; } } else if (waveInProgress) { if (!waveSpawned) { waveSpawned = true; enemiesKilled = 0; var waveData = levelData.waves[currentWave - 1]; if (waveData) { var enemyCount = waveData.count; var waveType = waveData.type; var isBoss = waveData.isBoss || false; enemiesInCurrentWave = enemyCount; updateUI(); for (var i = 0; i < enemyCount; i++) { var enemy = new Enemy(waveType); var spawnPoints = grid.spawns; var spawnGridX = spawnPoints && spawnPoints.length > 0 ? spawnPoints[0].x : Math.floor(grid.cells.length / 2); var randomOffsetX = (Math.random() - 0.5) * 0.9; var spawnY = -1 - i * 2; enemy.currentCellX = spawnGridX + randomOffsetX; enemy.currentCellY = spawnY; enemy.cellX = spawnGridX; enemy.cellY = 5; enemy.x = grid.x + enemy.currentCellX * CELL_SIZE; enemy.y = grid.y + enemy.currentCellY * CELL_SIZE; enemy.waveNumber = currentWave; if (isBoss) { enemy.isBoss = true; } var healthMultiplier = Math.pow(1.10, currentWave); enemy.maxHealth = Math.round(enemy.maxHealth * healthMultiplier); enemy.health = enemy.maxHealth; if (enemy.isFlying) { enemyLayerTop.addChild(enemy); if (enemy.shadow) { enemyLayerMiddle.addChild(enemy.shadow); } } else { enemyLayerBottom.addChild(enemy); } enemies.push(enemy); } } } else if (enemies.length === 0) { waveInProgress = false; currentWave++; if (currentWave > totalWaves) { LK.showYouWin(); } else { isBuildPhase = true; buildPhaseTimer = 60 * 10; } } } } function updateGameObjects() { if (friendlyUnits) { for (var i = 0; i < friendlyUnits.length; i++) { friendlyUnits[i].update(); } } if (activeSpells) { for (var i = activeSpells.length - 1; i >= 0; i--) { if (activeSpells[i]) { activeSpells[i].update(); } } } if (peopleRegenQueue) { for (var i = peopleRegenQueue.length - 1; i >= 0; i--) { var regenTime = 10 * 60; if (LK.ticks - peopleRegenQueue[i] >= regenTime) { if (people < maxPeople) { people++; updateUI(); } peopleRegenQueue.splice(i, 1); } } } for (var i = 0; i < towers.length; i++) { towers[i].update(); } for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; if (!bullet.parent) { bullets.splice(i, 1); } else { bullet.update(); } } 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); setGold(gold + goldEarned); if (isManaUnlocked) { var manaEarned = 0; switch (enemy.manaCategory) { case 'weak': manaEarned = 1; break; case 'strong': manaEarned = 2; break; case 'special': manaEarned = 5; break; case 'boss': manaEarned = 25; break; } setMana(mana + manaEarned); } gateContainer.addChild(new GoldIndicator(goldEarned, enemy.x, enemy.y)); if (enemy.parent) { enemy.parent.removeChild(enemy); } if (enemy.shadow && enemy.shadow.parent) { enemy.shadow.parent.removeChild(enemy.shadow); } enemies.splice(a, 1); continue; } if (enemy.update()) { if (enemy.parent) { enemy.parent.removeChild(enemy); } if (enemy.shadow && enemy.shadow.parent) { enemy.shadow.parent.removeChild(enemy.shadow); } enemies.splice(a, 1); lives = Math.max(0, lives - 1); updateUI(); if (lives <= 0) { LK.showGameOver(); } } } } function updatePreviews() { if (placementPreview.visible) { var activeGrid = currentActiveView === 'gate' ? grid : castleGrid; placementPreview.updatePlacementStatus(activeGrid); } } game.update = function () { if (currentScreenState !== 'gameplay') { return; } updateGameLogic(); updateGameObjects(); updatePreviews(); }; showTitleScreen();
===================================================================
--- original.js
+++ change.js
@@ -435,9 +435,8 @@
var distance = Math.sqrt(dx * dx + dy * dy);
// Ustawienie rotacji w kierunku celu
var angle = Math.atan2(dy, dx);
var enemyGraphics = self.children[0];
- enemyGraphics.rotation = angle;
if (distance < self.range) {
// Jesteśmy w zasięgu ataku - zatrzymujemy się i atakujemy
if (LK.ticks - self.lastAttack >= self.attackSpeed) {
self.attack();
@@ -486,9 +485,8 @@
var oy = self.currentTarget.y - self.currentCellY;
if (ox !== 0 || oy !== 0) {
var angle = Math.atan2(oy, ox);
var enemyGraphics = self.children[0];
- enemyGraphics.rotation = angle;
}
}
}
// Aktualizacja paska zdrowia
@@ -1130,44 +1128,43 @@
});
var SourceBuilding = Container.expand(function (buildingType) {
var self = Container.call(this);
self.buildingType = buildingType || 'default';
+ var buildingData = BUILDING_DATA[self.buildingType];
var assetId = 'building_' + self.buildingType;
var baseGraphics = self.attachAsset(assetId, {
anchorX: 0.5,
anchorY: 0.5
});
- // ZMIANA: Ustawiamy stały, mniejszy rozmiar dla ikon w panelu
baseGraphics.width = 130;
baseGraphics.height = 130;
- var initialCost = 100;
- var typeLabelShadow = new Text2(self.buildingType.charAt(0).toUpperCase() + self.buildingType.slice(1), {
+ var typeLabelShadow = new Text2(buildingData.name, {
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.buildingType.charAt(0).toUpperCase() + self.buildingType.slice(1), {
+ var typeLabel = new Text2(buildingData.name, {
size: 50,
fill: 0xFFFFFF,
weight: 800
});
typeLabel.anchor.set(0.5, 0.5);
typeLabel.y = -20;
self.addChild(typeLabel);
- var costLabelShadow = new Text2(initialCost, {
+ var costLabelShadow = new Text2(buildingData.cost, {
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(initialCost, {
+ var costLabel = new Text2(buildingData.cost, {
size: 50,
fill: 0xFFD700,
weight: 800
});
@@ -1999,8 +1996,64 @@
}
}
}
};
+var BUILDING_DATA = {
+ 'home': {
+ id: 'home',
+ name: 'Home',
+ size: {
+ w: 2,
+ h: 2
+ },
+ cost: 100
+ },
+ 'workshop': {
+ id: 'workshop',
+ name: 'Workshop',
+ size: {
+ w: 4,
+ h: 2
+ },
+ cost: 250
+ },
+ 'barracks': {
+ id: 'barracks',
+ name: 'Barracks',
+ size: {
+ w: 6,
+ h: 7
+ },
+ cost: 400
+ },
+ 'magic_academy': {
+ id: 'magic_academy',
+ name: 'Magic Academy',
+ size: {
+ w: 6,
+ h: 8
+ },
+ cost: 300
+ },
+ 'tavern': {
+ id: 'tavern',
+ name: 'Tavern',
+ size: {
+ w: 5,
+ h: 5
+ },
+ cost: 150
+ },
+ 'smithy': {
+ id: 'smithy',
+ name: 'Smithy',
+ size: {
+ w: 5,
+ h: 6
+ },
+ cost: 350
+ }
+};
var SPELL_DATA = {
'fire_wall': {
id: 'fire_wall',
name: 'Fire Wall',
@@ -2038,8 +2091,24 @@
duration: 8 * 60,
radius: 3.5 * CELL_SIZE
}
};
+var LEVEL_DATA_ALL = {
+ 1: {
+ levelName: "Level 1: Royal Road",
+ initialGold: 2000,
+ initialLives: 20,
+ mapLayout: ["22222222110S011222222222", "22222222AA00011222222222", "222222221100011222222222", "222222221100011222222222", "2222222211000AA222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "22222222110E011222222222"],
+ castleMapLayout: ["11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111", "11111111111111111"],
+ waves: [{
+ wave: 1,
+ type: 'normal',
+ count: 10
+ }],
+ specialTiles: []
+ }
+ // Tutaj w przyszłości dodamy level 2, 3 itd.
+};
var UNIT_DATA = {
'swordsman': {
id: 'swordsman',
name: 'Swordsman',
@@ -2059,8 +2128,11 @@
var enemies;
var towers;
var bullets;
var selectedTower;
+var isDragging;
+var isDraggingSpell;
+var panelActionTaken;
var gold;
var lives;
var mana;
var isManaUnlocked;
@@ -2238,29 +2310,12 @@
if (castleBuildings[i].id === buildingType) {
count++;
}
}
- var baseCost = {
- gold: 0,
- people: 0
- };
- switch (buildingType) {
- case 'home':
- baseCost.gold = 100;
- break;
- case 'workshop':
- baseCost.gold = 250;
- break;
- case 'magic_academy':
- baseCost.gold = 300;
- break;
- case 'barracks':
- baseCost.gold = 400;
- break;
- }
+ var baseCost = BUILDING_DATA[buildingType] ? BUILDING_DATA[buildingType].cost : 0;
var finalCost = {
- gold: Math.floor(baseCost.gold * Math.pow(1.2, count)),
- people: baseCost.people
+ gold: Math.floor(baseCost * Math.pow(1.2, count)),
+ people: 0 // Placeholder, if buildings cost people in the future
};
return finalCost;
}
// --- FUNKCJE EKRANÓW ---
@@ -2468,66 +2523,20 @@
duration: 300,
easing: tween.backOut
});
}
-function startGame(levelNumber) {
- 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;
- }
- gateContainer = new Container();
- game.addChild(gateContainer);
- castleContainer = new Container();
- castleBuildingsLayer = new Container();
- castleContainer.addChild(castleBuildingsLayer);
- game.addChild(castleContainer);
- castleContainer.visible = false;
- currentActiveView = 'gate';
- var castleMainBg = LK.getAsset('castle_bg_image', {});
- castleContainer.addChildAt(castleMainBg, 0);
- castleMainBg.x = -100;
- castleMainBg.y = -300;
- gateContainer.addChild(LK.getAsset('map1', {
- scaleMode: 'nearest'
- }));
- var castleGridWidth = 18;
- var castleGridHeight = 24;
- var tempCastleLayout = [];
- for (var h = 0; h < castleGridHeight; h++) {
- tempCastleLayout.push("1".repeat(castleGridWidth));
- }
- levelData = {
- levelName: "Level 1: Royal Road",
- initialGold: 2000,
- initialLives: 20,
- mapLayout: ["22222222110S011222222222", "22222222AA00011222222222", "222222221100011222222222", "222222221100011222222222", "2222222211000AA222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "222222221100011222222222", "22222222110E011222222222"],
- castleMapLayout: tempCastleLayout,
- waves: [{
- wave: 1,
- type: 'normal',
- count: 10
- }],
- specialTiles: []
- };
+function initializeGameState(levelData) {
pathId = 1;
maxScore = 0;
enemies = [];
towers = [];
bullets = [];
- friendlyUnitLayer = new Container();
+ friendlyUnits = [];
peopleRegenQueue = [];
+ activeSpells = [];
selectedTower = null;
- gold = levelData.initialGold;
castleBuildings = [];
+ gold = levelData.initialGold;
lives = levelData.initialLives;
mana = 0;
isManaUnlocked = false;
isArmyUnlocked = false;
@@ -2544,43 +2553,52 @@
isMagicPanelOpen = false;
isArmyPanelOpen = false;
people = 5;
maxPeople = 5;
- 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;
- peopleText = new Text2('People: 5/5', {
- size: 60,
- fill: 0xADD8E6,
- weight: 800
- });
- peopleText.anchor.set(0.5, 0.5);
- LK.gui.top.addChild(peopleText);
- peopleText.x = -spacing * 2;
- peopleText.y = topMargin;
- updateUI();
+ isDragging = false;
+ isDraggingSpell = false;
+ panelActionTaken = false;
+ currentActiveView = 'gate';
+}
+function setupScene() {
+ gateContainer = new Container();
+ game.addChild(gateContainer);
+ castleContainer = new Container();
+ castleBuildingsLayer = new Container();
+ castleContainer.addChild(castleBuildingsLayer);
+ game.addChild(castleContainer);
+ castleContainer.visible = false;
+ var castleMainBg = LK.getAsset('castle_bg_image', {});
+ castleContainer.addChildAt(castleMainBg, 0);
+ castleMainBg.x = -100;
+ castleMainBg.y = -300;
+ gateContainer.addChild(LK.getAsset('map1', {
+ scaleMode: 'nearest'
+ }));
debugLayer = new Container();
specialTilesLayer = new Container();
towerLayer = new Container();
+ friendlyUnitLayer = new Container();
buildableTilesLayer = new Container();
enemyLayerBottom = new Container();
enemyLayerMiddle = new Container();
enemyLayerTop = new Container();
enemyLayer = new Container();
enemyLayer.addChild(enemyLayerBottom);
enemyLayer.addChild(enemyLayerMiddle);
enemyLayer.addChild(enemyLayerTop);
+ gateContainer.addChild(buildableTilesLayer);
+ gateContainer.addChild(debugLayer);
+ gateContainer.addChild(specialTilesLayer);
+ gateContainer.addChild(towerLayer);
+ gateContainer.addChild(friendlyUnitLayer);
+ gateContainer.addChild(enemyLayer);
+ buildableTilesLayer.visible = false;
+}
+function setupGrid(levelData) {
grid = new Grid(24, 29 + 6);
grid.x = 150;
grid.y = 200 - CELL_SIZE * 4;
- gateContainer.addChild(buildableTilesLayer);
- buildableTilesLayer.visible = false;
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);
@@ -2612,11 +2630,15 @@
}
}
}
}
+ grid.pathFind();
+ grid.calculateAggressionPath();
+ var castleGridWidth = 17;
+ var castleGridHeight = 22;
castleGrid = new Grid(castleGridWidth, castleGridHeight);
- castleGrid.x = (2048 - castleGridWidth * CELL_SIZE) / 2;
- castleGrid.y = (2732 - castleGridHeight * CELL_SIZE) / 2;
+ castleGrid.x = (2048 - castleGridWidth * CELL_SIZE) / 2 - 30;
+ castleGrid.y = (2732 - castleGridHeight * CELL_SIZE) / 2 - 100;
var castleBuildableTilesLayer = new Container();
castleContainer.addChild(castleBuildableTilesLayer);
for (var y = 0; y < levelData.castleMapLayout.length; y++) {
for (var x = 0; x < levelData.castleMapLayout[y].length; x++) {
@@ -2637,34 +2659,29 @@
}
}
}
}
- 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;
- }
- }
+}
+function setupUIAndPanels() {
+ wavesText = new Text2('Wave: 0/' + totalWaves, {
+ size: 60,
+ fill: 0x00FFFF,
+ weight: 800
});
- grid.pathFind();
- grid.calculateAggressionPath();
- gateContainer.addChild(debugLayer);
- gateContainer.addChild(specialTilesLayer);
- gateContainer.addChild(towerLayer);
- gateContainer.addChild(friendlyUnitLayer);
- gateContainer.addChild(enemyLayer);
- var isDragging = false;
+ wavesText.anchor.set(0.5, 0.5);
+ LK.gui.top.addChild(wavesText);
+ wavesText.x = spacing;
+ wavesText.y = topMargin + 80;
+ peopleText = new Text2('People: 5/5', {
+ size: 60,
+ fill: 0xADD8E6,
+ weight: 800
+ });
+ peopleText.anchor.set(0.5, 0.5);
+ LK.gui.top.addChild(peopleText);
+ peopleText.x = -spacing * 2;
+ peopleText.y = topMargin;
+ updateUI();
startGameButton = new Container();
var startBuildingBg = startGameButton.attachAsset('notification', {
anchorX: 0.5,
anchorY: 0.5,
@@ -2699,9 +2716,8 @@
height: 400
});
panelBackground.tint = 0x222222;
panelBackground.alpha = 0.9;
- var towerIconSpacing = 280;
var towerTypes = ['guard', 'crossbow', 'mage', 'banner'];
var towerIconSpacing = 280;
var totalWidth = (towerTypes.length - 1) * towerIconSpacing;
var towerStartX = -totalWidth / 2;
@@ -2768,34 +2784,8 @@
unit.y = 0;
armyPanelContainer.addChild(unit);
}
buildButton = new Container();
- buildingDeck = ['home', 'workshop', 'barracks', 'magic_academy', 'tavern'];
- buildingHand = [];
- drawNewBuildingHand();
- var rerollButton = new Container();
- rerollButton.x = 2048 - 250;
- rerollButton.y = 2732 - 100;
- game.addChild(rerollButton);
- var rerollButtonBg = rerollButton.attachAsset('notification', {
- anchorX: 0.5,
- anchorY: 0.5,
- width: 300,
- height: 120,
- tint: 0xCC0000
- });
- var rerollButtonText = new Text2("Re-roll", {
- size: 60,
- fill: 0xFFFFFF,
- weight: 800
- });
- rerollButtonText.anchor.set(0.5, 0.5);
- rerollButton.addChild(rerollButtonText);
- rerollButton.down = function () {
- if (currentActiveView === 'castle') {
- drawNewBuildingHand();
- }
- };
game.addChild(buildButton);
buildButton.x = 2048 / 2 - 400;
buildButton.y = 2732 - 100;
var buildButtonBg = buildButton.attachAsset('notification', {
@@ -2904,346 +2894,285 @@
castleContainer.visible = false;
}
};
placementPreview = new PlacementPreview();
- spellPreview = new SpellPreview();
- game.addChild(spellPreview);
game.addChild(placementPreview);
placementPreview.visible = false;
- function drawNewBuildingHand() {
- while (castleBuildPanel.children.length > 1) {
- castleBuildPanel.removeChildAt(1);
+ spellPreview = new SpellPreview();
+ game.addChild(spellPreview);
+ spellPreview.visible = false;
+}
+function startGame(levelNumber) {
+ 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;
+ }
+ var levelData = LEVEL_DATA_ALL[levelNumber];
+ if (!levelData) {
+ console.error("Nie znaleziono danych dla poziomu: " + levelNumber);
+ return;
+ }
+ initializeGameState(levelData);
+ setupScene();
+ setupGrid(levelData);
+ setupUIAndPanels();
+ //... Tutaj będą kolejne wywołania pomocniczych funkcji, np. do handli eventów
+}
+function handleUIClick(x, y) {
+ var buttons = [{
+ btn: buildButton,
+ visible: true
+ }, {
+ btn: magicButton,
+ visible: magicButton.visible
+ }, {
+ btn: armyButton,
+ visible: armyButton.visible
+ }, {
+ btn: viewToggleButton,
+ visible: true
+ }];
+ for (var i = 0; i < buttons.length; i++) {
+ if (buttons[i].visible && buttons[i].btn.getBounds().contains(x, y)) {
+ buttons[i].btn.down();
+ return true;
}
- buildingHand = [];
- var availableBuildings = buildingDeck.slice();
- for (var i = 0; i < 3; i++) {
- if (availableBuildings.length === 0) {
- break;
- }
- var randomIndex = Math.floor(Math.random() * availableBuildings.length);
- var drawnBuilding = availableBuildings.splice(randomIndex, 1)[0];
- buildingHand.push(drawnBuilding);
- }
- var buildingIconSpacing = 280;
- var totalBuildingWidth = (buildingHand.length - 1) * buildingIconSpacing;
- var buildingStartX = -totalBuildingWidth / 2;
- for (var i = 0; i < buildingHand.length; i++) {
- var building = new SourceBuilding(buildingHand[i]);
- building.x = buildingStartX + i * buildingIconSpacing;
- building.y = 0;
- castleBuildPanel.addChild(building);
- }
}
- function placeBuilding(buildingType, gridX, gridY) {
- var cost = getBuildingCost(buildingType);
- var buildingSize = {
- w: 2,
- h: 2
+ return false;
+}
+function handlePanelClick(x, y) {
+ if (isBuildPanelOpen) {
+ var activePanel = currentActiveView === 'gate' ? buildPanelContainer : castleBuildPanel;
+ var sourceItems = activePanel.children.filter(function (c) {
+ return c instanceof SourceTower || c instanceof SourceBuilding;
+ });
+ var localClickPos = {
+ x: x - activePanel.x,
+ y: y - activePanel.y
};
- if (buildingType === 'workshop') {
- buildingSize = {
- w: 4,
- h: 4
- };
- }
- if (buildingType === 'magic_academy' || buildingType === 'barracks') {
- buildingSize = {
- w: 5,
- h: 5
- };
- }
- if (gold >= cost.gold) {
- setGold(gold - cost.gold);
- var newBuilding = new Building(buildingType, buildingSize);
- newBuilding.x = castleGrid.x + gridX * CELL_SIZE;
- newBuilding.y = castleGrid.y + gridY * CELL_SIZE;
- castleBuildingsLayer.addChild(newBuilding);
- castleBuildings.push(newBuilding);
- for (var i = 0; i < buildingSize.w; i++) {
- for (var j = 0; j < buildingSize.h; j++) {
- var cell = castleGrid.getCell(gridX + i, gridY + j);
- if (cell) {
- cell.type = 5;
- }
+ for (var i = 0; i < sourceItems.length; i++) {
+ var item = sourceItems[i];
+ var itemLeft = item.x - item.width / 2;
+ var itemRight = item.x + item.width / 2;
+ var itemTop = item.y - item.height / 2;
+ var itemBottom = item.y + item.height / 2;
+ if (localClickPos.x >= itemLeft && localClickPos.x <= itemRight && localClickPos.y >= itemTop && localClickPos.y <= itemBottom) {
+ isDragging = true;
+ placementPreview.visible = true;
+ var activeGrid = currentActiveView === 'gate' ? grid : castleGrid;
+ var itemSubType = item.towerType || item.buildingType;
+ placementPreview.itemType = item.towerType ? 'tower' : 'building';
+ placementPreview.subType = itemSubType;
+ if (placementPreview.itemType === 'tower') {
+ placementPreview.itemSize = {
+ w: 2,
+ h: 2
+ };
+ } else {
+ placementPreview.itemSize = BUILDING_DATA[itemSubType].size;
}
- }
- if (buildingType === 'home') {
- maxPeople += 5;
- updateUI();
- }
- if (buildingType === 'magic_academy' && !isManaUnlocked) {
- isManaUnlocked = true;
- manaText.visible = true;
- magicButton.visible = true;
- updateUI();
- }
- if (buildingType === 'barracks' && !isArmyUnlocked) {
- isArmyUnlocked = true;
- armyButton.visible = true;
- }
- var handIndex = buildingHand.indexOf(buildingType);
- if (handIndex > -1) {
- buildingHand.splice(handIndex, 1);
- }
- for (var i = castleBuildPanel.children.length - 1; i >= 0; i--) {
- var child = castleBuildPanel.children[i];
- if (child.buildingType === buildingType) {
- child.destroy();
- break;
+ placementPreview.updateAppearance();
+ if (currentActiveView === 'gate') {
+ buildableTilesLayer.visible = true;
+ } else {
+ castleBuildableTilesLayer.visible = true;
}
+ placementPreview.snapToGrid(x, y, activeGrid);
+ return true;
}
- if (buildingHand.length === 0) {
- drawNewBuildingHand();
- }
- return true;
- } else {
- var notification = game.addChild(new Notification("Not enough gold!"));
- notification.x = 2048 / 2;
- notification.y = grid.height - 50;
- return false;
}
}
- 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;
- }
- }
- game.down = function (x, y, obj) {
- if (obj && obj.down) {
- if (obj instanceof SourceUnit) {
- // Pozwól SourceUnit zająć się rekrutacją
- } else {
- return;
- }
- }
- if (panelActionTaken) {
- panelActionTaken = false;
- return;
- }
- var buildButtonBounds = buildButton.getBounds();
- if (buildButtonBounds.contains(x, y)) {
- return;
- }
- var magicButtonBounds = magicButton.getBounds();
- if (magicButton.visible && magicButtonBounds.contains(x, y)) {
- return;
- }
- var armyButtonBounds = armyButton.getBounds();
- if (armyButton.visible && armyButtonBounds.contains(x, y)) {
- return;
- }
- var viewToggleButtonBounds = viewToggleButton.getBounds();
- if (viewToggleButtonBounds.contains(x, y)) {
- return;
- }
- var upgradeMenuVisible = game.children.some(function (child) {
- return child instanceof UpgradeMenu;
+ if (isMagicPanelOpen) {
+ var sourceSpells = magicPanelContainer.children.filter(function (c) {
+ return c instanceof SourceSpell;
});
- if (upgradeMenuVisible) {
- return;
- }
- var clickedOnItemInPanel = false;
- if (isBuildPanelOpen) {
- var activePanel = currentActiveView === 'gate' ? buildPanelContainer : castleBuildPanel;
- var sourceItems = activePanel.children.filter(function (c) {
- return c instanceof SourceTower || c instanceof SourceBuilding;
- });
- var localClickPos = {
- x: x - activePanel.x,
- y: y - activePanel.y
- };
- for (var i = 0; i < sourceItems.length; i++) {
- var item = sourceItems[i];
- var itemBounds = {
- left: item.x - item.width / 2,
- right: item.x + item.width / 2,
- top: item.y - item.height / 2,
- bottom: item.y + item.height / 2
- };
- if (localClickPos.x >= itemBounds.left && localClickPos.x <= itemBounds.right && localClickPos.y >= itemBounds.top && localClickPos.y <= itemBounds.bottom) {
- clickedOnItemInPanel = true;
- isDragging = true;
- placementPreview.visible = true;
- var activeGrid = currentActiveView === 'gate' ? grid : castleGrid;
- var itemSubType = item.towerType || item.buildingType;
- placementPreview.itemType = item.towerType ? 'tower' : 'building';
- placementPreview.subType = itemSubType;
- if (placementPreview.itemType === 'tower') {
- placementPreview.itemSize = {
- w: 2,
- h: 2
- };
- } else {
- if (itemSubType === 'home') {
- placementPreview.itemSize = {
- w: 2,
- h: 2
- };
- }
- if (itemSubType === 'workshop') {
- placementPreview.itemSize = {
- w: 4,
- h: 2
- };
- }
- if (itemSubType === 'magic_academy' || itemSubType === 'barracks') {
- placementPreview.itemSize = {
- w: 5,
- h: 5
- };
- }
- }
- placementPreview.updateAppearance();
- if (currentActiveView === 'gate') {
- buildableTilesLayer.visible = true;
- } else {
- castleBuildableTilesLayer.visible = true;
- }
- placementPreview.snapToGrid(x, y, activeGrid);
- break;
+ var localClickPos = {
+ x: x - magicPanelContainer.x,
+ y: y - magicPanelContainer.y
+ };
+ for (var i = 0; i < sourceSpells.length; i++) {
+ var spell = sourceSpells[i];
+ var itemLeft = spell.x - spell.width / 2;
+ var itemRight = spell.x + spell.width / 2;
+ var itemTop = spell.y - spell.height / 2;
+ var itemBottom = spell.y + spell.height / 2;
+ if (localClickPos.x >= itemLeft && localClickPos.x <= itemRight && localClickPos.y >= itemTop && localClickPos.y <= itemBottom) {
+ var spellData = SPELL_DATA[spell.spellId];
+ if (mana >= spellData.cost) {
+ isDraggingSpell = true;
+ spellPreview.spellId = spell.spellId;
+ spellPreview.updateAppearance(spellData, true);
+ spellPreview.updatePosition(x, y);
+ } else {
+ var notification = game.addChild(new Notification("Not enough mana!"));
+ notification.x = 2048 / 2;
+ notification.y = grid.height - 50;
}
+ return true;
}
+ }
+ }
+ return false;
+}
+function handleClickOnMap(x, y) {
+ if (isMenuTransitioning) {
+ return;
+ }
+ var upgradeMenuVisible = game.children.some(function (child) {
+ return child instanceof UpgradeMenu;
+ });
+ if (upgradeMenuVisible) {
+ // Kliknięcia na menu są obsługiwane przez samo menu
+ return;
+ }
+ var aPanelIsOpen = isBuildPanelOpen || isMagicPanelOpen || isArmyPanelOpen;
+ if (aPanelIsOpen) {
+ var activePanel;
+ if (isBuildPanelOpen) {
+ activePanel = currentActiveView === 'gate' ? buildPanelContainer : castleBuildPanel;
} else if (isMagicPanelOpen) {
- var activePanel = magicPanelContainer;
- var sourceItems = magicPanelContainer.children.filter(function (c) {
- return c instanceof SourceSpell;
- });
- var localClickPos = {
- x: x - activePanel.x,
- y: y - activePanel.y
- };
- for (var i = 0; i < sourceItems.length; i++) {
- var item = sourceItems[i];
- var itemBounds = {
- left: item.x - item.width / 2,
- right: item.x + item.width / 2,
- top: item.y - item.height / 2,
- bottom: item.y + item.height / 2
- };
- if (localClickPos.x >= itemBounds.left && localClickPos.x <= itemBounds.right && localClickPos.y >= itemBounds.top && localClickPos.y <= itemBounds.bottom) {
- clickedOnItemInPanel = true;
- var spellData = SPELL_DATA[item.spellId];
- if (mana >= spellData.cost) {
- isDraggingSpell = true;
- spellPreview.spellId = item.spellId;
- spellPreview.updateAppearance(spellData, true);
- spellPreview.updatePosition(x, y);
- } else {
- var notification = game.addChild(new Notification("Not enough mana!"));
- notification.x = 2048 / 2;
- notification.y = grid.height - 50;
- }
- break;
- }
- }
+ activePanel = magicPanelContainer;
+ } else if (isArmyPanelOpen) {
+ activePanel = armyPanelContainer;
}
- var aPanelIsOpen = isBuildPanelOpen || isMagicPanelOpen || isArmyPanelOpen;
- if (aPanelIsOpen && !clickedOnItemInPanel) {
- var activePanel;
+ if (activePanel && !activePanel.getBounds().contains(x, y)) {
if (isBuildPanelOpen) {
- activePanel = currentActiveView === 'gate' ? buildPanelContainer : castleBuildPanel;
- } else if (isMagicPanelOpen) {
- activePanel = magicPanelContainer;
- } else if (isArmyPanelOpen) {
- activePanel = armyPanelContainer;
+ toggleBuildPanel(activePanel, 'close');
}
- if (activePanel && !activePanel.getBounds().contains(x, y)) {
- if (isBuildPanelOpen) {
- toggleBuildPanel(activePanel, 'close');
- }
- if (isMagicPanelOpen) {
- toggleMagicPanel(magicPanelContainer, 'close');
- }
- }
- }
- };
- game.move = function (x, y, obj) {
- if (isDragging) {
- var activeGrid = currentActiveView === 'gate' ? grid : castleGrid;
- var yOffset = currentActiveView === 'gate' ? CELL_SIZE * 1.5 : 0;
- placementPreview.snapToGrid(x, y - yOffset, activeGrid);
- } else if (isDraggingSpell) {
- spellPreview.updatePosition(x, y);
- }
- };
- game.up = function (x, y, obj) {
- if (isDraggingSpell) {
- isDraggingSpell = false;
- spellPreview.visible = false;
- var spellData = SPELL_DATA[spellPreview.spellId];
- if (mana >= spellData.cost) {
- setMana(mana - spellData.cost);
- var spellEffect = new FireWall(x, y, spellData);
- game.addChild(spellEffect);
- activeSpells.push(spellEffect);
- }
if (isMagicPanelOpen) {
toggleMagicPanel(magicPanelContainer, 'close');
}
- } else if (isDragging) {
- isDragging = false;
- placementPreview.visible = false;
- if (currentActiveView === 'gate') {
- buildableTilesLayer.visible = false;
- if (placementPreview.canPlace) {
- placeTower(placementPreview.gridX, placementPreview.gridY, placementPreview.subType);
- }
- } else {
- castleBuildableTilesLayer.visible = false;
- if (placementPreview.canPlace) {
- placeBuilding(placementPreview.subType, placementPreview.gridX, placementPreview.gridY);
- }
+ if (isArmyPanelOpen) {
+ toggleArmyPanel(armyPanelContainer, 'close');
}
- if (isBuildPanelOpen) {
- var targetPanel = currentActiveView === 'gate' ? buildPanelContainer : castleBuildPanel;
- toggleBuildPanel(targetPanel, 'close');
+ return;
+ }
+ }
+ 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)) {
+ tower.down(x, y);
+ clickedOnTower = true;
+ break;
+ }
+ }
+ if (!clickedOnTower) {
+ closeActiveUpgradeMenu();
+ }
+}
+function handleItemDrop(x, y) {
+ if (isDragging) {
+ isDragging = false;
+ placementPreview.visible = false;
+ if (currentActiveView === 'gate') {
+ buildableTilesLayer.visible = false;
+ if (placementPreview.canPlace) {
+ placeTower(placementPreview.gridX, placementPreview.gridY, placementPreview.subType);
}
} else {
- 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;
- }
+ castleBuildableTilesLayer.visible = false;
+ if (placementPreview.canPlace) {
+ placeBuilding(placementPreview.subType, placementPreview.gridX, placementPreview.gridY);
}
- var clickedOnMenu = false;
- var upgradeMenus = game.children.filter(function (child) {
- return child instanceof UpgradeMenu;
- });
- if (upgradeMenus.length > 0) {
- for (var i = 0; i < upgradeMenus.length; i++) {
- var menu = upgradeMenus[i];
- var menuBounds = menu.getBounds();
- if (menuBounds.contains(x, y)) {
- clickedOnMenu = true;
- break;
- }
- }
- }
- if (!clickedOnTower && !clickedOnMenu) {
- closeActiveUpgradeMenu();
- }
}
- };
+ if (isBuildPanelOpen) {
+ var targetPanel = currentActiveView === 'gate' ? buildPanelContainer : castleBuildPanel;
+ toggleBuildPanel(targetPanel, 'close');
+ }
+ return true;
+ }
+ return false;
}
-game.update = function () {
+function handleSpellCast(x, y) {
+ if (isDraggingSpell) {
+ isDraggingSpell = false;
+ spellPreview.visible = false;
+ var spellData = SPELL_DATA[spellPreview.spellId];
+ if (mana >= spellData.cost) {
+ setMana(mana - spellData.cost);
+ var spellEffect = new FireWall(x, y, spellData); // Na razie tylko ten, do rozbudowy
+ game.addChild(spellEffect);
+ activeSpells.push(spellEffect);
+ }
+ if (isMagicPanelOpen) {
+ toggleMagicPanel(magicPanelContainer, 'close');
+ }
+ return true;
+ }
+ return false;
+}
+game.down = function (x, y, obj) {
if (currentScreenState !== 'gameplay') {
return;
+ } // DODANY STRAŻNIK
+ if (obj && obj.down && obj instanceof SourceUnit) {
+ obj.down();
+ return;
}
+ if (panelActionTaken) {
+ panelActionTaken = false;
+ return;
+ }
+ if (handleUIClick(x, y)) {
+ return;
+ }
+ if (handlePanelClick(x, y)) {
+ return;
+ }
+ handleClickOnMap(x, y);
+};
+game.move = function (x, y, obj) {
+ if (currentScreenState !== 'gameplay') {
+ return;
+ } // DODANY STRAŻNIK
+ if (isDragging) {
+ var activeGrid = currentActiveView === 'gate' ? grid : castleGrid;
+ var yOffset = currentActiveView === 'gate' ? CELL_SIZE * 1.5 : 0;
+ placementPreview.snapToGrid(x, y - yOffset, activeGrid);
+ } else if (isDraggingSpell) {
+ spellPreview.updatePosition(x, y);
+ }
+};
+game.up = function (x, y, obj) {
+ if (currentScreenState !== 'gameplay') {
+ return;
+ } // DODANY STRAŻNIK
+ if (handleItemDrop(x, y)) {
+ return;
+ }
+ if (handleSpellCast(x, y)) {
+ return;
+ }
+ var clickedOnMenu = false;
+ var upgradeMenus = game.children.filter(function (child) {
+ return child instanceof UpgradeMenu;
+ });
+ if (upgradeMenus.length > 0) {
+ for (var i = 0; i < upgradeMenus.length; i++) {
+ var menu = upgradeMenus[i];
+ var menuBounds = menu.getBounds();
+ if (menuBounds.contains(x, y)) {
+ clickedOnMenu = true;
+ break;
+ }
+ }
+ }
+ if (!clickedOnMenu && selectedTower) {
+ closeActiveUpgradeMenu();
+ }
+};
+function updateGameLogic() {
if (isBuildPhase) {
if (buildPhaseTimer > 0) {
buildPhaseTimer--;
timerText.setText("Build Time: " + Math.ceil(buildPhaseTimer / 60));
@@ -3304,15 +3233,16 @@
buildPhaseTimer = 60 * 10;
}
}
}
+}
+function updateGameObjects() {
if (friendlyUnits) {
for (var i = 0; i < friendlyUnits.length; i++) {
friendlyUnits[i].update();
}
}
if (activeSpells) {
- // <-- To zabezpieczenie rozwiązuje problem
for (var i = activeSpells.length - 1; i >= 0; i--) {
if (activeSpells[i]) {
activeSpells[i].update();
}
@@ -3320,9 +3250,8 @@
}
if (peopleRegenQueue) {
for (var i = peopleRegenQueue.length - 1; i >= 0; i--) {
var regenTime = 10 * 60;
- // 10 sekund
if (LK.ticks - peopleRegenQueue[i] >= regenTime) {
if (people < maxPeople) {
people++;
updateUI();
@@ -3330,77 +3259,81 @@
peopleRegenQueue.splice(i, 1);
}
}
}
- if (!isBuildPhase) {
- 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);
- setGold(gold + goldEarned);
- if (isManaUnlocked) {
- var manaEarned = 0;
- switch (enemy.manaCategory) {
- case 'weak':
- manaEarned = 1;
- break;
- case 'strong':
- manaEarned = 2;
- break;
- case 'special':
- manaEarned = 5;
- break;
- case 'boss':
- manaEarned = 25;
- break;
- }
- setMana(mana + manaEarned);
+ for (var i = 0; i < towers.length; i++) {
+ towers[i].update();
+ }
+ for (var i = bullets.length - 1; i >= 0; i--) {
+ var bullet = bullets[i];
+ if (!bullet.parent) {
+ bullets.splice(i, 1);
+ } else {
+ bullet.update();
+ }
+ }
+ 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);
+ setGold(gold + goldEarned);
+ if (isManaUnlocked) {
+ var manaEarned = 0;
+ switch (enemy.manaCategory) {
+ case 'weak':
+ manaEarned = 1;
+ break;
+ case 'strong':
+ manaEarned = 2;
+ break;
+ case 'special':
+ manaEarned = 5;
+ break;
+ case 'boss':
+ manaEarned = 25;
+ break;
}
- gateContainer.addChild(new GoldIndicator(goldEarned, enemy.x, enemy.y));
- for (var i = 0; i < enemy.bulletsTargetingThis.length; i++) {
- enemy.bulletsTargetingThis[i].targetEnemy = null;
- }
- if (enemy.parent) {
- enemy.parent.removeChild(enemy);
- }
- if (enemy.shadow && enemy.shadow.parent) {
- enemy.shadow.parent.removeChild(enemy.shadow);
- }
- enemies.splice(a, 1);
- continue;
+ setMana(mana + manaEarned);
}
- if (enemy.update()) {
- if (enemy.parent) {
- enemy.parent.removeChild(enemy);
- }
- if (enemy.shadow && enemy.shadow.parent) {
- enemy.shadow.parent.removeChild(enemy.shadow);
- }
- enemies.splice(a, 1);
- lives = Math.max(0, lives - 1);
- updateUI();
- if (lives <= 0) {
- LK.showGameOver();
- }
+ gateContainer.addChild(new GoldIndicator(goldEarned, enemy.x, enemy.y));
+ if (enemy.parent) {
+ enemy.parent.removeChild(enemy);
}
+ if (enemy.shadow && enemy.shadow.parent) {
+ enemy.shadow.parent.removeChild(enemy.shadow);
+ }
+ enemies.splice(a, 1);
+ continue;
}
- 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 (enemy.update()) {
+ if (enemy.parent) {
+ enemy.parent.removeChild(enemy);
}
+ if (enemy.shadow && enemy.shadow.parent) {
+ enemy.shadow.parent.removeChild(enemy.shadow);
+ }
+ enemies.splice(a, 1);
+ lives = Math.max(0, lives - 1);
+ updateUI();
+ if (lives <= 0) {
+ LK.showGameOver();
+ }
}
}
+}
+function updatePreviews() {
if (placementPreview.visible) {
var activeGrid = currentActiveView === 'gate' ? grid : castleGrid;
placementPreview.updatePlacementStatus(activeGrid);
}
+}
+game.update = function () {
+ if (currentScreenState !== 'gameplay') {
+ return;
+ }
+ updateGameLogic();
+ updateGameObjects();
+ updatePreviews();
};
showTitleScreen();
\ No newline at end of file