User prompt
şimdi tüm bu değişkenleri optimize etmek için kodun en üst tarafına yerleştir. Tüm çarpanlar
User prompt
insan dili ile istemiyorum. doğrudan uygulamanın kodunu düzeltmeni istiyorum.
User prompt
gelen her düşman 10 base cana sahip olacak. wave sayısı ise can miktarını arttıracak. düşmanların base canı hesaplanırken base can çarpı dalga çarpı 1.25 olmalı.
User prompt
halen çift tıklayınca para azalmıyor. çift tıklayınca para azalması ve kulenin seviyesi artmalı. Aynı zamanda kule fiyatları global olarak artmalı. Her upgrade işlemi yada kule satın alımında kule base ücretleri 1.25 artmalı. her upgrade işlemi öncekine kıyasla base ücret ile kule seviyesinin çarpımı olarak güncellenmeli.
User prompt
kuleleri upgrade yaparken para harcamıyor. düzeltir misin?
User prompt
tüm upgrade ve satın almalar money üzerinden yapılsın. score global bir değişken ve öldürülen düşman sayısını ifade edecek.
User prompt
money pozisyonunu sağ alta al. açılışta sadece 50 para olsun
User prompt
score olduğu gibi kalsın ama satın alma için money ekle. satın alma işlemi money üzerinden olsun. Her düşman 10 para kazandırsın. wave sayısı arttıkça düşmanın kazandırdığı para wave * 10 olsun.
User prompt
sonsuz wave olabilir. kullanıcı skoru (scoreo) öldürülen düşman sayısı olarak hesaplanmalı. Puanlar satın alma için kullanılıyor. kule upgrade işlemi "kule fiyatı"x"kule seviyesi" olarak hesaplanmalı. Her kule güç değerine göre farklı başlangıç fiyatına sahip olacak. Aynı zamanda her kule satın alındığında tüm kulelerin fiyatı 1.25 artması gerekiyor.
User prompt
grid üzerine konulan her kule, kendi seviyesinden sorumlu olmalı. aynı türden diğer kuleleri etkilememeli
User prompt
kuleleri silebilmek için bir seçenek eklemen lazım. varsayılan olarak kulelerin seviyesi 1 olmalı
User prompt
güç artışı sonsuza kadar sürsün. çift tıklandığında rengi değişmesin. şu an mor kule koydum çift tıklayınca yeşil renge dönüştü. Seviyeleri her kulenin sol alt köşesinde sayı olarak yazsın
User prompt
haritaya konulan kulelere çift tıklandığında güçleri ve hızları 1.1x artsın
User prompt
Please fix the bug: 'nextWaveBtn is not defined' in or related to this line: 'nextWaveBtn.down = function (x, y, obj) {' Line Number: 1015
User prompt
play'e bastığımda auto yazısı kayboldu. Ayrıca auto yazısını wave ile aynı boyutta ve wave yazısının üstüne koy
User prompt
2. dalgada auto yazısı kayboldu
User prompt
auto: on yazısı ortada aşağıda olsun. Points yazısı rengi yeşil ve saydam olmayan şekilde olsun
User prompt
şimdi her renkteki kulenin farklı güç değerleri olsun. Mor olan kule haritanın her yerinden ateş edebilsin.
Code edit (1 edits merged)
Please save this source code
User prompt
düşman güzergahı kendi üzerinden geçemez, yukarı yönlü de olabilir. ama eğer aşağı dönüş imkanı bulması lazım yani 1. sutun yada son sutuna denk gelmemesi lazım
User prompt
tamam daha basit hale getirelim. Güzergah için bir başlangıç noktası seçilecek. gridin üst kısmında 10 kare var. bu karelerden 1. kare başlangıç noktası olamaz. 2-9 arası kareler başlangıç noktası olabilir. 10. kare başlangıç noktası olamaz. Aynı kural bitiş noktası için de geçerli. Sonra aralarında kare kare ilerleyerek bir yol çiz. Başlangıç noktasından her zaman güneye 1 kare ile başla. Ve bitirirken her zaman güneye 1 kare ile bitir. Ama kesinlikle her adım bir önceki kare ile yan yana olmak zorunda. ilk satır, son satır, ilk sutun ve son sutun üzerinde güzergah bulunamaz. ard arda 5 kareden fazla düz ilerlenemez.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // BuildTile class (for tower placement) var BuildTile = Container.expand(function () { var self = Container.call(this); // Attach tile asset (box, light gray) var tileAsset = self.attachAsset('buildTile', { anchorX: 0.5, anchorY: 0.5 }); self.occupied = false; self.tower = null; // For highlighting self.highlight = function (on) { tileAsset.tint = on ? 0x2ecc40 : 0xbdc3c7; }; return self; }); // Bullet class var Bullet = Container.expand(function () { var self = Container.call(this); // Attach bullet asset (small yellow box) var bulletAsset = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.target = null; self.damage = 5; self.speed = 24; self.update = function () { if (!self.target || self.target.health <= 0 || self.target.reachedBase) { self.destroyed = true; return; } var dx = self.target.x - self.x; var dy = self.target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 32) { // Hit self.target.takeDamage(self.damage); self.destroyed = true; return; } var moveDist = Math.min(self.speed, dist); self.x += dx / dist * moveDist; self.y += dy / dist * moveDist; }; return self; }); // Enemy (Creep) class var Enemy = Container.expand(function () { var self = Container.call(this); // Attach enemy asset (ellipse, purple) var enemyAsset = self.attachAsset('enemy', { anchorX: 0.5, anchorY: 0.5 }); // Enemy stats self.maxHealth = 10 + (currentWave - 1) * 5; self.health = self.maxHealth; self.speed = 2 + (currentWave - 1) * 0.2; // Slightly faster each wave self.reward = 5 + (currentWave - 1) * 2; // Path progress self.pathIndex = 0; self.pathProgress = 0; // For hit flash self.isFlashing = false; // Move along path self.update = function () { if (self.pathIndex >= path.length - 1) { return; } var from = path[self.pathIndex]; var to = path[self.pathIndex + 1]; var dx = to.x - from.x; var dy = to.y - from.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist === 0) { self.pathIndex++; self.pathProgress = 0; return; } var moveDist = self.speed; self.pathProgress += moveDist; if (self.pathProgress >= dist) { self.pathIndex++; self.pathProgress = 0; if (self.pathIndex >= path.length - 1) { // Reached end self.x = to.x; self.y = to.y; self.reachedBase = true; return; } } var t = self.pathProgress / dist; self.x = from.x + dx * t; self.y = from.y + dy * t; }; // Take damage self.takeDamage = function (dmg) { self.health -= dmg; if (!self.isFlashing) { self.isFlashing = true; tween(enemyAsset, { tint: 0xffffff }, { duration: 80, onFinish: function onFinish() { tween(enemyAsset, { tint: 0x8e44ad }, { duration: 120, onFinish: function onFinish() { self.isFlashing = false; } }); } }); } }; return self; }); // Tower class var Tower = Container.expand(function () { var self = Container.call(this); // Attach tower asset (box, blue) var towerAsset = self.attachAsset('tower', { anchorX: 0.5, anchorY: 0.5 }); // Tower stats self.level = 1; self.range = 320; self.damage = 5; self.fireRate = 60; // frames per shot self.cooldown = 0; // For upgrade flash self.isFlashing = false; // Show range circle (for placement) self.rangeCircle = self.attachAsset('rangeCircle', { anchorX: 0.5, anchorY: 0.5, alpha: 0.15 }); self.rangeCircle.width = self.range * 2; self.rangeCircle.height = self.range * 2; self.rangeCircle.visible = false; // Upgrade tower self.upgrade = function () { if (self.level >= 3) { return false; } self.level++; self.damage += 5; self.range += 40; self.fireRate = Math.max(30, self.fireRate - 10); self.rangeCircle.width = self.range * 2; self.rangeCircle.height = self.range * 2; // Flash for upgrade if (!self.isFlashing) { self.isFlashing = true; tween(towerAsset, { tint: 0xffff00 }, { duration: 120, onFinish: function onFinish() { tween(towerAsset, { tint: 0x3498db }, { duration: 180, onFinish: function onFinish() { self.isFlashing = false; } }); } }); } return true; }; // Tower attack logic self.update = function () { if (self.cooldown > 0) { self.cooldown--; return; } // Find nearest enemy in range var nearest = null; var minDist = 99999; for (var i = 0; i < enemies.length; i++) { var e = enemies[i]; var dx = e.x - self.x; var dy = e.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist <= self.range && dist < minDist) { nearest = e; minDist = dist; } } if (nearest) { // Shoot var b = new Bullet(); b.x = self.x; b.y = self.y; b.target = nearest; b.damage = self.damage; b.speed = 24; b.lastX = b.x; b.lastY = b.y; bullets.push(b); game.addChild(b); self.cooldown = self.fireRate; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000000 }); /**** * Game Code ****/ // --- Grid system --- // Set grid to 10x12 with margins, leave 1 row margin at bottom, and add 5 tower selection squares below grid var GRID_COLS = 10; var GRID_ROWS = 12; var GRID_SIZE = 160; // Calculate margins to center grid in 2048x2732, leaving 1 row margin at bottom for spacing var GRID_TOTAL_WIDTH = GRID_COLS * GRID_SIZE; var GRID_TOTAL_HEIGHT = GRID_ROWS * GRID_SIZE; var GRID_MARGIN_X = Math.floor((2048 - GRID_TOTAL_WIDTH) / 2); var GRID_MARGIN_Y = Math.floor((2732 - (GRID_TOTAL_HEIGHT + GRID_SIZE)) / 2); // +GRID_SIZE for 1 row margin at bottom // Helper to snap to grid (with margin) function snapToGrid(x, y) { return { x: GRID_MARGIN_X + Math.round((x - GRID_MARGIN_X) / GRID_SIZE) * GRID_SIZE, y: GRID_MARGIN_Y + Math.round((y - GRID_MARGIN_Y) / GRID_SIZE) * GRID_SIZE }; } // Path definition (array of {x, y}), snapped to grid // New rules: start/end not at corners, always start/end with 1 down, no path on grid edges, max 5 straight, only adjacent steps function generateSimplePath() { var pathArr = []; var visited = {}; // Ziyaret edilen hücreleri (ızgara koordinatları) takip etmek için function cellKey(c, r) { return c + "," + r; } // Başlangıç: Sütun 1 ile GRID_COLS-2 arasında rastgele bir sütun seç var startCols = []; for (var c = 1; c < GRID_COLS - 1; c++) { // Sütun 0 ve GRID_COLS-1 hariç startCols.push(c); } if (startCols.length === 0) { // Eğer ızgara çok darsa (örn: GRID_COLS < 3) console.error("Path generation: Not enough columns for path."); return [snapToGrid(GRID_MARGIN_X, GRID_MARGIN_Y)]; // Minimum bir yol döndür } var startCol = startCols[Math.floor(Math.random() * startCols.length)]; var curCol = startCol; var curRow = 0; // Başlangıç 0. satırda // İlk nokta: başlangıç hücresi (row 0, col) pathArr.push(snapToGrid(GRID_MARGIN_X + curCol * GRID_SIZE, GRID_MARGIN_Y + curRow * GRID_SIZE)); visited[cellKey(curCol, curRow)] = true; // Her zaman 1 aşağı hareket et (1. satıra) curRow = 1; // Eğer GRID_ROWS < 2 ise bu adım atlanmalı, ama TD oyunları için genelde daha büyük olur. if (GRID_ROWS < 2) { console.error("Path generation: Not enough rows for path."); // Sadece başlangıç noktasını içeren bir yol döndür return pathArr; } pathArr.push(snapToGrid(GRID_MARGIN_X + curCol * GRID_SIZE, GRID_MARGIN_Y + curRow * GRID_SIZE)); visited[cellKey(curCol, curRow)] = true; // Bitiş: Sütun 1 ile GRID_COLS-2 arasında rastgele bir sütun seç var endCols = []; for (var c = 1; c < GRID_COLS - 1; c++) { // Sütun 0 ve GRID_COLS-1 hariç endCols.push(c); } if (endCols.length === 0) { console.error("Path generation: Not enough columns for end path."); return pathArr; // Mevcut yolu döndür } var endCol = endCols[Math.floor(Math.random() * endCols.length)]; var endRow = GRID_ROWS - 1; // Son satır var preEndRow = GRID_ROWS - 2; // Bitişten bir önceki satır (yolun ana gövdesinin sonu) // Eğer preEndRow < curRow (yani GRID_ROWS <= 2 gibi durumlar) if (preEndRow < curRow) { // Zaten curRow (1) preEndRow'dan büyük veya eşitse, doğrudan bitişe gitmeyi dene // Bu durum, ızgara çok kısaysa (örn: 2 satır) oluşabilir. // Bu durumda, yol zaten (startCol,0) -> (startCol,1) şeklindedir. // Eğer endCol farklıysa ve yanlamasına hareket mümkünse bir adım atılabilir. if (curCol !== endCol && GRID_COLS > 2) {// Yanlamasına hareket için yer varsa // Basitçe endCol'e doğru bir adım atmaya çalış (eğer geçerliyse) // Bu senaryo için daha detaylı mantık gerekebilir, şimdilik basit tutuluyor. } // Son noktayı ekle (endCol, endRow) // Önce (endCol, preEndRow)'a (yani endCol, curRow'a) gidilmeli (eğer farklıysa) // Sonra (endCol, endRow)'a inilmeli. // Bu basit durumda, eğer curRow === preEndRow ise, sadece son adıma ihtiyaç var. if (curCol !== endCol && preEndRow === curRow) {// Eğer aynı satırda ama farklı sütundaysak // Yanlamasına hedef sütuna git (eğer aradaki boşluk kuralı izin verirse) // Bu basitlik için, bu adımı atlayıp doğrudan sonuca gideceğiz. // Daha karmaşık mantık, bu noktadan preEndRow/endCol'e bir yol bulmayı gerektirir. } pathArr.push(snapToGrid(GRID_MARGIN_X + endCol * GRID_SIZE, GRID_MARGIN_Y + endRow * GRID_SIZE)); visited[cellKey(endCol, endRow)] = true; // Son noktayı da ziyaret edilmiş say return pathArr; } var lastDir = "down"; // İlk hareket aşağı olduğu için var straightCount = 1; var maxRetries = 500; // Takılma durumunda maksimum deneme sayısı var retries = 0; // Ana yol oluşturma döngüsü: (curCol, curRow) => (endCol, preEndRow) while (!(curRow === preEndRow && curCol === endCol)) { retries++; if (retries > maxRetries) { // console.warn("Path generation: Max retries reached. Path may be incomplete or invalid."); // Takılma durumunda, yolu yeniden oluşturmayı deneyebilir veya hata verebiliriz. // Şimdilik, mevcut yolu döndürüp, muhtemelen eksik olacak. // Daha iyi bir çözüm, tüm fonksiyonu yeniden çağırmak veya null döndürmek olabilir. // Hızlı bir düzeltme olarak, burada kesip kalan yolu tamamlamaya çalışalım. break; } var potentialMoves = []; // Aşağı hareket: Eğer preEndRow'a ulaşmadıysak if (curRow < preEndRow) potentialMoves.push({ name: "down", dx: 0, dy: 1 }); // Yukarı hareket: Eğer satır 1'de değilsek ve son hareket aşağı değilse if (curRow > 1 && lastDir !== "down") potentialMoves.push({ name: "up", dx: 0, dy: -1 }); // Sola hareket: Eğer sütun 1'de değilsek ve son hareket sağa değilse if (curCol > 1 && lastDir !== "right") potentialMoves.push({ name: "left", dx: -1, dy: 0 }); // Sağa hareket: Eğer sütun GRID_COLS-2'de değilsek ve son hareket sola değilse if (curCol < GRID_COLS - 2 && lastDir !== "left") potentialMoves.push({ name: "right", dx: 1, dy: 0 }); var validMovesRaw = []; for (var i = 0; i < potentialMoves.length; i++) { var move = potentialMoves[i]; var testCol = curCol + move.dx; var testRow = curRow + move.dy; // Sınır kontrolü (gerçi potentialMoves oluşturulurken bu zaten dikkate alınıyor) // testCol [1, GRID_COLS-2] aralığında, testRow [1, GRID_ROWS-2] (preEndRow dahil) aralığında olmalı if (testCol < 1 || testCol > GRID_COLS - 2 || testRow < 1 || testRow > preEndRow) { continue; } // Kural 1: Kendi üzerinden geçme (hücre zaten ziyaret edilmiş) if (visited[cellKey(testCol, testRow)]) { continue; } // Kural 2: Boşluk kuralı var spacingOk = true; var orthogonalNeighbors = [{ c: testCol + 1, r: testRow }, { c: testCol - 1, r: testRow }, { c: testCol, r: testRow + 1 }, { c: testCol, r: testRow - 1 }]; for (var ni = 0; ni < orthogonalNeighbors.length; ni++) { var neighbor = orthogonalNeighbors[ni]; // Komşu, geldiğimiz hücre ise kontrol etme if (neighbor.c === curCol && neighbor.r === curRow) { continue; } if (visited[cellKey(neighbor.c, neighbor.r)]) { spacingOk = false; break; } } if (!spacingOk) { continue; } validMovesRaw.push(move); } if (validMovesRaw.length === 0) { // console.warn("Path generation: No valid moves from (" + curCol + "," + curRow + "). Trying to recover or break."); // Eğer geçerli hamle yoksa, takılmışız demektir. Döngüyü kır. // Daha gelişmiş bir çözüm backtracking olabilir. break; } var currentActionableMoves = validMovesRaw; if (straightCount >= 5) { var nonStraightMoves = validMovesRaw.filter(function (m) { return m.name !== lastDir; }); if (nonStraightMoves.length > 0) { currentActionableMoves = nonStraightMoves; // Dönüşü önceliklendir } // Eğer nonStraightMoves boşsa, yani tek seçenek düz gitmekse, // currentActionableMoves zaten validMovesRaw olarak kalır ve düz gitmeye izin verir. } var preferredMoves = []; for (var k = 0; k < currentActionableMoves.length; k++) { var moveCand = currentActionableMoves[k]; var candCol = curCol + moveCand.dx; var candRow = curRow + moveCand.dy; // Hedefe (endCol, preEndRow) yakınlaşmayı tercih et if (candCol === endCol && candRow === preEndRow) { // Doğrudan hedefe preferredMoves.push(moveCand); } else if (candCol === endCol || candRow === preEndRow) { // Eksenlerden birini hizala // Daha güçlü tercih: Manhattan mesafesini azaltanlar var distOld = Math.abs(curCol - endCol) + Math.abs(curRow - preEndRow); var distNew = Math.abs(candCol - endCol) + Math.abs(candRow - preEndRow); if (distNew < distOld) { preferredMoves.push(moveCand); } } } // Eğer tercih edilen hamle yoksa ve rastgelelik daha azsa, yine de tüm uygun hamleleri kullan if (preferredMoves.length === 0 && currentActionableMoves.length > 0) { // Hedefe doğru basit bir yönlendirme ekleyebiliriz var bestMoveByDistance = null; var minDistance = Infinity; for (var k = 0; k < currentActionableMoves.length; k++) { var moveCand = currentActionableMoves[k]; var candCol = curCol + moveCand.dx; var candRow = curRow + moveCand.dy; var distNew = Math.abs(candCol - endCol) + Math.abs(candRow - preEndRow); if (distNew < minDistance) { minDistance = distNew; bestMoveByDistance = moveCand; } } if (bestMoveByDistance) preferredMoves.push(bestMoveByDistance); } var selectedMove; if (preferredMoves.length > 0 && Math.random() < 0.85) { // Tercihli hareket olasılığını artır selectedMove = preferredMoves[Math.floor(Math.random() * preferredMoves.length)]; } else { // Tercihli yoksa veya rastgelelik, herhangi bir uygun hamleden seç selectedMove = currentActionableMoves[Math.floor(Math.random() * currentActionableMoves.length)]; } if (!selectedMove) { // Bu durumun oluşmaması gerekir eğer currentActionableMoves doluysa // console.warn("Path generation: selectedMove is null. Breaking."); break; } if (selectedMove.name === lastDir) { straightCount++; } else { straightCount = 1; } lastDir = selectedMove.name; curCol += selectedMove.dx; curRow += selectedMove.dy; visited[cellKey(curCol, curRow)] = true; pathArr.push(snapToGrid(GRID_MARGIN_X + curCol * GRID_SIZE, GRID_MARGIN_Y + curRow * GRID_SIZE)); } // while döngüsü sonu // (curCol, curRow) şimdi (endCol, preEndRow)'da olmalı (veya döngü takıldığı için daha erken bitti) // Eğer hedef sütuna ulaşamadıysak, oraya doğru bir çizgi çekmeyi deneyebiliriz (basitçe). // Bu, karmaşık bir mantık gerektirir ve mevcut yolun geçerliliğini bozabilir. // Şimdilik, preEndRow'a ulaştığımızı varsayalım. // Eğer preEndRow'a tam olarak hedeflenen endCol ile ulaşılmadıysa // ve curCol != endCol ise, bu durumu ele almak gerekir. // Mevcut kod, döngü bittiğinde curCol ve curRow'un endCol ve preEndRow olduğunu varsayar. // Eğer döngü 'break' ile erken biterse, curCol/curRow farklı olabilir. // Bu durumda son adım yine de (endCol, endRow)'a eklenir. Bu, kopuk bir yola neden olabilir. // En iyisi, eğer döngü erken biterse, bu fonksiyonun başarısız olduğunu belirtmek. // Ancak, orijinal yapıya sadık kalarak, son adımı ekleyelim. // Her zaman bitişe 1 aşağı hareketle tamamla: (endCol, endRow) // Eğer ızgara çok kısaysa (örn: GRID_ROWS=1), endRow=0, preEndRow=-1 olur. // Bu durumlar yukarıda (preEndRow < curRow kontrolü) ele alınmaya çalışıldı. if (GRID_ROWS > 0) { // En az bir satır varsa // Son noktayı eklemeden önce, son noktanın geçerliliğini kontrol etmek iyi olabilir. // Ancak, bu nokta (endCol, GRID_ROWS-1) genellikle "özel" kabul edilir. pathArr.push(snapToGrid(GRID_MARGIN_X + endCol * GRID_SIZE, GRID_MARGIN_Y + endRow * GRID_SIZE)); visited[cellKey(endCol, endRow)] = true; // Son noktayı da ziyaret edilmiş say } return pathArr; } var path = generateSimplePath(); // --- Start Game Button Logic --- var gameStarted = false; var startBtn = new Text2('▶ Start Game', { size: 120, fill: 0x27AE60 }); startBtn.anchor.set(0.5, 0.5); LK.gui.center.addChild(startBtn); function setStartBtnVisible(visible) { startBtn.visible = visible; } setStartBtnVisible(true); startBtn.down = function (x, y, obj) { if (!gameStarted) { gameStarted = true; setStartBtnVisible(false); setNextWaveBtnVisible(true); startWave(); // Start the first wave immediately } }; // Hide all gameplay UI until game starts setNextWaveBtnVisible(false); if (typeof kpTxt !== "undefined") { kpTxt.visible = false; } if (typeof healthTxt !== "undefined") { healthTxt.visible = false; } if (typeof waveTxt !== "undefined") { waveTxt.visible = false; } if (typeof nextWaveBtn !== "undefined") { nextWaveBtn.visible = false; } if (typeof autoNextWaveBtn !== "undefined") { autoNextWaveBtn.visible = false; } // Show gameplay UI when game starts function showGameplayUI() { kpTxt.visible = true; healthTxt.visible = true; waveTxt.visible = true; nextWaveBtn.visible = false; nextWaveBtn.x = 0; nextWaveBtn.y = kpTxt.y + kpTxt.height + 20; autoNextWaveBtn.x = 0; autoNextWaveBtn.y = nextWaveBtn.y + nextWaveBtn.height + 10; autoNextWaveBtn.visible = true; kpTxt.setText('Points: ' + knowledgePoints); healthTxt.setText('Base: ' + baseHealth); waveTxt.setText('Wave: ' + currentWave + '/' + maxWaves); } var _origStartWave = startWave; startWave = function startWave() { if (!gameStarted) { return; } showGameplayUI(); _origStartWave(); }; // Buildable tile logic removed for now var buildTilePositions = []; // Global game state var enemies = []; var towers = []; var bullets = []; // buildTiles removed for now var baseHealth = 10; var knowledgePoints = 30; var currentWave = 1; var maxWaves = 10; var waveInProgress = false; var waveTimer = null; var spawnIndex = 0; var spawnDelay = 40; // frames between spawns var enemiesToSpawn = 0; var selectedTile = null; // Asset initialization // Draw grid background (light gray squares) with margins and 10x12 grid area for (var row = 0; row < GRID_ROWS; row++) { for (var col = 0; col < GRID_COLS; col++) { var gridNode = LK.getAsset('buildTile', { anchorX: 0.5, anchorY: 0.5, width: GRID_SIZE - 4, height: GRID_SIZE - 4, color: 0xf4f4f4, shape: 'box', alpha: 0.18 }); gridNode.x = GRID_MARGIN_X + col * GRID_SIZE; gridNode.y = GRID_MARGIN_Y + row * GRID_SIZE; game.addChild(gridNode); } } // Draw 5 colored tower selection squares below the grid, centered horizontally var TOWER_SELECT_COUNT = 5; var TOWER_SELECT_SIZE = 140; var TOWER_SELECT_SPACING = 60; var TOWER_SELECT_COLORS = [0x3498db, 0xe74c3c, 0x2ecc40, 0xf1c40f, 0x8e44ad]; var TOWER_SELECT_LABELS = ['Mavi', 'Kırmızı', 'Yeşil', 'Sarı', 'Mor']; var towerSelectSquares = []; var totalWidth = TOWER_SELECT_COUNT * TOWER_SELECT_SIZE + (TOWER_SELECT_COUNT - 1) * TOWER_SELECT_SPACING; var startX = Math.floor((2048 - totalWidth) / 2) + TOWER_SELECT_SIZE / 2; var selectY = GRID_MARGIN_Y + GRID_ROWS * GRID_SIZE + GRID_SIZE / 2; for (var i = 0; i < TOWER_SELECT_COUNT; i++) { var square = LK.getAsset('buildTile', { anchorX: 0.5, anchorY: 0.5, width: TOWER_SELECT_SIZE, height: TOWER_SELECT_SIZE, color: TOWER_SELECT_COLORS[i], shape: 'box', alpha: 0.95 }); square.x = startX + i * (TOWER_SELECT_SIZE + TOWER_SELECT_SPACING); square.y = selectY; game.addChild(square); // Add label for each tower color var label = new Text2(TOWER_SELECT_LABELS[i], { size: 48, fill: "#fff" }); label.anchor.set(0.5, 0); label.x = square.x; label.y = square.y + TOWER_SELECT_SIZE / 2 + 8; game.addChild(label); towerSelectSquares.push(square); } // Draw path (for visual reference) for (var i = 0; i < path.length - 1; i++) { var from = path[i]; var to = path[i + 1]; var dx = to.x - from.x; var dy = to.y - from.y; var dist = Math.sqrt(dx * dx + dy * dy); var steps = Math.floor(dist / 40); for (var s = 0; s <= steps; s++) { var t = s / steps; var px = from.x + dx * t; var py = from.y + dy * t; var node = LK.getAsset('pathDot', { anchorX: 0.5, anchorY: 0.5, width: 32, height: 32, color: 0x7f8c8d, shape: 'ellipse' }); node.x = px; node.y = py; game.addChild(node); } } // Buildable tile objects removed for now // --- Tower Selection Bar --- var towerTypes = []; var selectedTowerType = null; var towerButtons = []; var towerBtnY = 2400; var towerBtnSpacing = 260; var towerBtnStartX = 400; var draggingTowerType = null; var draggingTowerGhost = null; // No tower selection buttons since normal tower is removed // --- Drag-and-drop tower placement from colored squares --- for (var i = 0; i < towerSelectSquares.length; i++) { (function (colorIdx) { var square = towerSelectSquares[colorIdx]; square.down = function (x, y, obj) { // Clean up any previous ghost if (draggingTowerGhost) { if (draggingTowerGhost.rangeCircle) { draggingTowerGhost.rangeCircle.destroy(); } draggingTowerGhost.destroy(); draggingTowerGhost = null; draggingTowerType = null; } draggingTowerType = colorIdx; draggingTowerGhost = new Tower(); // Snap to grid for initial drag position var gridX = Math.round((x - GRID_MARGIN_X) / GRID_SIZE); var gridY = Math.round((y - GRID_MARGIN_Y) / GRID_SIZE); var snapX = GRID_MARGIN_X + gridX * GRID_SIZE; var snapY = GRID_MARGIN_Y + gridY * GRID_SIZE; draggingTowerGhost.x = snapX; draggingTowerGhost.y = snapY; // Set color of ghost tower if (draggingTowerGhost.children && draggingTowerGhost.children.length > 0) { draggingTowerGhost.children[0].tint = TOWER_SELECT_COLORS[colorIdx]; } // Show range circle while dragging, and center it on the ghost if (draggingTowerGhost.rangeCircle) { draggingTowerGhost.rangeCircle.visible = true; draggingTowerGhost.rangeCircle.x = 0; draggingTowerGhost.rangeCircle.y = 0; } draggingTowerGhost.alpha = 0.7; game.addChild(draggingTowerGhost); }; })(i); } // Helper: check if a grid cell is on the path function isCellOnPath(gridX, gridY) { for (var i = 0; i < path.length; i++) { var px = path[i].x; var py = path[i].y; var cellX = Math.round((px - GRID_MARGIN_X) / GRID_SIZE); var cellY = Math.round((py - GRID_MARGIN_Y) / GRID_SIZE); if (cellX === gridX && cellY === gridY) { return true; } } return false; } // Helper: check if a tower already exists at grid cell function isTowerAtCell(gridX, gridY) { for (var i = 0; i < towers.length; i++) { var t = towers[i]; var tx = Math.round((t.x - GRID_MARGIN_X) / GRID_SIZE); var ty = Math.round((t.y - GRID_MARGIN_Y) / GRID_SIZE); if (tx === gridX && ty === gridY) { return true; } } return false; } // Dragging ghost tower with finger/mouse game.move = function (x, y, obj) { if (draggingTowerGhost) { // Snap ghost to grid for feedback var gridX = Math.round((x - GRID_MARGIN_X) / GRID_SIZE); var gridY = Math.round((y - GRID_MARGIN_Y) / GRID_SIZE); var snapX = GRID_MARGIN_X + gridX * GRID_SIZE; var snapY = GRID_MARGIN_Y + gridY * GRID_SIZE; draggingTowerGhost.x = snapX; draggingTowerGhost.y = snapY; // Always center rangeCircle on the ghost (0,0 in local coordinates) if (draggingTowerGhost.rangeCircle) { draggingTowerGhost.rangeCircle.x = 0; draggingTowerGhost.rangeCircle.y = 0; } // Visual feedback: tint red if invalid, normal if valid var valid = true; // Only allow inside grid if (gridX < 0 || gridX >= GRID_COLS || gridY < 0 || gridY >= GRID_ROWS || isCellOnPath(gridX, gridY) || isTowerAtCell(gridX, gridY)) { valid = false; } if (draggingTowerGhost.children && draggingTowerGhost.children.length > 0) { draggingTowerGhost.children[0].tint = valid ? TOWER_SELECT_COLORS[draggingTowerType] : 0x888888; } draggingTowerGhost.alpha = valid ? 0.7 : 0.35; } }; // Place tower on grid on up game.up = function (x, y, obj) { if (draggingTowerGhost && draggingTowerType !== null) { var gridX = Math.round((draggingTowerGhost.x - GRID_MARGIN_X) / GRID_SIZE); var gridY = Math.round((draggingTowerGhost.y - GRID_MARGIN_Y) / GRID_SIZE); var valid = true; if (gridX < 0 || gridX >= GRID_COLS || gridY < 0 || gridY >= GRID_ROWS || isCellOnPath(gridX, gridY) || isTowerAtCell(gridX, gridY)) { valid = false; } if (valid) { // Place tower if enough points var buildCost = 20; if (knowledgePoints >= buildCost) { knowledgePoints -= buildCost; var tower = new Tower(); tower.x = GRID_MARGIN_X + gridX * GRID_SIZE; tower.y = GRID_MARGIN_Y + gridY * GRID_SIZE; // Set color if (tower.children && tower.children.length > 0) { tower.children[0].tint = TOWER_SELECT_COLORS[draggingTowerType]; } towers.push(tower); game.addChild(tower); kpTxt.setText('Points: ' + knowledgePoints); } } // Clean up ghost if (draggingTowerGhost.rangeCircle) { draggingTowerGhost.rangeCircle.destroy(); } draggingTowerGhost.destroy(); draggingTowerGhost = null; draggingTowerType = null; return; } // If not placing, just clear ghost if (draggingTowerGhost) { if (draggingTowerGhost.rangeCircle) { draggingTowerGhost.rangeCircle.destroy(); } draggingTowerGhost.destroy(); draggingTowerGhost = null; draggingTowerType = null; } }; // Base indicator (ellipse, red) var baseNode = LK.getAsset('base', { anchorX: 0.5, anchorY: 0.5, width: 160, height: 160, color: 0xe74c3c, shape: 'ellipse' }); baseNode.x = path[path.length - 1].x; baseNode.y = path[path.length - 1].y; game.addChild(baseNode); // GUI: Points (KP) - Center top, smaller size, renamed to Points var kpTxt = new Text2('Points: ' + knowledgePoints, { size: 48, fill: 0x222222 }); kpTxt.anchor.set(0.5, 0); // center aligned, top kpTxt.x = 0; kpTxt.y = 10; LK.gui.top.addChild(kpTxt); // GUI: Base Health - Top right, smaller size var healthTxt = new Text2('Base: ' + baseHealth, { size: 48, fill: 0xE74C3C }); healthTxt.anchor.set(1, 0); // right aligned, top healthTxt.x = -40; // right margin healthTxt.y = 10; LK.gui.topRight.addChild(healthTxt); // GUI: Wave - Bottom left, smaller size var waveTxt = new Text2('Wave: ' + currentWave + '/' + maxWaves, { size: 48, fill: 0x2980B9 }); waveTxt.anchor.set(0, 1); // left aligned, bottom waveTxt.x = 40; waveTxt.y = -40; LK.gui.bottomLeft.addChild(waveTxt); // GUI: Next Wave Button var nextWaveBtn = new Text2('▶ Next Wave', { size: 90, fill: 0x27AE60 }); nextWaveBtn.anchor.set(0.5, 0); // Place nextWaveBtn under Points (kpTxt) in gui.top, centered horizontally nextWaveBtn.x = 0; nextWaveBtn.y = kpTxt.y + kpTxt.height + 20; LK.gui.top.addChild(nextWaveBtn); // Auto-next wave toggle var autoNextWave = false; var autoNextWaveBtn = new Text2('Auto: OFF', { size: 54, fill: 0x2980B9 }); autoNextWaveBtn.anchor.set(0.5, 0); autoNextWaveBtn.x = 0; autoNextWaveBtn.y = nextWaveBtn.y + nextWaveBtn.height + 10; LK.gui.top.addChild(autoNextWaveBtn); autoNextWaveBtn.visible = false; autoNextWaveBtn.down = function (x, y, obj) { autoNextWave = !autoNextWave; autoNextWaveBtn.setText('Auto: ' + (autoNextWave ? 'ON' : 'OFF')); }; // Show/hide next wave button function setNextWaveBtnVisible(visible) { // Defensive: Only set visible if nextWaveBtn is defined and has a visible property if (typeof nextWaveBtn !== "undefined" && nextWaveBtn && typeof nextWaveBtn.visible !== "undefined") { nextWaveBtn.visible = visible; } } setNextWaveBtnVisible(false); // Start next wave function startWave() { if (waveInProgress || currentWave > maxWaves) { return; } waveInProgress = true; setNextWaveBtnVisible(false); enemiesToSpawn = 6 + currentWave * 2; spawnIndex = 0; } // Next wave button event nextWaveBtn.down = function (x, y, obj) { if (!waveInProgress && currentWave <= maxWaves) { startWave(); } }; // Build/upgrade tower on tile function tryBuildOrUpgrade(tile) { if (tile.occupied) { // Try upgrade if (tile.tower.level < 3) { var upgradeCost = 20 + tile.tower.level * 15; if (knowledgePoints >= upgradeCost) { knowledgePoints -= upgradeCost; tile.tower.upgrade(); kpTxt.setText('Points: ' + knowledgePoints); } } } else { // Build new tower var buildCost = 20; if (knowledgePoints >= buildCost) { knowledgePoints -= buildCost; var tower = new Tower(); tower.x = tile.x; tower.y = tile.y; towers.push(tower); game.addChild(tower); tile.occupied = true; tile.tower = tower; kpTxt.setText('Points: ' + knowledgePoints); } } } // Highlight build tiles on touch game.down = function (x, y, obj) { // No tap-to-place logic; only drag-and-drop is supported for tower placement }; // Remove highlight/range on up // (handled by drag-and-drop up logic above) // Main game update game.update = function () { // Spawn enemies for wave if (waveInProgress && enemiesToSpawn > 0 && LK.ticks % spawnDelay === 0) { var enemy = new Enemy(); enemy.x = path[0].x; enemy.y = path[0].y; enemy.pathIndex = 0; enemy.pathProgress = 0; enemies.push(enemy); game.addChild(enemy); enemiesToSpawn--; } // Update enemies for (var i = enemies.length - 1; i >= 0; i--) { var e = enemies[i]; e.update(); if (e.health <= 0) { // Enemy defeated knowledgePoints += e.reward; kpTxt.setText('Points: ' + knowledgePoints); e.destroy(); enemies.splice(i, 1); kpTxt.setText('Points: ' + knowledgePoints); } else if (e.reachedBase) { // Enemy reached base baseHealth--; healthTxt.setText('Base: ' + baseHealth); e.destroy(); enemies.splice(i, 1); healthTxt.setText('Base: ' + baseHealth); LK.effects.flashScreen(0xe74c3c, 400); if (baseHealth <= 0) { LK.showGameOver(); return; } } } // Update towers for (var i = 0; i < towers.length; i++) { towers[i].update(); } // Update bullets for (var i = bullets.length - 1; i >= 0; i--) { var b = bullets[i]; b.update(); if (b.destroyed) { b.destroy(); bullets.splice(i, 1); } } // End wave if all enemies defeated and none left to spawn if (waveInProgress && enemies.length === 0 && enemiesToSpawn === 0) { waveInProgress = false; currentWave++; if (currentWave > maxWaves) { LK.showYouWin(); return; } waveTxt.setText('Wave: ' + currentWave + '/' + maxWaves); // Reposition nextWaveBtn and autoNextWaveBtn under Points (kpTxt) in case Points text size changes nextWaveBtn.x = 0; nextWaveBtn.y = kpTxt.y + kpTxt.height + 20; autoNextWaveBtn.x = 0; autoNextWaveBtn.y = nextWaveBtn.y + nextWaveBtn.height + 10; if (autoNextWave) { setNextWaveBtnVisible(false); // Start next wave automatically after a short delay for feedback LK.setTimeout(function () { if (!waveInProgress && currentWave <= maxWaves) { startWave(); } }, 700); } else { setNextWaveBtnVisible(true); } } };
===================================================================
--- original.js
+++ change.js
@@ -72,9 +72,11 @@
// For hit flash
self.isFlashing = false;
// Move along path
self.update = function () {
- if (self.pathIndex >= path.length - 1) return;
+ if (self.pathIndex >= path.length - 1) {
+ return;
+ }
var from = path[self.pathIndex];
var to = path[self.pathIndex + 1];
var dx = to.x - from.x;
var dy = to.y - from.y;
@@ -151,9 +153,11 @@
self.rangeCircle.height = self.range * 2;
self.rangeCircle.visible = false;
// Upgrade tower
self.upgrade = function () {
- if (self.level >= 3) return false;
+ if (self.level >= 3) {
+ return false;
+ }
self.level++;
self.damage += 5;
self.range += 40;
self.fireRate = Math.max(30, self.fireRate - 10);
@@ -247,125 +251,258 @@
// Path definition (array of {x, y}), snapped to grid
// New rules: start/end not at corners, always start/end with 1 down, no path on grid edges, max 5 straight, only adjacent steps
function generateSimplePath() {
var pathArr = [];
- // Start: pick a column from 1 to 8 (not 0 or 9)
+ var visited = {}; // Ziyaret edilen hücreleri (ızgara koordinatları) takip etmek için
+ function cellKey(c, r) {
+ return c + "," + r;
+ }
+ // Başlangıç: Sütun 1 ile GRID_COLS-2 arasında rastgele bir sütun seç
var startCols = [];
for (var c = 1; c < GRID_COLS - 1; c++) {
+ // Sütun 0 ve GRID_COLS-1 hariç
startCols.push(c);
}
+ if (startCols.length === 0) {
+ // Eğer ızgara çok darsa (örn: GRID_COLS < 3)
+ console.error("Path generation: Not enough columns for path.");
+ return [snapToGrid(GRID_MARGIN_X, GRID_MARGIN_Y)]; // Minimum bir yol döndür
+ }
var startCol = startCols[Math.floor(Math.random() * startCols.length)];
- var col = startCol;
- var row = 0;
- // First point: start cell (row 0, col)
- pathArr.push(snapToGrid(GRID_MARGIN_X + col * GRID_SIZE, GRID_MARGIN_Y + row * GRID_SIZE));
- // Always move 1 down to start
- row = 1;
- pathArr.push(snapToGrid(GRID_MARGIN_X + col * GRID_SIZE, GRID_MARGIN_Y + row * GRID_SIZE));
- // End: pick a column from 1 to 8 (not 0 or 9)
+ var curCol = startCol;
+ var curRow = 0; // Başlangıç 0. satırda
+ // İlk nokta: başlangıç hücresi (row 0, col)
+ pathArr.push(snapToGrid(GRID_MARGIN_X + curCol * GRID_SIZE, GRID_MARGIN_Y + curRow * GRID_SIZE));
+ visited[cellKey(curCol, curRow)] = true;
+ // Her zaman 1 aşağı hareket et (1. satıra)
+ curRow = 1;
+ // Eğer GRID_ROWS < 2 ise bu adım atlanmalı, ama TD oyunları için genelde daha büyük olur.
+ if (GRID_ROWS < 2) {
+ console.error("Path generation: Not enough rows for path.");
+ // Sadece başlangıç noktasını içeren bir yol döndür
+ return pathArr;
+ }
+ pathArr.push(snapToGrid(GRID_MARGIN_X + curCol * GRID_SIZE, GRID_MARGIN_Y + curRow * GRID_SIZE));
+ visited[cellKey(curCol, curRow)] = true;
+ // Bitiş: Sütun 1 ile GRID_COLS-2 arasında rastgele bir sütun seç
var endCols = [];
for (var c = 1; c < GRID_COLS - 1; c++) {
+ // Sütun 0 ve GRID_COLS-1 hariç
endCols.push(c);
}
+ if (endCols.length === 0) {
+ console.error("Path generation: Not enough columns for end path.");
+ return pathArr; // Mevcut yolu döndür
+ }
var endCol = endCols[Math.floor(Math.random() * endCols.length)];
- var endRow = GRID_ROWS - 1;
- // The cell before the end (must be adjacent to end, not on edge)
- var preEndRow = endRow - 1;
- // Path must stay within 1..8 for both col and row (never on edge)
- // Build path from (col, row) to (endCol, preEndRow)
- var curCol = col;
- var curRow = row;
- var lastDir = null; // "down", "left", "right", "up"
- var straightCount = 1;
- // Track visited cells to prevent self-crossing
- var visited = {};
- function cellKey(x, y) {
- return x + "," + y;
+ var endRow = GRID_ROWS - 1; // Son satır
+ var preEndRow = GRID_ROWS - 2; // Bitişten bir önceki satır (yolun ana gövdesinin sonu)
+ // Eğer preEndRow < curRow (yani GRID_ROWS <= 2 gibi durumlar)
+ if (preEndRow < curRow) {
+ // Zaten curRow (1) preEndRow'dan büyük veya eşitse, doğrudan bitişe gitmeyi dene
+ // Bu durum, ızgara çok kısaysa (örn: 2 satır) oluşabilir.
+ // Bu durumda, yol zaten (startCol,0) -> (startCol,1) şeklindedir.
+ // Eğer endCol farklıysa ve yanlamasına hareket mümkünse bir adım atılabilir.
+ if (curCol !== endCol && GRID_COLS > 2) {// Yanlamasına hareket için yer varsa
+ // Basitçe endCol'e doğru bir adım atmaya çalış (eğer geçerliyse)
+ // Bu senaryo için daha detaylı mantık gerekebilir, şimdilik basit tutuluyor.
+ }
+ // Son noktayı ekle (endCol, endRow)
+ // Önce (endCol, preEndRow)'a (yani endCol, curRow'a) gidilmeli (eğer farklıysa)
+ // Sonra (endCol, endRow)'a inilmeli.
+ // Bu basit durumda, eğer curRow === preEndRow ise, sadece son adıma ihtiyaç var.
+ if (curCol !== endCol && preEndRow === curRow) {// Eğer aynı satırda ama farklı sütundaysak
+ // Yanlamasına hedef sütuna git (eğer aradaki boşluk kuralı izin verirse)
+ // Bu basitlik için, bu adımı atlayıp doğrudan sonuca gideceğiz.
+ // Daha karmaşık mantık, bu noktadan preEndRow/endCol'e bir yol bulmayı gerektirir.
+ }
+ pathArr.push(snapToGrid(GRID_MARGIN_X + endCol * GRID_SIZE, GRID_MARGIN_Y + endRow * GRID_SIZE));
+ visited[cellKey(endCol, endRow)] = true; // Son noktayı da ziyaret edilmiş say
+ return pathArr;
}
- visited[cellKey(curCol, curRow)] = true;
- // We'll use a random walk with constraints, now allowing up, but never crossing itself
+ var lastDir = "down"; // İlk hareket aşağı olduğu için
+ var straightCount = 1;
+ var maxRetries = 500; // Takılma durumunda maksimum deneme sayısı
+ var retries = 0;
+ // Ana yol oluşturma döngüsü: (curCol, curRow) => (endCol, preEndRow)
while (!(curRow === preEndRow && curCol === endCol)) {
- var moves = [];
- // Can move down if not at preEndRow
- if (curRow < preEndRow) moves.push("down");
- // Can move up if not at row 1 and not reversing last move
- if (curRow > 1 && lastDir !== "down") moves.push("up");
- // Can move left if not at col 1
- if (curCol > 1) moves.push("left");
- // Can move right if not at col 8
- if (curCol < GRID_COLS - 2) moves.push("right");
- // Don't allow to go back to previous cell or cross itself
- var validMoves = [];
- for (var mi = 0; mi < moves.length; mi++) {
- var testCol = curCol;
- var testRow = curRow;
- if (moves[mi] === "down") testRow++;
- if (moves[mi] === "up") testRow--;
- if (moves[mi] === "left") testCol--;
- if (moves[mi] === "right") testCol++;
- // Don't allow to cross itself
- if (visited[cellKey(testCol, testRow)]) continue;
- // If moving down, don't allow to land on left/right edge (col 0 or 9)
- if (moves[mi] === "down" && (testCol === 0 || testCol === GRID_COLS - 1)) continue;
- // If moving up, don't allow to land on left/right edge (col 0 or 9)
- if (moves[mi] === "up" && (testCol === 0 || testCol === GRID_COLS - 1)) continue;
- // If moving up, must allow a future down (not get stuck at top)
- if (moves[mi] === "up" && testRow === 1 && (testCol === 0 || testCol === GRID_COLS - 1)) continue;
- validMoves.push(moves[mi]);
+ retries++;
+ if (retries > maxRetries) {
+ // console.warn("Path generation: Max retries reached. Path may be incomplete or invalid.");
+ // Takılma durumunda, yolu yeniden oluşturmayı deneyebilir veya hata verebiliriz.
+ // Şimdilik, mevcut yolu döndürüp, muhtemelen eksik olacak.
+ // Daha iyi bir çözüm, tüm fonksiyonu yeniden çağırmak veya null döndürmek olabilir.
+ // Hızlı bir düzeltme olarak, burada kesip kalan yolu tamamlamaya çalışalım.
+ break;
}
- // Prefer to move toward endCol/endRow if possible
- var preferred = [];
- for (var mi = 0; mi < validMoves.length; mi++) {
- var testCol = curCol;
- var testRow = curRow;
- if (validMoves[mi] === "down") testRow++;
- if (validMoves[mi] === "up") testRow--;
- if (validMoves[mi] === "left") testCol--;
- if (validMoves[mi] === "right") testCol++;
- if (testCol === endCol && testRow === preEndRow) {
- preferred.push(validMoves[mi]);
- } else if (testCol === endCol) {
- preferred.push(validMoves[mi]);
- } else if (testRow === preEndRow) {
- preferred.push(validMoves[mi]);
+ var potentialMoves = [];
+ // Aşağı hareket: Eğer preEndRow'a ulaşmadıysak
+ if (curRow < preEndRow) potentialMoves.push({
+ name: "down",
+ dx: 0,
+ dy: 1
+ });
+ // Yukarı hareket: Eğer satır 1'de değilsek ve son hareket aşağı değilse
+ if (curRow > 1 && lastDir !== "down") potentialMoves.push({
+ name: "up",
+ dx: 0,
+ dy: -1
+ });
+ // Sola hareket: Eğer sütun 1'de değilsek ve son hareket sağa değilse
+ if (curCol > 1 && lastDir !== "right") potentialMoves.push({
+ name: "left",
+ dx: -1,
+ dy: 0
+ });
+ // Sağa hareket: Eğer sütun GRID_COLS-2'de değilsek ve son hareket sola değilse
+ if (curCol < GRID_COLS - 2 && lastDir !== "left") potentialMoves.push({
+ name: "right",
+ dx: 1,
+ dy: 0
+ });
+ var validMovesRaw = [];
+ for (var i = 0; i < potentialMoves.length; i++) {
+ var move = potentialMoves[i];
+ var testCol = curCol + move.dx;
+ var testRow = curRow + move.dy;
+ // Sınır kontrolü (gerçi potentialMoves oluşturulurken bu zaten dikkate alınıyor)
+ // testCol [1, GRID_COLS-2] aralığında, testRow [1, GRID_ROWS-2] (preEndRow dahil) aralığında olmalı
+ if (testCol < 1 || testCol > GRID_COLS - 2 || testRow < 1 || testRow > preEndRow) {
+ continue;
}
+ // Kural 1: Kendi üzerinden geçme (hücre zaten ziyaret edilmiş)
+ if (visited[cellKey(testCol, testRow)]) {
+ continue;
+ }
+ // Kural 2: Boşluk kuralı
+ var spacingOk = true;
+ var orthogonalNeighbors = [{
+ c: testCol + 1,
+ r: testRow
+ }, {
+ c: testCol - 1,
+ r: testRow
+ }, {
+ c: testCol,
+ r: testRow + 1
+ }, {
+ c: testCol,
+ r: testRow - 1
+ }];
+ for (var ni = 0; ni < orthogonalNeighbors.length; ni++) {
+ var neighbor = orthogonalNeighbors[ni];
+ // Komşu, geldiğimiz hücre ise kontrol etme
+ if (neighbor.c === curCol && neighbor.r === curRow) {
+ continue;
+ }
+ if (visited[cellKey(neighbor.c, neighbor.r)]) {
+ spacingOk = false;
+ break;
+ }
+ }
+ if (!spacingOk) {
+ continue;
+ }
+ validMovesRaw.push(move);
}
- // If we've gone straight 5 times, must turn
+ if (validMovesRaw.length === 0) {
+ // console.warn("Path generation: No valid moves from (" + curCol + "," + curRow + "). Trying to recover or break.");
+ // Eğer geçerli hamle yoksa, takılmışız demektir. Döngüyü kır.
+ // Daha gelişmiş bir çözüm backtracking olabilir.
+ break;
+ }
+ var currentActionableMoves = validMovesRaw;
if (straightCount >= 5) {
- validMoves = validMoves.filter(function (m) {
- return m !== lastDir;
+ var nonStraightMoves = validMovesRaw.filter(function (m) {
+ return m.name !== lastDir;
});
+ if (nonStraightMoves.length > 0) {
+ currentActionableMoves = nonStraightMoves; // Dönüşü önceliklendir
+ }
+ // Eğer nonStraightMoves boşsa, yani tek seçenek düz gitmekse,
+ // currentActionableMoves zaten validMovesRaw olarak kalır ve düz gitmeye izin verir.
}
- // Pick move: prefer toward end, but randomize
- var move = null;
- if (preferred.length > 0 && Math.random() < 0.7) {
- move = preferred[Math.floor(Math.random() * preferred.length)];
- } else if (validMoves.length > 0) {
- move = validMoves[Math.floor(Math.random() * validMoves.length)];
- } else {
- // Should not happen, but fallback: try to go down if possible, else break
- if (moves.indexOf("down") !== -1) {
- move = "down";
- } else {
- break;
+ var preferredMoves = [];
+ for (var k = 0; k < currentActionableMoves.length; k++) {
+ var moveCand = currentActionableMoves[k];
+ var candCol = curCol + moveCand.dx;
+ var candRow = curRow + moveCand.dy;
+ // Hedefe (endCol, preEndRow) yakınlaşmayı tercih et
+ if (candCol === endCol && candRow === preEndRow) {
+ // Doğrudan hedefe
+ preferredMoves.push(moveCand);
+ } else if (candCol === endCol || candRow === preEndRow) {
+ // Eksenlerden birini hizala
+ // Daha güçlü tercih: Manhattan mesafesini azaltanlar
+ var distOld = Math.abs(curCol - endCol) + Math.abs(curRow - preEndRow);
+ var distNew = Math.abs(candCol - endCol) + Math.abs(candRow - preEndRow);
+ if (distNew < distOld) {
+ preferredMoves.push(moveCand);
+ }
}
}
- // Apply move
- if (move === lastDir) {
+ // Eğer tercih edilen hamle yoksa ve rastgelelik daha azsa, yine de tüm uygun hamleleri kullan
+ if (preferredMoves.length === 0 && currentActionableMoves.length > 0) {
+ // Hedefe doğru basit bir yönlendirme ekleyebiliriz
+ var bestMoveByDistance = null;
+ var minDistance = Infinity;
+ for (var k = 0; k < currentActionableMoves.length; k++) {
+ var moveCand = currentActionableMoves[k];
+ var candCol = curCol + moveCand.dx;
+ var candRow = curRow + moveCand.dy;
+ var distNew = Math.abs(candCol - endCol) + Math.abs(candRow - preEndRow);
+ if (distNew < minDistance) {
+ minDistance = distNew;
+ bestMoveByDistance = moveCand;
+ }
+ }
+ if (bestMoveByDistance) preferredMoves.push(bestMoveByDistance);
+ }
+ var selectedMove;
+ if (preferredMoves.length > 0 && Math.random() < 0.85) {
+ // Tercihli hareket olasılığını artır
+ selectedMove = preferredMoves[Math.floor(Math.random() * preferredMoves.length)];
+ } else {
+ // Tercihli yoksa veya rastgelelik, herhangi bir uygun hamleden seç
+ selectedMove = currentActionableMoves[Math.floor(Math.random() * currentActionableMoves.length)];
+ }
+ if (!selectedMove) {
+ // Bu durumun oluşmaması gerekir eğer currentActionableMoves doluysa
+ // console.warn("Path generation: selectedMove is null. Breaking.");
+ break;
+ }
+ if (selectedMove.name === lastDir) {
straightCount++;
} else {
straightCount = 1;
}
- lastDir = move;
- if (move === "down") curRow++;
- if (move === "up") curRow--;
- if (move === "left") curCol--;
- if (move === "right") curCol++;
+ lastDir = selectedMove.name;
+ curCol += selectedMove.dx;
+ curRow += selectedMove.dy;
visited[cellKey(curCol, curRow)] = true;
pathArr.push(snapToGrid(GRID_MARGIN_X + curCol * GRID_SIZE, GRID_MARGIN_Y + curRow * GRID_SIZE));
+ } // while döngüsü sonu
+ // (curCol, curRow) şimdi (endCol, preEndRow)'da olmalı (veya döngü takıldığı için daha erken bitti)
+ // Eğer hedef sütuna ulaşamadıysak, oraya doğru bir çizgi çekmeyi deneyebiliriz (basitçe).
+ // Bu, karmaşık bir mantık gerektirir ve mevcut yolun geçerliliğini bozabilir.
+ // Şimdilik, preEndRow'a ulaştığımızı varsayalım.
+ // Eğer preEndRow'a tam olarak hedeflenen endCol ile ulaşılmadıysa
+ // ve curCol != endCol ise, bu durumu ele almak gerekir.
+ // Mevcut kod, döngü bittiğinde curCol ve curRow'un endCol ve preEndRow olduğunu varsayar.
+ // Eğer döngü 'break' ile erken biterse, curCol/curRow farklı olabilir.
+ // Bu durumda son adım yine de (endCol, endRow)'a eklenir. Bu, kopuk bir yola neden olabilir.
+ // En iyisi, eğer döngü erken biterse, bu fonksiyonun başarısız olduğunu belirtmek.
+ // Ancak, orijinal yapıya sadık kalarak, son adımı ekleyelim.
+ // Her zaman bitişe 1 aşağı hareketle tamamla: (endCol, endRow)
+ // Eğer ızgara çok kısaysa (örn: GRID_ROWS=1), endRow=0, preEndRow=-1 olur.
+ // Bu durumlar yukarıda (preEndRow < curRow kontrolü) ele alınmaya çalışıldı.
+ if (GRID_ROWS > 0) {
+ // En az bir satır varsa
+ // Son noktayı eklemeden önce, son noktanın geçerliliğini kontrol etmek iyi olabilir.
+ // Ancak, bu nokta (endCol, GRID_ROWS-1) genellikle "özel" kabul edilir.
+ pathArr.push(snapToGrid(GRID_MARGIN_X + endCol * GRID_SIZE, GRID_MARGIN_Y + endRow * GRID_SIZE));
+ visited[cellKey(endCol, endRow)] = true; // Son noktayı da ziyaret edilmiş say
}
- // Now at (endCol, preEndRow). Always finish with 1 down to (endCol, endRow)
- pathArr.push(snapToGrid(GRID_MARGIN_X + endCol * GRID_SIZE, GRID_MARGIN_Y + endRow * GRID_SIZE));
return pathArr;
}
var path = generateSimplePath();
// --- Start Game Button Logic ---
@@ -389,13 +526,23 @@
}
};
// Hide all gameplay UI until game starts
setNextWaveBtnVisible(false);
-if (typeof kpTxt !== "undefined") kpTxt.visible = false;
-if (typeof healthTxt !== "undefined") healthTxt.visible = false;
-if (typeof waveTxt !== "undefined") waveTxt.visible = false;
-if (typeof nextWaveBtn !== "undefined") nextWaveBtn.visible = false;
-if (typeof autoNextWaveBtn !== "undefined") autoNextWaveBtn.visible = false;
+if (typeof kpTxt !== "undefined") {
+ kpTxt.visible = false;
+}
+if (typeof healthTxt !== "undefined") {
+ healthTxt.visible = false;
+}
+if (typeof waveTxt !== "undefined") {
+ waveTxt.visible = false;
+}
+if (typeof nextWaveBtn !== "undefined") {
+ nextWaveBtn.visible = false;
+}
+if (typeof autoNextWaveBtn !== "undefined") {
+ autoNextWaveBtn.visible = false;
+}
// Show gameplay UI when game starts
function showGameplayUI() {
kpTxt.visible = true;
healthTxt.visible = true;
@@ -411,9 +558,11 @@
waveTxt.setText('Wave: ' + currentWave + '/' + maxWaves);
}
var _origStartWave = startWave;
startWave = function startWave() {
- if (!gameStarted) return;
+ if (!gameStarted) {
+ return;
+ }
showGameplayUI();
_origStartWave();
};
// Buildable tile logic removed for now
@@ -527,9 +676,11 @@
var square = towerSelectSquares[colorIdx];
square.down = function (x, y, obj) {
// Clean up any previous ghost
if (draggingTowerGhost) {
- if (draggingTowerGhost.rangeCircle) draggingTowerGhost.rangeCircle.destroy();
+ if (draggingTowerGhost.rangeCircle) {
+ draggingTowerGhost.rangeCircle.destroy();
+ }
draggingTowerGhost.destroy();
draggingTowerGhost = null;
draggingTowerType = null;
}
@@ -563,9 +714,11 @@
var px = path[i].x;
var py = path[i].y;
var cellX = Math.round((px - GRID_MARGIN_X) / GRID_SIZE);
var cellY = Math.round((py - GRID_MARGIN_Y) / GRID_SIZE);
- if (cellX === gridX && cellY === gridY) return true;
+ if (cellX === gridX && cellY === gridY) {
+ return true;
+ }
}
return false;
}
// Helper: check if a tower already exists at grid cell
@@ -573,9 +726,11 @@
for (var i = 0; i < towers.length; i++) {
var t = towers[i];
var tx = Math.round((t.x - GRID_MARGIN_X) / GRID_SIZE);
var ty = Math.round((t.y - GRID_MARGIN_Y) / GRID_SIZE);
- if (tx === gridX && ty === gridY) return true;
+ if (tx === gridX && ty === gridY) {
+ return true;
+ }
}
return false;
}
// Dragging ghost tower with finger/mouse
@@ -631,17 +786,21 @@
kpTxt.setText('Points: ' + knowledgePoints);
}
}
// Clean up ghost
- if (draggingTowerGhost.rangeCircle) draggingTowerGhost.rangeCircle.destroy();
+ if (draggingTowerGhost.rangeCircle) {
+ draggingTowerGhost.rangeCircle.destroy();
+ }
draggingTowerGhost.destroy();
draggingTowerGhost = null;
draggingTowerType = null;
return;
}
// If not placing, just clear ghost
if (draggingTowerGhost) {
- if (draggingTowerGhost.rangeCircle) draggingTowerGhost.rangeCircle.destroy();
+ if (draggingTowerGhost.rangeCircle) {
+ draggingTowerGhost.rangeCircle.destroy();
+ }
draggingTowerGhost.destroy();
draggingTowerGhost = null;
draggingTowerType = null;
}
@@ -719,9 +878,11 @@
}
setNextWaveBtnVisible(false);
// Start next wave
function startWave() {
- if (waveInProgress || currentWave > maxWaves) return;
+ if (waveInProgress || currentWave > maxWaves) {
+ return;
+ }
waveInProgress = true;
setNextWaveBtnVisible(false);
enemiesToSpawn = 6 + currentWave * 2;
spawnIndex = 0;