User prompt
Görev veren NPC'ye tıkladığımız zaman Karakterin yukarısında Görev hakkında ''Kabul Et - Reddet'' butonları olsun. Ona göre etkileşimde olalım ve ona göre görevi yapalım veya yapmayalım. Verdiğimiz yanıta göre yapay zeka cevap versin ve verdiği görev hakkında işlem yapsın.
User prompt
NPC'ye tıkladığım zaman NPC görevi veya bulunduğum etkileşim Level altında küçük şekilde ve bir süre spawn olsun ve ne konuda etkileşimde bulundum ise bileyim.
User prompt
NPC soru sorduğunda, tıkladığım zaman NPC bir süre dursun.
User prompt
NPC Trade sorusu soruyor. tıkla diyor. Tıklıyorum, ticaret paneli açılmıyor.
User prompt
NPC'ye yaklaşıyorum ama hiçbirşey olmuyor. Ne çıkıyor ise, NPC üzerinde çıksın yaklaşınca.
User prompt
Haritada unsurların spawn olma oranını genel olarak ortalama %10 oranında artır. (Korsanlar hariç)
User prompt
Wave altına, Level ve EXP sistemi ekle, oyundaki RPG görevlerinden EXP kazanalım. RPG Macera oyunu olarak sistemi kur.
User prompt
Please fix the bug: 'TypeError: Cannot read properties of undefined (reading 'indexOf')' in or related to this line: 'if (!npcActive && tradeText.text.indexOf("Trade:") !== 0 && tradeText.text.indexOf("Special:") !== 0) {' Line Number: 1428
User prompt
NPC'ler ile ilgili etkileşim kuralım ve birçok RPG ve macera konusu ekle. Bazen de ticaret yapalım. NPC'ler yapay zeka oyuncuları tarafından yönetilsin.
User prompt
Kod içinde olan veya olmayan, oyuna uygun birçok özelliği veya mekaniği aktif et. Uyumsuz ise ekleme. Birbirinden farklı, Ticaret ve RPG katacak mekanik ve özellik ekle.
User prompt
Haritada unsurların spawn olma oranını genel olarak ortalama %10 oranında artır.
User prompt
''Reduce spawn rate of all entities and asteroids by 80%'' bunu %70 olarak değiştir. Ama korsanlar için %85 olsun. Çünkü korsan çok.
User prompt
Kod içinde olan veya olmayan, oyuna uygun birçok özelliği veya mekaniği aktif et. Uyumsuz ise ekleme. Birbirinden farklı, eğlence katacak mekanik ve özellik ekle.
User prompt
Asteroid'lere de otomatik ateş olsun. Ancak şuan haritada spawn olan unsur oranını %80 oranında azalt. Çok fazla unsur var.
User prompt
Kod içinde olan veya olmayan, oyuna uygun birçok özelliği veya mekaniği aktif et. Uyumsuz ise ekleme. Birbirinden farklı, eğlence katacak mekanik ve özellik ekle.
User prompt
Coins ve hammadde sayıları ekranda görünmüyor. Görünecek şekilde ekranda ki dış alanları sola kaydır.
User prompt
Düşmana, çok yaklaşırsak bize saldırsın ve biz ona çok yaklaşırsak saldıralım. Yoksa normal seyrinde devam etsinler.
User prompt
Haritada ki unsurlar otomatik spawn olma sistemini kaldır. İlerledikçe harita dışından spawn olarak gelsinler veya rastgele dolaşsınlar. sabit durmasın hiç birşey. Bir akış olsun.
User prompt
Harita sürekli aşağı doğru kaymasın, Karakterin gittiği yöne doğru haritada gidelim.
User prompt
Please fix the bug: 'TypeError: Cannot set properties of undefined (setting 'fill')' in or related to this line: 'healthText.style.fill = "#00ff00";' Line Number: 255
Code edit (1 edits merged)
Please save this source code
User prompt
Galactic Trader: Sonsuz Görevler
Initial prompt
Oyuncu, haritanın merkezinde bir uzay mekiğini joystick ile kontrol eder. Sonsuz bir galakside rastgele NPC’ler belirir ve oyuncuya görevler ile ticaret fırsatları sunar. Korsan savaş gemileriyle karşılaşılır; savaşarak coin ve hammadde toplanır. Toplanan kaynaklar ticaretle satılır, yeni yükseltmeler ve ekipmanlar alınır. Görevler ve ticaret ilerledikçe yeni bölgeler ve zorluklar açılır. Oyun, sonsuz ilerleme ve sürekli gelişen görevlerle devam eder.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Asteroid = Container.expand(function () { var self = Container.call(this); var asteroidGraphics = self.attachAsset('resource', { anchorX: 0.5, anchorY: 0.5, tint: 0x8B4513, scaleX: 2, scaleY: 2 }); self.health = 50; self.resources = Math.floor(Math.random() * 10) + 5; self.vx = (Math.random() - 0.5) * 0.5; self.vy = (Math.random() - 0.5) * 0.5; self.rotationSpeed = (Math.random() - 0.5) * 0.02; self.takeDamage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xffffff, 100); }; self.update = function () { self.x += self.vx; self.y += self.vy; self.rotation += self.rotationSpeed; }; return self; }); // Black Hole Hazard Class var BlackHole = Container.expand(function () { var self = Container.call(this); var bhGraphics = self.attachAsset('star', { anchorX: 0.5, anchorY: 0.5, scaleX: 8, scaleY: 8, tint: 0x222222 }); self.radius = 200; self.pullStrength = 0.7 + Math.random() * 0.5; self.damageRadius = 120; self.update = function () { // Animate black hole bhGraphics.rotation += 0.03; // Pull ship if in range var dx = ship.x - self.x; var dy = ship.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < self.radius) { var pull = (self.radius - dist) / self.radius * self.pullStrength; ship.x -= dx / dist * pull; ship.y -= dy / dist * pull; // Camera follows ship, so move all objects accordingly for (var i = 0; i < stars.length; i++) { stars[i].x -= dx / dist * pull; stars[i].y -= dy / dist * pull; } for (var i = 0; i < npcs.length; i++) { npcs[i].x -= dx / dist * pull; npcs[i].y -= dy / dist * pull; } for (var i = 0; i < pirates.length; i++) { pirates[i].x -= dx / dist * pull; pirates[i].y -= dy / dist * pull; } for (var i = 0; i < coins.length; i++) { coins[i].x -= dx / dist * pull; coins[i].y -= dy / dist * pull; } for (var i = 0; i < resources.length; i++) { resources[i].x -= dx / dist * pull; resources[i].y -= dy / dist * pull; } for (var i = 0; i < bullets.length; i++) { bullets[i].x -= dx / dist * pull; bullets[i].y -= dy / dist * pull; } for (var i = 0; i < enemyBullets.length; i++) { enemyBullets[i].x -= dx / dist * pull; enemyBullets[i].y -= dy / dist * pull; } for (var i = 0; i < asteroids.length; i++) { asteroids[i].x -= dx / dist * pull; asteroids[i].y -= dy / dist * pull; } for (var i = 0; i < upgradeStations.length; i++) { upgradeStations[i].x -= dx / dist * pull; upgradeStations[i].y -= dy / dist * pull; } // boostParticles removed (no boost system) for (var i = 0; i < explosions.length; i++) { explosions[i].x -= dx / dist * pull; explosions[i].y -= dy / dist * pull; } } // Damage ship if too close if (dist < self.damageRadius) { if (!self.lastDamaged || LK.ticks - self.lastDamaged > 30) { ship.takeDamage(10); self.lastDamaged = LK.ticks; LK.effects.flashObject(ship, 0x000000, 200); } } }; return self; }); var Bullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 15; self.damage = 10; self.directionX = 0; self.directionY = -1; self._outOfRangeTick = null; self._fadeStartTick = null; self._expireTick = null; self._range = typeof ATTACK_RANGE !== "undefined" ? ATTACK_RANGE : 220; self._startX = null; self._startY = null; self.update = function () { if (self._startX === null || self._startY === null) { self._startX = self.x; self._startY = self.y; } self.x += self.directionX * self.speed; self.y += self.directionY * self.speed; // Out of range detection: bullet range = attack range var dx = self.x - self._startX; var dy = self.y - self._startY; var dist = Math.sqrt(dx * dx + dy * dy); var outOfRange = dist > self._range; if (outOfRange && self._outOfRangeTick === null) { self._outOfRangeTick = LK.ticks; self._fadeStartTick = self._outOfRangeTick; self._expireTick = self._fadeStartTick + 60; // 1 second fade } // Fade out logic if out of range if (self._fadeStartTick !== null && self._expireTick !== null) { var now = LK.ticks; var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick)); self.alpha = 1 - fadeProgress; if (now >= self._expireTick) { self._shouldRemove = true; } } else { self.alpha = 1; } }; return self; }); var Coin = Container.expand(function () { var self = Container.call(this); var coinGraphics = self.attachAsset('coin', { anchorX: 0.5, anchorY: 0.5 }); self.value = Math.floor(Math.random() * 10) + 5; // --- Timed fade-out system --- self._spawnTick = LK.ticks; self._fadeStartTick = self._spawnTick + 180; // 3 seconds visible self._expireTick = self._fadeStartTick + 120; // 2 seconds fade (total 5s) self.update = function () { self.rotation += 0.05; // Timed fade-out logic if (typeof self._expireTick === "number" && typeof self._fadeStartTick === "number") { var now = LK.ticks; if (now >= self._fadeStartTick) { // Start fading out var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick)); self.alpha = 1 - fadeProgress; if (now >= self._expireTick) { // Mark for removal (actual removal in game.update) self._shouldRemove = true; } } else { self.alpha = 1; } } }; return self; }); // CoinBox: Toplanınca 50-100 coin veren, fade-out ile kaybolan obje var CoinBox = Container.expand(function () { var self = Container.call(this); var boxGraphics = self.attachAsset('coinbox', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.9, scaleY: 0.9 }); self.value = 50 + Math.floor(Math.random() * 51); // 50-100 coin arası // Timed fade-out system self._spawnTick = LK.ticks; self._fadeStartTick = self._spawnTick + 240; // 4 seconds visible self._expireTick = self._fadeStartTick + 120; // 2 seconds fade (total 6s) self.update = function () { boxGraphics.rotation += 0.07; // Timed fade-out logic if (typeof self._expireTick === "number" && typeof self._fadeStartTick === "number") { var now = LK.ticks; if (now >= self._fadeStartTick) { var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick)); self.alpha = 1 - fadeProgress; if (now >= self._expireTick) { self._shouldRemove = true; } } else { self.alpha = 1; } } }; return self; }); // DerelictDebris: Enkaz, üstüne gelince 0-50 arası rastgele hammadde verir ve fade-out ile kaybolur var DerelictDebris = Container.expand(function () { var self = Container.call(this); // Use randomly selected pirate asset for debris appearance var pirateImageIndex = Math.floor(Math.random() * 20) + 1; var pirateAssetName = 'pirate' + pirateImageIndex; var debrisGraphics = self.attachAsset(pirateAssetName, { anchorX: 0.5, anchorY: 0.5, tint: 0x00ff00, // yeşil enkaz scaleX: 1.5, scaleY: 1.5 }); self.type = 'derelict'; self.rotationSpeed = 0.01; self._collected = false; self._resourceGiven = false; self._fadeStartTick = null; self._expireTick = null; self._resourceAmount = Math.floor(Math.random() * 51); // 0-50 arası self.update = function () { self.rotation += self.rotationSpeed; // Fade-out logic if (self._fadeStartTick !== null && self._expireTick !== null) { var now = LK.ticks; var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick)); self.alpha = 1 - fadeProgress; if (now >= self._expireTick) { self._shouldRemove = true; } } else { self.alpha = 1; } }; return self; }); // --- DRON CLASS --- // Dron: ship'i takip eder, %80 yakınındaki maden/coinleri toplar ve ship'e getirir var Dron = Container.expand(function () { var self = Container.call(this); // Randomly select one of 2 NPC images for dron appearance var npcImageIndex = Math.floor(Math.random() * 2) + 1; var npcAssetName = 'npc' + npcImageIndex; var dronGraphics = self.attachAsset(npcAssetName, { anchorX: 0.5, anchorY: 0.5, tint: 0x1a2dcb, scaleX: 1.2, scaleY: 1.2 }); self._followOffset = { x: -100 + Math.random() * 40, y: 80 + Math.random() * 40 }; self._targetItem = null; self._collecting = false; self._collectSpeed = 10; self.update = function () { // --- Hafif float/wobble animasyonu: Dron sabit durmasın, havada uçuyormuş gibi hareket etsin --- if (typeof self._floatBaseY === "undefined") { self._floatBaseY = self.y; } if (typeof self._floatBaseX === "undefined") { self._floatBaseX = self.x; } if (typeof self._floatPhase === "undefined") { self._floatPhase = Math.random() * Math.PI * 2; } if (typeof self._floatPhaseX === "undefined") { self._floatPhaseX = Math.random() * Math.PI * 2; } // Ship'i takip et (eğer item toplamıyorsa) if (!self._collecting && typeof ship !== "undefined" && ship) { var targetX = ship.x + self._followOffset.x; var targetY = ship.y + self._followOffset.y; var dx = targetX - self.x; var dy = targetY - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { self.x += dx / dist * 7; self.y += dy / dist * 7; } // Float baseyi güncelle ki float animasyonu ship ile birlikte hareket etsin self._floatBaseX = self.x; self._floatBaseY = self.y; } // Eğer item topluyorsa, ona doğru git if (self._collecting && self._targetItem) { var dx = self._targetItem.x - self.x; var dy = self._targetItem.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { self.x += dx / dist * self._collectSpeed; self.y += dy / dist * self._collectSpeed; } else { // Toplama mesafesine ulaştı // Coin veya resource ise ship'e getir self._targetItem._dronCollected = true; // Ship'e doğru item'ı taşı self._targetItem._beingCarried = true; self._targetItem._carrier = self; self._collecting = false; self._targetItem = null; } // Float baseyi güncelle ki float animasyonu item toplarken de düzgün çalışsın self._floatBaseX = self.x; self._floatBaseY = self.y; } // Eğer item taşıyorsa, item'ı ship'e doğru taşı for (var i = 0; i < coins.length; i++) { var c = coins[i]; if (c._beingCarried && c._carrier === self) { var dx = ship.x - c.x; var dy = ship.y - c.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { c.x += dx / dist * self._collectSpeed; c.y += dy / dist * self._collectSpeed; } else { // Ship'e ulaştı, coin topla playerCoins += c.value; c.destroy(); coins.splice(i, 1); LK.getSound('collect').play(); updateUI && updateUI(); saveProgress && saveProgress(); } } } for (var i = 0; i < resources.length; i++) { var r = resources[i]; if (r._beingCarried && r._carrier === self) { var dx = ship.x - r.x; var dy = ship.y - r.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { r.x += dx / dist * self._collectSpeed; r.y += dy / dist * self._collectSpeed; } else { // Ship'e ulaştı, resource topla var resourceTypes = ['metal', 'energy', 'crystal', 'gas', 'ice', 'uranium', 'silicon', 'carbon', 'plasma', 'antimatter']; var idx = resourceTypes.indexOf(r.type); if (idx >= 0) { playerResources[idx] += r.amount; checkResourceCollectAchievements && checkResourceCollectAchievements(); } r.destroy(); resources.splice(i, 1); LK.getSound('collect').play(); updateUI && updateUI(); saveProgress && saveProgress(); } } } // Eğer item toplamıyorsa, yakındaki item'ı ara if (!self._collecting && !self._targetItem) { // %25 ship'e yakın, ekranda olan coin/resource bul var found = false; var maxDist = Math.max(2048, 2732) * 0.25; for (var i = 0; i < coins.length; i++) { var c = coins[i]; if (!c._dronCollected && Math.sqrt(Math.pow(c.x - ship.x, 2) + Math.pow(c.y - ship.y, 2)) < maxDist) { self._targetItem = c; self._collecting = true; found = true; break; } } if (!found) { for (var i = 0; i < resources.length; i++) { var r = resources[i]; if (!r._dronCollected && Math.sqrt(Math.pow(r.x - ship.x, 2) + Math.pow(r.y - ship.y, 2)) < maxDist) { self._targetItem = r; self._collecting = true; break; } } } } // --- Hafif float/wobble animasyonu uygula --- // Dron'un gerçek pozisyonunu floatBaseX/floatBaseY olarak tut, ek olarak x/y'ye küçük bir sinusoidal offset uygula // Amplitüd: 8px (y), 4px (x), Periyot: 2-3 saniye arası var floatY = Math.sin(LK.ticks * 0.05 + self._floatPhase) * 8; var floatX = Math.cos(LK.ticks * 0.033 + self._floatPhaseX) * 4; self.x = self._floatBaseX + floatX; self.y = self._floatBaseY + floatY; }; return self; }); var EnemyBullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('enemyBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.damage = 5; self.directionX = 0; self.directionY = 0; self._outOfRangeTick = null; self._fadeStartTick = null; self._expireTick = null; self._range = typeof ATTACK_RANGE !== "undefined" ? ATTACK_RANGE : 220; self._startX = null; self._startY = null; self.update = function () { if (self._startX === null || self._startY === null) { self._startX = self.x; self._startY = self.y; } self.x += self.directionX * self.speed; self.y += self.directionY * self.speed; // Out of range detection: bullet range = attack range var dx = self.x - self._startX; var dy = self.y - self._startY; var dist = Math.sqrt(dx * dx + dy * dy); var outOfRange = dist > self._range; if (outOfRange && self._outOfRangeTick === null) { self._outOfRangeTick = LK.ticks; self._fadeStartTick = self._outOfRangeTick; self._expireTick = self._fadeStartTick + 60; // 1 second fade } // Fade out logic if out of range if (self._fadeStartTick !== null && self._expireTick !== null) { var now = LK.ticks; var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick)); self.alpha = 1 - fadeProgress; if (now >= self._expireTick) { self._shouldRemove = true; } } else { self.alpha = 1; } }; return self; }); // --- FRIEND CLASS --- // Dost birlik: ship'i takip eder, saldırı sırasında %50 hasar ile saldırır var Friend = Container.expand(function () { var self = Container.call(this); // Randomly select one of 2 NPC images for friend appearance var npcImageIndex = Math.floor(Math.random() * 2) + 1; var npcAssetName = 'npc' + npcImageIndex; var friendGraphics = self.attachAsset(npcAssetName, { anchorX: 0.5, anchorY: 0.5, tint: 0x83de44, scaleX: 1.1, scaleY: 1.1 }); // --- Dynamic follow offset: will be updated every frame for movement/spacing --- self._followOffset = { x: 80 - Math.random() * 40, y: 80 + Math.random() * 40 }; self._attackTarget = null; self._attacking = false; self._attackStartTick = 0; self._lastAttackTick = 0; self._attackDelay = 60; // 1 saniye (60fps) self._attackInterval = 10; // her 10 tickte bir ateş et (ateş aralığı hızlı kalsın, istenirse 5 yapılabilir) // --- For movement/spacing --- self._moveAngle = Math.random() * Math.PI * 2; self._moveTimer = Math.floor(Math.random() * 60); self._friendIdx = -1; // will be set in update self._desiredDist = 90; // base distance from ship self._avoidDist = 70; // min distance to other friends self.update = function () { // Find my index in friends array for spacing if (typeof friends !== "undefined" && Array.isArray(friends)) { for (var fi = 0; fi < friends.length; fi++) { if (friends[fi] === self) { self._friendIdx = fi; break; } } } // --- Dynamic orbit/spacing around ship --- if (typeof ship !== "undefined" && ship) { // Orbit angle: spread friends evenly in a circle, animate with time for movement var n = 0, myIdx = 0; for (var fi = 0; fi < friends.length; fi++) { if (friends[fi]) { n++; } if (friends[fi] === self) { myIdx = fi; } } if (n === 0) { n = 1; } var baseAngle = Math.PI * 2 * (myIdx / n); var timeWobble = Math.sin((LK.ticks + myIdx * 30) / 60) * 0.5 + Math.cos((LK.ticks + myIdx * 60) / 80) * 0.3; var angle = baseAngle + timeWobble; var radius = self._desiredDist + 20 * Math.sin((LK.ticks + myIdx * 40) / 50); // Target position around ship var targetX = ship.x + Math.cos(angle) * radius; var targetY = ship.y + Math.sin(angle) * radius; // --- Avoid overlapping with other friends --- if (typeof friends !== "undefined" && Array.isArray(friends)) { for (var fi = 0; fi < friends.length; fi++) { var f2 = friends[fi]; if (f2 && f2 !== self) { var dx = self.x - f2.x; var dy = self.y - f2.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < self._avoidDist && dist > 0) { // Push away from other friend var push = (self._avoidDist - dist) * 0.15; targetX += dx / dist * push; targetY += dy / dist * push; } } } } // --- Smoothly move toward target position --- var dx = targetX - self.x; var dy = targetY - self.y; var dist = Math.sqrt(dx * dx + dy * dy); var moveSpeed = 5 + Math.sin((LK.ticks + myIdx * 20) / 40) * 1.5; if (dist > 2) { self.x += dx / dist * Math.min(moveSpeed, dist); self.y += dy / dist * Math.min(moveSpeed, dist); } } // --- Friend ship rotation: rotate to match player ship direction --- if (typeof ship !== "undefined" && ship && typeof ship.rotation === "number") { // Smoothly rotate friend ship to match player ship rotation var targetRotation = ship.rotation; var rotDiff = targetRotation - self.rotation; // Normalize angle difference to [-π, π] while (rotDiff > Math.PI) { rotDiff -= Math.PI * 2; } while (rotDiff < -Math.PI) { rotDiff += Math.PI * 2; } // Apply smooth rotation with lerp factor var rotLerp = 0.1; // Adjust for rotation smoothness self.rotation += rotDiff * rotLerp; } // --- Saldırı kontrolü --- if (self._attacking && self._attackTarget && typeof self._attackTarget.x === "number" && typeof self._attackTarget.y === "number") { // 2 saniye gecikmeden sonra saldır if (LK.ticks - self._attackStartTick >= self._attackDelay) { // Hedef hala hayatta mı? if (self._attackTarget.health > 0) { // Her attackInterval tickte bir ateş et if (LK.ticks - self._lastAttackTick > self._attackInterval) { var bullet = new Bullet(); bullet.x = self.x; bullet.y = self.y; var angle = Math.atan2(self._attackTarget.y - self.y, self._attackTarget.x - self.x); bullet.directionX = Math.cos(angle); bullet.directionY = Math.sin(angle); bullet.rotation = angle + Math.PI / 2; bullet.damage = Math.round((ship.damage || 10) * 0.5); bullets.push(bullet); game.addChild(bullet); self._lastAttackTick = LK.ticks; // Calculate distance from ship to friend bullet spawn point and adjust volume var dx = bullet.x - ship.x; var dy = bullet.y - ship.y; var distanceFromShip = Math.sqrt(dx * dx + dy * dy); // Calculate volume based on distance (closer = louder, farther = quieter) // Max distance for sound falloff is 500 pixels var maxSoundDistance = 500; var volumeMultiplier = Math.max(0.1, Math.min(1.0, 1.0 - distanceFromShip / maxSoundDistance)); // Get the sound and play with adjusted volume var shootSound = LK.getSound('shoot'); var originalVolume = shootSound.volume || 0.3; // fallback to default volume shootSound.volume = originalVolume * volumeMultiplier; shootSound.play(); // Restore original volume for next play shootSound.volume = originalVolume; } } else { // Hedef öldü, saldırıyı bırak self._attacking = false; self._attackTarget = null; } } } }; // Saldırı başlat self.startAttack = function (target) { self._attackTarget = target; self._attacking = true; self._attackStartTick = LK.ticks; self._lastAttackTick = LK.ticks; }; // Saldırı bırak self.stopAttack = function () { self._attacking = false; self._attackTarget = null; }; return self; }); // GrupKorsan (Group Pirate) class: 2-4 pirates moving and attacking together, with reward label under each var GrupKorsan = Container.expand(function () { var self = Container.call(this); // Use randomly selected pirate asset for each group member var pirateImageIndex = Math.floor(Math.random() * 20) + 1; var pirateAssetName = 'pirate' + pirateImageIndex; var pirateGraphics = self.attachAsset(pirateAssetName, { anchorX: 0.5, anchorY: 0.5 }); // Group member index and group size (set externally) self.groupIndex = 0; self.groupSize = 2; // Level and stats (set externally) self.level = 1; self.health = 30; self.speed = 2; self.fireRate = 40; self.lastFire = 0; self.damage = 10; self.defense = 5; self.loot = 200; self._isGrupKorsan = true; // --- Enemy Text --- self._enemyText = new Text2("Enemy", { size: 18, fill: 0xff4444, align: "center" }); self._enemyText.anchor.set(0.5, 0); self._enemyText.x = 0; self._enemyText.y = -50; // Above the pirate sprite self.addChild(self._enemyText); // --- Level Text --- self._levelText = new Text2("Lv." + self.level, { size: 22, fill: 0xcccccc, align: "center" }); self._levelText.anchor.set(0.5, 0); self._levelText.x = 0; self._levelText.y = pirateGraphics.height / 2 + 4; self.addChild(self._levelText); // Reward label and coin value text removed for GrupKorsan // --- Health bar --- self._healthBar = new HealthBar(); self._healthBar.set({ maxValue: function maxValue() { return self.health > 0 ? self.health : 0; }, getValueFn: function getValueFn() { return self.health > 0 ? self.health : 0; }, getMaxFn: function getMaxFn() { return 30 + (self.level - 1) * 10; }, width: 60, height: 10 }); self._healthBar.y = -60; self.addChild(self._healthBar); // --- Group movement: all members follow the same target/angle, set externally --- self._groupTarget = null; self._groupAngle = 0; self._groupMoveSpeed = 2; // --- Damage handler --- self.takeDamage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xffffff, 100); }; // --- Update --- self.update = function () { // Update enemy text position in case of movement if (self._enemyText) { self._enemyText.x = 0; self._enemyText.y = -50; } // Update level/reward text positions if (self._levelText) { self._levelText.x = 0; self._levelText.y = pirateGraphics.height / 2 + 4; self._levelText.setText("Lv." + self.level); } // Reward label and coin value update removed for GrupKorsan // Health bar update if (self._healthBar && self._healthBar.update) { self._healthBar.update(); } // Group movement: follow group target/angle if set if (self._groupTarget && typeof self._groupTarget.x === "number" && typeof self._groupTarget.y === "number") { var dx = self._groupTarget.x - self.x; var dy = self._groupTarget.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { self.x += dx / dist * self._groupMoveSpeed; self.y += dy / dist * self._groupMoveSpeed; self._groupAngle = Math.atan2(dy, dx); self.rotation = self._groupAngle + Math.PI / 2; } } else if (typeof self._groupAngle === "number") { // Move in group direction if no target self.x += Math.cos(self._groupAngle) * self._groupMoveSpeed; self.y += Math.sin(self._groupAngle) * self._groupMoveSpeed; self.rotation = self._groupAngle + Math.PI / 2; } }; return self; }); // HealthBar class for displaying health above entities var HealthBar = Container.expand(function () { var self = Container.call(this); // width, height, color, maxValue, getValueFn self._width = 60; self._height = 10; self._color = 0x00ff00; self._maxValue = 100; self._getValueFn = null; self._getMaxFn = null; self._bg = self.attachAsset('resource', { anchorX: 0.5, anchorY: 0.5, scaleX: self._width / 40, scaleY: self._height / 40, tint: 0x222222 }); self._bar = self.attachAsset('resource', { anchorX: 0.5, anchorY: 0.5, scaleX: (self._width - 4) / 40, scaleY: (self._height - 4) / 40, tint: self._color }); self._bar.x = 0; self._bar.y = 0; self.set = function (opts) { self._maxValue = opts.maxValue || 100; self._getValueFn = opts.getValueFn; self._getMaxFn = opts.getMaxFn; self._color = opts.color || 0x00ff00; self._bar.tint = self._color; if (opts.width) { self._width = opts.width; self._bg.scaleX = self._width / 40; self._bar.scaleX = (self._width - 4) / 40; } if (opts.height) { self._height = opts.height; self._bg.scaleY = self._height / 40; self._bar.scaleY = (self._height - 4) / 40; } }; self.update = function () { var value = self._getValueFn ? self._getValueFn() : self._maxValue; var maxValue = self._getMaxFn ? self._getMaxFn() : self._maxValue; var ratio = Math.max(0, Math.min(1, value / maxValue)); // Color: green > yellow > red if (ratio > 0.6) { self._bar.tint = 0x00ff00; } else if (ratio > 0.3) { self._bar.tint = 0xffff00; } else { self._bar.tint = 0xff0000; } self._bar.scaleX = (self._width - 4) * ratio / 40; }; return self; }); // --- MECHANIC CLASS --- // Mechanic: Automatically repairs ship when health is low var Mechanic = Container.expand(function () { var self = Container.call(this); // Randomly select one of 2 NPC images for mechanic appearance var npcImageIndex = Math.floor(Math.random() * 2) + 1; var npcAssetName = 'npc' + npcImageIndex; var mechanicGraphics = self.attachAsset(npcAssetName, { anchorX: 0.5, anchorY: 0.5, tint: 0xff6600, // Orange tint for mechanic scaleX: 1.1, scaleY: 1.1 }); self._followOffset = { x: -120 + Math.random() * 40, y: -80 + Math.random() * 40 }; self._repairDelay = 180; // 3 seconds between repairs self._lastRepairTick = 0; self.update = function () { // Float animation like Dron if (typeof self._floatBaseY === "undefined") { self._floatBaseY = self.y; } if (typeof self._floatBaseX === "undefined") { self._floatBaseX = self.x; } if (typeof self._floatPhase === "undefined") { self._floatPhase = Math.random() * Math.PI * 2; } if (typeof self._floatPhaseX === "undefined") { self._floatPhaseX = Math.random() * Math.PI * 2; } // Follow ship if (typeof ship !== "undefined" && ship) { var targetX = ship.x + self._followOffset.x; var targetY = ship.y + self._followOffset.y; var dx = targetX - self.x; var dy = targetY - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { self.x += dx / dist * 6; self.y += dy / dist * 6; } self._floatBaseX = self.x; self._floatBaseY = self.y; } // Auto repair ship when health is below 50% if (ship && ship.health < ship.maxHealth * 0.5 && LK.ticks - self._lastRepairTick > self._repairDelay) { var repairAmount = Math.round(ship.maxHealth * 0.1); // Repair 10% of max health ship.health = Math.min(ship.health + repairAmount, ship.maxHealth); self._lastRepairTick = LK.ticks; // Visual effect LK.effects.flashObject(ship, 0x00ff99, 300); // Floating label var label = new Text2("Mechanic Repair: +" + repairAmount + " HP", { size: 32, fill: 0x00ff99, align: "center" }); label.anchor.set(0.5, 0.5); label.x = ship.x; label.y = ship.y - 100; if (typeof game !== "undefined" && game.addChild) { game.addChild(label); } LK.setTimeout(function () { if (label.parent) { label.parent.removeChild(label); } }, 1200); updateUI && updateUI(); } // Float animation var floatY = Math.sin(LK.ticks * 0.04 + self._floatPhase) * 6; var floatX = Math.cos(LK.ticks * 0.03 + self._floatPhaseX) * 3; self.x = self._floatBaseX + floatX; self.y = self._floatBaseY + floatY; }; return self; }); // Helper: open Crew panel var NPC = Container.expand(function () { var self = Container.call(this); // Randomly select one of 2 NPC images var npcImageIndex = Math.floor(Math.random() * 2) + 1; var npcAssetName = 'npc' + npcImageIndex; var npcGraphics = self.attachAsset(npcAssetName, { anchorX: 0.5, anchorY: 0.5 }); self.isAI = true; // AI-driven self.baseY = 0; self.moveSpeed = 1 + Math.random() * 2; self.moveAngle = Math.random() * Math.PI * 2; self.wanderTimer = 0; self.lastAIAction = 0; self.targetX = null; self.targetY = null; self.rpgName = "NPC-" + Math.floor(Math.random() * 10000); // --- NPC Level System --- // Level range: wave 1 => 1-3, wave 2 => 3-5, wave 3 => 5-7, etc. var minLevel = 1 + (typeof waveNumber !== "undefined" ? waveNumber : 0) * 2; var maxLevel = minLevel + 2; self.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1)); self.health = 100 + (self.level - 1) * 10; // --- Level Text --- self._levelText = new Text2("Lv." + self.level, { size: 22, fill: 0xcccccc, align: "center" }); self._levelText.anchor.set(0.5, 0); self._levelText.x = 0; self._levelText.y = 40; // Under the NPC sprite self.addChild(self._levelText); // Only floating AI logic, no quests/events/trade // Add dummy interact method to prevent TypeError self.interact = function () { // No interaction for basic NPCs, return empty string return ""; }; self.update = function () { // Update level text position in case of movement if (self._levelText) { self._levelText.x = 0; self._levelText.y = npcGraphics.height / 2 + 4; self._levelText.setText("Lv." + self.level); } // Pause logic: If pauseUntil is set and not expired, skip movement if (self.pauseUntil && LK.ticks < self.pauseUntil) { // Still paused, only float animation self.y += Math.sin(LK.ticks * 0.05) * 0.5; return; } // AI: Sometimes move toward a random point, sometimes wander self.wanderTimer++; if (self.wanderTimer > 120 + Math.random() * 120) { if (Math.random() < 0.5) { // Pick a random point to move toward (simulate AI goal seeking) self.targetX = self.x + (Math.random() - 0.5) * 800; self.targetY = self.y + (Math.random() - 0.5) * 800; } else { self.targetX = null; self.targetY = null; } self.moveAngle = Math.random() * Math.PI * 2; self.wanderTimer = 0; } // Move toward target if set, else wander if (self.targetX !== null && self.targetY !== null) { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { self.x += dx / dist * self.moveSpeed; self.y += dy / dist * self.moveSpeed; self.moveAngle = Math.atan2(dy, dx); self._targetRotation = self.moveAngle + Math.PI / 2; } else { self.targetX = null; self.targetY = null; } } else { // Wander randomly with smooth gliding motion (no sharp turns) if (typeof self._wanderVX === "undefined") { self._wanderVX = Math.cos(self.moveAngle) * self.moveSpeed; } if (typeof self._wanderVY === "undefined") { self._wanderVY = Math.sin(self.moveAngle) * self.moveSpeed; } // Target velocity for this wander direction var targetVX = Math.cos(self.moveAngle) * self.moveSpeed; var targetVY = Math.sin(self.moveAngle) * self.moveSpeed; // Smoothly interpolate velocity for gliding effect self._wanderVX += (targetVX - self._wanderVX) * 0.03; self._wanderVY += (targetVY - self._wanderVY) * 0.03; self.x += self._wanderVX; self.y += self._wanderVY; self._targetRotation = Math.atan2(self._wanderVY, self._wanderVX) + Math.PI / 2; } // Smoothly rotate toward target rotation if (typeof self._targetRotation === "number") { var diff = self._targetRotation - self.rotation; while (diff > Math.PI) { diff -= Math.PI * 2; } while (diff < -Math.PI) { diff += Math.PI * 2; } var lerp = 0.05; self.rotation += diff * lerp; } // Float animation on top of movement self.y += Math.sin(LK.ticks * 0.05) * 0.5; }; return self; }); var Pirate = Container.expand(function () { var self = Container.call(this); // Randomly select one of 20 pirate images var pirateImageIndex = Math.floor(Math.random() * 20) + 1; var pirateAssetName = 'pirate' + pirateImageIndex; var pirateGraphics = self.attachAsset(pirateAssetName, { anchorX: 0.5, anchorY: 0.5 }); // --- Pirate Level & Scaling --- self.level = typeof waveNumber !== "undefined" ? 1 + waveNumber : 1; self.health = 30 + (self.level - 1) * 10; self.speed = 2 + Math.floor((self.level - 1) / 10) * 0.2; // Slight speed up every 10 waves self.fireRate = 60 - Math.min((self.level - 1) * 2, 30); // Faster fire at higher levels self.lastFire = 0; self.loot = Math.floor(Math.random() * 30) + 10 + (self.level - 1) * 2; self.moveAngle = Math.random() * Math.PI * 2; self.patrolTimer = 0; self.aggroRange = 600; // Korsan saldırı ve savunma gücü seviyeye göre orantılı artar self.damage = 5 + Math.floor((self.level - 1) * 1.5); // Saldırı gücü daha hızlı artar self.defense = 2 + Math.floor((self.level - 1) * 1.2); // Savunma gücü de seviyeye göre artar // --- Enemy Text --- self._enemyText = new Text2("Enemy", { size: 18, fill: 0xff4444, align: "center" }); self._enemyText.anchor.set(0.5, 0); self._enemyText.x = 0; self._enemyText.y = -50; // Above the pirate sprite self.addChild(self._enemyText); // --- Level Text --- self._levelText = new Text2("Lv." + self.level, { size: 22, fill: 0xcccccc, align: "center" }); self._levelText.anchor.set(0.5, 0); self._levelText.x = 0; self._levelText.y = 40; // Under the pirate sprite self.addChild(self._levelText); // Katil korsan için "Ödül:" yazısı ekle self._rewardText = null; if (self.rpgName === "Katil" || self._isKatil) { self._rewardText = new Text2("Reward:", { size: 22, fill: 0xffcc00, align: "center" }); self._rewardText.anchor.set(0.5, 0); self._rewardText.x = 0; self._rewardText.y = self._levelText.y + 28; // Seviye yazısının hemen altında, aynı büyüklükte self.addChild(self._rewardText); } // Level text only, defense text removed self.takeDamage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xffffff, 100); }; self.update = function () { // Update enemy text position in case of movement if (self._enemyText) { self._enemyText.x = 0; self._enemyText.y = -50; } // Update level text position in case of movement if (self._levelText) { self._levelText.x = 0; self._levelText.y = pirateGraphics.height / 2 + 4; self._levelText.setText("Lv." + self.level); } // Katil korsan için ödül yazısı pozisyonunu güncel tut if (self.rpgName === "Katil" || self._isKatil) { if (!self._rewardText) { self._rewardText = new Text2("Ödül:", { size: 22, fill: 0xffcc00, align: "center" }); self._rewardText.anchor.set(0.5, 0); self.addChild(self._rewardText); } self._rewardText.x = 0; self._rewardText.y = self._levelText.y + 28; self._rewardText.visible = true; // Katil korsan için coin miktarı yazısı ekle/güncelle var rewardCoinValue = 0; // Katil korsan için coin değeri, öldüğünde verilecek coin miktarı ile aynı olmalı // Oyun kodunda coin.value = coin.value * (1 + Math.floor(waveNumber / 3)) * 10; // Burada coin.value rastgele 5-14 arası, ama göstermek için ortalama değer (ör: 10) kullanılabilir // Alternatif: self.level ve waveNumber'a göre hesapla var baseCoin = 10; // ortalama coin var waveNum = typeof waveNumber !== "undefined" ? waveNumber : 0; rewardCoinValue = baseCoin * (1 + Math.floor(waveNum / 3)) * 10; if (!self._rewardCoinText) { self._rewardCoinText = new Text2(rewardCoinValue + " coin", { size: 22, fill: 0xffcc00, align: "center" }); self._rewardCoinText.anchor.set(0.5, 0); self.addChild(self._rewardCoinText); } self._rewardCoinText.x = 0; self._rewardCoinText.y = self._rewardText.y + 28; self._rewardCoinText.setText(rewardCoinValue + " coin"); self._rewardCoinText.visible = true; } else if (self._rewardText) { self._rewardText.visible = false; if (self._rewardCoinText) { self._rewardCoinText.visible = false; } } // Defense text removed // Movement AI self.patrolTimer++; if (self.patrolTimer > 180) { self.moveAngle = Math.random() * Math.PI * 2; self.patrolTimer = 0; } // Patrol movement if (self._squadTargetNPC && self._squadTargetNPC.questType === 'defend' && self._squadTargetNPC.questActive) { // Korsan, NPC'yi hedefle var target = self._squadTargetNPC; // Eğer defend quest'te yardım kabul edildiyse, oyuncuya da saldırabilir if (target._defendHelpAccepted === true && Math.random() < 0.5) { // %50 ihtimalle oyuncuya saldır target = Math.random() < 0.5 ? self._squadTargetNPC : ship; } var dx = target.x - self.x; var dy = target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; self.moveAngle = Math.atan2(dy, dx); self._targetRotation = self.moveAngle + Math.PI / 2; } else { // Hedefe çok yaklaştıysa sadece dön var angle = Math.atan2(target.y - self.y, target.x - self.x); self._targetRotation = angle + Math.PI / 2; } // Ateş et if (LK.ticks - self.lastFire > self.fireRate) { var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y; var angle = Math.atan2(target.y - self.y, target.x - self.x); bullet.directionX = Math.cos(angle); bullet.directionY = Math.sin(angle); bullet.rotation = angle + Math.PI / 2; enemyBullets.push(bullet); if (game && game.addChild) { game.addChild(bullet); } self.lastFire = LK.ticks; // Keep pirate rotated to attack direction after firing self._targetRotation = angle + Math.PI / 2; } } else { // Wander randomly with smooth gliding motion (no sharp turns) if (typeof self._wanderVX === "undefined") { self._wanderVX = Math.cos(self.moveAngle) * self.speed * 0.5; } if (typeof self._wanderVY === "undefined") { self._wanderVY = Math.sin(self.moveAngle) * self.speed * 0.5; } // Target velocity for this wander direction var targetVX = Math.cos(self.moveAngle) * self.speed * 0.5; var targetVY = Math.sin(self.moveAngle) * self.speed * 0.5; // Smoothly interpolate velocity for gliding effect self._wanderVX += (targetVX - self._wanderVX) * 0.03; self._wanderVY += (targetVY - self._wanderVY) * 0.03; self.x += self._wanderVX; self.y += self._wanderVY; self._targetRotation = Math.atan2(self._wanderVY, self._wanderVX) + Math.PI / 2; } // Smoothly rotate toward target rotation if (typeof self._targetRotation === "number") { var diff = self._targetRotation - self.rotation; while (diff > Math.PI) { diff -= Math.PI * 2; } while (diff < -Math.PI) { diff += Math.PI * 2; } var lerp = 0.05; self.rotation += diff * lerp; } }; return self; }); // Pirate Base Class var PirateBase = Container.expand(function () { var self = Container.call(this); // Use randomly selected pirate asset for base appearance var pirateImageIndex = Math.floor(Math.random() * 20) + 1; var pirateAssetName = 'pirate' + pirateImageIndex; var baseGraphics = self.attachAsset(pirateAssetName, { anchorX: 0.5, anchorY: 0.5, tint: 0x990000, scaleX: 3, scaleY: 3 }); self.health = 300; self.maxHealth = 300; self.attackPower = 10; self.defensePower = 10; self.spawnRadius = 350; self.pirates = []; self.lastPirateSpawn = 0; self.maxPirates = 10; self._destroyed = false; // Health bar self._healthBar = new HealthBar(); self._healthBar.set({ maxValue: function maxValue() { return self.maxHealth; }, getValueFn: function getValueFn() { return self.health; }, getMaxFn: function getMaxFn() { return self.maxHealth; }, width: 120, height: 16, color: 0xff0000 }); self._healthBar.y = -120; self.addChild(self._healthBar); self.takeDamage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xff0000, 120); if (self.health <= 0 && !self._destroyed) { self.health = 0; self._destroyed = true; // --- BAŞARIM: Korsan üssü yok etme --- if (typeof checkPirateBaseAchievements === "function") { checkPirateBaseAchievements(); } // Remove all pirates around base for (var i = 0; i < self.pirates.length; i++) { if (self.pirates[i] && !self.pirates[i]._destroyed) { self.pirates[i].destroy(); } } // Explosion effect createExplosion(self.x, self.y); // Floating label var label = new Text2("Pirate Base Destroyed!", { size: 48, fill: 0xffcc00, align: "center" }); label.anchor.set(0.5, 0.5); label.x = self.x; label.y = self.y - 180; if (game && game.addChild) { game.addChild(label); } self._destroyed = true; self.destroy(); } }; self.update = function () { // Health bar update if (self._healthBar && self._healthBar.update) { self._healthBar.update(); } // Remove dead pirates from list for (var i = self.pirates.length - 1; i >= 0; i--) { if (!self.pirates[i] || self.pirates[i]._destroyed) { self.pirates.splice(i, 1); } } // Spawn pirates if less than max if (!self._destroyed && self.pirates.length < self.maxPirates && LK.ticks - self.lastPirateSpawn > 90) { var pirate = new Pirate(); var angle = Math.random() * Math.PI * 2; pirate.x = self.x + Math.cos(angle) * (self.spawnRadius + Math.random() * 60); pirate.y = self.y + Math.sin(angle) * (self.spawnRadius + Math.random() * 60); pirate.health = 30 + self.attackPower * 2; pirate.damage = 5 + self.attackPower; pirate.rpgName = "BaseKorsan-" + Math.floor(Math.random() * 10000); pirate._baseRef = self; // Add health bar to pirate pirate._healthBar = new HealthBar(); pirate._healthBar.set({ maxValue: function maxValue() { return pirate.health > 0 ? pirate.health : 0; }, getValueFn: function getValueFn() { return pirate.health > 0 ? pirate.health : 0; }, getMaxFn: function getMaxFn() { return 30 + self.attackPower * 2; }, width: 60, height: 10 }); pirate._healthBar.y = -60; pirate.addChild(pirate._healthBar); pirates.push(pirate); self.pirates.push(pirate); if (game && game.addChild) { game.addChild(pirate); } self.lastPirateSpawn = LK.ticks; } // Base defense: shoot at player if close var distToShip = Math.sqrt(Math.pow(ship.x - self.x, 2) + Math.pow(ship.y - self.y, 2)); if (!self._destroyed && distToShip < self.spawnRadius + 80 && LK.ticks % 30 === 0) { var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y; var angle = Math.atan2(ship.y - self.y, ship.x - self.x); bullet.directionX = Math.cos(angle); bullet.directionY = Math.sin(angle); bullet.rotation = angle + Math.PI / 2; bullet.damage = 10 + self.attackPower; enemyBullets.push(bullet); if (game && game.addChild) { game.addChild(bullet); } } }; return self; }); var Resource = Container.expand(function () { var self = Container.call(this); var resourceGraphics = self.attachAsset('resource', { anchorX: 0.5, anchorY: 0.5 }); // 10 farklı hammadde tipi var resourceTypes = ['metal', 'energy', 'crystal', 'gas', 'ice', 'uranium', 'silicon', 'carbon', 'plasma', 'antimatter']; // --- Değerli hammaddelerin daha az çıkmasını sağlamak için ağırlıklı seçim --- // Market kaldırıldı, sabit fiyatlar kullanılıyor var prices = [100, 80, 120, 90, 70, 200, 110, 60, 150, 300]; // Her hammaddenin ağırlığı: 1 / (fiyat^0.7) ile ters orantılı (daha değerli = daha az) // 0.7 üssüyle daha yumuşak bir dağılım, istenirse 1.0 yapılabilir var weights = []; var totalWeight = 0; for (var i = 0; i < prices.length; i++) { var w = 1 / Math.pow(prices[i], 0.7); weights.push(w); totalWeight += w; } // Rastgele ağırlıklı seçim var r = Math.random() * totalWeight; var acc = 0, idx = 0; for (var i = 0; i < weights.length; i++) { acc += weights[i]; if (r <= acc) { idx = i; break; } } self.type = resourceTypes[idx]; // Miktar: Değerli hammaddelerde miktar da az olsun // maxAmount = 1 + 4 * (ucuzluk ağırlığı) var maxAmount = Math.max(1, Math.round(1 + 4 * (weights[idx] / totalWeight * prices.length))); self.amount = Math.floor(Math.random() * maxAmount) + 1; // --- Timed fade-out system --- self._spawnTick = LK.ticks; self._fadeStartTick = self._spawnTick + 180; // 3 seconds visible self._expireTick = self._fadeStartTick + 120; // 2 seconds fade (total 5s) self.update = function () { self.rotation += 0.03; // Timed fade-out logic if (typeof self._expireTick === "number" && typeof self._fadeStartTick === "number") { var now = LK.ticks; if (now >= self._fadeStartTick) { // Start fading out var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick)); self.alpha = 1 - fadeProgress; if (now >= self._expireTick) { // Mark for removal (actual removal in game.update) self._shouldRemove = true; } } else { self.alpha = 1; } } }; return self; }); var Ship = Container.expand(function () { var self = Container.call(this); var shipGraphics = self.attachAsset('ship', { anchorX: 0.5, anchorY: 0.5 }); self.health = 100; self.maxHealth = 100; self.shield = 50; self.maxShield = 50; self.shieldRegenDelay = 0; self.speed = 5; self.fireRate = 10; self.damage = 10; self.lastFire = 0; self.takeDamage = function (amount) { // Shield absorbs damage first if (self.shield > 0) { var shieldDamage = Math.min(amount, self.shield); self.shield -= shieldDamage; amount -= shieldDamage; LK.effects.flashObject(self, 0x0088ff, 100); self.shieldRegenDelay = 180; // 3 seconds delay before shield regen } if (amount > 0) { self.health -= amount; LK.effects.flashObject(self, 0xff0000, 200); } if (self.health <= 0) { // --- Clear all world entities on respawn --- var _destroyAll = function _destroyAll(arr) { if (!arr) { return; } for (var i = arr.length - 1; i >= 0; i--) { if (arr[i] && typeof arr[i].destroy === "function") { arr[i].destroy(); } arr.splice(i, 1); } }; // Remove all pirates, npcs, asteroids, coins, resources, enemyBullets, bullets, stars, explosions, upgradeStations, pirateBases self.health = 0; // --- Respawn logic instead of Game Over --- // 1. Lose 50% coins and 50% of each resource var lostCoins = Math.floor(playerCoins * 0.5); playerCoins = Math.floor(playerCoins * 0.5); var lostResources = []; for (var i = 0; i < playerResources.length; i++) { var lost = Math.floor(playerResources[i] * 0.5); lostResources.push(lost); playerResources[i] = Math.floor(playerResources[i] * 0.5); } // 2. Respawn ship at a random far location, restore health/shield var respawnRadius = 1200 + Math.random() * 1800; // 1200-3000 px away from last position var respawnAngle = Math.random() * Math.PI * 2; self.x = self.x + Math.cos(respawnAngle) * respawnRadius; self.y = self.y + Math.sin(respawnAngle) * respawnRadius; self.health = self.maxHealth; self.shield = self.maxShield; self.shieldRegenDelay = 180; _destroyAll(pirates); _destroyAll(npcs); _destroyAll(asteroids); _destroyAll(coins); _destroyAll(resources); _destroyAll(enemyBullets); _destroyAll(bullets); _destroyAll(stars); _destroyAll(explosions); _destroyAll(upgradeStations); _destroyAll(pirateBases); // Also clear any katil korsan respawn timer if (typeof window._katilPirateRespawnTimer !== "undefined" && window._katilPirateRespawnTimer) { LK.clearTimeout(window._katilPirateRespawnTimer); window._katilPirateRespawnTimer = null; } window._katilPirateSpawned = false; // Katil korsan öldü ve dünya sıfırlandıktan sonra tekrar spawn et if (typeof window._katilPirateRespawnTimer !== "undefined" && window._katilPirateRespawnTimer) { LK.clearTimeout(window._katilPirateRespawnTimer); window._katilPirateRespawnTimer = null; } window._katilPirateRespawnTimer = LK.setTimeout(function () { if (!window._katilPirateSpawned) { var pirate = new Pirate(); // Katil korsan statlarını tekrar ayarla var minLevel = 1 + waveNumber * 2; pirate.level = minLevel + 10; pirate.health = (30 + (pirate.level - 1) * 10) * 10; pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10; pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10; pirate.rpgName = "Katil"; pirate._isKatil = true; pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3)); pirate._attackRange = Math.floor(600 * 0.9); // Katil korsan için %10 daha kısa menzil window._katilPirateSpawned = true; // Diğer korsanlar gibi kenar spawn algoritmasıyla doğsun var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y - 1400; break; case 1: pirate.x = ship.x + 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; case 2: pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y + 1400; break; case 3: pirate.x = ship.x - 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; } // Add health bar to katil pirate pirate._healthBar = new HealthBar(); pirate._healthBar.set({ maxValue: function maxValue() { return pirate.health > 0 ? pirate.health : 0; }, getValueFn: function getValueFn() { return pirate.health > 0 ? pirate.health : 0; }, getMaxFn: function getMaxFn() { return 30 + waveNumber * 10; }, width: 60, height: 10 }); pirate._healthBar.y = -60; pirate.addChild(pirate._healthBar); pirates.push(pirate); if (typeof game !== "undefined" && game.addChild) { game.addChild(pirate); } } window._katilPirateRespawnTimer = null; }, 6000); // Optionally reset wave progress enemiesKilled = 0; // Respawn initial world elements for (var i = 0; i < 135; i++) { spawnStar(true); } for (var i = 0; i < 6; i++) { spawnNPC(); } for (var i = 0; i < 6; i++) { spawnPirate(); } for (var i = 0; i < 2; i++) { spawnAsteroid(); } // Track respawn tick for input blocking window._lastRespawnTick = LK.ticks; // 3. Show floating label for penalty var lostText = "-%50 coin & hammadde kaybı! Yeniden doğdun."; var label = new Text2(lostText, { size: 48, fill: 0xff4444, align: "center" }); label.anchor.set(0.5, 0.5); label.x = 1024; label.y = 1366 - 200; if (typeof game !== "undefined" && game.addChild) { game.addChild(label); } LK.setTimeout(function () { if (label.parent) { label.parent.removeChild(label); } }, 1800); // 4. Update UI and save progress if (typeof updateUI === "function") { updateUI(); } if (typeof saveProgress === "function") { saveProgress(); } } }; self.update = function () { // Shield regeneration if (self.shieldRegenDelay > 0) { self.shieldRegenDelay--; } else if (self.shield < self.maxShield) { self.shield = Math.min(self.shield + 0.1, self.maxShield); } // --- SHIP ROTATION: Keep ship rotated to last movement or attack direction, but turn smoothly --- // If joystick is being dragged, rotate to movement direction if (typeof isDragging !== "undefined" && isDragging && typeof joystickHandle !== "undefined" && typeof joystickBase !== "undefined") { var dx = joystickHandle.x - joystickBase.x; var dy = joystickHandle.y - joystickBase.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 10) { self._lastMoveAngle = Math.atan2(dy, dx); self._targetRotation = self._lastMoveAngle + Math.PI / 2; } } else if (typeof game !== "undefined" && typeof game._lastShipAttackAngle !== "undefined") { // If not moving, but last attack angle exists, keep ship rotated to last attack direction self._targetRotation = game._lastShipAttackAngle + Math.PI / 2; } // Smoothly rotate toward target rotation if (typeof self._targetRotation === "number") { // Calculate shortest angle difference var diff = self._targetRotation - self.rotation; while (diff > Math.PI) { diff -= Math.PI * 2; } while (diff < -Math.PI) { diff += Math.PI * 2; } // Lerp factor: lower = softer (0.05 = very soft, 0.2 = sharp) var lerp = 0.05; self.rotation += diff * lerp; } }; return self; }); var Star = Container.expand(function () { var self = Container.call(this); var starGraphics = self.attachAsset('star', { anchorX: 0.5, anchorY: 0.5 }); self.speed = Math.random() * 2 + 0.5; starGraphics.alpha = Math.random() * 0.8 + 0.2; self.vx = (Math.random() - 0.5) * 0.5; self.vy = (Math.random() - 0.5) * 0.5; self.update = function () { // Slow floating movement self.x += self.vx; self.y += self.vy; }; return self; }); // Trade Station Class var TradeStation = Container.expand(function () { var self = Container.call(this); // Randomly select one of 2 NPC images for trade station appearance var npcImageIndex = Math.floor(Math.random() * 2) + 1; var npcAssetName = 'npc' + npcImageIndex; var tradeGraphics = self.attachAsset(npcAssetName, { anchorX: 0.5, anchorY: 0.5, tint: 0x00ff99, scaleX: 2, scaleY: 2 }); self.type = 'trade'; self.rotationSpeed = 0.01; self.offerType = Math.random() < 0.5 ? 'buy' : 'sell'; // buy: station buys from player, sell: station sells to player self.resourceType = Math.random() < 0.5 ? 'metal' : 'energy'; self.amount = Math.floor(Math.random() * 10) + 5; self.price = Math.floor(Math.random() * 30) + 10; self.specialOffer = Math.random() < 0.2; // 20% chance for special upgrade offer self.specialUpgrade = null; if (self.specialOffer) { var upgrades = [{ name: 'maxHealth', label: 'Max Health +30', cost: 200 }, { name: 'maxShield', label: 'Max Shield +20', cost: 180 }, { name: 'damage', label: 'Damage +5', cost: 150 }, { name: 'speed', label: 'Speed +1', cost: 120 }]; self.specialUpgrade = upgrades[Math.floor(Math.random() * upgrades.length)]; } self.update = function () { self.rotation += self.rotationSpeed; // Pulse effect var scale = 2 + Math.sin(LK.ticks * 0.05) * 0.1; tradeGraphics.scaleX = scale; tradeGraphics.scaleY = scale; }; return self; }); // Wormhole Teleporter Class var Wormhole = Container.expand(function () { var self = Container.call(this); var whGraphics = self.attachAsset('star', { anchorX: 0.5, anchorY: 0.5, scaleX: 5, scaleY: 5, tint: 0x00ffff }); self.radius = 120; self.cooldown = 0; self.update = function () { whGraphics.rotation += 0.07; // Teleport ship if close and not on cooldown var dx = ship.x - self.x; var dy = ship.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < self.radius && self.cooldown <= 0) { // Teleport ship to a random far location var tx = ship.x + (Math.random() - 0.5) * 4000; var ty = ship.y + (Math.random() - 0.5) * 4000; var dx2 = tx - ship.x; var dy2 = ty - ship.y; ship.x = tx; ship.y = ty; // Move all objects to keep camera effect for (var i = 0; i < stars.length; i++) { stars[i].x -= dx2; stars[i].y -= dy2; } for (var i = 0; i < npcs.length; i++) { npcs[i].x -= dx2; npcs[i].y -= dy2; } for (var i = 0; i < pirates.length; i++) { pirates[i].x -= dx2; pirates[i].y -= dy2; } for (var i = 0; i < coins.length; i++) { coins[i].x -= dx2; coins[i].y -= dy2; } for (var i = 0; i < resources.length; i++) { resources[i].x -= dx2; resources[i].y -= dy2; } for (var i = 0; i < bullets.length; i++) { bullets[i].x -= dx2; bullets[i].y -= dy2; } for (var i = 0; i < enemyBullets.length; i++) { enemyBullets[i].x -= dx2; enemyBullets[i].y -= dy2; } for (var i = 0; i < asteroids.length; i++) { asteroids[i].x -= dx2; asteroids[i].y -= dy2; } for (var i = 0; i < upgradeStations.length; i++) { upgradeStations[i].x -= dx2; upgradeStations[i].y -= dy2; } // boostParticles removed (no boost system) for (var i = 0; i < explosions.length; i++) { explosions[i].x -= dx2; explosions[i].y -= dy2; } for (var i = 0; i < blackHoles.length; i++) { blackHoles[i].x -= dx2; blackHoles[i].y -= dy2; } for (var i = 0; i < wormholes.length; i++) { if (wormholes[i] !== self) { wormholes[i].x -= dx2; wormholes[i].y -= dy2; } } self.cooldown = 180; LK.effects.flashScreen(0x00ffff, 500); } if (self.cooldown > 0) { self.cooldown--; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000033 }); /**** * Game Code ****/ // Her friend, en yakın pirate'ı (veya asteroid) bulup saldıracak, ship gibi // --- FRIENDS: Dost birlikler NPC gibi düşmana karşı savaşa girsin --- if (typeof friends !== "undefined" && Array.isArray(friends)) { for (var i = 0; i < friends.length; i++) { var friend = friends[i]; if (!friend) { continue; } // En yakın pirate'ı bul var nearestTarget = null; var nearestDistance = Infinity; for (var j = 0; j < pirates.length; j++) { var pirate = pirates[j]; if (!pirate) { continue; } var dist = Math.sqrt(Math.pow(pirate.x - friend.x, 2) + Math.pow(pirate.y - friend.y, 2)); if (dist < nearestDistance) { nearestDistance = dist; nearestTarget = pirate; } } // Asteroidleri de hedef olarak eklemek isterseniz, aşağıdaki kodu açabilirsiniz: // for (var j = 0; j < asteroids.length; j++) { // var asteroid = asteroids[j]; // var dist = Math.sqrt(Math.pow(asteroid.x - friend.x, 2) + Math.pow(asteroid.y - friend.y, 2)); // if (dist < nearestDistance) { // nearestDistance = dist; // nearestTarget = asteroid; // } // } // Saldırı menzili ship ile aynı olsun (ATTACK_RANGE) var friendRange = typeof ATTACK_RANGE !== "undefined" ? ATTACK_RANGE : 220; if (nearestTarget && nearestDistance < friendRange) { // Eğer friend zaten bu hedefe saldırmıyorsa, saldırıyı başlat if (!friend._attacking || friend._attackTarget !== nearestTarget) { if (typeof friend.startAttack === "function") { friend.startAttack(nearestTarget); } } } else { // Hedef yoksa saldırıyı bırak if (typeof friend.stopAttack === "function") { friend.stopAttack(); } } } } var ship; var joystickBase; var joystickHandle; var isDragging = false; var bullets = []; var enemyBullets = []; var npcs = []; var pirates = []; var coins = []; var resources = []; var stars = []; var asteroids = []; var upgradeStations = []; var explosions = []; var blackHoles = []; var wormholes = []; var tradeStations = []; var pirateBases = []; var tradeText = null; // No trade text needed // Helper: spawn a pirate base at a random edge, with pirates around it function spawnPirateBase() { var base = new PirateBase(); // Spawn from edges of visible area var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: // Top base.x = ship.x + (Math.random() - 0.5) * 2000; base.y = ship.y - 1600; break; case 1: // Right base.x = ship.x + 1600; base.y = ship.y + (Math.random() - 0.5) * 2000; break; case 2: // Bottom base.x = ship.x + (Math.random() - 0.5) * 2000; base.y = ship.y + 1600; break; case 3: // Left base.x = ship.x - 1600; base.y = ship.y + (Math.random() - 0.5) * 2000; break; } ; pirateBases.push(base); game.addChild(base); // Spawn initial pirates around base for (var i = 0; i < 10; i++) { var pirate = new Pirate(); var angle = Math.PI * 2 * (i / 10); pirate.x = base.x + Math.cos(angle) * (base.spawnRadius + Math.random() * 60); pirate.y = base.y + Math.sin(angle) * (base.spawnRadius + Math.random() * 60); // Level range: wave 1 => 1-3, wave 2 => 3-5, wave 3 => 5-7, etc. var minLevel = 1 + waveNumber * 2; var maxLevel = minLevel + 2; pirate.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1)); pirate.health = 30 + (pirate.level - 1) * 10 + base.attackPower * 2; pirate.damage = 5 + Math.floor((pirate.level - 1) * 1.5) + base.attackPower; pirate.defense = 2 + Math.floor((pirate.level - 1) * 1.2) + base.defensePower; pirate.rpgName = "BaseKorsan-" + Math.floor(Math.random() * 10000); pirate._baseRef = base; // Add health bar to pirate pirate._healthBar = new HealthBar(); pirate._healthBar.set({ maxValue: function maxValue() { return pirate.health > 0 ? pirate.health : 0; }, getValueFn: function getValueFn() { return pirate.health > 0 ? pirate.health : 0; }, getMaxFn: function getMaxFn() { return 30 + base.attackPower * 2; }, width: 60, height: 10 }); pirate._healthBar.y = -60; pirate.addChild(pirate._healthBar); pirates.push(pirate); base.pirates.push(pirate); game.addChild(pirate); } } // tradeText UI removed (no tradeText in this version) var camera = { x: 0, y: 0 }; // --- Helper for multi-pirate attack quest --- // If groupCount >= 2, spawn GrupKorsan squad with reward label under each function spawnPirateSquad(centerX, centerY, count, targetNPC) { var squad = []; if (count >= 2) { // GrupKorsan: all move together, same target/angle var minLevel = 1 + waveNumber * 2 + 5; var maxLevel = minLevel + 2; var groupAngle = Math.random() * Math.PI * 2; var groupTarget = targetNPC || { x: centerX + Math.cos(groupAngle) * 400, y: centerY + Math.sin(groupAngle) * 400 }; for (var i = 0; i < count; i++) { var gpirate = new GrupKorsan(); gpirate.groupIndex = i; gpirate.groupSize = count; gpirate.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1)); // Katil Korsan'ın statları: level = minLevel+10, health = (30 + (level-1)*10)*10, damage = (5 + ...)*10, defense = (2 + ...)*10 // GrupKorsan, Katil Korsan'ın statlarının %20 daha düşüğü olacak şekilde ayarlanır var katilHealth = (30 + (gpirate.level - 1) * 10) * 10; var katilDamage = (5 + Math.floor((gpirate.level - 1) * 1.5)) * 10; var katilDefense = (2 + Math.floor((gpirate.level - 1) * 1.2)) * 10; gpirate.health = Math.round(katilHealth * 0.8); gpirate.damage = Math.round(katilDamage * 0.8); gpirate.defense = Math.round(katilDefense * 0.8); gpirate.loot = 200 + (gpirate.level - 1) * 50; // --- GrupKorsan arası mesafe %15 oranında açıldı (200px yerine 200*1.15=230px) --- // Her bir GrupKorsan'ın pozisyonu, merkezden eşit aralıklı ve %15 daha uzak olacak şekilde ayarlanır var grupDistance = 200 * 1.15; var angleOffset = Math.PI * 2 * i / count; gpirate.x = centerX + Math.cos(groupAngle + angleOffset) * grupDistance + (Math.random() - 0.5) * 60; gpirate.y = centerY + Math.sin(groupAngle + angleOffset) * grupDistance + (Math.random() - 0.5) * 60; gpirate._groupTarget = groupTarget; gpirate._groupAngle = groupAngle; gpirate._groupMoveSpeed = 2 + Math.random() * 0.5; gpirate.rpgName = "GrupKorsan-" + (i + 1) + "/" + count; gpirate._isGrupKorsan = true; pirates.push(gpirate); game.addChild(gpirate); squad.push(gpirate); } } else { // Fallback: single pirate for (var i = 0; i < count; i++) { var pirate = new Pirate(); var angle = Math.PI * 2 * (i / count); pirate.x = centerX + Math.cos(angle) * 200 + (Math.random() - 0.5) * 60; pirate.y = centerY + Math.sin(angle) * 200 + (Math.random() - 0.5) * 60; // Level range: wave 1 => 1-3, wave 2 => 3-5, wave 3 => 5-7, etc. var minLevel = 1 + waveNumber * 2; var maxLevel = minLevel + 2; pirate.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1)); pirate.health = 30 + (pirate.level - 1) * 10; pirate.damage = 5 + Math.floor((pirate.level - 1) * 1.5); pirate.defense = 2 + Math.floor((pirate.level - 1) * 1.2); pirate.rpgName = "Korsan-" + Math.floor(Math.random() * 10000); pirate._squadTargetNPC = targetNPC; pirates.push(pirate); game.addChild(pirate); squad.push(pirate); } } return squad; } // Trade station tap detection (RPG/ticaret) for (var i = 0; i < tradeStations.length; i++) { var ts = tradeStations[i]; var dist = Math.sqrt(Math.pow(ts.x - ship.x, 2) + Math.pow(ts.y - ship.y, 2)); if (dist < 180) { game._lastTradeStationTap = LK.ticks; break; } } var waveNumber = 0; var enemiesKilled = 0; var totalDistance = 0; // Player stats var playerCoins = storage.coins || 0; // 10 hammadde için oyuncu envanteri var playerResources = [storage.metal || 0, // metal storage.energy || 0, // energy storage.crystal || 0, // crystal storage.gas || 0, // gas storage.ice || 0, // ice storage.uranium || 0, // uranium storage.silicon || 0, // silicon storage.carbon || 0, // carbon storage.plasma || 0, // plasma storage.antimatter || 0 // antimatter ]; // Aliases for compatibility with old code var playerMetal = playerResources[0]; var playerEnergy = playerResources[1]; var questsCompleted = storage.questsCompleted || 0; // --- Player Level/EXP --- var playerLevel = typeof storage.playerLevel !== "undefined" ? storage.playerLevel : 1; var playerEXP = typeof storage.playerEXP !== "undefined" ? storage.playerEXP : 0; function expToNext(level) { // Simple EXP curve: next = 10 + 5*(level-1) return 10 + 5 * (level - 1); } // UI Elements // Calculate the unified font size (30% smaller than expText, then %10 büyüt) var unifiedFontSize = Math.round(38 * 0.7 * 1.1); // Add coin icon var coinIcon = LK.getAsset('coin', { anchorX: 0.5, anchorY: 0.5, x: 100, y: 35, scaleX: 1.2, scaleY: 1.2 }); LK.gui.topLeft.addChild(coinIcon); var coinText = new Text2('Coins: ' + playerCoins, { size: unifiedFontSize, fill: 0xFFFF00 }); coinText.anchor.set(0, 0); coinText.x = 120; coinText.y = 20; LK.gui.topLeft.addChild(coinText); // --- NPC ile savaş butonu --- var npcFightBtnFont = Math.round(unifiedFontSize * 1.1); window.npcFightBtn = new Text2("Fight NPCs", { size: npcFightBtnFont, fill: 0xff4444, align: "center" }); window.npcFightBtn.anchor.set(0.5, 0.5); // Place at bottom center, above the bottom edge window.npcFightBtn.x = 2048 / 2; window.npcFightBtn.y = 2732 - 300; // moved 2 lines (about 120px) higher LK.gui.bottom.addChild(window.npcFightBtn); window.npcFightMode = false; // --- UZAY İSTASYONU TESLİMAT BUTTON (above Reset, 2 rows up) --- if (!window.stationDeliveryBtn) { var deliveryBtnFont = Math.round(unifiedFontSize * 1.1); // window.stationDeliveryBtn = new Text2("", { ... }); // BUTTON IS HIDDEN, DO NOT CREATE // window.stationDeliveryBtn.anchor.set(1, 1); // window.stationDeliveryBtn.x = -40; // window.stationDeliveryBtn.y = -40 - 2 * 90; // Add image above the button, 10% higher (yukarıya %10) if (!window.stationDeliveryBtnImage) { // Use the special image for the transparent button area var imgAsset = LK.getAsset('stationDeliveryBtnAreaImg', { anchorX: 1, anchorY: 1, scaleX: 0.7, scaleY: 0.7 }); // Place image at the same x, but y is 10% higher (10% of image height above the button) // Keep the station image fixed (do not move it up) imgAsset.x = -40; imgAsset.y = -40 - 2 * 90 - imgAsset.height * 1.1; window.stationDeliveryBtnImage = imgAsset; LK.gui.bottomRight.addChild(window.stationDeliveryBtnImage); // Add a transparent button area exactly the size and position of the image for İstasyon Teslimat var stationDeliveryBtnArea = new Text2("", { size: 1, fill: 0xffffff, align: "center" }); stationDeliveryBtnArea.anchor.set(1, 1); // Place the button area exactly behind the image, matching its position and size stationDeliveryBtnArea.x = imgAsset.x; // Move the button area 1 row (imgAsset.height) above the image stationDeliveryBtnArea.y = imgAsset.y - imgAsset.height; stationDeliveryBtnArea.width = imgAsset.width; stationDeliveryBtnArea.height = imgAsset.height; stationDeliveryBtnArea.alpha = 0; // fully transparent, but still receives taps window.stationDeliveryBtnArea = stationDeliveryBtnArea; // Add the button area BEFORE the image so it is behind LK.gui.bottomRight.addChild(stationDeliveryBtnArea); LK.gui.bottomRight.addChild(window.stationDeliveryBtnImage); } // DO NOT ADD THE BUTTON TO THE GUI (HIDDEN) // Add 'İstasyona Hammadde Gönder' text 2 rows below the button if (!window.stationSendResourceText) { var sendTextFont = Math.max(8, Math.round(unifiedFontSize * 1.0) - 2); window.stationSendResourceText = new Text2("", { size: sendTextFont, fill: 0xffffff, align: "center" }); window.stationSendResourceText.anchor.set(1, 1); // 2 rows below the stationDeliveryBtn (row = 90px) // Use the same x/y as the image, but offset as if the button was there window.stationSendResourceText.x = -40; window.stationSendResourceText.y = -40 - 2 * 90 + 1 * 90; LK.gui.bottomRight.addChild(window.stationSendResourceText); } } // --- RESET BUTTON (bottom right) --- if (!game._resetBtn) { game._resetBtn = new Text2("Reset", { size: Math.round(unifiedFontSize * 1.1), fill: 0xff4444, align: "center" }); game._resetBtn.anchor.set(1, 1); // Place at bottom right, with margin from edge game._resetBtn.x = -40; game._resetBtn.y = -40; LK.gui.bottomRight.addChild(game._resetBtn); } // --- UZAY İSTASYONU TESLİMAT PANEL SYSTEM --- // State for delivery system if (!window.stationDeliveryState) { // 10 hammadde için rastgele 50-200 arası istek, bonus %2-5 arası window.stationDeliveryState = { requests: [], delivered: [], completed: false, bonus: 0 }; for (var i = 0; i < 10; i++) { var req = 50 + Math.floor(Math.random() * 151); // 50-200 window.stationDeliveryState.requests.push(req); window.stationDeliveryState.delivered.push(0); } window.stationDeliveryState.bonus = 2 + Math.floor(Math.random() * 4); // 2-5 window.stationDeliveryState.completed = false; window.stationDeliveryState.rewardClaimed = false; } // Helper to open/close panel window.openStationDeliveryPanel = function () { if (window.stationDeliveryPanel) { return; } // Increase font size by 2 for all panel texts/buttons var panelFontSize = unifiedFontSize + 4; var panelBtnFontSize = panelFontSize; var labelFontSize = panelFontSize; var closeBtnFontSize = panelFontSize; var claimBtnFontSize = panelFontSize; var bonusFontSize = panelFontSize; var infoFontSize = panelFontSize; var titleFontSize = panelFontSize; // Calculate new panel size to fit enlarged text/buttons var rowH = 80 + 8; // add 8px for extra font size var panelW = 900 + 120; // add width for larger text/buttons var panelH = 1100 + 2 * rowH; // add height for larger text/buttons var px = 2048 / 2, py = 2732 / 2; // Panel BG window.stationDeliveryPanelBG = LK.getAsset('resource', { anchorX: 0.5, anchorY: 0.5, x: px, y: py, scaleX: panelW / 40, scaleY: panelH / 40, tint: 0x223355, alpha: 0.97 }); // Title window.stationDeliveryPanelTitle = new Text2("Space Station Delivery", { size: titleFontSize, fill: 0x00ffcc, align: "center" }); window.stationDeliveryPanelTitle.anchor.set(0.5, 0); window.stationDeliveryPanelTitle.x = px; window.stationDeliveryPanelTitle.y = py - panelH / 2 + 40; // Info text window.stationDeliveryPanelInfoText = new Text2("Complete the station's special resource requests!\nDeliver all to sell at +" + window.stationDeliveryState.bonus + "% bonus price.", { size: infoFontSize, fill: 0xffffff, align: "center" }); window.stationDeliveryPanelInfoText.anchor.set(0.5, 0); window.stationDeliveryPanelInfoText.x = px; window.stationDeliveryPanelInfoText.y = window.stationDeliveryPanelTitle.y + 60; // Close button window.stationDeliveryPanelCloseBtn = new Text2("Close", { size: Math.round((unifiedFontSize + 4) * 1.3), // 2 font larger than Coins, then 30% larger fill: 0xff4444, align: "center" }); window.stationDeliveryPanelCloseBtn.anchor.set(0.5, 0.5); // Move close button above the panel title, with extra margin window.stationDeliveryPanelCloseBtn.x = px; window.stationDeliveryPanelCloseBtn.y = window.stationDeliveryPanelTitle.y - 110; // further above title // Resource request labels and delivery buttons window.stationDeliveryLabels = []; window.stationDeliveryDeliverBtns = []; window.stationDeliveryStockLabels = []; // Move all resource rows down by 3% of the panel height var startY = window.stationDeliveryPanelInfoText.y + 80 + Math.round(panelH * 0.03); for (var i = 0; i < window.resourceLabelOrder.length; i++) { var resObj = window.resourceLabelOrder[i]; var req = window.stationDeliveryState.requests[resObj.origIdx]; var delivered = window.stationDeliveryState.delivered[resObj.origIdx]; var label = new Text2(resObj.name + ": " + delivered + " / " + req, { size: labelFontSize, fill: 0xffffff, align: "left" }); label.anchor.set(0, 0.5); label.x = px - panelW / 2 + 60; label.y = startY + i * rowH; window.stationDeliveryLabels.push(label); // Oyuncu stok durumu etiketi var stockAmount = typeof playerResources[resObj.origIdx] !== "undefined" ? playerResources[resObj.origIdx] : 0; var stockLabel = new Text2("Stock: " + stockAmount, { size: Math.max(18, Math.round(labelFontSize * 0.85)), fill: 0xcccccc, align: "left" }); stockLabel.anchor.set(0, 0.5); stockLabel.x = label.x + 340; stockLabel.y = label.y; window.stationDeliveryStockLabels.push(stockLabel); // Deliver button var teslimBtn = new Text2("Deliver", { size: panelBtnFontSize, fill: 0x00ff99, align: "center" }); teslimBtn.anchor.set(0.5, 0.5); teslimBtn.x = px + 220; teslimBtn.y = label.y; teslimBtn._stationResIdx = resObj.origIdx; window.stationDeliveryDeliverBtns.push(teslimBtn); } // Bonus info window.stationDeliveryPanelBonusText = new Text2("Complete all requests to sell at +" + window.stationDeliveryState.bonus + "% bonus price.", { size: bonusFontSize, fill: 0xffff00, align: "center" }); window.stationDeliveryPanelBonusText.anchor.set(0.5, 0); window.stationDeliveryPanelBonusText.x = px; window.stationDeliveryPanelBonusText.y = startY + rowH * 10 + 30; // Complete delivery (claim reward) button window.stationDeliveryPanelClaimBtn = new Text2("Deliver All & Claim Reward", { size: claimBtnFontSize, fill: 0xffcc00, align: "center" }); window.stationDeliveryPanelClaimBtn.anchor.set(0.5, 0.5); window.stationDeliveryPanelClaimBtn.x = px; window.stationDeliveryPanelClaimBtn.y = window.stationDeliveryPanelBonusText.y + 90; // Add to game game.addChild(window.stationDeliveryPanelBG); game.addChild(window.stationDeliveryPanelTitle); game.addChild(window.stationDeliveryPanelInfoText); game.addChild(window.stationDeliveryPanelCloseBtn); for (var i = 0; i < window.stationDeliveryLabels.length; i++) { game.addChild(window.stationDeliveryLabels[i]); } for (var i = 0; i < window.stationDeliveryStockLabels.length; i++) { game.addChild(window.stationDeliveryStockLabels[i]); } for (var i = 0; i < window.stationDeliveryDeliverBtns.length; i++) { game.addChild(window.stationDeliveryDeliverBtns[i]); } game.addChild(window.stationDeliveryPanelBonusText); game.addChild(window.stationDeliveryPanelClaimBtn); window.stationDeliveryPanel = true; }; window.closeStationDeliveryPanel = function () { if (!window.stationDeliveryPanel) { return; } if (window.stationDeliveryPanelBG && window.stationDeliveryPanelBG.parent) { window.stationDeliveryPanelBG.parent.removeChild(window.stationDeliveryPanelBG); } if (window.stationDeliveryPanelTitle && window.stationDeliveryPanelTitle.parent) { window.stationDeliveryPanelTitle.parent.removeChild(window.stationDeliveryPanelTitle); } if (window.stationDeliveryPanelInfoText && window.stationDeliveryPanelInfoText.parent) { window.stationDeliveryPanelInfoText.parent.removeChild(window.stationDeliveryPanelInfoText); } if (window.stationDeliveryPanelCloseBtn && window.stationDeliveryPanelCloseBtn.parent) { window.stationDeliveryPanelCloseBtn.parent.removeChild(window.stationDeliveryPanelCloseBtn); } if (window.stationDeliveryLabels) { for (var i = 0; i < window.stationDeliveryLabels.length; i++) { if (window.stationDeliveryLabels[i] && window.stationDeliveryLabels[i].parent) { window.stationDeliveryLabels[i].parent.removeChild(window.stationDeliveryLabels[i]); } } } if (window.stationDeliveryDeliverBtns) { for (var i = 0; i < window.stationDeliveryDeliverBtns.length; i++) { if (window.stationDeliveryDeliverBtns[i] && window.stationDeliveryDeliverBtns[i].parent) { window.stationDeliveryDeliverBtns[i].parent.removeChild(window.stationDeliveryDeliverBtns[i]); } } } if (window.stationDeliveryPanelBonusText && window.stationDeliveryPanelBonusText.parent) { window.stationDeliveryPanelBonusText.parent.removeChild(window.stationDeliveryPanelBonusText); } if (window.stationDeliveryPanelClaimBtn && window.stationDeliveryPanelClaimBtn.parent) { window.stationDeliveryPanelClaimBtn.parent.removeChild(window.stationDeliveryPanelClaimBtn); } window.stationDeliveryPanel = null; window.stationDeliveryPanelBG = null; window.stationDeliveryPanelTitle = null; window.stationDeliveryPanelInfoText = null; window.stationDeliveryPanelCloseBtn = null; window.stationDeliveryLabels = null; window.stationDeliveryDeliverBtns = null; window.stationDeliveryPanelBonusText = null; window.stationDeliveryPanelClaimBtn = null; // Hide station stock labels when panel is closed if (window.stationDeliveryStockLabels) { for (var i = 0; i < window.stationDeliveryStockLabels.length; i++) { if (window.stationDeliveryStockLabels[i] && window.stationDeliveryStockLabels[i].parent) { window.stationDeliveryStockLabels[i].parent.removeChild(window.stationDeliveryStockLabels[i]); } } window.stationDeliveryStockLabels = null; } }; // --- GAME TIME LABEL --- // Track game start time in ticks var gameStartTick = LK.ticks; var gameTimeText = new Text2('Game Time: 0:00', { size: unifiedFontSize, fill: 0xcccccc }); gameTimeText.anchor.set(0, 0); gameTimeText.x = coinText.x + coinText.width + 40; gameTimeText.y = coinText.y; LK.gui.topLeft.addChild(gameTimeText); // Add an image below the Game Time label if (!window.gameTimeImage) { // Use an existing image asset, e.g. 'teslimEtBtnBg' as a placeholder // Move the Global Panel image and button to the row above the station image var imgScale = 0.7; var imgAsset = LK.getAsset('teslimEtBtnBg', { anchorX: 1, anchorY: 1, scaleX: imgScale, scaleY: imgScale }); // Place the image at the same x as the stationDeliveryBtnImage, but 1 row above it // If stationDeliveryBtnImage exists, align to it var refImg = window.stationDeliveryBtnImage; if (refImg) { imgAsset.x = refImg.x; imgAsset.y = refImg.y - refImg.height; // 1 row above station image } else { // fallback: 10% right from left edge, and 10px below the Game Time label imgAsset.x = Math.round(2048 * 0.10); imgAsset.y = gameTimeText.y + gameTimeText.height + 10; } window.gameTimeImage = imgAsset; LK.gui.bottomRight.addChild(window.gameTimeImage); // --- Add a new independent image file at the position 2 rows above Hammadde Takası (Resource Trade) image --- // Remove the previously created image if it exists if (window.resourceTradePanelImage && window.resourceTradePanelImage.parent) { window.resourceTradePanelImage.parent.removeChild(window.resourceTradePanelImage); window.resourceTradePanelImage = null; } // Create a new independent image at the same position (now 1 row above Hammadde Takası image) if (!window.resourceTradePanelIndependentImage) { var independentImgScale = 0.7; var independentImgAsset = LK.getAsset('resourceTradePanelIndependentCus', { anchorX: 1, anchorY: 1, scaleX: independentImgScale, scaleY: independentImgScale }); // Place 1 row above the Hammadde Takası (Resource Trade) image independentImgAsset.x = imgAsset.x; independentImgAsset.y = imgAsset.y - imgAsset.height * 1; window.resourceTradePanelIndependentImage = independentImgAsset; LK.gui.bottomRight.addChild(independentImgAsset); // Move the Hammadde Stoğu panel button (tap area) 2 rows above the image, image stays fixed if (!window.resourceTradePanelIndependentBtn) { var btn = new Text2("", { size: 1, fill: 0xffffff, align: "center" }); btn.anchor.set(1, 1); btn.x = independentImgAsset.x; btn.y = independentImgAsset.y - independentImgAsset.height * 2; // 2 rows above the image btn.width = independentImgAsset.width; btn.height = independentImgAsset.height; btn.alpha = 0; // fully transparent, but still receives taps window.resourceTradePanelIndependentBtn = btn; LK.gui.bottomRight.addChild(btn); } } // Add a transparent button area exactly the size and position of the image for Global Market var marketBtn = new Text2("Global Market", { size: 1, fill: 0xffffff, align: "center" }); marketBtn.anchor.set(1, 1); marketBtn.x = imgAsset.x; // Move the button area 2 rows (imgAsset.height * 2) above the image, image stays fixed marketBtn.y = imgAsset.y - imgAsset.height * 2; // Grow the button area 10% downward and shrink it 5% from the top marketBtn.width = imgAsset.width; marketBtn.height = imgAsset.height * 1.10 * 0.95; // first grow 10%, then shrink 5% from top marketBtn.alpha = 0; // fully transparent, but still receives taps window.globalMarketBtn = marketBtn; LK.gui.bottomRight.addChild(marketBtn); } // --- RESOURCE TRADE PANEL STATE --- if (!window.resourceTradePanelState) { // 10 hammadde için değerler (1-10 arası, rastgele başlat) window.resourceTradeValues = []; for (var i = 0; i < 10; i++) { window.resourceTradeValues.push(1 + Math.floor(Math.random() * 10)); } window.resourceTradePanelState = { open: false, lastValueUpdateTick: LK.ticks }; } // Timer: her 5 saniyede bir (300 tick) değerleri değiştir if (!window._resourceTradeValueTimer) { window._resourceTradeValueTimer = LK.setInterval(function () { if (!window.resourceTradeValues) { return; } for (var i = 0; i < window.resourceTradeValues.length; i++) { // Değeri -3/+3 arası değiştir, 1-10 aralığında tut var delta = Math.floor(Math.random() * 7) - 3; window.resourceTradeValues[i] = Math.max(1, Math.min(10, window.resourceTradeValues[i] + delta)); } // Panel açıksa UI güncelle if (window.resourceTradePanelState && window.resourceTradePanelState.open && typeof window.updateResourceTradePanel === "function") { window.updateResourceTradePanel(); } }, 5000); } // Resource UI: create 10 vertically stacked resource labels, left-aligned // Sort resourceTypes by name length descending, keep original index for mapping to playerResources var resourceTypes = ['Metal', 'Energy', 'Crystal', 'Gas', 'Ice', 'Uranium', 'Silicon', 'Carbon', 'Plasma', 'Antimatter']; var resourceTypeObjs = []; for (var i = 0; i < resourceTypes.length; i++) { resourceTypeObjs.push({ name: resourceTypes[i], origIdx: i }); } resourceTypeObjs.sort(function (a, b) { // Sort by name length descending, then alphabetically for ties if (b.name.length !== a.name.length) { return b.name.length - a.name.length; } return a.name.localeCompare(b.name); }); window.resourceLabels = []; window.resourceLabelOrder = resourceTypeObjs; // Save for updateUI var resourceIconMargin = 8; var resourceLabelStartY = Math.round(2732 * 0.05); // 5% down from the top for (var i = 0; i < resourceTypeObjs.length; i++) { var label = new Text2(resourceTypeObjs[i].name + ': 0', { size: unifiedFontSize, fill: 0xFFFFFF }); label.anchor.set(0, 0); label.x = 20; // flush to left edge, but leave margin for menu icon label.y = resourceLabelStartY + i * (unifiedFontSize + resourceIconMargin); // Do NOT add to GUI, so resource stock text is hidden on main screen // LK.gui.topLeft.addChild(label); window.resourceLabels.push(label); } // --- RESOURCE TRADE PANEL LOGIC --- window.openResourceTradePanel = function () { if (window.resourceTradePanelState.open) { return; } window.resourceTradePanelState.open = true; // Increase font size by 2 for all panel texts/buttons var panelFontSize = unifiedFontSize + 4; var panelBtnFontSize = panelFontSize; var labelFontSize = panelFontSize; var closeBtnFontSize = panelFontSize; var infoFontSize = panelFontSize; var titleFontSize = panelFontSize; // Panel boyutları ve konum (resize for larger text/buttons) var rowH = 80 + 8; var panelW = 1200 + 120; var panelH = 1200 + 2 * rowH; var px = 2048 / 2, py = 2732 / 2; // Panel BG window.resourceTradePanelBG = LK.getAsset('resource', { anchorX: 0.5, anchorY: 0.5, x: px, y: py, scaleX: panelW / 40, scaleY: panelH / 40, tint: 0x223355, alpha: 0.97 }); // Başlık window.resourceTradePanelTitle = new Text2("Resource Trading", { size: titleFontSize, fill: 0x00ffcc, align: "center" }); window.resourceTradePanelTitle.anchor.set(0.5, 0); window.resourceTradePanelTitle.x = px; window.resourceTradePanelTitle.y = py - panelH / 2 + 40; // Info text window.resourceTradePanelInfoText = new Text2("You can trade your resources.\nEach resource value changes between 5-30 and updates every 5 seconds.", { size: infoFontSize, fill: 0xffffff, align: "center" }); window.resourceTradePanelInfoText.anchor.set(0.5, 0); window.resourceTradePanelInfoText.x = px; window.resourceTradePanelInfoText.y = window.resourceTradePanelTitle.y + 60; // Kapat butonu window.resourceTradePanelCloseBtn = new Text2("Close", { size: Math.round((unifiedFontSize + 4) * 1.3), // 2 font larger than Coins, then 30% larger fill: 0xff4444, align: "center" }); window.resourceTradePanelCloseBtn.anchor.set(0.5, 0.5); window.resourceTradePanelCloseBtn.x = px; window.resourceTradePanelCloseBtn.y = window.resourceTradePanelTitle.y - 110; // further above title // Hammadde satırları ve butonları window.resourceTradeLabels = []; window.resourceTradeSellBtns = []; window.resourceTradeSellAllBtns = []; window.resourceTradeBuyBtns = []; var startY = window.resourceTradePanelInfoText.y + 80 + rowH * 2; // move all rows down by 2 rows (1 row further) for (var i = 0; i < window.resourceLabelOrder.length; i++) { var resObj = window.resourceLabelOrder[i]; var idx = resObj.origIdx; var label = new Text2(resObj.name + ": " + (playerResources[idx] || 0) + " | Value: " + (window.resourceTradeValues ? window.resourceTradeValues[idx] : 0), { size: labelFontSize, fill: 0xffffff, align: "left" }); label.anchor.set(0, 0.5); label.x = px - panelW / 2 + 60; label.y = startY + i * rowH; window.resourceTradeLabels.push(label); // Sell button var sellBtn = new Text2("Sell", { size: panelBtnFontSize, fill: 0x00ff99, align: "center" }); sellBtn.anchor.set(0.5, 0.5); sellBtn.x = px + 220; sellBtn.y = label.y; sellBtn._resourceIdx = idx; window.resourceTradeSellBtns.push(sellBtn); // Sell All button var sellAllBtn = new Text2("Sell All", { size: panelBtnFontSize, fill: 0x00cc99, align: "center" }); sellAllBtn.anchor.set(0.5, 0.5); sellAllBtn.x = px + 400; sellAllBtn.y = label.y; sellAllBtn._resourceIdx = idx; window.resourceTradeSellAllBtns.push(sellAllBtn); // Buy button var buyBtn = new Text2("Buy", { size: panelBtnFontSize, fill: 0x0099ff, align: "center" }); buyBtn.anchor.set(0.5, 0.5); buyBtn.x = px + 580; buyBtn.y = label.y; buyBtn._resourceIdx = idx; window.resourceTradeBuyBtns.push(buyBtn); } // Add to game game.addChild(window.resourceTradePanelBG); game.addChild(window.resourceTradePanelTitle); game.addChild(window.resourceTradePanelInfoText); game.addChild(window.resourceTradePanelCloseBtn); for (var i = 0; i < window.resourceTradeLabels.length; i++) { game.addChild(window.resourceTradeLabels[i]); } for (var i = 0; i < window.resourceTradeSellBtns.length; i++) { game.addChild(window.resourceTradeSellBtns[i]); } for (var i = 0; i < window.resourceTradeSellAllBtns.length; i++) { game.addChild(window.resourceTradeSellAllBtns[i]); } for (var i = 0; i < window.resourceTradeBuyBtns.length; i++) { game.addChild(window.resourceTradeBuyBtns[i]); } // Panel güncelleme fonksiyonu window.updateResourceTradePanel = function () { for (var i = 0; i < window.resourceLabelOrder.length; i++) { var resObj = window.resourceLabelOrder[i]; var idx = resObj.origIdx; if (window.resourceTradeLabels[i]) { window.resourceTradeLabels[i].setText(resObj.name + ": " + (playerResources[idx] || 0) + " | Değer: " + (window.resourceTradeValues ? window.resourceTradeValues[idx] : 0)); } } }; window.updateResourceTradePanel(); }; window.closeResourceTradePanel = function () { if (!window.resourceTradePanelState.open) { return; } window.resourceTradePanelState.open = false; if (window.resourceTradePanelBG && window.resourceTradePanelBG.parent) { window.resourceTradePanelBG.parent.removeChild(window.resourceTradePanelBG); } if (window.resourceTradePanelTitle && window.resourceTradePanelTitle.parent) { window.resourceTradePanelTitle.parent.removeChild(window.resourceTradePanelTitle); } if (window.resourceTradePanelInfoText && window.resourceTradePanelInfoText.parent) { window.resourceTradePanelInfoText.parent.removeChild(window.resourceTradePanelInfoText); } if (window.resourceTradePanelCloseBtn && window.resourceTradePanelCloseBtn.parent) { window.resourceTradePanelCloseBtn.parent.removeChild(window.resourceTradePanelCloseBtn); } if (window.resourceTradeLabels) { for (var i = 0; i < window.resourceTradeLabels.length; i++) { if (window.resourceTradeLabels[i] && window.resourceTradeLabels[i].parent) { window.resourceTradeLabels[i].parent.removeChild(window.resourceTradeLabels[i]); } } } if (window.resourceTradeSellBtns) { for (var i = 0; i < window.resourceTradeSellBtns.length; i++) { if (window.resourceTradeSellBtns[i] && window.resourceTradeSellBtns[i].parent) { window.resourceTradeSellBtns[i].parent.removeChild(window.resourceTradeSellBtns[i]); } } } if (window.resourceTradeSellAllBtns) { for (var i = 0; i < window.resourceTradeSellAllBtns.length; i++) { if (window.resourceTradeSellAllBtns[i] && window.resourceTradeSellAllBtns[i].parent) { window.resourceTradeSellAllBtns[i].parent.removeChild(window.resourceTradeSellAllBtns[i]); } } } if (window.resourceTradeBuyBtns) { for (var i = 0; i < window.resourceTradeBuyBtns.length; i++) { if (window.resourceTradeBuyBtns[i] && window.resourceTradeBuyBtns[i].parent) { window.resourceTradeBuyBtns[i].parent.removeChild(window.resourceTradeBuyBtns[i]); } } } window.resourceTradePanelBG = null; window.resourceTradePanelTitle = null; window.resourceTradePanelInfoText = null; window.resourceTradePanelCloseBtn = null; window.resourceTradeLabels = null; window.resourceTradeSellBtns = null; window.resourceTradeSellAllBtns = null; window.resourceTradeBuyBtns = null; }; var healthText = new Text2('Health: 100/100', { size: unifiedFontSize, fill: 0x00FF00 }); healthText.anchor.set(0.5, 0); healthText._isHealthBar = true; // Mark for tap detection LK.gui.top.addChild(healthText); var shieldText = new Text2('Shield: 50/50', { size: unifiedFontSize, fill: 0x0088FF }); shieldText.anchor.set(0.5, 0); shieldText.y = 60; LK.gui.top.addChild(shieldText); // --- Add waveText and expText UI for wave/level display --- // --- REPAIR PANEL WARNING SUPPRESSION --- // When repair panel is closed, set a suppression timer for 30 seconds (1800 ticks) if (window.repairPanel && !window._repairPanelSuppressUntil) { window._repairPanelSuppressUntil = LK.ticks + 1800; } // (removed duplicate handler, see main game.down below) var waveText = new Text2('Wave: 1', { size: unifiedFontSize, fill: 0xffffff }); waveText.anchor.set(0.5, 0); waveText.x = 1024; waveText.y = 60; // Move down to make space for market button LK.gui.top.addChild(waveText); var expText = new Text2('Level: 1 EXP: 0/10', { size: unifiedFontSize, fill: 0xffffff }); expText.anchor.set(0.5, 0); expText.x = 1024; expText.y = 70; LK.gui.top.addChild(expText); // --- Add image to right edge, 3 rows below waveText --- if (!window.levelPanelBelowImg) { // Use the 'levelPanelBelowImg' asset as the image var imgAsset = LK.getAsset('levelPanelBelowImg', { anchorX: 1, // right edge anchorY: 0, // top scaleX: 0.7, scaleY: 0.7 }); // Place at right edge (x=0 in bottomRight, so x=-margin from right) // LK.gui.topRight is anchored at (1,0), so x=0 is right edge, y=0 is top // waveText.y is 60, so 3 rows below is 60 + 3*unifiedFontSize + 3*10 (10px margin per row) var rowH = unifiedFontSize + 10; imgAsset.x = -40; // 40px margin from right edge imgAsset.y = waveText.y + 3 * rowH; window.levelPanelBelowImg = imgAsset; LK.gui.topRight.addChild(imgAsset); // --- Achievements Panel Tap Area (transparent, over Achievements image) --- if (!window.achievementsPanelBtn) { var achBtn = new Text2("", { size: 1, fill: 0xffffff, align: "center" }); achBtn.anchor.set(1, 0); achBtn.x = imgAsset.x; achBtn.y = imgAsset.y; achBtn.width = imgAsset.width; achBtn.height = imgAsset.height; achBtn.alpha = 0; // fully transparent, but receives taps window.achievementsPanelBtn = achBtn; LK.gui.topRight.addChild(achBtn); } // --- Add new image just below Achievements image (right edge, keep all others fixed) --- if (!window.newImageAboveStorage) { var newImgAsset = LK.getAsset('newImageAboveStorage', { anchorX: 1, anchorY: 0, scaleX: 0.7, scaleY: 0.7 }); // Place just below Achievements image (levelPanelBelowImg) if (window.levelPanelBelowImg) { newImgAsset.x = window.levelPanelBelowImg.x; newImgAsset.y = window.levelPanelBelowImg.y + window.levelPanelBelowImg.height + 10; } else if (typeof imgAsset !== "undefined") { // fallback: just below the previous image newImgAsset.x = imgAsset.x; newImgAsset.y = imgAsset.y + imgAsset.height + 10; } else { // fallback: right edge, 2 rows from top newImgAsset.x = -40; newImgAsset.y = 10 + newImgAsset.height * 2 + 20; } window.newImageAboveStorage = newImgAsset; LK.gui.topRight.addChild(newImgAsset); } // --- Add upg image to right edge, 3 rows below waveText, just below the previous image --- if (!window.upgPanelBelowImg) { var upgImgAsset = LK.getAsset('upg', { anchorX: 1, anchorY: 0, scaleX: 0.7, scaleY: 0.7 }); upgImgAsset.x = -40; upgImgAsset.y = imgAsset.y + imgAsset.height + 10 + upgImgAsset.height + 10; // move 1 row (image height + 10px) further down window.upgPanelBelowImg = upgImgAsset; LK.gui.topRight.addChild(upgImgAsset); // Add a transparent tap area over the upg image for opening the Upgrades panel if (!window.upgPanelBtn) { var upgBtn = new Text2("", { size: 1, fill: 0xffffff, align: "center" }); upgBtn.anchor.set(1, 0); upgBtn.x = upgImgAsset.x; // Move the button 1 row (image height + 10px) further down, image stays fixed upgBtn.y = upgImgAsset.y + upgImgAsset.height + 10; // Make the button area 1.5x wider and 1.5x taller than the image, centered on the image's position upgBtn.width = upgImgAsset.width * 1.5; upgBtn.height = upgImgAsset.height * 1.5; // Shift the button left and up so it stays centered on the image upgBtn.x = upgImgAsset.x - (upgBtn.width - upgImgAsset.width) / 2; upgBtn.y = upgImgAsset.y + upgImgAsset.height + 10 - (upgBtn.height - upgImgAsset.height) / 2; upgBtn.alpha = 0; // fully transparent, but receives taps window.upgPanelBtn = upgBtn; LK.gui.topRight.addChild(upgBtn); } } } // --- Achievements Panel Logic --- // 50 achievements, with difficulty and rewards if (!window.achievementsList) { window.achievementsList = [ // id, name, desc, reward, difficulty { id: 1, name: "First Blood", desc: "Destroy your first pirate.", reward: 50, difficulty: "Easy" }, { id: 2, name: "Miner", desc: "Collect resources from an asteroid.", reward: 30, difficulty: "Easy" }, { id: 3, name: "Trader", desc: "Trade a resource.", reward: 40, difficulty: "Easy" }, { id: 5, name: "Killer Pirate", desc: "Destroy the killer pirate.", reward: 500, difficulty: "Hard" }, { id: 6, name: "Rich", desc: "Accumulate 1000 coins.", reward: 100, difficulty: "Medium" }, { id: 7, name: "Mechanic", desc: "Repair your ship.", reward: 30, difficulty: "Easy" }, { id: 8, name: "Level Up", desc: "Reach level 5.", reward: 80, difficulty: "Medium" }, { id: 9, name: "Shield Master", desc: "Fully charge your shield.", reward: 40, difficulty: "Easy" }, { id: 11, name: "Pirate Hunter", desc: "Destroy 10 pirates.", reward: 120, difficulty: "Medium" }, { id: 12, name: "Asteroid Destroyer", desc: "Destroy 5 asteroids.", reward: 80, difficulty: "Medium" }, { id: 13, name: "Trade Master", desc: "Complete 10 trades total.", reward: 150, difficulty: "Hard" }, { id: 14, name: "Explorer", desc: "Travel a total of 10,000 distance units.", reward: 100, difficulty: "Medium" }, { id: 15, name: "Pirate King", desc: "Destroy 50 pirates.", reward: 300, difficulty: "Hard" }, { id: 16, name: "Resource King", desc: "Collect 100 units of all resources.", reward: 200, difficulty: "Hard" }, { id: 17, name: "Upgrader", desc: "Purchase an upgrade.", reward: 60, difficulty: "Easy" }, { id: 18, name: "Repair Specialist", desc: "Repair 5 times.", reward: 100, difficulty: "Medium" }, { id: 19, name: "Wave Passer", desc: "Reach wave 5.", reward: 80, difficulty: "Medium" }, { id: 20, name: "Wave Master", desc: "Reach wave 10.", reward: 200, difficulty: "Hard" }, { id: 21, name: "Shield King", desc: "Upgrade shield to 200.", reward: 120, difficulty: "Hard" }, { id: 22, name: "Beast", desc: "Upgrade health to 300.", reward: 120, difficulty: "Hard" }, { id: 23, name: "Fast", desc: "Upgrade speed to 10.", reward: 100, difficulty: "Hard" }, { id: 24, name: "Bullet Storm", desc: "Reduce fire rate to 5.", reward: 100, difficulty: "Hard" }, { id: 25, name: "Energy Master", desc: "Collect a total of 500 energy.", reward: 100, difficulty: "Medium" }, { id: 26, name: "Metal Worker", desc: "Collect a total of 500 metal.", reward: 100, difficulty: "Medium" }, { id: 27, name: "Ice Man", desc: "Collect a total of 100 ice.", reward: 80, difficulty: "Medium" }, { id: 28, name: "Plasma Hunter", desc: "Collect a total of 50 plasma.", reward: 80, difficulty: "Medium" }, { id: 29, name: "Antimatter Collector", desc: "Collect a total of 10 antimatter.", reward: 120, difficulty: "Hard" }, { id: 33, name: "Space Station", desc: "Make a delivery to the station.", reward: 100, difficulty: "Medium" }, { id: 34, name: "Big Delivery", desc: "Complete all station requests.", reward: 300, difficulty: "Hard" }, { id: 35, name: "Pirate Base", desc: "Destroy a pirate base.", reward: 200, difficulty: "Hard" }, { id: 36, name: "Pirate Base Hunter", desc: "Destroy 3 pirate bases.", reward: 400, difficulty: "Very Hard" }, { id: 38, name: "Resource Tycoon", desc: "Collect 500 units of all resources.", reward: 400, difficulty: "Very Hard" }, { id: 39, name: "Trade Tycoon", desc: "Complete a total of 100 trades.", reward: 400, difficulty: "Very Hard" }, { id: 40, name: "Level Master", desc: "Reach level 20.", reward: 400, difficulty: "Very Hard" }, { id: 41, name: "Wave Legend", desc: "Reach wave 20.", reward: 500, difficulty: "Very Hard" }, { id: 42, name: "Space Conqueror", desc: "Travel a total of 100,000 distance units.", reward: 500, difficulty: "Very Hard" }, { id: 43, name: "Pirate Slayer", desc: "Destroy 100 pirates.", reward: 600, difficulty: "Very Hard" }, { id: 44, name: "Asteroid King", desc: "Destroy 50 asteroids.", reward: 400, difficulty: "Very Hard" }, { id: 45, name: "Upgrade King", desc: "Max out all upgrades.", reward: 600, difficulty: "Very Hard" }, { id: 46, name: "Repair King", desc: "Repair 20 times.", reward: 300, difficulty: "Very Hard" }, // NEW: Active gameplay feature achievements { id: 51, name: "Asteroid Hunter", desc: "Destroy 20 asteroids.", reward: 150, difficulty: "Medium" }, { id: 52, name: "Pirate Raider", desc: "Destroy 10 pirates in one wave.", reward: 200, difficulty: "Hard" }, { id: 53, name: "Fast Collector", desc: "Collect 10 resources in one minute.", reward: 120, difficulty: "Medium" }, { id: 54, name: "Rich Miner", desc: "Have 1000 coins and 100 resources at the same time.", reward: 250, difficulty: "Hard" }, { id: 55, name: "Upgrade Series", desc: "Buy 3 upgrades in a row.", reward: 180, difficulty: "Medium" }, { id: 56, name: "Achievement Hunter", desc: "Complete all achievements.", reward: 1000, difficulty: "Legendary" }, // --- NEW 10 ACHIEVEMENTS (latest features) --- { id: 57, name: "GroupPirate Hunter", desc: "Completely destroy a GroupPirate squad.", reward: 250, difficulty: "Hard" }, { id: 58, name: "CoinBox Hunter", desc: "Collect a CoinBox.", reward: 80, difficulty: "Easy" }, { id: 59, name: "CoinBox Tycoon", desc: "Collect a total of 10 CoinBoxes.", reward: 300, difficulty: "Hard" }, { id: 60, name: "Wreck Hunter", desc: "Collect a Derelict Wreck.", reward: 60, difficulty: "Easy" }, { id: 61, name: "Wreck Tycoon", desc: "Collect a total of 10 Derelict Wrecks.", reward: 250, difficulty: "Medium" }, { id: 62, name: "Double Pirate", desc: "Fight 2 pirates at the same time.", reward: 120, difficulty: "Medium" }, { id: 63, name: "Triple GroupPirate", desc: "Fight 3 GroupPirates at the same time.", reward: 200, difficulty: "Hard" }, { id: 64, name: "Asteroid Rain", desc: "Destroy 5 asteroids in one wave.", reward: 180, difficulty: "Hard" }, { id: 65, name: "Resource Burst", desc: "Collect more than 20 resources at once.", reward: 220, difficulty: "Hard" }, { id: 66, name: "Pirate Swarm", desc: "Fight more than 5 pirates in one wave.", reward: 250, difficulty: "Very Hard" }]; // Save progress if (!storage.achievements) { storage.achievements = []; } window.achievementsProgress = storage.achievements; } // --- BAŞARIM MEKANİĞİ EKLENDİ --- // Yardımcı fonksiyon: başarıma sahip mi? function hasAchievement(id) { return window.achievementsProgress && window.achievementsProgress.indexOf(id) !== -1; } // Yardımcı fonksiyon: başarıma ekle ve ödül ver function grantAchievement(id) { if (!window.achievementsProgress) { window.achievementsProgress = []; } if (window.achievementsProgress.indexOf(id) !== -1) { return; } // Zaten kazanıldı window.achievementsProgress.push(id); storage.achievements = window.achievementsProgress; // Ödül ver var ach = null; for (var i = 0; i < window.achievementsList.length; i++) { if (window.achievementsList[i].id === id) { ach = window.achievementsList[i]; break; } } if (ach && typeof playerCoins !== "undefined") { playerCoins += ach.reward; updateUI && updateUI(); saveProgress && saveProgress(); // Show a short notification var label = new Text2("Achievement Earned!\n" + ach.name + "\n+" + ach.reward + " coin", { size: 44, fill: 0x00ffcc, align: "center" }); label.anchor.set(0.5, 0.5); label.x = 1024; label.y = 400; if (typeof game !== "undefined" && game.addChild) { game.addChild(label); } LK.setTimeout(function () { if (label.parent) { label.parent.removeChild(label); } }, 1800); } // Tüm başarımlar kazanıldıysa 50. başarıma bak if (window.achievementsProgress.length === 50 && !hasAchievement(50)) { grantAchievement(50); } } // --- BAŞARIMLARIN TAKİBİ İÇİN SAYACLAR --- // Oyun içi sayaçlar if (!window._achPirateKills) { window._achPirateKills = 0; } if (!window._achAsteroidKills) { window._achAsteroidKills = 0; } if (!window._achTradeCount) { window._achTradeCount = 0; } if (!window._achRepairCount) { window._achRepairCount = 0; } if (!window._achQuestComplete) { window._achQuestComplete = 0; } if (!window._achNPCKills) { window._achNPCKills = 0; } if (!window._achStationDeliver) { window._achStationDeliver = 0; } if (!window._achStationAllDeliver) { window._achStationAllDeliver = 0; } if (!window._achPirateBaseKills) { window._achPirateBaseKills = 0; } if (!window._achPirateBaseKills3) { window._achPirateBaseKills3 = 0; } if (!window._achUpgradeCount) { window._achUpgradeCount = 0; } if (!window._achKatilEscape) { window._achKatilEscape = false; } if (!window._achKatilKill) { window._achKatilKill = false; } if (!window._achWormhole) { window._achWormhole = false; } if (!window._achPirateTrade) { window._achPirateTrade = false; } if (!window._achProtectNPC) { window._achProtectNPC = false; } if (!window._ach3PirateFight) { window._ach3PirateFight = false; } // --- BAŞARIM KONTROLLERİ --- // 1. Korsan öldürme (İlk Kan, Korsan Avcısı, Korsan Kralı, Korsan Katili) function checkPirateKillAchievements() { window._achPirateKills++; if (window._achPirateKills === 1) { grantAchievement(1); } // İlk Kan if (window._achPirateKills === 10) { grantAchievement(11); } // Korsan Avcısı if (window._achPirateKills === 50) { grantAchievement(15); } // Korsan Kralı if (window._achPirateKills === 100) { grantAchievement(43); } // Korsan Katili } // 2. Katil korsan öldürme function checkKatilKillAchievement() { if (!window._achKatilKill) { window._achKatilKill = true; grantAchievement(5); // Katil Korsan } } // 3. Asteroit patlatma function checkAsteroidKillAchievements() { window._achAsteroidKills++; if (window._achAsteroidKills === 5) { grantAchievement(12); } // Asteroit Patlatıcı if (window._achAsteroidKills === 50) { grantAchievement(44); } // Asteroit Kralı } // 4. Hammadde toplama (Madenci, Hammadde Kralı, Hammadde Zengini, Enerji Ustası, Metalci, Buz Adam, Plazma Avcısı, Antimaddeci) function checkResourceCollectAchievements() { // Madenci grantAchievement(2); // Enerji Ustası, Metalci, Buz Adam, Plazma Avcısı, Antimaddeci, Hammadde Kralı, Hammadde Zengini var res = playerResources; if (res[1] >= 500) { grantAchievement(25); } // Enerji Ustası if (res[0] >= 500) { grantAchievement(26); } // Metalci if (res[4] >= 100) { grantAchievement(27); } // Buz Adam if (res[8] >= 50) { grantAchievement(28); } // Plazma Avcısı if (res[9] >= 10) { grantAchievement(29); } // Antimaddeci var all100 = true, all500 = true; for (var i = 0; i < res.length; i++) { if (res[i] < 100) { all100 = false; } if (res[i] < 500) { all500 = false; } } if (all100) { grantAchievement(16); } // Hammadde Kralı if (all500) { grantAchievement(38); } // Hammadde Zengini } // 5. Takas (Tüccar, Ticaret Ustası, Takas Zengini) function checkTradeAchievements() { window._achTradeCount++; if (window._achTradeCount === 1) { grantAchievement(3); } // Tüccar if (window._achTradeCount === 10) { grantAchievement(13); } // Ticaret Ustası if (window._achTradeCount === 100) { grantAchievement(39); } // Takas Zengini } // 6. Seviye atlama (Seviye Atla, Seviye Ustası) function checkLevelAchievements() { if (playerLevel >= 5) { grantAchievement(8); } // Seviye Atla if (playerLevel >= 20) { grantAchievement(40); } // Seviye Ustası } // 7. Coin biriktirme (Zengin) function checkCoinAchievements() { if (playerCoins >= 1000) { grantAchievement(6); } // Zengin } // 8. Tamir (Mekanik, Tamirci, Tamir Kralı) function checkRepairAchievements() { window._achRepairCount++; if (window._achRepairCount === 1) { grantAchievement(7); } // Mekanik if (window._achRepairCount === 5) { grantAchievement(18); } // Tamirci if (window._achRepairCount === 20) { grantAchievement(46); } // Tamir Kralı } // 9. Kalkanı doldurma (Kalkan Ustası, Kalkan Kralı) function checkShieldAchievements() { if (ship.shield >= ship.maxShield && ship.shield >= 50) { grantAchievement(9); } // Kalkan Ustası if (ship.maxShield >= 200) { grantAchievement(21); } // Kalkan Kralı } // 10. Canı yükseltme (Canavar) function checkHealthAchievements() { if (ship.maxHealth >= 300) { grantAchievement(22); } // Canavar } // 11. Hızı yükseltme (Hızlı) function checkSpeedAchievements() { if (ship.speed >= 10) { grantAchievement(23); } // Hızlı } // 12. Atış hızını düşürme (Ateş Yağmuru) function checkFireRateAchievements() { if (ship.fireRate <= 5) { grantAchievement(24); } // Ateş Yağmuru } // 13. Görev tamamlama (Görev Adamı) function checkQuestAchievements() { window._achQuestComplete++; if (window._achQuestComplete === 1) { grantAchievement(10); } // Görev Adamı } // 14. Dalga geçme (Dalga Geç, Dalga Ustası, Dalga Efsanesi) function checkWaveAchievements() { if (waveNumber + 1 >= 5) { grantAchievement(19); } // Dalga Geç if (waveNumber + 1 >= 10) { grantAchievement(20); } // Dalga Ustası if (waveNumber + 1 >= 20) { grantAchievement(41); } // Dalga Efsanesi } // 15. Yol katetme (Gezgin, Uzay Fatihi) function checkDistanceAchievements() { if (totalDistance >= 10000) { grantAchievement(14); } // Gezgin if (totalDistance >= 100000) { grantAchievement(42); } // Uzay Fatihi } // 16. Upgrade satın alma (Upgradeci, Upgrade Kralı) function checkUpgradeAchievements() { window._achUpgradeCount++; if (window._achUpgradeCount === 1) { grantAchievement(17); } // Upgradeci // Tüm upgrade'ler maksimuma çıkarıldıysa if (ship.maxHealth >= 300 && ship.maxShield >= 200 && ship.damage >= 50 && ship.speed >= 10 && ship.fireRate <= 5) { grantAchievement(45); // Upgrade Kralı } } // 17. Korsan üssü yok etme (Korsan Üssü, Korsan Üssü Avcısı) function checkPirateBaseAchievements() { window._achPirateBaseKills++; if (window._achPirateBaseKills === 1) { grantAchievement(35); } // Korsan Üssü if (window._achPirateBaseKills === 3) { grantAchievement(36); } // Korsan Üssü Avcısı } // 18. NPC öldürme (NPC Avcısı) function checkNPCKillAchievements() { window._achNPCKills++; if (window._achNPCKills === 10) { grantAchievement(48); } // NPC Avcısı } // 19. İstasyon teslimatı (Uzay İstasyonu, Büyük Teslimat) function checkStationDeliveryAchievements(allDone) { window._achStationDeliver++; grantAchievement(33); // Uzay İstasyonu if (allDone) { window._achStationAllDeliver++; grantAchievement(34); // Büyük Teslimat } } // 20. Solucan deliğinden geçme (Kaşif) function checkWormholeAchievement() { if (!window._achWormhole) { window._achWormhole = true; grantAchievement(4); // Kaşif } } // 21. Katil korsandan kaçma (Katil Kaçışı) function checkKatilEscapeAchievement() { if (!window._achKatilEscape) { window._achKatilEscape = true; grantAchievement(37); // Katil Kaçışı } } // 22. Bir korsandan kaçma (Korsan Kaçakçısı) function checkPirateEscapeAchievement() { grantAchievement(30); // Korsan Kaçakçısı } // 23. Bir NPC'yi koruma (Dost Canlısı) function checkProtectNPCAchievement() { if (!window._achProtectNPC) { window._achProtectNPC = true; grantAchievement(31); // Dost Canlısı } } // 24. Aynı anda 3 korsanla savaş (Korsan Saldırısı) function check3PirateFightAchievement() { if (!window._ach3PirateFight) { window._ach3PirateFight = true; grantAchievement(32); // Korsan Saldırısı } } // 25. Bir korsanla ticaret yapma (Korsan Dostu) function checkPirateTradeAchievement() { if (!window._achPirateTrade) { window._achPirateTrade = true; grantAchievement(47); // Korsan Dostu } } // 26. 30 dakika hayatta kalma (Uzayda Hayatta Kal) function checkSurvive30MinAchievement(elapsedSeconds) { if (elapsedSeconds >= 1800) { grantAchievement(49); } } // --- Achievements Panel Open/Close --- window.openAchievementsPanel = function (page) { if (window.achievementsPanelBG) { return; } var pageSize = 10; var total = window.achievementsList.length; var totalPages = Math.ceil(total / pageSize); var currentPage = typeof page === "number" ? Math.max(1, Math.min(page, totalPages)) : 1; window._achievementsPanelPage = currentPage; var panelW = 1100, panelH = 1200; var px = 2048 / 2, py = 2732 / 2; // BG var bg = LK.getAsset('resource', { anchorX: 0.5, anchorY: 0.5, x: px, y: py, scaleX: panelW / 40, scaleY: panelH / 40, tint: 0x222244, alpha: 0.97 }); // Title var title = new Text2("Achievements", { size: Math.round(unifiedFontSize * 1.3) + 2, fill: 0x00ffcc, align: "center" }); title.anchor.set(0.5, 0); title.x = px; title.y = py - panelH / 2 + 40; // Page info var pageInfo = new Text2("Page " + currentPage + "/" + totalPages, { size: Math.round(unifiedFontSize * 0.9) + 2, fill: 0xffffff, align: "center" }); pageInfo.anchor.set(0.5, 0); pageInfo.x = px; pageInfo.y = title.y + 60; // Achievements rows var startY = pageInfo.y + 60; var rowH = 90; var achRows = []; for (var i = 0; i < pageSize; i++) { var idx = (currentPage - 1) * pageSize + i; if (idx >= total) { break; } var ach = window.achievementsList[idx]; var unlocked = window.achievementsProgress.indexOf(ach.id) !== -1; var text = (unlocked ? "✅ " : "⬜ ") + ach.name + " - " + ach.desc + " [" + ach.difficulty + "]\nÖdül: " + ach.reward + " coin"; var color = unlocked ? 0x00ff99 : 0xffffff; var row = new Text2(text, { size: Math.round(unifiedFontSize * 0.9) + 2, fill: color, align: "left" }); row.anchor.set(0, 0.5); row.x = px - panelW / 2 + 40; row.y = startY + i * rowH; achRows.push(row); } // Page number buttons (1-2-3...) var btnW = 45, btnH = 35; var pageBtns = []; var pageBtnY = py + panelH / 2 - btnH / 2 - 20; var pageBtnStartX = px - (totalPages - 1) * (btnW + 10) / 2; for (var p = 1; p <= totalPages; p++) { var isCurrent = p === currentPage; var btn = new Text2("" + p, { size: Math.round(unifiedFontSize * 0.6) + 2, fill: isCurrent ? 0x00ffcc : 0xffffff, align: "center" }); btn.anchor.set(0.5, 0.5); btn.x = pageBtnStartX + (p - 1) * (btnW + 10); btn.y = pageBtnY; btn.width = btnW; btn.height = btnH; btn._achPageBtn = p; if (isCurrent) { btn.fill = 0x00ffcc; btn.setText("[" + p + "]"); } pageBtns.push(btn); } // Close button var closeBtn = new Text2("Close", { size: Math.round((unifiedFontSize + 4) * 1.3), fill: 0xff4444, align: "center" }); closeBtn.anchor.set(0.5, 0.5); closeBtn.x = px; closeBtn.y = title.y - 80; closeBtn._achCloseBtn = true; // Add to game game.addChild(bg); game.addChild(title); game.addChild(pageInfo); for (var i = 0; i < achRows.length; i++) { game.addChild(achRows[i]); } for (var i = 0; i < pageBtns.length; i++) { game.addChild(pageBtns[i]); } game.addChild(closeBtn); // Store refs for tap logic/cleanup window.achievementsPanelBG = bg; window.achievementsPanelTitle = title; window.achievementsPanelPageInfo = pageInfo; window.achievementsPanelRows = achRows; window.achievementsPanelPageBtns = pageBtns; window.achievementsPanelCloseBtn = closeBtn; }; window.closeAchievementsPanel = function () { if (!window.achievementsPanelBG) { return; } if (window.achievementsPanelBG.parent) { window.achievementsPanelBG.parent.removeChild(window.achievementsPanelBG); } if (window.achievementsPanelTitle && window.achievementsPanelTitle.parent) { window.achievementsPanelTitle.parent.removeChild(window.achievementsPanelTitle); } if (window.achievementsPanelPageInfo && window.achievementsPanelPageInfo.parent) { window.achievementsPanelPageInfo.parent.removeChild(window.achievementsPanelPageInfo); } if (window.achievementsPanelRows) { for (var i = 0; i < window.achievementsPanelRows.length; i++) { if (window.achievementsPanelRows[i] && window.achievementsPanelRows[i].parent) { window.achievementsPanelRows[i].parent.removeChild(window.achievementsPanelRows[i]); } } } if (window.achievementsPanelPageBtns) { for (var i = 0; i < window.achievementsPanelPageBtns.length; i++) { if (window.achievementsPanelPageBtns[i] && window.achievementsPanelPageBtns[i].parent) { window.achievementsPanelPageBtns[i].parent.removeChild(window.achievementsPanelPageBtns[i]); } } } if (window.achievementsPanelCloseBtn && window.achievementsPanelCloseBtn.parent) { window.achievementsPanelCloseBtn.parent.removeChild(window.achievementsPanelCloseBtn); } window.achievementsPanelBG = null; window.achievementsPanelTitle = null; window.achievementsPanelPageInfo = null; window.achievementsPanelRows = null; window.achievementsPanelPageBtns = null; window.achievementsPanelCloseBtn = null; window._achievementsPanelPage = null; }; // --- CREW PANEL SYSTEM --- // Global arrays for friends, dron, and mechanic var friends = []; var dron = null; var mechanic = null; // Helper: open Crew panel window.openCrewPanel = function () { if (window.crewPanelBG) { return; } var px = 2048 / 2, py = 2732 / 2; var panelW = 900, panelH = 900; var rowH = 110; var fontSize = Math.round(unifiedFontSize * 1.1) + 2; // BG var bg = LK.getAsset('resource', { anchorX: 0.5, anchorY: 0.5, x: px, y: py, scaleX: panelW / 40, scaleY: panelH / 40, tint: 0x1a2dcb, alpha: 0.97 }); // Title var title = new Text2("Crew (Friends & Dron)", { size: fontSize + 8, fill: 0x00ffcc, align: "center" }); title.anchor.set(0.5, 0); title.x = px; title.y = py - panelH / 2 + 40; // Info var info = new Text2("You can buy up to 3 Friends. Each Friend attacks with 50% of your attack power.\nDron collects nearby resources/coins.\nMechanic automatically repairs when health drops below 50%.", { size: fontSize - 2, fill: 0xffffff, align: "center" }); info.anchor.set(0.5, 0); info.x = px; info.y = title.y + 60; // Friend satın al butonları ve etiketleri window.crewFriendBtns = []; window.crewFriendLabels = []; for (var i = 0; i < 3; i++) { var label = new Text2("Friend #" + (i + 1) + (friends[i] ? " (Purchased)" : " (Empty)"), { size: fontSize, fill: friends[i] ? 0x00ff99 : 0xffffff, align: "left" }); label.anchor.set(0, 0.5); label.x = px - panelW / 2 + 60; label.y = info.y + 100 + rowH + i * rowH; window.crewFriendLabels.push(label); var btn = new Text2(friends[i] ? "Purchased" : "Buy (500 coin)", { size: fontSize, fill: friends[i] ? 0x888888 : 0x00ff99, align: "center" }); btn.anchor.set(0.5, 0.5); btn.x = px + 220; btn.y = label.y; btn._friendIdx = i; window.crewFriendBtns.push(btn); } // Dron satın al etiketi ve butonu var dronLabel = new Text2("Dron: " + (dron ? "Purchased" : "None"), { size: fontSize, fill: dron ? 0x00ff99 : 0xffffff, align: "left" }); dronLabel.anchor.set(0, 0.5); dronLabel.x = px - panelW / 2 + 60; dronLabel.y = info.y + 100 + rowH + 3 * rowH; var dronBtn = new Text2(dron ? "Purchased" : "Buy (1200 coin)", { size: fontSize, fill: dron ? 0x888888 : 0x00ff99, align: "center" }); dronBtn.anchor.set(0.5, 0.5); dronBtn.x = px + 220; dronBtn.y = dronLabel.y; // Mechanic satın al etiketi ve butonu var mechanicLabel = new Text2("Mechanic: " + (window.mechanic ? "Purchased" : "None"), { size: fontSize, fill: window.mechanic ? 0x00ff99 : 0xffffff, align: "left" }); mechanicLabel.anchor.set(0, 0.5); mechanicLabel.x = px - panelW / 2 + 60; mechanicLabel.y = info.y + 100 + rowH + 4 * rowH; var mechanicBtn = new Text2(window.mechanic ? "Purchased" : "Buy (800 coin)", { size: fontSize, fill: window.mechanic ? 0x888888 : 0x00ff99, align: "center" }); mechanicBtn.anchor.set(0.5, 0.5); mechanicBtn.x = px + 220; mechanicBtn.y = mechanicLabel.y; // Close button var closeBtn = new Text2("Close", { size: fontSize + 6, fill: 0xff4444, align: "center" }); closeBtn.anchor.set(0.5, 0.5); closeBtn.x = px; closeBtn.y = title.y - 80; // Add to game game.addChild(bg); game.addChild(title); game.addChild(info); for (var i = 0; i < window.crewFriendLabels.length; i++) { game.addChild(window.crewFriendLabels[i]); } for (var i = 0; i < window.crewFriendBtns.length; i++) { game.addChild(window.crewFriendBtns[i]); } game.addChild(dronLabel); game.addChild(dronBtn); game.addChild(mechanicLabel); game.addChild(mechanicBtn); game.addChild(closeBtn); // Store refs for tap logic/cleanup window.crewPanelBG = bg; window.crewPanelTitle = title; window.crewPanelInfo = info; window.crewPanelDronLabel = dronLabel; window.crewPanelDronBtn = dronBtn; window.crewPanelMechanicLabel = mechanicLabel; window.crewPanelMechanicBtn = mechanicBtn; window.crewPanelCloseBtn = closeBtn; }; window.closeCrewPanel = function () { if (!window.crewPanelBG) { return; } if (window.crewPanelBG.parent) { window.crewPanelBG.parent.removeChild(window.crewPanelBG); } if (window.crewPanelTitle && window.crewPanelTitle.parent) { window.crewPanelTitle.parent.removeChild(window.crewPanelTitle); } if (window.crewPanelInfo && window.crewPanelInfo.parent) { window.crewPanelInfo.parent.removeChild(window.crewPanelInfo); } if (window.crewFriendLabels) { for (var i = 0; i < window.crewFriendLabels.length; i++) { if (window.crewFriendLabels[i] && window.crewFriendLabels[i].parent) { window.crewFriendLabels[i].parent.removeChild(window.crewFriendLabels[i]); } } } if (window.crewFriendBtns) { for (var i = 0; i < window.crewFriendBtns.length; i++) { if (window.crewFriendBtns[i] && window.crewFriendBtns[i].parent) { window.crewFriendBtns[i].parent.removeChild(window.crewFriendBtns[i]); } } } if (window.crewPanelDronLabel && window.crewPanelDronLabel.parent) { window.crewPanelDronLabel.parent.removeChild(window.crewPanelDronLabel); } if (window.crewPanelDronBtn && window.crewPanelDronBtn.parent) { window.crewPanelDronBtn.parent.removeChild(window.crewPanelDronBtn); } if (window.crewPanelMechanicLabel && window.crewPanelMechanicLabel.parent) { window.crewPanelMechanicLabel.parent.removeChild(window.crewPanelMechanicLabel); } if (window.crewPanelMechanicBtn && window.crewPanelMechanicBtn.parent) { window.crewPanelMechanicBtn.parent.removeChild(window.crewPanelMechanicBtn); } if (window.crewPanelCloseBtn && window.crewPanelCloseBtn.parent) { window.crewPanelCloseBtn.parent.removeChild(window.crewPanelCloseBtn); } window.crewPanelBG = null; window.crewPanelTitle = null; window.crewPanelInfo = null; window.crewFriendLabels = null; window.crewFriendBtns = null; window.crewPanelDronLabel = null; window.crewPanelDronBtn = null; window.crewPanelMechanicLabel = null; window.crewPanelMechanicBtn = null; window.crewPanelCloseBtn = null; }; // --- Upgrades Panel Logic --- if (!window.openUpgradesPanel) { window.openUpgradesPanel = function () { if (window.upgradesPanel) { return; } // Panel size and position var panelW = 900, panelH = 1100; // Center the panel on the screen var px = 2048 / 2; var py = 2732 / 2; // Panel BG var bg = LK.getAsset('resource', { anchorX: 0.5, anchorY: 0.5, x: px, y: py, scaleX: panelW / 40, scaleY: panelH / 40, tint: 0x222244, alpha: 0.97 }); // Title var title = new Text2("Upgrades", { size: Math.round(unifiedFontSize * 1.3), fill: 0x00ffcc, align: "center" }); title.anchor.set(0.5, 0); title.x = px; title.y = py - panelH / 2 + 40; // Upgrade options (example: maxHealth, maxShield, damage, speed, fireRate) var upgFont = unifiedFontSize + 2; var upgSpacing = 120; var upgStartY = title.y + 120; var upgBtns = []; var upgDefs = [{ label: "Attack Range +5%\n(300 coin)", type: "range", fill: 0x99ff99, cost: { coins: 300, metal: 0, energy: 0 } }, { label: "Heal 5%\n(50 coin)", type: "heal5", fill: 0xff6699, cost: { coins: 50, metal: 0, energy: 0 } }, { label: "Max Health +20\n(600 coin, 60 metal)", type: "maxHealth", fill: 0x00ff99, cost: { coins: 600, metal: 60, energy: 0 } }, { label: "Max Shield +10\n(720 coin, 72 metal)", type: "maxShield", fill: 0x00ccff, cost: { coins: 720, metal: 72, energy: 0 } }, { label: "Damage +5\n(900 coin, 90 energy)", type: "damage", fill: 0xffcc00, cost: { coins: 900, metal: 0, energy: 90 } }, { label: "Speed +10%\n(1000 coin)", type: "speed", fill: 0x00ffff, cost: { coins: 1000, metal: 0, energy: 0 } }, { label: "Fire Rate +10%\n(1200 coin)", type: "fireRate", fill: 0xff99ff, cost: { coins: 1200, metal: 0, energy: 0 } }]; for (var i = 0; i < upgDefs.length; i++) { var upg = upgDefs[i]; var btn = new Text2(upg.label, { size: upgFont, fill: upg.fill, align: "center" }); btn.anchor.set(0.5, 0.5); btn.x = px; btn.y = upgStartY + i * upgSpacing; btn._isUpgradesPanelBtn = true; btn._upgradeType = upg.type; btn._upgradeCost = upg.cost; upgBtns.push(btn); } // Close button var closeBtn = new Text2("Close", { size: Math.round((unifiedFontSize + 4) * 1.3), fill: 0xff4444, align: "center" }); closeBtn.anchor.set(0.5, 0.5); closeBtn.x = px; closeBtn.y = title.y - 80; closeBtn._isUpgradesPanelCloseBtn = true; // Add to game game.addChild(bg); game.addChild(title); for (var i = 0; i < upgBtns.length; i++) { game.addChild(upgBtns[i]); } game.addChild(closeBtn); // Store refs for tap logic/cleanup window.upgradesPanel = bg; window.upgradesPanelTitle = title; window.upgradesPanelBtns = upgBtns; window.upgradesPanelCloseBtn = closeBtn; }; window.closeUpgradesPanel = function () { if (!window.upgradesPanel) { return; } if (window.upgradesPanel && window.upgradesPanel.parent) { window.upgradesPanel.parent.removeChild(window.upgradesPanel); } if (window.upgradesPanelTitle && window.upgradesPanelTitle.parent) { window.upgradesPanelTitle.parent.removeChild(window.upgradesPanelTitle); } if (window.upgradesPanelBtns) { for (var i = 0; i < window.upgradesPanelBtns.length; i++) { if (window.upgradesPanelBtns[i] && window.upgradesPanelBtns[i].parent) { window.upgradesPanelBtns[i].parent.removeChild(window.upgradesPanelBtns[i]); } } } if (window.upgradesPanelCloseBtn && window.upgradesPanelCloseBtn.parent) { window.upgradesPanelCloseBtn.parent.removeChild(window.upgradesPanelCloseBtn); } window.upgradesPanel = null; window.upgradesPanelTitle = null; window.upgradesPanelBtns = null; window.upgradesPanelCloseBtn = null; }; } // No wave, minimap, exp, or coordinate UI in this version // Create joystick joystickBase = game.addChild(LK.getAsset('joystickBase', { anchorX: 0.5, anchorY: 0.5, x: 300, y: 2432, scaleX: 1.3, scaleY: 1.3 })); joystickBase.alpha = 0.5; joystickHandle = game.addChild(LK.getAsset('joystickHandle', { anchorX: 0.5, anchorY: 0.5, x: 300, y: 2432, scaleX: 1.3, scaleY: 1.3 })); joystickHandle.alpha = 0.7; // Create ship ship = game.addChild(new Ship()); ship.x = 1024; ship.y = 1366; // Add health bar to ship ship._healthBar = new HealthBar(); ship._healthBar.set({ maxValue: function maxValue() { return ship.maxHealth; }, getValueFn: function getValueFn() { return ship.health; }, getMaxFn: function getMaxFn() { return ship.maxHealth; }, width: 80, height: 12 }); ship._healthBar.y = -70; ship.addChild(ship._healthBar); // Start background music LK.playMusic('bgmusic'); // Initialize star field (increased by 50%) and make them colorful for (var i = 0; i < 135; i++) { // 90 * 1.5 = 135 spawnStar(true); // pass true to enable colorful stars } // Spawn more map elements at start (20% more) for (var i = 0; i < 6; i++) { // was 5, now 6 spawnNPC(); } for (var i = 0; i < 6; i++) { // was 5, now 6 spawnPirate(); } // Ensure at least one Katil korsan exists at game start if (typeof window._katilPirateSpawned === "undefined" || !window._katilPirateSpawned) { var pirate = new Pirate(); var minLevel = 1 + waveNumber * 2; pirate.level = minLevel + 10; pirate.health = (30 + (pirate.level - 1) * 10) * 10; pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10; pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10; pirate.rpgName = "Katil"; pirate._isKatil = true; pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3)); pirate._attackRange = Math.floor(600 * 0.9); // Katil korsan için %10 daha kısa menzil window._katilPirateSpawned = true; // Spawn from edge like other pirates var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y - 1400; break; case 1: pirate.x = ship.x + 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; case 2: pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y + 1400; break; case 3: pirate.x = ship.x - 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; } // Add health bar to katil pirate pirate._healthBar = new HealthBar(); pirate._healthBar.set({ maxValue: function maxValue() { return pirate.health > 0 ? pirate.health : 0; }, getValueFn: function getValueFn() { return pirate.health > 0 ? pirate.health : 0; }, getMaxFn: function getMaxFn() { return 30 + waveNumber * 10; }, width: 60, height: 10 }); pirate._healthBar.y = -60; pirate.addChild(pirate._healthBar); pirates.push(pirate); game.addChild(pirate); } for (var i = 0; i < 2; i++) { // was 4, now 2 (50% reduction) spawnAsteroid(); } // Helper functions function spawnStar(colorful) { var star = new Star(); // If colorful is true, assign a random color if (colorful) { // Pick a random color from a vibrant palette var palette = [0xffe066, // yellow 0xff66cc, // pink 0x66ffcc, // aqua 0x66ccff, // blue 0xcc66ff, // purple 0xff6666, // red 0x66ff66, // green 0xffffff, // white 0xffcc66, // orange 0x66ffff // cyan ]; var color = palette[Math.floor(Math.random() * palette.length)]; if (star.children && star.children.length > 0 && star.children[0]) { star.children[0].tint = color; } } // Spawn at a random position within a large area around the ship var radius = 1800 + Math.random() * 1200; // 1800-3000 px from ship var angle = Math.random() * Math.PI * 2; star.x = ship.x + Math.cos(angle) * radius; star.y = ship.y + Math.sin(angle) * radius; stars.push(star); game.addChild(star); } function spawnNPC() { var npc = new NPC(); // Spawn from edges of visible area var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: // Top npc.x = ship.x + (Math.random() - 0.5) * 2000; npc.y = ship.y - 1500; break; case 1: // Right npc.x = ship.x + 1500; npc.y = ship.y + (Math.random() - 0.5) * 2000; break; case 2: // Bottom npc.x = ship.x + (Math.random() - 0.5) * 2000; npc.y = ship.y + 1500; break; case 3: // Left npc.x = ship.x - 1500; npc.y = ship.y + (Math.random() - 0.5) * 2000; break; } npc.baseY = npc.y; // --- Set NPC level and health to match pirate scaling --- var minLevel = 1 + waveNumber * 2; var maxLevel = minLevel + 2; npc.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1)); npc.health = 100 + (npc.level - 1) * 10; // Add health bar to NPC (for defend/hunt/escort/delivery/explore) npc._healthBar = new HealthBar(); npc._healthBar.set({ maxValue: function maxValue() { return npc.health !== undefined ? npc.health : 100; }, getValueFn: function getValueFn() { return npc.health !== undefined ? npc.health : 100; }, getMaxFn: function getMaxFn() { return 100 + (npc.level - 1) * 10; }, width: 60, height: 10 }); npc._healthBar.y = -60; npc.addChild(npc._healthBar); npcs.push(npc); game.addChild(npc); } function spawnPirate() { var pirate = new Pirate(); // Spawn from edges of visible area var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: // Top pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y - 1400; break; case 1: // Right pirate.x = ship.x + 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; case 2: // Bottom pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y + 1400; break; case 3: // Left pirate.x = ship.x - 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; } // Set pirate level and scale stats based on wave // Level range: wave 1 => 1-3, wave 2 => 3-5, wave 3 => 5-7, etc. var minLevel = 1 + waveNumber * 2; var maxLevel = minLevel + 2; // Katil korsan spawn etme şansı: Eğer henüz yoksa ve %20 şansla, bu korsanı Katil yap if (typeof window._katilPirateSpawned === "undefined") { window._katilPirateSpawned = false; } if (!window._katilPirateSpawned && Math.random() < 0.2) { // Katil korsan! pirate.level = minLevel + 10; pirate.health = (30 + (pirate.level - 1) * 10) * 10; pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10; pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10; pirate.rpgName = "Katil"; pirate._isKatil = true; window._katilPirateSpawned = true; // Katil korsan saldırı oranını 3 kat arttır pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3)); pirate._attackRange = Math.floor(600 * 0.9); // Katil korsan için %10 daha kısa menzil // Add Enemy text to Katil Korsan if (!pirate._enemyText) { pirate._enemyText = new Text2("Enemy", { size: 18, fill: 0xff4444, align: "center" }); pirate._enemyText.anchor.set(0.5, 0); pirate._enemyText.x = 0; pirate._enemyText.y = -50; pirate.addChild(pirate._enemyText); } // Katil korsan için özel bir renk veya efekt istenirse burada eklenebilir } else { pirate.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1)); pirate.health = 30 + (pirate.level - 1) * 10; pirate.damage = 5 + Math.floor((pirate.level - 1) * 1.5); pirate.defense = 2 + Math.floor((pirate.level - 1) * 1.2); } // Add health bar to pirate pirate._healthBar = new HealthBar(); pirate._healthBar.set({ maxValue: function maxValue() { return pirate.health > 0 ? pirate.health : 0; }, getValueFn: function getValueFn() { return pirate.health > 0 ? pirate.health : 0; }, getMaxFn: function getMaxFn() { return 30 + waveNumber * 10; }, width: 60, height: 10 }); pirate._healthBar.y = -60; pirate.addChild(pirate._healthBar); pirates.push(pirate); game.addChild(pirate); } function spawnAsteroid() { var asteroid = new Asteroid(); var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: asteroid.x = ship.x + (Math.random() - 0.5) * 2200; asteroid.y = ship.y - 1600; break; case 1: asteroid.x = ship.x + 1600; asteroid.y = ship.y + (Math.random() - 0.5) * 2200; break; case 2: asteroid.x = ship.x + (Math.random() - 0.5) * 2200; asteroid.y = ship.y + 1600; break; case 3: asteroid.x = ship.x - 1600; asteroid.y = ship.y + (Math.random() - 0.5) * 2200; break; } // Add health bar to asteroid asteroid._healthBar = new HealthBar(); asteroid._healthBar.set({ maxValue: function maxValue() { return asteroid.health; }, getValueFn: function getValueFn() { return asteroid.health; }, getMaxFn: function getMaxFn() { return 50; }, width: 50, height: 8 }); asteroid._healthBar.y = -50; asteroid.addChild(asteroid._healthBar); asteroids.push(asteroid); game.addChild(asteroid); } // Spawn an upgrade station at a random edge and add it to the world function spawnUpgradeStation() { // Artık UpgradeStation yerine DerelictDebris (enkaz) spawn ediyoruz var debris = new DerelictDebris(); // Spawn from edges of visible area var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: debris.x = ship.x + (Math.random() - 0.5) * 1800; debris.y = ship.y - 1400; break; case 1: debris.x = ship.x + 1400; debris.y = ship.y + (Math.random() - 0.5) * 1800; break; case 2: debris.x = ship.x + (Math.random() - 0.5) * 1800; debris.y = ship.y + 1400; break; case 3: debris.x = ship.x - 1400; debris.y = ship.y + (Math.random() - 0.5) * 1800; break; } upgradeStations.push(debris); game.addChild(debris); } // No trade stations in this version function createExplosion(x, y) { // %95 küçült: 8 * 0.05 = 0.4, en az 1 partikül bırakıyoruz for (var i = 0; i < 1; i++) { var particle = LK.getAsset('explosion', { anchorX: 0.5, anchorY: 0.5, x: x, y: y, scaleX: 0.22, // %95 küçült (1 - 0.95 = 0.05, yani 0.05x, ama çok küçük olur, 0.22 ile 8 partikülün toplam alanı korunur) scaleY: 0.22 }); particle.vx = (Math.random() - 0.5) * 2; // Hızı da azalt, daha küçük efekt için particle.vy = (Math.random() - 0.5) * 2; particle.life = 12; // Daha kısa ömür explosions.push(particle); game.addChild(particle); } } // No black holes or wormholes in this version function updateUI() { coinText.setText('Coins: ' + playerCoins); // Keep playerMetal/playerEnergy in sync with playerResources playerMetal = playerResources[0]; playerEnergy = playerResources[1]; // Use sorted resource label order for display if (window.resourceLabels && window.resourceLabelOrder && window.resourceLabels.length === window.resourceLabelOrder.length) { for (var i = 0; i < window.resourceLabelOrder.length; i++) { var resObj = window.resourceLabelOrder[i]; window.resourceLabels[i].setText(resObj.name + ': ' + (playerResources[resObj.origIdx] || 0)); } } // (No market price display) var healthPercent = Math.round(ship.health / ship.maxHealth * 100); healthText.setText('Health: %' + healthPercent); var shieldPercent = Math.round(ship.shield / ship.maxShield * 100); shieldText.setText('Shield: %' + shieldPercent); // Restore wave and exp UI if (typeof waveText !== "undefined") { waveText.setText('Wave: ' + (waveNumber + 1)); } if (typeof expText !== "undefined") { expText.setText('Level: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel)); } waveText.setText('Wave: ' + (waveNumber + 1)); expText.setText('Level: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel)); // Show player level/exp in GUI (top right, above repText) if (!window.levelText) { window.levelText = new Text2('Seviye: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel), { size: unifiedFontSize, fill: 0xffffff }); window.levelText.anchor.set(1, 0); window.levelText.x = -20; window.levelText.y = 20; // moved up by one row (40px) LK.gui.topRight.addChild(window.levelText); } window.levelText.setText('Seviye: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel)); // Update player level/exp in top right if (window.levelText) { window.levelText.setText('Seviye: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel)); } // Update health text color if (ship.health > 60) { healthText.fill = 0x00ff00; } else if (ship.health > 30) { healthText.fill = 0xffff00; } else { healthText.fill = 0xff0000; } // Update shield text color if (ship.shield > 30) { shieldText.fill = 0x0088ff; } else if (ship.shield > 10) { shieldText.fill = 0xffff00; } else { shieldText.fill = 0xff0000; } storage.playerLevel = playerLevel; storage.playerEXP = playerEXP; } function saveProgress() { storage.coins = playerCoins; var resourceKeys = ['metal', 'energy', 'crystal', 'gas', 'ice', 'uranium', 'silicon', 'carbon', 'plasma', 'antimatter']; for (var i = 0; i < resourceKeys.length; i++) { storage[resourceKeys[i]] = playerResources[i] || 0; } // Keep playerMetal/playerEnergy in sync with playerResources playerMetal = playerResources[0]; playerEnergy = playerResources[1]; storage.questsCompleted = questsCompleted; } // Event handlers game.down = function (x, y, obj) { // --- BLOCK ALL TAPS IF ANY PANEL IS OPEN (fixes tap blocking bug) --- // --- ALSO BLOCK ALL INPUT IF SHIP IS DEAD --- if (window.stationDeliveryPanel && window.stationDeliveryPanelBG && window.stationDeliveryPanelBG.parent || window.resourceTradePanelState && window.resourceTradePanelState.open || window._resourceStockPanel && window._resourceStockPanel.bg && window._resourceStockPanel.bg.parent || window.shipUpgradePanel && window.shipUpgradePanel.parent || window.repairPanel && window.repairPanel.parent || ship && ship.health <= 0) { // If a panel is open or ship is dead, only allow taps on that panel's close/confirm buttons (handled below), block all other taps // (Panel-specific tap logic is handled further down, so just return here to block all other UI/joystick/game taps) // Exception: let panel close/confirm buttons work (handled in their own blocks) // If ship is dead, block all input until respawn label is gone if (ship && ship.health <= 0) { return; } // (Do not return here, let the rest of the handler run for panel close/confirm buttons) } else { // --- PAUSE BUTTON (top left) tap detection: always allow pause tap, never block it --- if (x < 100 && y < 100) { // This is the reserved area for the platform pause/menu button. // Let the LK engine handle the pause/menu tap, do not block it. // Do not return here, let the engine handle it. } // --- GLOBAL MARKET BUTTON HANDLER (exact image area) --- if (window.globalMarketBtn && window.globalMarketBtn.parent) { var btn = window.globalMarketBtn; var bx = btn.x, by = btn.y; var bw = btn.width || 320, bh = btn.height || 80; // LK.gui.bottomRight is anchored at (1,1), so x,y negative from bottom right // The area is anchored at (1,1), so its (x,y) is the bottom right corner of the area // So the area covers (bx-bw, by-bh) to (bx, by) if (x > 2048 + bx - bw && x < 2048 + bx && y > 2732 + by - bh && y < 2732 + by) { window.openResourceTradePanel(); return; } } // --- CREW PANEL BUTTON HANDLER (just below Achievements image) --- if (window.newImageAboveStorage) { // Place a transparent tap area over the newImageAboveStorage image if not already present if (!window.crewPanelBtn) { var btn = new Text2("", { size: 1, fill: 0xffffff, align: "center" }); btn.anchor.set(1, 0); btn.x = window.newImageAboveStorage.x; // Move the Crew panel button to exactly the same y as the image btn.y = window.newImageAboveStorage.y; // Grow the button downward by the image's height (so it covers 2x the image height, starting at image.y) btn.width = window.newImageAboveStorage.width; btn.height = window.newImageAboveStorage.height * 2; btn.alpha = 0; window.crewPanelBtn = btn; LK.gui.topRight.addChild(btn); } var btn = window.crewPanelBtn; var bx = btn.x, by = btn.y; var bw = btn.width, bh = btn.height; // LK.gui.topRight is anchored at (1,0), so x negative from right, y positive from top if (x > 2048 + bx - bw && x < 2048 + bx && y > by && y < by + bh) { window.openCrewPanel(); return; } } // --- ACHIEVEMENTS PANEL BUTTON HANDLER (Achievements image tap) --- if (window.achievementsPanelBtn && window.achievementsPanelBtn.parent) { var btn = window.achievementsPanelBtn; var bx = btn.x, by = btn.y; var bw = btn.width, bh = btn.height; // LK.gui.topRight is anchored at (1,0), so x negative from right, y positive from top // So the area covers (2048+bx-bw, by) to (2048+bx, by+bh) if (x > 2048 + bx - bw && x < 2048 + bx && y > by && y < by + bh) { window.openAchievementsPanel(1); return; } } // --- CREW PANEL HANDLER --- if (window.crewPanelBG) { // Close btn if (window.crewPanelCloseBtn) { var bx = window.crewPanelCloseBtn.x, by = window.crewPanelCloseBtn.y; var bw = window.crewPanelCloseBtn.width || 300, bh = window.crewPanelCloseBtn.height || 80; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { window.closeCrewPanel(); return; } } // Friend satın al btns if (window.crewFriendBtns) { for (var i = 0; i < window.crewFriendBtns.length; i++) { var btn = window.crewFriendBtns[i]; var bx = btn.x, by = btn.y; var bw = btn.width || 220, bh = btn.height || 60; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { var idx = btn._friendIdx; if (!friends[idx] && playerCoins >= 500) { playerCoins -= 500; // Create Friend instance (see class below) var friend = new Friend(); friend.x = ship.x + 80 + 60 * idx; friend.y = ship.y + 80; friends[idx] = friend; game.addChild(friend); btn.setText("Purchased"); btn.fill = 0x888888; if (window.crewFriendLabels && window.crewFriendLabels[idx]) { window.crewFriendLabels[idx].setText("Friend #" + (idx + 1) + " (Purchased)"); window.crewFriendLabels[idx].fill = 0x00ff99; } updateUI && updateUI(); saveProgress && saveProgress(); } else if (friends[idx]) { btn.setText("Already Purchased"); btn.fill = 0x888888; } else if (playerCoins < 500) { btn.setText("Insufficient coins!"); btn.fill = 0xff4444; } // Reset label after short delay (function (b, orig, origFill) { LK.setTimeout(function () { b.setText(friends[idx] ? "Purchased" : "Buy (500 coin)"); b.fill = friends[idx] ? 0x888888 : 0x00ff99; }, 900); })(btn, "Buy (500 coin)", 0x00ff99); return; } } } // Dron satın al btn if (window.crewPanelDronBtn) { var btn = window.crewPanelDronBtn; var bx = btn.x, by = btn.y; var bw = btn.width || 220, bh = btn.height || 60; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { if (!dron && playerCoins >= 1200) { playerCoins -= 1200; dron = new Dron(); dron.x = ship.x - 100; dron.y = ship.y + 80; game.addChild(dron); btn.setText("Purchased"); btn.fill = 0x888888; if (window.crewPanelDronLabel) { window.crewPanelDronLabel.setText("Dron: Purchased"); window.crewPanelDronLabel.fill = 0x00ff99; } updateUI && updateUI(); saveProgress && saveProgress(); } else if (dron) { btn.setText("Already Purchased"); btn.fill = 0x888888; } else if (playerCoins < 1200) { btn.setText("Insufficient coins!"); btn.fill = 0xff4444; } (function (b, orig, origFill) { LK.setTimeout(function () { b.setText(dron ? "Purchased" : "Buy (1200 coin)"); b.fill = dron ? 0x888888 : 0x00ff99; }, 900); })(btn, "Buy (1200 coin)", 0x00ff99); return; } } // Mechanic satın al btn if (window.crewPanelMechanicBtn) { var btn = window.crewPanelMechanicBtn; var bx = btn.x, by = btn.y; var bw = btn.width || 220, bh = btn.height || 60; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { if (!window.mechanic && playerCoins >= 800) { playerCoins -= 800; window.mechanic = new Mechanic(); window.mechanic.x = ship.x - 120; window.mechanic.y = ship.y - 80; game.addChild(window.mechanic); btn.setText("Purchased"); btn.fill = 0x888888; if (window.crewPanelMechanicLabel) { window.crewPanelMechanicLabel.setText("Mechanic: Purchased"); window.crewPanelMechanicLabel.fill = 0x00ff99; } updateUI && updateUI(); saveProgress && saveProgress(); } else if (window.mechanic) { btn.setText("Already Purchased"); btn.fill = 0x888888; } else if (playerCoins < 800) { btn.setText("Insufficient coins!"); btn.fill = 0xff4444; } (function (b, orig, origFill) { LK.setTimeout(function () { b.setText(window.mechanic ? "Purchased" : "Buy (800 coin)"); b.fill = window.mechanic ? 0x888888 : 0x00ff99; }, 900); })(btn, "Buy (800 coin)", 0x00ff99); return; } } // Block all other taps when panel is open return; } // --- UPGRADE PANEL BUTTON HANDLER (upg image tap) --- if (window.upgPanelBtn && window.upgPanelBtn.parent) { var btn = window.upgPanelBtn; var bx = btn.x, by = btn.y; var bw = btn.width, bh = btn.height; // LK.gui.topRight is anchored at (1,0), so x negative from right, y positive from top // So the area covers (2048+bx-bw, by) to (2048+bx, by+bh) if (x > 2048 + bx - bw && x < 2048 + bx && y > by && y < by + bh) { window.openUpgradesPanel(); return; } } } // --- RESOURCE TRADE PANEL INDEPENDENT BUTTON HANDLER --- // If the independent button exists, check tap if (window.resourceTradePanelIndependentBtn && window.resourceTradePanelIndependentBtn.parent) { var btn = window.resourceTradePanelIndependentBtn; var bx = btn.x, by = btn.y; var bw = btn.width, bh = btn.height; // LK.gui.bottomRight is anchored at (1,1), so x,y negative from bottom right // The area is anchored at (1,1), so its (x,y) is the bottom right corner of the area // So the area covers (bx-bw, by-bh) to (bx, by) if (x > 2048 + bx - bw && x < 2048 + bx && y > 2732 + by - bh && y < 2732 + by) { // Open 'Hammadde Stoğu' panel if (typeof window.openResourceStockPanel === "function") { window.openResourceStockPanel(); } else { // Fallback: show a simple panel with player's resource stock if (window._resourceStockPanel) { return; } var panelW = 900, panelH = 900; var px = 2048 / 2, py = 2732 / 2; // Increase font size by 2 for all panel texts/buttons var panelFontSize = unifiedFontSize + 4; var labelFontSize = panelFontSize; var closeBtnFontSize = panelFontSize; var titleFontSize = panelFontSize; // Resize panel background to fit enlarged text/buttons var rowH = 70 + 8; var panelW = 900 + 120; var panelH = 900 + 2 * rowH; var bg = LK.getAsset('resource', { anchorX: 0.5, anchorY: 0.5, x: px, y: py, scaleX: panelW / 40, scaleY: panelH / 40, tint: 0x223355, alpha: 0.97 }); var title = new Text2("Hammadde Stoğu", { size: titleFontSize, fill: 0x00ffcc, align: "center" }); title.anchor.set(0.5, 0); title.x = px; title.y = py - panelH / 2 + 40; // List resources var labels = []; var startY = title.y + 80; for (var i = 0; i < window.resourceLabelOrder.length; i++) { var resObj = window.resourceLabelOrder[i]; var idx = resObj.origIdx; var label = new Text2(resObj.name + ": " + (playerResources[idx] || 0), { size: labelFontSize, fill: 0xffffff, align: "left" }); label.anchor.set(0, 0.5); label.x = px - panelW / 2 + 60; label.y = startY + i * rowH; labels.push(label); } // Close button var closeBtn = new Text2("Kapat", { size: Math.round((unifiedFontSize + 4) * 1.3), // 2 font larger than Coins, then 30% larger fill: 0xff4444, align: "center" }); closeBtn.anchor.set(0.5, 0.5); closeBtn.x = px; closeBtn.y = title.y - 110; // above the title // Add to game game.addChild(bg); game.addChild(title); for (var i = 0; i < labels.length; i++) { game.addChild(labels[i]); } game.addChild(closeBtn); // Store refs for tap logic/cleanup window._resourceStockPanel = { bg: bg, title: title, labels: labels, closeBtn: closeBtn }; // Add close logic closeBtn._isResourceStockCloseBtn = true; } return; } } // --- RESOURCE TRADE PANEL HANDLER --- if (window._resourceStockPanel && window._resourceStockPanel.closeBtn) { var btn = window._resourceStockPanel.closeBtn; var bx = btn.x, by = btn.y; var bw = btn.width || 300, bh = btn.height || 80; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { // Remove all panel elements if (window._resourceStockPanel.bg && window._resourceStockPanel.bg.parent) { window._resourceStockPanel.bg.parent.removeChild(window._resourceStockPanel.bg); } if (window._resourceStockPanel.title && window._resourceStockPanel.title.parent) { window._resourceStockPanel.title.parent.removeChild(window._resourceStockPanel.title); } if (window._resourceStockPanel.labels) { for (var i = 0; i < window._resourceStockPanel.labels.length; i++) { if (window._resourceStockPanel.labels[i] && window._resourceStockPanel.labels[i].parent) { window._resourceStockPanel.labels[i].parent.removeChild(window._resourceStockPanel.labels[i]); } } } if (window._resourceStockPanel.closeBtn && window._resourceStockPanel.closeBtn.parent) { window._resourceStockPanel.closeBtn.parent.removeChild(window._resourceStockPanel.closeBtn); } window._resourceStockPanel = null; return; } } if (window.resourceTradePanelState && window.resourceTradePanelState.open) { // Close btn if (window.resourceTradePanelCloseBtn) { var bx = window.resourceTradePanelCloseBtn.x, by = window.resourceTradePanelCloseBtn.y; var bw = window.resourceTradePanelCloseBtn.width || 300, bh = window.resourceTradePanelCloseBtn.height || 80; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { window.closeResourceTradePanel(); return; } } // Sat btns if (window.resourceTradeSellBtns) { for (var i = 0; i < window.resourceTradeSellBtns.length; i++) { var btn = window.resourceTradeSellBtns[i]; var bx = btn.x, by = btn.y; var bw = btn.width || 120, bh = btn.height || 60; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { var idx = btn._resourceIdx; var amount = playerResources[idx] || 0; var value = window.resourceTradeValues ? window.resourceTradeValues[idx] : 0; if (amount > 0 && value > 0) { playerCoins += value * amount; playerResources[idx] = 0; // --- BAŞARIM: Takas yapınca --- checkTradeAchievements(); btn.setText("Satıldı!"); btn.fill = 0x00ffcc; if (window.updateResourceTradePanel) { window.updateResourceTradePanel(); } updateUI(); saveProgress(); } else { btn.setText("Yok!"); btn.fill = 0xff4444; } // Reset label after short delay (function (b, orig, origFill) { LK.setTimeout(function () { b.setText("Sat"); b.fill = 0x00ff99; }, 700); })(btn, "Sat", 0x00ff99); return; } } } // Tümünü Sat btns if (window.resourceTradeSellAllBtns) { for (var i = 0; i < window.resourceTradeSellAllBtns.length; i++) { var btn = window.resourceTradeSellAllBtns[i]; var bx = btn.x, by = btn.y; var bw = btn.width || 180, bh = btn.height || 60; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { var idx = btn._resourceIdx; var amount = playerResources[idx] || 0; var value = window.resourceTradeValues ? window.resourceTradeValues[idx] : 0; if (amount > 0 && value > 0) { playerCoins += value * amount; playerResources[idx] = 0; btn.setText("Hepsi Satıldı!"); btn.fill = 0x00ffcc; if (window.updateResourceTradePanel) { window.updateResourceTradePanel(); } updateUI(); saveProgress(); } else { btn.setText("Yok!"); btn.fill = 0xff4444; } (function (b, orig, origFill) { LK.setTimeout(function () { b.setText("Tümünü Sat"); b.fill = 0x00cc99; }, 700); })(btn, "Tümünü Sat", 0x00cc99); return; } } } // Al btns if (window.resourceTradeBuyBtns) { for (var i = 0; i < window.resourceTradeBuyBtns.length; i++) { var btn = window.resourceTradeBuyBtns[i]; var bx = btn.x, by = btn.y; var bw = btn.width || 120, bh = btn.height || 60; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { var idx = btn._resourceIdx; var value = window.resourceTradeValues ? window.resourceTradeValues[idx] : 0; // Satın alma miktarı: 1 (veya istenirse artırılabilir) var buyAmount = 1; var totalCost = value * buyAmount; if (playerCoins >= totalCost && value > 0) { playerCoins -= totalCost; playerResources[idx] = (playerResources[idx] || 0) + buyAmount; btn.setText("Alındı!"); btn.fill = 0x00ffcc; if (window.updateResourceTradePanel) { window.updateResourceTradePanel(); } updateUI(); saveProgress(); } else { btn.setText("Yetersiz coin!"); btn.fill = 0xff4444; } (function (b, orig, origFill) { LK.setTimeout(function () { b.setText("Al"); b.fill = 0x0099ff; }, 700); })(btn, "Al", 0x0099ff); return; } } } // Block all other taps when panel is open return; } // --- UZAY İSTASYONU TESLİMAT BUTTON HANDLER --- // Handle tap on stationDeliveryBtnArea (transparent area, exactly image size) if (window.stationDeliveryBtnArea && window.stationDeliveryBtnArea.parent) { var btn = window.stationDeliveryBtnArea; var bx = btn.x, by = btn.y; var bw = btn.width, bh = btn.height; // LK.gui.bottomRight is anchored at (1,1), so x,y negative from bottom right // The area is anchored at (1,1), so its (x,y) is the bottom right corner of the area // So the area covers (bx-bw, by-bh) to (bx, by) if (x > 2048 + bx - bw && x < 2048 + bx && y > 2732 + by - bh && y < 2732 + by) { window.openStationDeliveryPanel(); return; } } // LK.gui.bottomRight is anchored at (1,1), so x,y negative from bottom right if (window.stationDeliveryBtn && window.stationDeliveryBtn.parent) { var btn = window.stationDeliveryBtn; var bx = btn.x, by = btn.y; // Button is about 180x80 px, so check if x is within 200px of right edge and y within 300-480px of bottom if (x > 2048 - 200 && y > 2732 - 300 && y < 2732 - 100) { window.openStationDeliveryPanel(); return; } } // --- UZAY İSTASYONU TESLİMAT PANEL HANDLER --- if (window.stationDeliveryPanel) { // Close btn if (window.stationDeliveryPanelCloseBtn) { var bx = window.stationDeliveryPanelCloseBtn.x, by = window.stationDeliveryPanelCloseBtn.y; var bw = window.stationDeliveryPanelCloseBtn.width || 300, bh = window.stationDeliveryPanelCloseBtn.height || 80; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { window.closeStationDeliveryPanel(); return; } } // Teslim Et btns if (window.stationDeliveryDeliverBtns) { for (var i = 0; i < window.stationDeliveryDeliverBtns.length; i++) { var btn = window.stationDeliveryDeliverBtns[i]; var bx = btn.x, by = btn.y; var bw = btn.width || 120, bh = btn.height || 60; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { var idx = btn._stationResIdx; var req = window.stationDeliveryState.requests[idx]; var delivered = window.stationDeliveryState.delivered[idx]; var canDeliver = Math.min(playerResources[idx], req - delivered); if (canDeliver > 0) { playerResources[idx] -= canDeliver; window.stationDeliveryState.delivered[idx] += canDeliver; btn.setText("Teslim Edildi!"); btn.fill = 0x00ffcc; // Update label if (window.stationDeliveryLabels && window.stationDeliveryLabels[i]) { window.stationDeliveryLabels[i].setText(window.resourceLabelOrder[i].name + ": " + window.stationDeliveryState.delivered[idx] + " / " + req); } // Update stock label if (window.stationDeliveryStockLabels && window.stationDeliveryStockLabels[i]) { var stockAmount = typeof playerResources[idx] !== "undefined" ? playerResources[idx] : 0; window.stationDeliveryStockLabels[i].setText("Stok: " + stockAmount); } updateUI(); saveProgress(); } else { btn.setText("Yok!"); btn.fill = 0xff4444; } // Reset label after short delay (function (b, orig, origFill) { LK.setTimeout(function () { b.setText(orig); b.fill = origFill; }, 700); })(btn, "Teslim Et", 0x00ff99); return; } } } // Teslimatı Tamamla (ödül al) butonu if (window.stationDeliveryPanelClaimBtn) { var btn = window.stationDeliveryPanelClaimBtn; var bx = btn.x, by = btn.y; var bw = btn.width || 400, bh = btn.height || 80; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { // Check if all delivered var allDone = true; for (var i = 0; i < 10; i++) { if (window.stationDeliveryState.delivered[i] < window.stationDeliveryState.requests[i]) { allDone = false; break; } } if (allDone && !window.stationDeliveryState.rewardClaimed) { // Calculate total value (normal market price * delivered) * (1+bonus%) var totalValue = 0; for (var i = 0; i < 10; i++) { var price = 1; // Market removed, use static price totalValue += price * window.stationDeliveryState.requests[i]; } var bonus = window.stationDeliveryState.bonus; var reward = Math.round(totalValue * (1 + bonus / 100)); playerCoins += reward; window.stationDeliveryState.rewardClaimed = true; btn.setText("Ödül Alındı! +" + reward + " coin"); btn.fill = 0x00ffcc; // Show floating label var label = new Text2("İstasyon Teslimatı Tamamlandı!\n+" + reward + " coin", { size: 48, fill: 0xffcc00, align: "center" }); label.anchor.set(0.5, 0.5); label.x = 1024; label.y = 600; game.addChild(label); LK.setTimeout(function () { if (label.parent) { label.parent.removeChild(label); } }, 1800); updateUI(); saveProgress(); } else if (window.stationDeliveryState.rewardClaimed) { btn.setText("Zaten Alındı!"); btn.fill = 0x888888; } else { btn.setText("Eksik Teslimat!"); btn.fill = 0xff4444; } // Reset label after short delay (function (b, orig, origFill) { LK.setTimeout(function () { b.setText("Tümünü Teslim Et & Ödülü Al"); b.fill = 0xffcc00; }, 1200); })(btn, "Tümünü Teslim Et & Ödülü Al", 0xffcc00); return; } } // Block all other taps when panel is open return; } // --- RESET BUTTON HANDLER --- if (game._resetBtn && game._resetBtn.parent) { // Convert GUI coordinates for bottomRight // LK.gui.bottomRight is anchored at (1,1) so x,y are negative from bottom right var btn = game._resetBtn; var bx = btn.x, by = btn.y; // Button is about 180x80 px, but let's use a safe area // Since anchor is (1,1), bottom right, and x/y are negative from bottom right // So, for a tap, check if x is within 200px of right edge and y within 100px of bottom // LK.gui.bottomRight: x=0,y=0 is bottom right, so x,y negative if (x > 2048 - 200 && y > 2732 - 100) { // Clear all persistent storage, including achievements and all custom progress storage.coins = 200; // Başlangıçta 200 coin ile başla storage.metal = 0; storage.energy = 0; storage.crystal = 0; storage.gas = 0; storage.ice = 0; storage.uranium = 0; storage.silicon = 0; storage.carbon = 0; storage.plasma = 0; storage.antimatter = 0; storage.questsCompleted = 0; storage.playerLevel = 1; storage.playerEXP = 0; // Clear all achievements storage.achievements = []; // Clear all custom progress keys storage.rangeUpgradeLevel = 0; storage.fireRateUpgradeLevel = 0; // Remove any other custom keys that may have been set // (defensive: remove all keys except built-in ones) for (var k in storage) { if (storage.hasOwnProperty(k) && ["coins", "metal", "energy", "crystal", "gas", "ice", "uranium", "silicon", "carbon", "plasma", "antimatter", "questsCompleted", "playerLevel", "playerEXP", "achievements", "rangeUpgradeLevel", "fireRateUpgradeLevel"].indexOf(k) === -1) { try { storage[k] = 0; } catch (e) {} } } // Show a quick feedback btn.setText("Reset!"); btn.fill = 0x00ff99; // Reload game (reset all progress) LK.showGameOver(); return; } } // --- NPC ile savaş butonu handler --- if (window.npcFightBtn) { var bx = window.npcFightBtn.x, by = window.npcFightBtn.y; var bw = window.npcFightBtn.width || 400, bh = window.npcFightBtn.height || 100; // Accept generous tap area if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { window.npcFightMode = !window.npcFightMode; if (window.npcFightMode) { window.npcFightBtn.setText("NPC Savaş: AÇIK"); window.npcFightBtn.fill = 0xff0000; } else { window.npcFightBtn.setText("NPC ile Savaş"); window.npcFightBtn.fill = 0xff4444; } return; } } if (window.repairPanel && window.repairBtn && window.repairBtn.parent) { // Check tap on repair button var btn = window.repairBtn; var bx = btn.x, by = btn.y; if (x > bx - 90 && x < bx + 90 && y > by - 40 && y < by + 40) { // Try to repair var costCoins = btn._repairCostCoins, costMetal = btn._repairCostMetal, costEnergy = btn._repairCostEnergy; if (playerCoins >= costCoins && playerMetal >= costMetal && playerEnergy >= costEnergy) { playerCoins -= costCoins; playerMetal -= costMetal; playerEnergy -= costEnergy; ship.health = ship.maxHealth; // --- BAŞARIM: Tamir --- checkRepairAchievements(); updateUI(); saveProgress(); // Show feedback btn.setText("Tamir Edildi!"); btn.fill = 0x00ffcc; // Remove panel after short delay var expire = LK.ticks + 40; var origUpdate = game.update; game.update = function () { if (LK.ticks > expire) { if (window.repairPanel && window.repairPanel.parent) { window.repairPanel.parent.removeChild(window.repairPanel); } if (window.repairText && window.repairText.parent) { window.repairText.parent.removeChild(window.repairText); } if (window.repairBtn && window.repairBtn.parent) { window.repairBtn.parent.removeChild(window.repairBtn); } if (window.cancelRepairBtn && window.cancelRepairBtn.parent) { window.cancelRepairBtn.parent.removeChild(window.cancelRepairBtn); } window.repairPanel = null; window.repairText = null; window.repairBtn = null; window.cancelRepairBtn = null; game.update = origUpdate; } if (origUpdate) { origUpdate.apply(game, arguments); } }; } else { // Not enough resources btn.setText("Yetersiz kaynak!"); btn.fill = 0xff4444; } return; } // Check tap on cancel button var cbtn = window.cancelRepairBtn; if (cbtn) { var cbx = cbtn.x, cby = cbtn.y; if (x > cbx - 90 && x < cbx + 90 && y > cby - 40 && y < cby + 40) { // Remove all repair panel elements, allow closing even if not repaired if (window.repairPanel && window.repairPanel.parent) { window.repairPanel.parent.removeChild(window.repairPanel); } if (window.repairText && window.repairText.parent) { window.repairText.parent.removeChild(window.repairText); } if (window.repairBtn && window.repairBtn.parent) { window.repairBtn.parent.removeChild(window.repairBtn); } if (window.cancelRepairBtn && window.cancelRepairBtn.parent) { window.cancelRepairBtn.parent.removeChild(window.cancelRepairBtn); } window.repairPanel = null; window.repairText = null; window.repairBtn = null; window.cancelRepairBtn = null; window._showRepairPanel = false; return; } } } // --- Health bar tap to show repair panel --- if (healthText && healthText.parent && healthText._isHealthBar && ship.health < ship.maxHealth && ship.health > 0) { // Get healthText bounds in screen coordinates // healthText is anchored at (0.5, 0), at top center // Let's estimate its bounds var hx = healthText.x, hy = healthText.y; var hw = healthText.width || 300; var hh = healthText.height || 60; // Accept generous tap area if (x > hx - hw / 2 && x < hx + hw / 2 && y > hy && y < hy + hh) { window._showRepairPanel = true; return; } } // --- SHIP TAP: Show upgrade/can-buy-health panel --- // Oyuncu gemisi resmi üzerindeki buton GİZLENDİ. Artık bu panel gemi resmine tıklanarak açılamaz. // (Bu alanı bilerek boş bırakıyoruz, panel açılmayacak.) // --- Only allow joystick drag if ship is alive --- if (ship && ship.health > 0 && Math.sqrt(Math.pow(x - joystickBase.x, 2) + Math.pow(y - joystickBase.y, 2)) < 130) { isDragging = true; joystickHandle.x = x; joystickHandle.y = y; return; // Prevent other interactions when dragging joystick } // --- If any panel is open, block all other game/ship input (except panel close/confirm buttons) --- if (window.stationDeliveryPanel && window.stationDeliveryPanelBG && window.stationDeliveryPanelBG.parent || window.resourceTradePanelState && window.resourceTradePanelState.open || window._resourceStockPanel && window._resourceStockPanel.bg && window._resourceStockPanel.bg.parent || window.shipUpgradePanel && window.shipUpgradePanel.parent || window.repairPanel && window.repairPanel.parent) { // Only allow panel close/confirm buttons (handled below), block all other game/ship input // (Do not return here, let the rest of the handler run for panel close/confirm buttons) } else { // --- SHIP UPGRADE PANEL BUTTONS --- if (window.shipUpgradePanel) { // (existing ship upgrade panel logic unchanged) // ... (no change here) } // --- ACHIEVEMENTS PANEL BUTTONS --- if (window.achievementsPanelBG) { // Close button var cb = window.achievementsPanelCloseBtn; if (cb) { var bx = cb.x, by = cb.y; var bw = cb.width || 300, bh = cb.height || 80; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { window.closeAchievementsPanel(); return; } } // Page number buttons var pageBtns = window.achievementsPanelPageBtns; if (pageBtns) { for (var i = 0; i < pageBtns.length; i++) { var btn = pageBtns[i]; var bx = btn.x, by = btn.y; var bw = btn.width || 90, bh = btn.height || 70; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { if (window._achievementsPanelPage !== btn._achPageBtn) { window.closeAchievementsPanel(); window.openAchievementsPanel(btn._achPageBtn); } return; } } } // Block all other taps when panel is open return; } // --- UPGRADE PANEL BUTTONS --- if (window.upgradesPanel) { // Upgrade buttons var btns = window.upgradesPanelBtns || []; for (var i = 0; i < btns.length; i++) { var btn = btns[i]; if (!btn) { continue; } var bx = btn.x, by = btn.y; var bw = btn.width || 400, bh = btn.height || 80; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { // Try to buy upgrade var upg = btn._upgradeType; var cost = btn._upgradeCost; if (upg === "range" && playerCoins >= cost.coins) { // Increase attack range by 5% if (typeof window._rangeUpgradeLevel === "undefined") { window._rangeUpgradeLevel = 0; } window._rangeUpgradeLevel++; // Store in storage for persistence storage.rangeUpgradeLevel = window._rangeUpgradeLevel; playerCoins -= cost.coins; LK.effects.flashObject(ship, 0x99ff99, 400); btn.setText("Alındı!"); btn.fill = 0x00ffcc; // --- BAŞARIM: Upgrade satın alma --- checkUpgradeAchievements(); updateUI(); saveProgress(); } else if (upg === "heal5" && playerCoins >= cost.coins) { // Heal 5% of max health, but not above max var healAmount = Math.round(ship.maxHealth * 0.05); ship.health = Math.min(ship.health + healAmount, ship.maxHealth); playerCoins -= cost.coins; LK.effects.flashObject(ship, 0xff6699, 400); btn.setText("Yenilendi!"); btn.fill = 0x00ffcc; // --- BAŞARIM: Tamir --- checkRepairAchievements(); updateUI(); saveProgress(); } else if (upg === "maxHealth" && playerCoins >= cost.coins && playerResources[0] >= cost.metal) { ship.maxHealth += 20; ship.health = ship.maxHealth; playerCoins -= cost.coins; playerResources[0] -= cost.metal; LK.effects.flashObject(ship, 0x00ff99, 400); btn.setText("Alındı!"); btn.fill = 0x00ffcc; // --- BAŞARIM: Upgrade satın alma --- checkUpgradeAchievements(); updateUI(); saveProgress(); } else if (upg === "maxShield" && playerCoins >= cost.coins && playerResources[0] >= cost.metal) { ship.maxShield += 10; ship.shield = ship.maxShield; playerCoins -= cost.coins; playerResources[0] -= cost.metal; LK.effects.flashObject(ship, 0x00ccff, 400); btn.setText("Alındı!"); btn.fill = 0x00ffcc; // --- BAŞARIM: Upgrade satın alma --- checkUpgradeAchievements(); updateUI(); saveProgress(); } else if (upg === "damage" && playerCoins >= cost.coins && playerResources[1] >= cost.energy) { ship.damage += 5; playerCoins -= cost.coins; playerResources[1] -= cost.energy; LK.effects.flashObject(ship, 0xffcc00, 400); btn.setText("Alındı!"); btn.fill = 0x00ffcc; // --- BAŞARIM: Upgrade satın alma --- checkUpgradeAchievements(); updateUI(); saveProgress(); } else if (upg === "speed" && playerCoins >= cost.coins) { ship.speed = Math.round(ship.speed * 1.1 * 100) / 100; playerCoins -= cost.coins; LK.effects.flashObject(ship, 0x00ffff, 400); btn.setText("Alındı!"); btn.fill = 0x00ffcc; // --- BAŞARIM: Upgrade satın alma --- checkUpgradeAchievements(); updateUI(); saveProgress(); } else if (upg === "fireRate") { if (typeof window._fireRateUpgradeLevel === "undefined") { window._fireRateUpgradeLevel = 0; } if (window._fireRateUpgradeLevel >= 5) { btn.setText("Maks. seviye!"); btn.fill = 0xff4444; LK.setTimeout(function () { btn.setText("Atış Hızı +10%\n(1200 coin)"); btn.fill = 0xff99ff; }, 900); } else if (playerCoins >= cost.coins) { ship.fireRate = Math.max(2, Math.round(ship.fireRate * 0.9)); playerCoins -= cost.coins; window._fireRateUpgradeLevel++; storage.fireRateUpgradeLevel = window._fireRateUpgradeLevel; LK.effects.flashObject(ship, 0xff99ff, 400); btn.setText("Alındı!"); btn.fill = 0x00ffcc; updateUI(); saveProgress(); } else { btn.setText("Yetersiz kaynak!"); btn.fill = 0xff4444; LK.setTimeout(function () { btn.setText("Atış Hızı +10%\n(1200 coin)"); btn.fill = 0xff99ff; }, 900); } } else { var origText = ""; var origFill = btn.fill; if (upg === "maxHealth") { origText = "Max Can +20\n(600 coin, 60 metal)"; origFill = 0x00ff99; } else if (upg === "maxShield") { origText = "Max Kalkan +10\n(720 coin, 72 metal)"; origFill = 0x00ccff; } else if (upg === "damage") { origText = "Hasar +5\n(900 coin, 90 enerji)"; origFill = 0xffcc00; } else if (upg === "speed") { origText = "Hız +10%\n(1000 coin)"; origFill = 0x00ffff; } else if (upg === "fireRate") { origText = "Atış Hızı +10%\n(1200 coin)"; origFill = 0xff99ff; } else { origText = btn.text; origFill = btn.fill; } btn.setText("Yetersiz kaynak!"); btn.fill = 0xff4444; // Reset label after short delay LK.setTimeout(function () { btn.setText(origText); btn.fill = origFill; }, 900); } return; } } // Close button var cb = window.upgradesPanelCloseBtn; if (cb) { var bx = cb.x, by = cb.y; var bw = cb.width || 300, bh = cb.height || 80; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { window.closeUpgradesPanel(); return; } } } // --- Handle Accept/Reject quest button taps --- if (game._questDecisionAcceptBtn && game._questDecisionAcceptBtn.parent) { var btn = game._questDecisionAcceptBtn; var bx = btn.x, by = btn.y; // Button is about 180x80 px if (x > bx - 90 && x < bx + 90 && y > by - 40 && y < by + 40) { // Accept quest var npc = btn._questNPC; if (npc) { // Defend quest özel: yardım kabul edildi if (npc.questType === 'defend') { npc._defendHelpAccepted = true; // Show floating label for accepted var acceptLabel = new Text2("Yardım kabul edildi!", { size: 36, fill: 0x00ff99, align: "center" }); acceptLabel.anchor.set(0.5, 0.5); acceptLabel.x = npc.x; acceptLabel.y = npc.y + 110; game.addChild(acceptLabel); npc._npcInteractionLabel = acceptLabel; npc._npcInteractionLabelExpire = LK.ticks + 120; npc.pauseUntil = LK.ticks + 120; npc._npcText && npc._npcText.parent && npc._npcText.parent.removeChild(npc._npcText); npc._npcText = new Text2(npc.rpgName + ": Teşekkürler! Beni korsanlardan koru!", { size: 32, fill: 0x00ffcc, align: "center" }); npc._npcText.anchor.set(0.5, 1); npc._npcText.x = npc.x; npc._npcText.y = npc.y - 70; game.addChild(npc._npcText); } else { // Mark quest as accepted (progress 1 means started) npc.questProgress = 1; // Show NPC response var acceptLabel = new Text2("Görev kabul edildi!", { size: 36, fill: 0x00ff99, align: "center" }); acceptLabel.anchor.set(0.5, 0.5); acceptLabel.x = npc.x; acceptLabel.y = npc.y + 110; game.addChild(acceptLabel); npc._npcInteractionLabel = acceptLabel; npc._npcInteractionLabelExpire = LK.ticks + 120; npc.pauseUntil = LK.ticks + 120; npc._npcText && npc._npcText.parent && npc._npcText.parent.removeChild(npc._npcText); npc._npcText = new Text2(npc.rpgName + ": Harika! Görev başladı.", { size: 32, fill: 0x00ffcc, align: "center" }); npc._npcText.anchor.set(0.5, 1); npc._npcText.x = npc.x; npc._npcText.y = npc.y - 70; game.addChild(npc._npcText); } // Remove quest panel if (npc._questDecisionPanel && npc._questDecisionPanel.parent) { npc._questDecisionPanel.parent.removeChild(npc._questDecisionPanel); } if (npc._questDecisionText && npc._questDecisionText.parent) { npc._questDecisionText.parent.removeChild(npc._questDecisionText); } if (npc._questDecisionAcceptBtn && npc._questDecisionAcceptBtn.parent) { npc._questDecisionAcceptBtn.parent.removeChild(npc._questDecisionAcceptBtn); } if (npc._questDecisionRejectBtn && npc._questDecisionRejectBtn.parent) { npc._questDecisionRejectBtn.parent.removeChild(npc._questDecisionRejectBtn); } npc._questDecisionPanel = null; npc._questDecisionText = null; npc._questDecisionAcceptBtn = null; npc._questDecisionRejectBtn = null; game._questDecisionPanel = null; game._questDecisionAcceptBtn = null; game._questDecisionRejectBtn = null; game._questDecisionText = null; game._questDecisionNPC = null; } return; } } if (game._questDecisionRejectBtn && game._questDecisionRejectBtn.parent) { var btn = game._questDecisionRejectBtn; var bx = btn.x, by = btn.y; if (x > bx - 90 && x < bx + 90 && y > by - 40 && y < by + 40) { // Reject quest var npc = btn._questNPC; if (npc) { if (npc.questType === 'defend') { npc._defendHelpAccepted = false; // Show floating label for rejected var rejectLabel = new Text2("Yardım edilmedi.", { size: 36, fill: 0xff4444, align: "center" }); rejectLabel.anchor.set(0.5, 0.5); rejectLabel.x = npc.x; rejectLabel.y = npc.y + 110; game.addChild(rejectLabel); npc._npcInteractionLabel = rejectLabel; npc._npcInteractionLabelExpire = LK.ticks + 120; npc.pauseUntil = LK.ticks + 120; npc._npcText && npc._npcText.parent && npc._npcText.parent.removeChild(npc._npcText); npc._npcText = new Text2(npc.rpgName + ": Anlaşıldı, kendi başıma savaşacağım!", { size: 32, fill: 0x00ffcc, align: "center" }); npc._npcText.anchor.set(0.5, 1); npc._npcText.x = npc.x; npc._npcText.y = npc.y - 70; game.addChild(npc._npcText); } else { // Mark quest as inactive npc.questActive = false; // Show floating label for rejected var rejectLabel = new Text2("Görev reddedildi.", { size: 36, fill: 0xff4444, align: "center" }); rejectLabel.anchor.set(0.5, 0.5); rejectLabel.x = npc.x; rejectLabel.y = npc.y + 110; game.addChild(rejectLabel); npc._npcInteractionLabel = rejectLabel; npc._npcInteractionLabelExpire = LK.ticks + 120; npc.pauseUntil = LK.ticks + 120; npc._npcText && npc._npcText.parent && npc._npcText.parent.removeChild(npc._npcText); npc._npcText = new Text2(npc.rpgName + ": Belki başka zaman!", { size: 32, fill: 0x00ffcc, align: "center" }); npc._npcText.anchor.set(0.5, 1); npc._npcText.x = npc.x; npc._npcText.y = npc.y - 70; game.addChild(npc._npcText); } // Remove quest panel if (npc._questDecisionPanel && npc._questDecisionPanel.parent) { npc._questDecisionPanel.parent.removeChild(npc._questDecisionPanel); } if (npc._questDecisionText && npc._questDecisionText.parent) { npc._questDecisionText.parent.removeChild(npc._questDecisionText); } if (npc._questDecisionAcceptBtn && npc._questDecisionAcceptBtn.parent) { npc._questDecisionAcceptBtn.parent.removeChild(npc._questDecisionAcceptBtn); } if (npc._questDecisionRejectBtn && npc._questDecisionRejectBtn.parent) { npc._questDecisionRejectBtn.parent.removeChild(npc._questDecisionRejectBtn); } npc._questDecisionPanel = null; npc._questDecisionText = null; npc._questDecisionAcceptBtn = null; npc._questDecisionRejectBtn = null; game._questDecisionPanel = null; game._questDecisionAcceptBtn = null; game._questDecisionRejectBtn = null; game._questDecisionText = null; game._questDecisionNPC = null; } return; } } // Check for NPC tap (RPG/adventure/trade) for (var i = 0; i < npcs.length; i++) { var npc = npcs[i]; var dist = Math.sqrt(Math.pow(npc.x - ship.x, 2) + Math.pow(npc.y - ship.y, 2)); if (dist < 180) { game._lastNPCTap = LK.ticks; break; } } // --- Distress event tap detection --- if (window.distressEvent && window.distressEvent.helpBtn && window.distressEvent.ignoreBtn) { game._lastDistressTapTick = LK.ticks; game._lastDistressTapX = x; game._lastDistressTapY = y; } } }; game.move = function (x, y, obj) { // Only allow joystick drag if ship is alive if (isDragging && ship && ship.health > 0) { var dx = x - joystickBase.x; var dy = y - joystickBase.y; var distance = Math.sqrt(dx * dx + dy * dy); // Clamp handle to joystick base radius (130px for 30% larger size) if (distance > 130) { dx = dx / distance * 130; dy = dy / distance * 130; } joystickHandle.x = joystickBase.x + dx; joystickHandle.y = joystickBase.y + dy; } }; game.up = function (x, y, obj) { // Only allow joystick release if ship is alive isDragging = false; if (ship && ship.health > 0) { // Snap handle back to base joystickHandle.x = joystickBase.x; joystickHandle.y = joystickBase.y; } }; // Main game loop game.update = function () { // --- PERIODIC WORLD CLEANUP TO PREVENT LAG --- // Every 600 ticks (~10 seconds), remove excess or orphaned entities if (typeof window._lastWorldCleanupTick === "undefined") { window._lastWorldCleanupTick = LK.ticks; } if (LK.ticks - window._lastWorldCleanupTick > 600) { // Defensive: Remove destroyed, undefined, or offscreen objects from all arrays var cleanupArray = function cleanupArray(arr, maxCount, offscreenDist) { if (!arr) { return; } for (var i = arr.length - 1; i >= 0; i--) { var obj = arr[i]; // Remove if destroyed, undefined, or missing x/y if (!obj || typeof obj.x !== "number" || typeof obj.y !== "number" || obj._destroyed) { if (obj && typeof obj.destroy === "function") { obj.destroy(); } arr.splice(i, 1); continue; } // Remove if far offscreen (relative to ship) if (typeof ship !== "undefined" && ship && typeof ship.x === "number" && typeof ship.y === "number" && typeof offscreenDist === "number") { var dx = obj.x - ship.x; var dy = obj.y - ship.y; if (Math.sqrt(dx * dx + dy * dy) > offscreenDist) { if (typeof obj.destroy === "function") { obj.destroy(); } arr.splice(i, 1); continue; } } } // Clamp array to maxCount (remove oldest if too many) if (typeof maxCount === "number" && arr.length > maxCount) { for (var j = arr.length - 1; j >= maxCount; j--) { if (arr[j] && typeof arr[j].destroy === "function") { arr[j].destroy(); } arr.splice(j, 1); } } }; // Clean up all major arrays // Remove any nulls from arrays (defensive) var compactArray = function compactArray(arr) { if (!arr) { return; } for (var i = arr.length - 1; i >= 0; i--) { if (!arr[i]) { arr.splice(i, 1); } } }; cleanupArray(bullets, 100, 3000); cleanupArray(enemyBullets, 100, 3000); cleanupArray(pirates, 30, 4000); cleanupArray(npcs, 20, 4000); cleanupArray(asteroids, 10, 4000); cleanupArray(coins, 30, 4000); cleanupArray(resources, 30, 4000); cleanupArray(stars, 200, 6000); cleanupArray(upgradeStations, 5, 6000); cleanupArray(explosions, 30, 4000); cleanupArray(pirateBases, 5, 6000); compactArray(bullets); compactArray(enemyBullets); compactArray(pirates); compactArray(npcs); compactArray(asteroids); compactArray(coins); compactArray(resources); compactArray(stars); compactArray(upgradeStations); compactArray(explosions); compactArray(pirateBases); window._lastWorldCleanupTick = LK.ticks; } // --- SHIP MOVEMENT SYSTEM --- // Defensive: always reset deltas var shipDX = 0; var shipDY = 0; // Block all ship movement and input if ship is dead or just respawned if (ship && ship.health <= 0) { // Do not move ship, do not process joystick, do not update camera, do not fire, etc. // Optionally, you could add a "dead" animation or effect here. // (Handled by respawn logic) return; } else if (typeof window._lastRespawnTick !== "undefined" && LK.ticks - window._lastRespawnTick < 60) { // Block all input/movement for 1 second after respawn return; } else { // Block ship movement if ANY panel is open (but always allow joystick to move) if (!(window.stationDeliveryPanel && window.stationDeliveryPanelBG && window.stationDeliveryPanelBG.parent) && !(window.resourceTradePanelState && window.resourceTradePanelState.open) && !(window._resourceStockPanel && window._resourceStockPanel.bg && window._resourceStockPanel.bg.parent) && !(window.shipUpgradePanel && window.shipUpgradePanel.parent) && !(window.repairPanel && window.repairPanel.parent)) { // Only move if dragging joystick if (isDragging) { var dx = joystickHandle.x - joystickBase.x; var dy = joystickHandle.y - joystickBase.y; var distance = Math.sqrt(dx * dx + dy * dy); // Only move if joystick is moved enough if (distance > 10) { // Normalize direction, scale by ship speed and joystick displacement var norm = Math.min(distance, 130) / 130; var effectiveSpeed = ship.speed * norm; shipDX = dx / distance * effectiveSpeed; shipDY = dy / distance * effectiveSpeed; // Rotate ship to face movement direction ship.rotation = Math.atan2(dy, dx) + Math.PI / 2; } } } // Actually move the ship's logical position, but keep it visually centered ship.x += shipDX; ship.y += shipDY; } // Track total distance traveled totalDistance += Math.sqrt(shipDX * shipDX + shipDY * shipDY); // --- Keep ship always visually centered --- // Calculate the offset needed to keep the ship at the center of the screen var centerX = 2048 / 2; var centerY = 2732 / 2; var offsetX = centerX - ship.x; var offsetY = centerY - ship.y; // Visually place the ship at the center ship.x = centerX; ship.y = centerY; // Move all world objects by the offset so the ship appears to move through the world for (var i = 0; i < stars.length; i++) { stars[i].x += offsetX; stars[i].y += offsetY; } for (var i = 0; i < npcs.length; i++) { npcs[i].x += offsetX; npcs[i].y += offsetY; } for (var i = 0; i < pirates.length; i++) { pirates[i].x += offsetX; pirates[i].y += offsetY; } for (var i = 0; i < coins.length; i++) { coins[i].x += offsetX; coins[i].y += offsetY; } for (var i = 0; i < resources.length; i++) { resources[i].x += offsetX; resources[i].y += offsetY; } for (var i = 0; i < bullets.length; i++) { bullets[i].x += offsetX; bullets[i].y += offsetY; } for (var i = 0; i < enemyBullets.length; i++) { enemyBullets[i].x += offsetX; enemyBullets[i].y += offsetY; } for (var i = 0; i < asteroids.length; i++) { asteroids[i].x += offsetX; asteroids[i].y += offsetY; } for (var i = 0; i < upgradeStations.length; i++) { upgradeStations[i].x += offsetX; upgradeStations[i].y += offsetY; } // --- DerelictDebris (enkaz) toplama ve fade-out sistemi --- for (var i = upgradeStations.length - 1; i >= 0; i--) { var debris = upgradeStations[i]; if (debris && debris.type === 'derelict' && !debris._collected && debris.intersects(ship)) { debris._collected = true; // 10 hammadde için rastgele bir tip seç var resourceTypes = ['metal', 'energy', 'crystal', 'gas', 'ice', 'uranium', 'silicon', 'carbon', 'plasma', 'antimatter']; var idx = Math.floor(Math.random() * resourceTypes.length); if (debris._resourceAmount > 0) { playerResources[idx] += debris._resourceAmount; // Başarım kontrolü checkResourceCollectAchievements && checkResourceCollectAchievements(); // Kısa bir label göster var label = new Text2("+" + debris._resourceAmount + " " + resourceTypes[idx], { size: 36, fill: 0x00ff00, align: "center" }); label.anchor.set(0.5, 0.5); label.x = ship.x; label.y = ship.y - 120; game.addChild(label); LK.setTimeout(function () { if (label.parent) { label.parent.removeChild(label); } }, 900); updateUI && updateUI(); saveProgress && saveProgress(); } // Fade-out başlat debris._fadeStartTick = LK.ticks; debris._expireTick = debris._fadeStartTick + 60; // 1 saniyede kaybolsun } // Fade-out tamamlandıysa sil if (debris && debris._shouldRemove) { debris.destroy(); upgradeStations.splice(i, 1); } } // boostParticles removed (no boost system) for (var i = 0; i < explosions.length; i++) { explosions[i].x += offsetX; explosions[i].y += offsetY; } // Auto fire at very close pirates or asteroids // Saldırı mesafesi %10 arttırıldı (200 -> 220) // +%5 menzil yükseltmeleri uygula var baseRange = 350; var rangeBonus = (typeof window._rangeUpgradeLevel !== "undefined" ? window._rangeUpgradeLevel : storage.rangeUpgradeLevel || 0) * 0.05; var ATTACK_RANGE = Math.round(baseRange * (1 + rangeBonus)); // %10 daha fazla menzil Katil Korsan ve GrupKorsan'a karşı var ATTACK_RANGE_PIRATE_SPECIAL = Math.round(ATTACK_RANGE * 1.1); if (LK.ticks - ship.lastFire > ship.fireRate && (pirates.length > 0 || asteroids.length > 0)) { // Find nearest pirate or asteroid within 220 pixels var nearestTarget = null; var nearestDistance = Infinity; var isPirate = false; // Check pirates for (var i = 0; i < pirates.length; i++) { var pirate = pirates[i]; var dist = Math.sqrt(Math.pow(pirate.x - ship.x, 2) + Math.pow(pirate.y - ship.y, 2)); var isSpecialPirate = pirate._isKatil === true || pirate._isGrupKorsan === true; var rangeToUse = isSpecialPirate ? ATTACK_RANGE_PIRATE_SPECIAL : ATTACK_RANGE; if (dist < rangeToUse && dist < nearestDistance) { nearestDistance = dist; nearestTarget = pirate; isPirate = true; } } // Check asteroids for (var i = 0; i < asteroids.length; i++) { var dist = Math.sqrt(Math.pow(asteroids[i].x - ship.x, 2) + Math.pow(asteroids[i].y - ship.y, 2)); if (dist < ATTACK_RANGE && dist < nearestDistance) { nearestDistance = dist; nearestTarget = asteroids[i]; isPirate = false; } } if (nearestTarget) { var bullet = new Bullet(); bullet.x = ship.x; bullet.y = ship.y; var angle = Math.atan2(nearestTarget.y - ship.y, nearestTarget.x - ship.x); bullet.directionX = Math.cos(angle); bullet.directionY = Math.sin(angle); bullet.rotation = angle + Math.PI / 2; // Store last attack angle for ship rotation game._lastShipAttackAngle = angle; if (typeof ship !== "undefined") { ship._lastMoveAngle = angle; } bullets.push(bullet); game.addChild(bullet); ship.lastFire = LK.ticks; // Calculate distance from ship to bullet spawn point and adjust volume var dx = bullet.x - ship.x; var dy = bullet.y - ship.y; var distanceFromShip = Math.sqrt(dx * dx + dy * dy); // Calculate volume based on distance (closer = louder, farther = quieter) // Max distance for sound falloff is 500 pixels var maxSoundDistance = 500; var volumeMultiplier = Math.max(0.1, Math.min(1.0, 1.0 - distanceFromShip / maxSoundDistance)); // Get the sound and play with adjusted volume var shootSound = LK.getSound('shoot'); var originalVolume = shootSound.volume || 0.3; // fallback to default volume shootSound.volume = originalVolume * volumeMultiplier; shootSound.play(); // Restore original volume for next play shootSound.volume = originalVolume; // --- FRIENDS: Saldırı başlat --- // Her friend, 1 saniye sonra saldırmaya başlar ve oyuncu saldırıyı bırakana kadar devam eder for (var i = 0; i < friends.length; i++) { if (friends[i] && typeof friends[i].startAttack === "function") { // Eğer zaten bu hedefe saldırıyorsa, tekrar başlatma if (!friends[i]._attacking || friends[i]._attackTarget !== nearestTarget) { friends[i]._attackDelay = 60; // 1 saniye gecikme (her saldırı başlatıldığında sıfırla) friends[i].startAttack(nearestTarget); } } } } else { // --- FRIENDS: Saldırı bırak --- for (var i = 0; i < friends.length; i++) { if (friends[i] && typeof friends[i].stopAttack === "function") { friends[i].stopAttack(); } } } } // Update bullets for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; if (!bullet || typeof bullet.x === "undefined" || typeof bullet.y === "undefined") { // Defensive: remove undefined or destroyed bullet if (bullet && typeof bullet.destroy === "function") { bullet.destroy(); } bullets.splice(i, 1); continue; } // Remove if marked for removal by fade-out if (bullet._shouldRemove) { bullet.destroy(); bullets.splice(i, 1); continue; } // (No longer destroy instantly when out of range, fade-out will handle it) // Check collision with pirates for (var j = pirates.length - 1; j >= 0; j--) { if (bullet.intersects(pirates[j])) { // Small explosion effect at collision createExplosion(bullet.x, bullet.y); pirates[j].takeDamage(bullet.damage); var killedByPlayer = !bullet._npcBullet; var killedByNPC = !!bullet._npcBullet; if (pirates[j].health <= 0) { enemiesKilled++; createExplosion(pirates[j].x, pirates[j].y); // --- BAŞARIM: Korsan öldürme --- if (killedByPlayer) { checkPirateKillAchievements(); // Katil korsan öldürme if (pirates[j]._isKatil) { checkKatilKillAchievement(); } } // Drop loot var coin = new Coin(); coin.x = pirates[j].x; coin.y = pirates[j].y; // Katil korsan ise 10 kat coin ver if (pirates[j]._isKatil) { coin.value = coin.value * (1 + Math.floor(waveNumber / 3)) * 10; // Katil korsan öldü, tekrar spawn edilebilir window._katilPirateSpawned = false; // Katil korsan öldükten veya yok olduğunda tekrar spawn için timer başlat if (typeof window._katilPirateRespawnTimer !== "undefined" && window._katilPirateRespawnTimer) { LK.clearTimeout(window._katilPirateRespawnTimer); } window._katilPirateRespawnTimer = LK.setTimeout(function () { if (!window._katilPirateSpawned) { // Katil korsanı tekrar spawn et var pirate = new Pirate(); // Katil korsan statlarını tekrar ayarla var minLevel = 1 + waveNumber * 2; pirate.level = minLevel + 10; pirate.health = (30 + (pirate.level - 1) * 10) * 10; pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10; pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10; pirate.rpgName = "Katil"; pirate._isKatil = true; pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3)); pirate._attackRange = Math.floor(600 * 0.9); // Katil korsan için %10 daha kısa menzil window._katilPirateSpawned = true; // Diğer korsanlar gibi kenar spawn algoritmasıyla doğsun var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: // Top pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y - 1400; break; case 1: // Right pirate.x = ship.x + 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; case 2: // Bottom pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y + 1400; break; case 3: // Left pirate.x = ship.x - 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; } // Add health bar to katil pirate pirate._healthBar = new HealthBar(); pirate._healthBar.set({ maxValue: function maxValue() { return pirate.health > 0 ? pirate.health : 0; }, getValueFn: function getValueFn() { return pirate.health > 0 ? pirate.health : 0; }, getMaxFn: function getMaxFn() { return 30 + waveNumber * 10; }, width: 60, height: 10 }); pirate._healthBar.y = -60; pirate.addChild(pirate._healthBar); pirates.push(pirate); game.addChild(pirate); } window._katilPirateRespawnTimer = null; }, 6000); // 6 saniye sonra tekrar spawn // Katil korsan yok olduğunda da tekrar spawn edilmesini sağla for (var i = pirates.length - 1; i >= 0; i--) { var p = pirates[i]; if (p && p._isKatil && (p._destroyed || typeof p.x === "undefined" || typeof p.y === "undefined")) { // Katil korsan yok olduysa, tekrar spawn için timer başlat window._katilPirateSpawned = false; if (typeof window._katilPirateRespawnTimer !== "undefined" && window._katilPirateRespawnTimer) { LK.clearTimeout(window._katilPirateRespawnTimer); } window._katilPirateRespawnTimer = LK.setTimeout(function () { if (!window._katilPirateSpawned) { var pirate = new Pirate(); var minLevel = 1 + waveNumber * 2; pirate.level = minLevel + 10; pirate.health = (30 + (pirate.level - 1) * 10) * 10; pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10; pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10; pirate.rpgName = "Katil"; pirate._isKatil = true; pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3)); pirate._attackRange = Math.floor(600 * 0.9); // Katil korsan için %10 daha kısa menzil window._katilPirateSpawned = true; var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y - 1400; break; case 1: pirate.x = ship.x + 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; case 2: pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y + 1400; break; case 3: pirate.x = ship.x - 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; } pirate._healthBar = new HealthBar(); pirate._healthBar.set({ maxValue: function maxValue() { return pirate.health > 0 ? pirate.health : 0; }, getValueFn: function getValueFn() { return pirate.health > 0 ? pirate.health : 0; }, getMaxFn: function getMaxFn() { return 30 + waveNumber * 10; }, width: 60, height: 10 }); pirate._healthBar.y = -60; pirate.addChild(pirate._healthBar); pirates.push(pirate); game.addChild(pirate); } window._katilPirateRespawnTimer = null; }, 6000); break; // Sadece bir kere tetiklenmeli } } // Katil etiketiyle özel bir label göster var katilLabel = new Text2("KATİL KORSAN ÖLDÜRÜLDÜ!\n+10x coin", { size: 40, fill: 0xff0000, align: "center" }); katilLabel.anchor.set(0.5, 0.5); katilLabel.x = pirates[j].x; katilLabel.y = pirates[j].y - 120; game.addChild(katilLabel); LK.setTimeout(function () { if (katilLabel.parent) { katilLabel.parent.removeChild(katilLabel); } }, 1500); } else { coin.value = coin.value * (1 + Math.floor(waveNumber / 3)); } coins.push(coin); game.addChild(coin); // GrupKorsan öldüyse %25 ihtimalle CoinBox düşür if (pirates[j]._isGrupKorsan && Math.random() < 0.25) { var coinBox = new CoinBox(); coinBox.x = pirates[j].x + (Math.random() - 0.5) * 40; coinBox.y = pirates[j].y + (Math.random() - 0.5) * 40; coins.push(coinBox); // CoinBox da coins array'ine eklenir, toplama ve fade-out için game.addChild(coinBox); // Kısa bir label göster var boxLabel = new Text2("COINBOX!", { size: 32, fill: 0xffd700, align: "center" }); boxLabel.anchor.set(0.5, 0.5); boxLabel.x = coinBox.x; boxLabel.y = coinBox.y - 60; game.addChild(boxLabel); LK.setTimeout(function () { if (boxLabel.parent) { boxLabel.parent.removeChild(boxLabel); } }, 900); } if (Math.random() < 0.3) { var resource = new Resource(); resource.x = pirates[j].x + (Math.random() - 0.5) * 50; resource.y = pirates[j].y + (Math.random() - 0.5) * 50; resources.push(resource); game.addChild(resource); } // Remove KILL label after 2 seconds if present if (pirates[j]._killLabel) { // If not already expiring, set expire tick if (!pirates[j]._killLabel._expireTick) { pirates[j]._killLabel._expireTick = LK.ticks + 120; } } // Remove KILL label if expired if (pirates[j]._killLabel && pirates[j]._killLabel._expireTick && LK.ticks > pirates[j]._killLabel._expireTick) { if (pirates[j]._killLabel.parent) { pirates[j]._killLabel.parent.removeChild(pirates[j]._killLabel); } pirates[j]._killLabel = null; } if (killedByPlayer) { // Oyuncu öldürdü: sadece exp kazan var pirateExp = 3 + Math.floor(Math.random() * 3); playerEXP += pirateExp; // Level up if enough EXP while (playerEXP >= expToNext(playerLevel)) { playerEXP -= expToNext(playerLevel); playerLevel++; LK.effects.flashObject(ship, 0x00ffcc, 800); checkLevelAchievements(); } } else if (killedByNPC) { // NPC öldürdü: yoluna devam etsin, rep/exp yok // Kısa bir etiket göster (isteğe bağlı) var npcLabel = new Text2("NPC KILL", { size: 28, fill: 0xccccff, align: "center" }); npcLabel.anchor.set(0.5, 0.5); npcLabel.x = pirates[j].x; npcLabel.y = pirates[j].y - 80; game.addChild(npcLabel); // Fade out and remove after 700ms LK.setTimeout(function () { if (npcLabel.parent) { npcLabel.parent.removeChild(npcLabel); } }, 700); } // Update UI after EXP gain updateUI(); pirates[j].destroy(); pirates.splice(j, 1); // Check for wave completion if (enemiesKilled >= 10 + waveNumber * 5) { waveNumber++; enemiesKilled = 0; // --- BAŞARIM: Dalga geçme --- checkWaveAchievements(); // Spawn upgrade station every 3 waves if (waveNumber % 3 === 0) { spawnUpgradeStation(); } } } bullet.destroy(); bullets.splice(i, 1); LK.getSound('hit').play(); break; } } // --- PLAYER CAN ATTACK NPCs IF FIGHT MODE IS ON --- if (window.npcFightMode) { for (var j = npcs.length - 1; j >= 0; j--) { var npc = npcs[j]; if (bullet.intersects(npc)) { // Small explosion effect at collision createExplosion(bullet.x, bullet.y); if (typeof npc.health === "undefined") { npc.health = 100; } npc.health -= bullet.damage; LK.effects.flashObject(npc, 0xff0000, 120); // Floating label for NPC hit var npcHitLabel = new Text2("NPC HIT", { size: 24, fill: 0xff8888, align: "center" }); npcHitLabel.anchor.set(0.5, 0.5); npcHitLabel.x = npc.x; npcHitLabel.y = npc.y - 60; game.addChild(npcHitLabel); LK.setTimeout(function (label) { if (label.parent) { label.parent.removeChild(label); } }.bind(null, npcHitLabel), 600); // Remove NPC if dead if (npc.health <= 0) { // Floating label for NPC kill var npcKillLabel = new Text2("NPC KILL", { size: 28, fill: 0xccccff, align: "center" }); npcKillLabel.anchor.set(0.5, 0.5); npcKillLabel.x = npc.x; npcKillLabel.y = npc.y - 80; game.addChild(npcKillLabel); LK.setTimeout(function (label) { if (label.parent) { label.parent.removeChild(label); } }.bind(null, npcKillLabel), 700); // Oyuncu NPC öldürdü: coin kaybı uygula var coinLoss = 10 + Math.floor(Math.random() * 41); // 10-50 arası coin kaybı playerCoins = Math.max(0, playerCoins - coinLoss); // Kısa bir etiket göster var coinLossLabel = new Text2("-" + coinLoss + " coin", { size: 28, fill: 0xff4444, align: "center" }); coinLossLabel.anchor.set(0.5, 0.5); coinLossLabel.x = npc.x; coinLossLabel.y = npc.y - 120; game.addChild(coinLossLabel); LK.setTimeout(function (label) { if (label.parent) { label.parent.removeChild(label); } }, 900, coinLossLabel); updateUI(); // Remove hostile label if present if (npc._hostileLabel && npc._hostileLabel.parent) { npc._hostileLabel.parent.removeChild(npc._hostileLabel); npc._hostileLabel = null; } // Instantly remove Düşman! label if it exists (defensive, in case above missed) if (npc._hostileLabel && npc._hostileLabel.parent) { npc._hostileLabel.parent.removeChild(npc._hostileLabel); npc._hostileLabel = null; } npc.destroy(); npcs.splice(j, 1); } bullet.destroy(); bullets.splice(i, 1); LK.getSound('hit').play(); break; } } } // Check collision with asteroids for (var j = asteroids.length - 1; j >= 0; j--) { if (bullet.intersects(asteroids[j])) { // Small explosion effect at collision createExplosion(bullet.x, bullet.y); asteroids[j].takeDamage(bullet.damage); if (asteroids[j].health <= 0) { // --- BAŞARIM: Asteroit patlatma --- checkAsteroidKillAchievements(); // Drop multiple resources (reduced by 80%) var dropCount = Math.max(1, Math.floor(asteroids[j].resources * 0.2)); for (var k = 0; k < dropCount; k++) { var resource = new Resource(); resource.x = asteroids[j].x + (Math.random() - 0.5) * 100; resource.y = asteroids[j].y + (Math.random() - 0.5) * 100; resource.amount = Math.floor(Math.random() * 3) + 1; resources.push(resource); game.addChild(resource); } createExplosion(asteroids[j].x, asteroids[j].y); asteroids[j].destroy(); asteroids.splice(j, 1); } bullet.destroy(); bullets.splice(i, 1); LK.getSound('hit').play(); break; } } } // Update enemy bullets for (var i = enemyBullets.length - 1; i >= 0; i--) { var bullet = enemyBullets[i]; if (!bullet || typeof bullet.x === "undefined" || typeof bullet.y === "undefined") { // Defensive: remove undefined or destroyed bullet if (bullet && typeof bullet.destroy === "function") { bullet.destroy(); } enemyBullets.splice(i, 1); continue; } // Remove if marked for removal by fade-out if (bullet._shouldRemove) { bullet.destroy(); enemyBullets.splice(i, 1); continue; } // (No longer destroy instantly when out of range, fade-out will handle it) if (bullet.intersects(ship)) { // Small explosion effect at collision createExplosion(bullet.x, bullet.y); ship.takeDamage(bullet.damage); updateUI(); bullet.destroy(); enemyBullets.splice(i, 1); LK.getSound('hit').play(); continue; } // --- NEW: Pirate bullets can hit NPCs --- for (var n = npcs.length - 1; n >= 0; n--) { var npc = npcs[n]; if (bullet.intersects(npc)) { // Small explosion effect at collision createExplosion(bullet.x, bullet.y); // Damage NPC if (typeof npc.health === "undefined") { npc.health = 100; } npc.health -= bullet.damage; LK.effects.flashObject(npc, 0xff0000, 120); // Floating label for NPC hit (optional) var npcHitLabel = new Text2("NPC HIT", { size: 24, fill: 0xff8888, align: "center" }); npcHitLabel.anchor.set(0.5, 0.5); npcHitLabel.x = npc.x; npcHitLabel.y = npc.y - 60; game.addChild(npcHitLabel); LK.setTimeout(function (label) { if (label.parent) { label.parent.removeChild(label); } }.bind(null, npcHitLabel), 600); // Remove NPC if dead if (npc.health <= 0) { // Floating label for NPC kill var npcKillLabel = new Text2("NPC KILL", { size: 28, fill: 0xccccff, align: "center" }); npcKillLabel.anchor.set(0.5, 0.5); npcKillLabel.x = npc.x; npcKillLabel.y = npc.y - 80; game.addChild(npcKillLabel); LK.setTimeout(function (label) { if (label.parent) { label.parent.removeChild(label); } }.bind(null, npcKillLabel), 700); // Remove hostile label if present if (npc._hostileLabel && npc._hostileLabel.parent) { npc._hostileLabel.parent.removeChild(npc._hostileLabel); npc._hostileLabel = null; } // Remove NPC from world npc.destroy(); npcs.splice(n, 1); } bullet.destroy(); enemyBullets.splice(i, 1); LK.getSound('hit').play(); break; } } } // Update pirates for (var i = 0; i < pirates.length; i++) { var pirate = pirates[i]; if (pirate._healthBar && pirate._healthBar.update) { pirate._healthBar.update(); } // --- Pirate AI: Always attack the nearest target (NPC or player) --- var nearestTarget = null; var minDist = Infinity; // Find nearest NPC for (var n = 0; n < npcs.length; n++) { var npc = npcs[n]; var distToNPC = Math.sqrt(Math.pow(npc.x - pirate.x, 2) + Math.pow(npc.y - pirate.y, 2)); if (distToNPC < minDist) { minDist = distToNPC; nearestTarget = npc; } } // Compare with player distance var distToPlayer = Math.sqrt(Math.pow(ship.x - pirate.x, 2) + Math.pow(ship.y - pirate.y, 2)); if (distToPlayer < minDist) { minDist = distToPlayer; nearestTarget = ship; } // If there is a target, move and attack var pirateAttackRange = typeof pirate._attackRange === "number" ? pirate._attackRange : 600; if (nearestTarget && minDist < pirateAttackRange) { // --- 10% mesafe kuralı: hedefe %10 mesafe bırak, iç içe girme --- var targetRadius = 40; // Varsayılan hedef yarıçapı (NPC/ship/pirate boyutu) if (nearestTarget._healthBar && nearestTarget._healthBar._width) { targetRadius = Math.max(targetRadius, nearestTarget._healthBar._width / 2); } var selfRadius = 35; // Korsan yarıçapı if (pirate._healthBar && pirate._healthBar._width) { selfRadius = Math.max(selfRadius, pirate._healthBar._width / 2); } var minBuffer = (minDist + selfRadius + targetRadius) * 0.15; // %15 buffer var minAllowedDist = selfRadius + targetRadius + minBuffer; var angle = Math.atan2(nearestTarget.y - pirate.y, nearestTarget.x - pirate.x); // Sadece minAllowedDist'ten uzaktaysa yaklaş if (minDist > minAllowedDist) { var moveDist = Math.min(pirate.speed, minDist - minAllowedDist); pirate.x += Math.cos(angle) * moveDist; pirate.y += Math.sin(angle) * moveDist; pirate.rotation = angle + Math.PI / 2; } else { // Hedefe çok yaklaştıysa sadece dön, hareket etme pirate.rotation = angle + Math.PI / 2; } // Shoot at target if (LK.ticks - pirate.lastFire > pirate.fireRate) { var bullet = new EnemyBullet(); bullet.x = pirate.x; bullet.y = pirate.y; bullet.directionX = Math.cos(angle); bullet.directionY = Math.sin(angle); bullet.rotation = angle + Math.PI / 2; bullet._pirateBullet = true; enemyBullets.push(bullet); game.addChild(bullet); pirate.lastFire = LK.ticks; // Calculate distance from ship to pirate bullet spawn point and adjust volume var dx = bullet.x - ship.x; var dy = bullet.y - ship.y; var distanceFromShip = Math.sqrt(dx * dx + dy * dy); // Calculate volume based on distance (closer = louder, farther = quieter) // Max distance for sound falloff is 500 pixels var maxSoundDistance = 500; var volumeMultiplier = Math.max(0.1, Math.min(1.0, 1.0 - distanceFromShip / maxSoundDistance)); // Get the sound and play with adjusted volume var shootSound = LK.getSound('shoot'); var originalVolume = shootSound.volume || 0.3; // fallback to default volume shootSound.volume = originalVolume * volumeMultiplier; shootSound.play(); // Restore original volume for next play shootSound.volume = originalVolume; } } else { // Wander randomly pirate.x += Math.cos(pirate.moveAngle) * pirate.speed * 0.5; pirate.y += Math.sin(pirate.moveAngle) * pirate.speed * 0.5; } // Remove pirates that are too far away var dist = Math.sqrt(Math.pow(ship.x - pirate.x, 2) + Math.pow(ship.y - pirate.y, 2)); if (dist > 2000) { // Katil korsan ise tekrar spawn için timer başlat if (pirate._isKatil) { window._katilPirateSpawned = false; if (typeof window._katilPirateRespawnTimer !== "undefined" && window._katilPirateRespawnTimer) { LK.clearTimeout(window._katilPirateRespawnTimer); } window._katilPirateRespawnTimer = LK.setTimeout(function () { if (!window._katilPirateSpawned) { var pirate = new Pirate(); var minLevel = 1 + waveNumber * 2; pirate.level = minLevel + 10; pirate.health = (30 + (pirate.level - 1) * 10) * 10; pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10; pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10; pirate.rpgName = "Katil"; pirate._isKatil = true; pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3)); pirate._attackRange = Math.floor(600 * 0.9); window._katilPirateSpawned = true; var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y - 1400; break; case 1: pirate.x = ship.x + 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; case 2: pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y + 1400; break; case 3: pirate.x = ship.x - 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; } pirate._healthBar = new HealthBar(); pirate._healthBar.set({ maxValue: function maxValue() { return pirate.health > 0 ? pirate.health : 0; }, getValueFn: function getValueFn() { return pirate.health > 0 ? pirate.health : 0; }, getMaxFn: function getMaxFn() { return 30 + waveNumber * 10; }, width: 60, height: 10 }); pirate._healthBar.y = -60; pirate.addChild(pirate._healthBar); pirates.push(pirate); game.addChild(pirate); } window._katilPirateRespawnTimer = null; }, 6000); } pirate.destroy(); pirates.splice(i, 1); i--; } } // Update NPCs for (var i = npcs.length - 1; i >= 0; i--) { var npc = npcs[i]; if (npc._healthBar && npc._healthBar.update) { npc._healthBar.update(); } // Remove NPCs that are too far away var dist = Math.sqrt(Math.pow(npc.x - ship.x, 2) + Math.pow(npc.y - ship.y, 2)); if (dist > 2500) { npc.destroy(); npcs.splice(i, 1); continue; } // Remove any floating text if player is not close if (npc._npcText && npc._npcText.parent) { npc._npcText.parent.removeChild(npc._npcText); npc._npcText = null; } // --- NPC ATTITUDE: Rep sistemi kaldırıldı, sadece NPC Saldırı butonuna göre saldırı --- // Eğer NPC Saldırı aktifse, her durumda NPC'ler oyuncuya saldırır var npcHostile = false; if (window.npcSaldiriBtn && window.npcSaldiriBtn._active) { npcHostile = true; } else { npcHostile = false; } // --- NPC AI: Always attack the nearest target (pirate or player) --- // If NPC Saldırı is PASİF, NPCs never attack the player var nearestTarget = null; var minDist = Infinity; // Find nearest pirate for (var j = 0; j < pirates.length; j++) { var p = pirates[j]; if (p._destroyed) { continue; } var d = Math.sqrt(Math.pow(p.x - npc.x, 2) + Math.pow(p.y - npc.y, 2)); if (d < minDist) { minDist = d; nearestTarget = p; } } // Compare with player distance only if NPC Saldırı is AKTİF var npcSaldiriActive = window.npcSaldiriBtn && window.npcSaldiriBtn._active; if (npcSaldiriActive) { var distToPlayer = Math.sqrt(Math.pow(ship.x - npc.x, 2) + Math.pow(ship.y - npc.y, 2)); if (distToPlayer < minDist) { minDist = distToPlayer; nearestTarget = ship; } } // If there is a target, move and attack if (nearestTarget && minDist < 600) { // --- 10% mesafe kuralı: hedefe %10 mesafe bırak, iç içe girme --- var targetRadius = 40; if (nearestTarget._healthBar && nearestTarget._healthBar._width) { targetRadius = Math.max(targetRadius, nearestTarget._healthBar._width / 2); } var selfRadius = 30; if (npc._healthBar && npc._healthBar._width) { selfRadius = Math.max(selfRadius, npc._healthBar._width / 2); } var minBuffer = (minDist + selfRadius + targetRadius) * 0.15; var minAllowedDist = selfRadius + targetRadius + minBuffer; var dx = nearestTarget.x - npc.x; var dy = nearestTarget.y - npc.y; var d = Math.sqrt(dx * dx + dy * dy); if (d > minAllowedDist) { var moveDist = Math.min(npc.moveSpeed * 1.2, d - minAllowedDist); npc.x += dx / d * moveDist; npc.y += dy / d * moveDist; } // Shoot at target (use NPC bullet fire rate) if (typeof npc._aiAttackTimer === "undefined") { npc._aiAttackTimer = 0; } npc._aiAttackTimer++; // Use the same fire rate as pirates (EnemyBullet speed) var npcFireRate = 8; if (typeof npc.fireRate === "number") { npcFireRate = npc.fireRate; } else { npcFireRate = 40; } if (d < 300 && npc._aiAttackTimer > npcFireRate) { var bullet; var angle = Math.atan2(nearestTarget.y - npc.y, nearestTarget.x - npc.x); if (nearestTarget === ship) { bullet = new EnemyBullet(); bullet._npcBullet = true; } else { bullet = new Bullet(); bullet._npcBullet = true; } bullet.x = npc.x; bullet.y = npc.y; bullet.directionX = Math.cos(angle); bullet.directionY = Math.sin(angle); bullet.rotation = angle + Math.PI / 2; if (nearestTarget === ship) { enemyBullets.push(bullet); } else { bullets.push(bullet); } game.addChild(bullet); npc._aiAttackTimer = 0; // Calculate distance from ship to NPC bullet spawn point and adjust volume var dx = bullet.x - ship.x; var dy = bullet.y - ship.y; var distanceFromShip = Math.sqrt(dx * dx + dy * dy); // Calculate volume based on distance (closer = louder, farther = quieter) // Max distance for sound falloff is 500 pixels var maxSoundDistance = 500; var volumeMultiplier = Math.max(0.1, Math.min(1.0, 1.0 - distanceFromShip / maxSoundDistance)); // Get the sound and play with adjusted volume var shootSound = LK.getSound('shoot'); var originalVolume = shootSound.volume || 0.3; // fallback to default volume shootSound.volume = originalVolume * volumeMultiplier; shootSound.play(); // Restore original volume for next play shootSound.volume = originalVolume; } // Show angry label above NPC if attacking player if (nearestTarget === ship) { if (!npc._hostileLabel || !npc._hostileLabel.parent) { npc._hostileLabel = new Text2("Düşman!", { size: 32, fill: 0xff4444, align: "center" }); npc._hostileLabel.anchor.set(0.5, 0.5); npc._hostileLabel.x = npc.x; npc._hostileLabel.y = npc.y - 80; npc._hostileLabel.alpha = 1; game.addChild(npc._hostileLabel); } if (npc._hostileLabel) { npc._hostileLabel.x = npc.x; npc._hostileLabel.y = npc.y - 80; npc._hostileLabel.alpha = 1; npc._hostileLabel._expireTick = LK.ticks + 60; // 1 second } if (npc._hostileLabel && npc._hostileLabel._expireTick && LK.ticks > npc._hostileLabel._expireTick) { if (npc._hostileLabel.parent) { npc._hostileLabel.parent.removeChild(npc._hostileLabel); } npc._hostileLabel = null; } else if (npc._hostileLabel && npc._hostileLabel._expireTick) { var fadeStart = npc._hostileLabel._expireTick - 20; if (LK.ticks > fadeStart) { npc._hostileLabel.alpha = Math.max(0, (npc._hostileLabel._expireTick - LK.ticks) / 20); } else { npc._hostileLabel.alpha = 1; } } } else { // Remove hostile label if present if (npc._hostileLabel && npc._hostileLabel.parent) { npc._hostileLabel.parent.removeChild(npc._hostileLabel); npc._hostileLabel = null; } } } else { // Remove hostile label if present if (npc._hostileLabel && npc._hostileLabel.parent) { npc._hostileLabel.parent.removeChild(npc._hostileLabel); npc._hostileLabel = null; } } } // Update asteroids for (var i = asteroids.length - 1; i >= 0; i--) { var asteroid = asteroids[i]; if (asteroid._healthBar && asteroid._healthBar.update) { asteroid._healthBar.update(); } var dist = Math.sqrt(Math.pow(asteroid.x - ship.x, 2) + Math.pow(asteroid.y - ship.y, 2)); if (dist > 2500) { asteroid.destroy(); asteroids.splice(i, 1); } } // Update explosion particles for (var i = explosions.length - 1; i >= 0; i--) { var particle = explosions[i]; particle.x += particle.vx; particle.y += particle.vy; particle.vx *= 0.9; particle.vy *= 0.9; particle.life--; particle.alpha = particle.life / 30; particle.scaleX = particle.scaleY = 1 + (30 - particle.life) / 10; if (particle.life <= 0) { particle.destroy(); explosions.splice(i, 1); } } // Collect items for (var i = coins.length - 1; i >= 0; i--) { // Remove if marked for removal by fade-out if (coins[i]._shouldRemove) { coins[i].destroy(); coins.splice(i, 1); continue; } if (coins[i].intersects(ship)) { // CoinBox ise özel toplama efekti ve 50-100 coin ver if (typeof coins[i].value === "number" && coins[i].value >= 50 && coins[i].value <= 100 && coins[i].constructor && coins[i].constructor.name === "CoinBox") { playerCoins += coins[i].value; // Kısa bir label göster var boxLabel = new Text2("+" + coins[i].value + " coin!", { size: 36, fill: 0xffd700, align: "center" }); boxLabel.anchor.set(0.5, 0.5); boxLabel.x = ship.x; boxLabel.y = ship.y - 120; game.addChild(boxLabel); LK.setTimeout(function () { if (boxLabel.parent) { boxLabel.parent.removeChild(boxLabel); } }, 900); coins[i].destroy(); coins.splice(i, 1); LK.getSound('collect').play(); updateUI(); saveProgress(); continue; } // Normal coin ise playerCoins += coins[i].value; coins[i].destroy(); coins.splice(i, 1); LK.getSound('collect').play(); updateUI(); saveProgress(); } } for (var i = resources.length - 1; i >= 0; i--) { // Remove if marked for removal by fade-out if (resources[i]._shouldRemove) { resources[i].destroy(); resources.splice(i, 1); continue; } if (resources[i].intersects(ship)) { // 10 tip için index bul var resourceTypes = ['metal', 'energy', 'crystal', 'gas', 'ice', 'uranium', 'silicon', 'carbon', 'plasma', 'antimatter']; var idx = resourceTypes.indexOf(resources[i].type); if (idx >= 0) { playerResources[idx] += resources[i].amount; // --- BAŞARIM: Hammadde toplama --- checkResourceCollectAchievements(); } resources[i].destroy(); resources.splice(i, 1); LK.getSound('collect').play(); updateUI(); saveProgress(); } } // --- BAŞARIM: Coin biriktirme --- checkCoinAchievements(); // --- BAŞARIM: Seviye atlama --- checkLevelAchievements(); // --- BAŞARIM: Kalkan doldurma --- checkShieldAchievements(); // --- BAŞARIM: Can yükseltme --- checkHealthAchievements(); // --- BAŞARIM: Hız yükseltme --- checkSpeedAchievements(); // --- BAŞARIM: Atış hızı yükseltme --- checkFireRateAchievements(); // --- BAŞARIM: Yol katetme --- checkDistanceAchievements(); // --- BAŞARIM: 30 dakika hayatta kalma --- var elapsedTicks = LK.ticks - gameStartTick; var elapsedSeconds = Math.floor(elapsedTicks / 60); checkSurvive30MinAchievement(elapsedSeconds); // Update stars for (var i = stars.length - 1; i >= 0; i--) { var dist = Math.sqrt(Math.pow(stars[i].x - ship.x, 2) + Math.pow(stars[i].y - ship.y, 2)); if (dist > 2000) { stars[i].destroy(); stars.splice(i, 1); } } // Spawn entities (star spawn frequency is now random interval) if (typeof window._nextStarSpawnTick === "undefined") { window._nextStarSpawnTick = LK.ticks + Math.floor(20 + Math.random() * 60); // 20-80 ticks } if (LK.ticks >= window._nextStarSpawnTick) { spawnStar(true); window._nextStarSpawnTick = LK.ticks + Math.floor(20 + Math.random() * 60); // 20-80 ticks } // Spawn pirates and NPCs at intervals if (pirates.length < 6 && Math.random() < 0.3) { spawnPirate(); } if (npcs.length < 4 && Math.random() < 0.2) { spawnNPC(); } // --- ASTEROID SYSTEM RESTORED --- // Spawn asteroids if less than 3 in the world (50% reduction from 6) // Also halve the spawn rate if (asteroids.length < 3 && Math.random() < 0.0125) { spawnAsteroid(); } // --- GRUP KORSAN SQUAD SYSTEM (at least 1 squad always present, Katil Korsan mantığı ile) --- if (typeof window._grupKorsanSpawned === "undefined") { window._grupKorsanSpawned = false; } if (typeof window._grupKorsanRespawnTimer === "undefined") { window._grupKorsanRespawnTimer = null; } // Count living GrupKorsan pirates var grupKorsanCount = 0; for (var i = 0; i < pirates.length; i++) { if (pirates[i] && pirates[i]._isGrupKorsan) { grupKorsanCount++; } } // If none exist, spawn a new squad (2-4 members) after 6 seconds, like Katil Korsan if (grupKorsanCount === 0 && !window._grupKorsanSpawned && !window._grupKorsanRespawnTimer) { window._grupKorsanRespawnTimer = LK.setTimeout(function () { if (!window._grupKorsanSpawned) { // Pick squad size 2-4 var squadSize = 2 + Math.floor(Math.random() * 3); // 2,3,4 // Spawn from edge like Katil Korsan var edge = Math.floor(Math.random() * 4); var centerX = ship.x, centerY = ship.y; switch (edge) { case 0: centerX = ship.x + (Math.random() - 0.5) * 1800; centerY = ship.y - 1400; break; case 1: centerX = ship.x + 1400; centerY = ship.y + (Math.random() - 0.5) * 1800; break; case 2: centerX = ship.x + (Math.random() - 0.5) * 1800; centerY = ship.y + 1400; break; case 3: centerX = ship.x - 1400; centerY = ship.y + (Math.random() - 0.5) * 1800; break; } spawnPirateSquad(centerX, centerY, squadSize); window._grupKorsanSpawned = true; } window._grupKorsanRespawnTimer = null; }, 6000); } // If all GrupKorsan pirates are dead or removed, allow respawn again if (grupKorsanCount === 0 && window._grupKorsanSpawned) { window._grupKorsanSpawned = false; // If timer is running, clear it to avoid double spawn if (window._grupKorsanRespawnTimer) { LK.clearTimeout(window._grupKorsanRespawnTimer); window._grupKorsanRespawnTimer = null; } } // Update score LK.setScore(playerCoins + questsCompleted * 100 + enemiesKilled * 10 + Math.floor(totalDistance / 100)); if (ship._healthBar && ship._healthBar.update) { ship._healthBar.update(); } updateUI(); // --- AUTO-REPAIR PANEL WHEN HEALTH IS LOW OR HEALTH BAR TAPPED --- // Show repair panel ONLY if explicitly requested (not automatically when health decreases) if (!window.repairPanel && ship.health < ship.maxHealth && ship.health > 0 && window._showRepairPanel && (!window._repairPanelSuppressUntil || LK.ticks > window._repairPanelSuppressUntil)) { // Do nothing: do not auto-open repair panel when health decreases // The repair panel will only open if window._showRepairPanel is set by explicit user action (e.g. health bar tap) } else if (window.repairPanel && ship.health >= ship.maxHealth * 0.7) { // Remove repair panel if health is restored if (window.repairPanel.parent) { window.repairPanel.parent.removeChild(window.repairPanel); } if (window.repairText && window.repairText.parent) { window.repairText.parent.removeChild(window.repairText); } if (window.repairBtn && window.repairBtn.parent) { window.repairBtn.parent.removeChild(window.repairBtn); } if (window.cancelRepairBtn && window.cancelRepairBtn.parent) { window.cancelRepairBtn.parent.removeChild(window.cancelRepairBtn); } window.repairPanel = null; window.repairText = null; window.repairBtn = null; window.cancelRepairBtn = null; window._showRepairPanel = false; } // If health < 50% and not dead, do NOT auto-show repair panel (disabled as per new requirement) // (window._showRepairPanel is only set by explicit user action, e.g. health bar tap) // --- GAME TIME UPDATE --- // Calculate elapsed time in seconds var elapsedTicks = LK.ticks - gameStartTick; var elapsedSeconds = Math.floor(elapsedTicks / 60); var minutes = Math.floor(elapsedSeconds / 60); var seconds = elapsedSeconds % 60; if (typeof gameTimeText !== "undefined") { // Format as mm:ss var timeStr = "Game Time: " + minutes + ":" + (seconds < 10 ? "0" : "") + seconds; gameTimeText.setText(timeStr); // Always keep gameTimeText to the right of coinText gameTimeText.x = coinText.x + coinText.width + 40; gameTimeText.y = coinText.y; } // --- Synchronize LK.ticks with real game time --- // This ensures all time-based systems use real elapsed time, not just frame count LK.ticks = gameStartTick + Math.floor((Date.now() - (window._gameStartRealTime || (window._gameStartRealTime = Date.now()))) / (1000 / 60)); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Asteroid = Container.expand(function () {
var self = Container.call(this);
var asteroidGraphics = self.attachAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x8B4513,
scaleX: 2,
scaleY: 2
});
self.health = 50;
self.resources = Math.floor(Math.random() * 10) + 5;
self.vx = (Math.random() - 0.5) * 0.5;
self.vy = (Math.random() - 0.5) * 0.5;
self.rotationSpeed = (Math.random() - 0.5) * 0.02;
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xffffff, 100);
};
self.update = function () {
self.x += self.vx;
self.y += self.vy;
self.rotation += self.rotationSpeed;
};
return self;
});
// Black Hole Hazard Class
var BlackHole = Container.expand(function () {
var self = Container.call(this);
var bhGraphics = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 8,
scaleY: 8,
tint: 0x222222
});
self.radius = 200;
self.pullStrength = 0.7 + Math.random() * 0.5;
self.damageRadius = 120;
self.update = function () {
// Animate black hole
bhGraphics.rotation += 0.03;
// Pull ship if in range
var dx = ship.x - self.x;
var dy = ship.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < self.radius) {
var pull = (self.radius - dist) / self.radius * self.pullStrength;
ship.x -= dx / dist * pull;
ship.y -= dy / dist * pull;
// Camera follows ship, so move all objects accordingly
for (var i = 0; i < stars.length; i++) {
stars[i].x -= dx / dist * pull;
stars[i].y -= dy / dist * pull;
}
for (var i = 0; i < npcs.length; i++) {
npcs[i].x -= dx / dist * pull;
npcs[i].y -= dy / dist * pull;
}
for (var i = 0; i < pirates.length; i++) {
pirates[i].x -= dx / dist * pull;
pirates[i].y -= dy / dist * pull;
}
for (var i = 0; i < coins.length; i++) {
coins[i].x -= dx / dist * pull;
coins[i].y -= dy / dist * pull;
}
for (var i = 0; i < resources.length; i++) {
resources[i].x -= dx / dist * pull;
resources[i].y -= dy / dist * pull;
}
for (var i = 0; i < bullets.length; i++) {
bullets[i].x -= dx / dist * pull;
bullets[i].y -= dy / dist * pull;
}
for (var i = 0; i < enemyBullets.length; i++) {
enemyBullets[i].x -= dx / dist * pull;
enemyBullets[i].y -= dy / dist * pull;
}
for (var i = 0; i < asteroids.length; i++) {
asteroids[i].x -= dx / dist * pull;
asteroids[i].y -= dy / dist * pull;
}
for (var i = 0; i < upgradeStations.length; i++) {
upgradeStations[i].x -= dx / dist * pull;
upgradeStations[i].y -= dy / dist * pull;
}
// boostParticles removed (no boost system)
for (var i = 0; i < explosions.length; i++) {
explosions[i].x -= dx / dist * pull;
explosions[i].y -= dy / dist * pull;
}
}
// Damage ship if too close
if (dist < self.damageRadius) {
if (!self.lastDamaged || LK.ticks - self.lastDamaged > 30) {
ship.takeDamage(10);
self.lastDamaged = LK.ticks;
LK.effects.flashObject(ship, 0x000000, 200);
}
}
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 15;
self.damage = 10;
self.directionX = 0;
self.directionY = -1;
self._outOfRangeTick = null;
self._fadeStartTick = null;
self._expireTick = null;
self._range = typeof ATTACK_RANGE !== "undefined" ? ATTACK_RANGE : 220;
self._startX = null;
self._startY = null;
self.update = function () {
if (self._startX === null || self._startY === null) {
self._startX = self.x;
self._startY = self.y;
}
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
// Out of range detection: bullet range = attack range
var dx = self.x - self._startX;
var dy = self.y - self._startY;
var dist = Math.sqrt(dx * dx + dy * dy);
var outOfRange = dist > self._range;
if (outOfRange && self._outOfRangeTick === null) {
self._outOfRangeTick = LK.ticks;
self._fadeStartTick = self._outOfRangeTick;
self._expireTick = self._fadeStartTick + 60; // 1 second fade
}
// Fade out logic if out of range
if (self._fadeStartTick !== null && self._expireTick !== null) {
var now = LK.ticks;
var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick));
self.alpha = 1 - fadeProgress;
if (now >= self._expireTick) {
self._shouldRemove = true;
}
} else {
self.alpha = 1;
}
};
return self;
});
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.value = Math.floor(Math.random() * 10) + 5;
// --- Timed fade-out system ---
self._spawnTick = LK.ticks;
self._fadeStartTick = self._spawnTick + 180; // 3 seconds visible
self._expireTick = self._fadeStartTick + 120; // 2 seconds fade (total 5s)
self.update = function () {
self.rotation += 0.05;
// Timed fade-out logic
if (typeof self._expireTick === "number" && typeof self._fadeStartTick === "number") {
var now = LK.ticks;
if (now >= self._fadeStartTick) {
// Start fading out
var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick));
self.alpha = 1 - fadeProgress;
if (now >= self._expireTick) {
// Mark for removal (actual removal in game.update)
self._shouldRemove = true;
}
} else {
self.alpha = 1;
}
}
};
return self;
});
// CoinBox: Toplanınca 50-100 coin veren, fade-out ile kaybolan obje
var CoinBox = Container.expand(function () {
var self = Container.call(this);
var boxGraphics = self.attachAsset('coinbox', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.9,
scaleY: 0.9
});
self.value = 50 + Math.floor(Math.random() * 51); // 50-100 coin arası
// Timed fade-out system
self._spawnTick = LK.ticks;
self._fadeStartTick = self._spawnTick + 240; // 4 seconds visible
self._expireTick = self._fadeStartTick + 120; // 2 seconds fade (total 6s)
self.update = function () {
boxGraphics.rotation += 0.07;
// Timed fade-out logic
if (typeof self._expireTick === "number" && typeof self._fadeStartTick === "number") {
var now = LK.ticks;
if (now >= self._fadeStartTick) {
var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick));
self.alpha = 1 - fadeProgress;
if (now >= self._expireTick) {
self._shouldRemove = true;
}
} else {
self.alpha = 1;
}
}
};
return self;
});
// DerelictDebris: Enkaz, üstüne gelince 0-50 arası rastgele hammadde verir ve fade-out ile kaybolur
var DerelictDebris = Container.expand(function () {
var self = Container.call(this);
// Use randomly selected pirate asset for debris appearance
var pirateImageIndex = Math.floor(Math.random() * 20) + 1;
var pirateAssetName = 'pirate' + pirateImageIndex;
var debrisGraphics = self.attachAsset(pirateAssetName, {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00ff00,
// yeşil enkaz
scaleX: 1.5,
scaleY: 1.5
});
self.type = 'derelict';
self.rotationSpeed = 0.01;
self._collected = false;
self._resourceGiven = false;
self._fadeStartTick = null;
self._expireTick = null;
self._resourceAmount = Math.floor(Math.random() * 51); // 0-50 arası
self.update = function () {
self.rotation += self.rotationSpeed;
// Fade-out logic
if (self._fadeStartTick !== null && self._expireTick !== null) {
var now = LK.ticks;
var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick));
self.alpha = 1 - fadeProgress;
if (now >= self._expireTick) {
self._shouldRemove = true;
}
} else {
self.alpha = 1;
}
};
return self;
});
// --- DRON CLASS ---
// Dron: ship'i takip eder, %80 yakınındaki maden/coinleri toplar ve ship'e getirir
var Dron = Container.expand(function () {
var self = Container.call(this);
// Randomly select one of 2 NPC images for dron appearance
var npcImageIndex = Math.floor(Math.random() * 2) + 1;
var npcAssetName = 'npc' + npcImageIndex;
var dronGraphics = self.attachAsset(npcAssetName, {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x1a2dcb,
scaleX: 1.2,
scaleY: 1.2
});
self._followOffset = {
x: -100 + Math.random() * 40,
y: 80 + Math.random() * 40
};
self._targetItem = null;
self._collecting = false;
self._collectSpeed = 10;
self.update = function () {
// --- Hafif float/wobble animasyonu: Dron sabit durmasın, havada uçuyormuş gibi hareket etsin ---
if (typeof self._floatBaseY === "undefined") {
self._floatBaseY = self.y;
}
if (typeof self._floatBaseX === "undefined") {
self._floatBaseX = self.x;
}
if (typeof self._floatPhase === "undefined") {
self._floatPhase = Math.random() * Math.PI * 2;
}
if (typeof self._floatPhaseX === "undefined") {
self._floatPhaseX = Math.random() * Math.PI * 2;
}
// Ship'i takip et (eğer item toplamıyorsa)
if (!self._collecting && typeof ship !== "undefined" && ship) {
var targetX = ship.x + self._followOffset.x;
var targetY = ship.y + self._followOffset.y;
var dx = targetX - self.x;
var dy = targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 10) {
self.x += dx / dist * 7;
self.y += dy / dist * 7;
}
// Float baseyi güncelle ki float animasyonu ship ile birlikte hareket etsin
self._floatBaseX = self.x;
self._floatBaseY = self.y;
}
// Eğer item topluyorsa, ona doğru git
if (self._collecting && self._targetItem) {
var dx = self._targetItem.x - self.x;
var dy = self._targetItem.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 10) {
self.x += dx / dist * self._collectSpeed;
self.y += dy / dist * self._collectSpeed;
} else {
// Toplama mesafesine ulaştı
// Coin veya resource ise ship'e getir
self._targetItem._dronCollected = true;
// Ship'e doğru item'ı taşı
self._targetItem._beingCarried = true;
self._targetItem._carrier = self;
self._collecting = false;
self._targetItem = null;
}
// Float baseyi güncelle ki float animasyonu item toplarken de düzgün çalışsın
self._floatBaseX = self.x;
self._floatBaseY = self.y;
}
// Eğer item taşıyorsa, item'ı ship'e doğru taşı
for (var i = 0; i < coins.length; i++) {
var c = coins[i];
if (c._beingCarried && c._carrier === self) {
var dx = ship.x - c.x;
var dy = ship.y - c.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 10) {
c.x += dx / dist * self._collectSpeed;
c.y += dy / dist * self._collectSpeed;
} else {
// Ship'e ulaştı, coin topla
playerCoins += c.value;
c.destroy();
coins.splice(i, 1);
LK.getSound('collect').play();
updateUI && updateUI();
saveProgress && saveProgress();
}
}
}
for (var i = 0; i < resources.length; i++) {
var r = resources[i];
if (r._beingCarried && r._carrier === self) {
var dx = ship.x - r.x;
var dy = ship.y - r.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 10) {
r.x += dx / dist * self._collectSpeed;
r.y += dy / dist * self._collectSpeed;
} else {
// Ship'e ulaştı, resource topla
var resourceTypes = ['metal', 'energy', 'crystal', 'gas', 'ice', 'uranium', 'silicon', 'carbon', 'plasma', 'antimatter'];
var idx = resourceTypes.indexOf(r.type);
if (idx >= 0) {
playerResources[idx] += r.amount;
checkResourceCollectAchievements && checkResourceCollectAchievements();
}
r.destroy();
resources.splice(i, 1);
LK.getSound('collect').play();
updateUI && updateUI();
saveProgress && saveProgress();
}
}
}
// Eğer item toplamıyorsa, yakındaki item'ı ara
if (!self._collecting && !self._targetItem) {
// %25 ship'e yakın, ekranda olan coin/resource bul
var found = false;
var maxDist = Math.max(2048, 2732) * 0.25;
for (var i = 0; i < coins.length; i++) {
var c = coins[i];
if (!c._dronCollected && Math.sqrt(Math.pow(c.x - ship.x, 2) + Math.pow(c.y - ship.y, 2)) < maxDist) {
self._targetItem = c;
self._collecting = true;
found = true;
break;
}
}
if (!found) {
for (var i = 0; i < resources.length; i++) {
var r = resources[i];
if (!r._dronCollected && Math.sqrt(Math.pow(r.x - ship.x, 2) + Math.pow(r.y - ship.y, 2)) < maxDist) {
self._targetItem = r;
self._collecting = true;
break;
}
}
}
}
// --- Hafif float/wobble animasyonu uygula ---
// Dron'un gerçek pozisyonunu floatBaseX/floatBaseY olarak tut, ek olarak x/y'ye küçük bir sinusoidal offset uygula
// Amplitüd: 8px (y), 4px (x), Periyot: 2-3 saniye arası
var floatY = Math.sin(LK.ticks * 0.05 + self._floatPhase) * 8;
var floatX = Math.cos(LK.ticks * 0.033 + self._floatPhaseX) * 4;
self.x = self._floatBaseX + floatX;
self.y = self._floatBaseY + floatY;
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.damage = 5;
self.directionX = 0;
self.directionY = 0;
self._outOfRangeTick = null;
self._fadeStartTick = null;
self._expireTick = null;
self._range = typeof ATTACK_RANGE !== "undefined" ? ATTACK_RANGE : 220;
self._startX = null;
self._startY = null;
self.update = function () {
if (self._startX === null || self._startY === null) {
self._startX = self.x;
self._startY = self.y;
}
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
// Out of range detection: bullet range = attack range
var dx = self.x - self._startX;
var dy = self.y - self._startY;
var dist = Math.sqrt(dx * dx + dy * dy);
var outOfRange = dist > self._range;
if (outOfRange && self._outOfRangeTick === null) {
self._outOfRangeTick = LK.ticks;
self._fadeStartTick = self._outOfRangeTick;
self._expireTick = self._fadeStartTick + 60; // 1 second fade
}
// Fade out logic if out of range
if (self._fadeStartTick !== null && self._expireTick !== null) {
var now = LK.ticks;
var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick));
self.alpha = 1 - fadeProgress;
if (now >= self._expireTick) {
self._shouldRemove = true;
}
} else {
self.alpha = 1;
}
};
return self;
});
// --- FRIEND CLASS ---
// Dost birlik: ship'i takip eder, saldırı sırasında %50 hasar ile saldırır
var Friend = Container.expand(function () {
var self = Container.call(this);
// Randomly select one of 2 NPC images for friend appearance
var npcImageIndex = Math.floor(Math.random() * 2) + 1;
var npcAssetName = 'npc' + npcImageIndex;
var friendGraphics = self.attachAsset(npcAssetName, {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x83de44,
scaleX: 1.1,
scaleY: 1.1
});
// --- Dynamic follow offset: will be updated every frame for movement/spacing ---
self._followOffset = {
x: 80 - Math.random() * 40,
y: 80 + Math.random() * 40
};
self._attackTarget = null;
self._attacking = false;
self._attackStartTick = 0;
self._lastAttackTick = 0;
self._attackDelay = 60; // 1 saniye (60fps)
self._attackInterval = 10; // her 10 tickte bir ateş et (ateş aralığı hızlı kalsın, istenirse 5 yapılabilir)
// --- For movement/spacing ---
self._moveAngle = Math.random() * Math.PI * 2;
self._moveTimer = Math.floor(Math.random() * 60);
self._friendIdx = -1; // will be set in update
self._desiredDist = 90; // base distance from ship
self._avoidDist = 70; // min distance to other friends
self.update = function () {
// Find my index in friends array for spacing
if (typeof friends !== "undefined" && Array.isArray(friends)) {
for (var fi = 0; fi < friends.length; fi++) {
if (friends[fi] === self) {
self._friendIdx = fi;
break;
}
}
}
// --- Dynamic orbit/spacing around ship ---
if (typeof ship !== "undefined" && ship) {
// Orbit angle: spread friends evenly in a circle, animate with time for movement
var n = 0,
myIdx = 0;
for (var fi = 0; fi < friends.length; fi++) {
if (friends[fi]) {
n++;
}
if (friends[fi] === self) {
myIdx = fi;
}
}
if (n === 0) {
n = 1;
}
var baseAngle = Math.PI * 2 * (myIdx / n);
var timeWobble = Math.sin((LK.ticks + myIdx * 30) / 60) * 0.5 + Math.cos((LK.ticks + myIdx * 60) / 80) * 0.3;
var angle = baseAngle + timeWobble;
var radius = self._desiredDist + 20 * Math.sin((LK.ticks + myIdx * 40) / 50);
// Target position around ship
var targetX = ship.x + Math.cos(angle) * radius;
var targetY = ship.y + Math.sin(angle) * radius;
// --- Avoid overlapping with other friends ---
if (typeof friends !== "undefined" && Array.isArray(friends)) {
for (var fi = 0; fi < friends.length; fi++) {
var f2 = friends[fi];
if (f2 && f2 !== self) {
var dx = self.x - f2.x;
var dy = self.y - f2.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < self._avoidDist && dist > 0) {
// Push away from other friend
var push = (self._avoidDist - dist) * 0.15;
targetX += dx / dist * push;
targetY += dy / dist * push;
}
}
}
}
// --- Smoothly move toward target position ---
var dx = targetX - self.x;
var dy = targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var moveSpeed = 5 + Math.sin((LK.ticks + myIdx * 20) / 40) * 1.5;
if (dist > 2) {
self.x += dx / dist * Math.min(moveSpeed, dist);
self.y += dy / dist * Math.min(moveSpeed, dist);
}
}
// --- Friend ship rotation: rotate to match player ship direction ---
if (typeof ship !== "undefined" && ship && typeof ship.rotation === "number") {
// Smoothly rotate friend ship to match player ship rotation
var targetRotation = ship.rotation;
var rotDiff = targetRotation - self.rotation;
// Normalize angle difference to [-π, π]
while (rotDiff > Math.PI) {
rotDiff -= Math.PI * 2;
}
while (rotDiff < -Math.PI) {
rotDiff += Math.PI * 2;
}
// Apply smooth rotation with lerp factor
var rotLerp = 0.1; // Adjust for rotation smoothness
self.rotation += rotDiff * rotLerp;
}
// --- Saldırı kontrolü ---
if (self._attacking && self._attackTarget && typeof self._attackTarget.x === "number" && typeof self._attackTarget.y === "number") {
// 2 saniye gecikmeden sonra saldır
if (LK.ticks - self._attackStartTick >= self._attackDelay) {
// Hedef hala hayatta mı?
if (self._attackTarget.health > 0) {
// Her attackInterval tickte bir ateş et
if (LK.ticks - self._lastAttackTick > self._attackInterval) {
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
var angle = Math.atan2(self._attackTarget.y - self.y, self._attackTarget.x - self.x);
bullet.directionX = Math.cos(angle);
bullet.directionY = Math.sin(angle);
bullet.rotation = angle + Math.PI / 2;
bullet.damage = Math.round((ship.damage || 10) * 0.5);
bullets.push(bullet);
game.addChild(bullet);
self._lastAttackTick = LK.ticks;
// Calculate distance from ship to friend bullet spawn point and adjust volume
var dx = bullet.x - ship.x;
var dy = bullet.y - ship.y;
var distanceFromShip = Math.sqrt(dx * dx + dy * dy);
// Calculate volume based on distance (closer = louder, farther = quieter)
// Max distance for sound falloff is 500 pixels
var maxSoundDistance = 500;
var volumeMultiplier = Math.max(0.1, Math.min(1.0, 1.0 - distanceFromShip / maxSoundDistance));
// Get the sound and play with adjusted volume
var shootSound = LK.getSound('shoot');
var originalVolume = shootSound.volume || 0.3; // fallback to default volume
shootSound.volume = originalVolume * volumeMultiplier;
shootSound.play();
// Restore original volume for next play
shootSound.volume = originalVolume;
}
} else {
// Hedef öldü, saldırıyı bırak
self._attacking = false;
self._attackTarget = null;
}
}
}
};
// Saldırı başlat
self.startAttack = function (target) {
self._attackTarget = target;
self._attacking = true;
self._attackStartTick = LK.ticks;
self._lastAttackTick = LK.ticks;
};
// Saldırı bırak
self.stopAttack = function () {
self._attacking = false;
self._attackTarget = null;
};
return self;
});
// GrupKorsan (Group Pirate) class: 2-4 pirates moving and attacking together, with reward label under each
var GrupKorsan = Container.expand(function () {
var self = Container.call(this);
// Use randomly selected pirate asset for each group member
var pirateImageIndex = Math.floor(Math.random() * 20) + 1;
var pirateAssetName = 'pirate' + pirateImageIndex;
var pirateGraphics = self.attachAsset(pirateAssetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Group member index and group size (set externally)
self.groupIndex = 0;
self.groupSize = 2;
// Level and stats (set externally)
self.level = 1;
self.health = 30;
self.speed = 2;
self.fireRate = 40;
self.lastFire = 0;
self.damage = 10;
self.defense = 5;
self.loot = 200;
self._isGrupKorsan = true;
// --- Enemy Text ---
self._enemyText = new Text2("Enemy", {
size: 18,
fill: 0xff4444,
align: "center"
});
self._enemyText.anchor.set(0.5, 0);
self._enemyText.x = 0;
self._enemyText.y = -50; // Above the pirate sprite
self.addChild(self._enemyText);
// --- Level Text ---
self._levelText = new Text2("Lv." + self.level, {
size: 22,
fill: 0xcccccc,
align: "center"
});
self._levelText.anchor.set(0.5, 0);
self._levelText.x = 0;
self._levelText.y = pirateGraphics.height / 2 + 4;
self.addChild(self._levelText);
// Reward label and coin value text removed for GrupKorsan
// --- Health bar ---
self._healthBar = new HealthBar();
self._healthBar.set({
maxValue: function maxValue() {
return self.health > 0 ? self.health : 0;
},
getValueFn: function getValueFn() {
return self.health > 0 ? self.health : 0;
},
getMaxFn: function getMaxFn() {
return 30 + (self.level - 1) * 10;
},
width: 60,
height: 10
});
self._healthBar.y = -60;
self.addChild(self._healthBar);
// --- Group movement: all members follow the same target/angle, set externally ---
self._groupTarget = null;
self._groupAngle = 0;
self._groupMoveSpeed = 2;
// --- Damage handler ---
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xffffff, 100);
};
// --- Update ---
self.update = function () {
// Update enemy text position in case of movement
if (self._enemyText) {
self._enemyText.x = 0;
self._enemyText.y = -50;
}
// Update level/reward text positions
if (self._levelText) {
self._levelText.x = 0;
self._levelText.y = pirateGraphics.height / 2 + 4;
self._levelText.setText("Lv." + self.level);
}
// Reward label and coin value update removed for GrupKorsan
// Health bar update
if (self._healthBar && self._healthBar.update) {
self._healthBar.update();
}
// Group movement: follow group target/angle if set
if (self._groupTarget && typeof self._groupTarget.x === "number" && typeof self._groupTarget.y === "number") {
var dx = self._groupTarget.x - self.x;
var dy = self._groupTarget.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 10) {
self.x += dx / dist * self._groupMoveSpeed;
self.y += dy / dist * self._groupMoveSpeed;
self._groupAngle = Math.atan2(dy, dx);
self.rotation = self._groupAngle + Math.PI / 2;
}
} else if (typeof self._groupAngle === "number") {
// Move in group direction if no target
self.x += Math.cos(self._groupAngle) * self._groupMoveSpeed;
self.y += Math.sin(self._groupAngle) * self._groupMoveSpeed;
self.rotation = self._groupAngle + Math.PI / 2;
}
};
return self;
});
// HealthBar class for displaying health above entities
var HealthBar = Container.expand(function () {
var self = Container.call(this);
// width, height, color, maxValue, getValueFn
self._width = 60;
self._height = 10;
self._color = 0x00ff00;
self._maxValue = 100;
self._getValueFn = null;
self._getMaxFn = null;
self._bg = self.attachAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: self._width / 40,
scaleY: self._height / 40,
tint: 0x222222
});
self._bar = self.attachAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: (self._width - 4) / 40,
scaleY: (self._height - 4) / 40,
tint: self._color
});
self._bar.x = 0;
self._bar.y = 0;
self.set = function (opts) {
self._maxValue = opts.maxValue || 100;
self._getValueFn = opts.getValueFn;
self._getMaxFn = opts.getMaxFn;
self._color = opts.color || 0x00ff00;
self._bar.tint = self._color;
if (opts.width) {
self._width = opts.width;
self._bg.scaleX = self._width / 40;
self._bar.scaleX = (self._width - 4) / 40;
}
if (opts.height) {
self._height = opts.height;
self._bg.scaleY = self._height / 40;
self._bar.scaleY = (self._height - 4) / 40;
}
};
self.update = function () {
var value = self._getValueFn ? self._getValueFn() : self._maxValue;
var maxValue = self._getMaxFn ? self._getMaxFn() : self._maxValue;
var ratio = Math.max(0, Math.min(1, value / maxValue));
// Color: green > yellow > red
if (ratio > 0.6) {
self._bar.tint = 0x00ff00;
} else if (ratio > 0.3) {
self._bar.tint = 0xffff00;
} else {
self._bar.tint = 0xff0000;
}
self._bar.scaleX = (self._width - 4) * ratio / 40;
};
return self;
});
// --- MECHANIC CLASS ---
// Mechanic: Automatically repairs ship when health is low
var Mechanic = Container.expand(function () {
var self = Container.call(this);
// Randomly select one of 2 NPC images for mechanic appearance
var npcImageIndex = Math.floor(Math.random() * 2) + 1;
var npcAssetName = 'npc' + npcImageIndex;
var mechanicGraphics = self.attachAsset(npcAssetName, {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xff6600,
// Orange tint for mechanic
scaleX: 1.1,
scaleY: 1.1
});
self._followOffset = {
x: -120 + Math.random() * 40,
y: -80 + Math.random() * 40
};
self._repairDelay = 180; // 3 seconds between repairs
self._lastRepairTick = 0;
self.update = function () {
// Float animation like Dron
if (typeof self._floatBaseY === "undefined") {
self._floatBaseY = self.y;
}
if (typeof self._floatBaseX === "undefined") {
self._floatBaseX = self.x;
}
if (typeof self._floatPhase === "undefined") {
self._floatPhase = Math.random() * Math.PI * 2;
}
if (typeof self._floatPhaseX === "undefined") {
self._floatPhaseX = Math.random() * Math.PI * 2;
}
// Follow ship
if (typeof ship !== "undefined" && ship) {
var targetX = ship.x + self._followOffset.x;
var targetY = ship.y + self._followOffset.y;
var dx = targetX - self.x;
var dy = targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 10) {
self.x += dx / dist * 6;
self.y += dy / dist * 6;
}
self._floatBaseX = self.x;
self._floatBaseY = self.y;
}
// Auto repair ship when health is below 50%
if (ship && ship.health < ship.maxHealth * 0.5 && LK.ticks - self._lastRepairTick > self._repairDelay) {
var repairAmount = Math.round(ship.maxHealth * 0.1); // Repair 10% of max health
ship.health = Math.min(ship.health + repairAmount, ship.maxHealth);
self._lastRepairTick = LK.ticks;
// Visual effect
LK.effects.flashObject(ship, 0x00ff99, 300);
// Floating label
var label = new Text2("Mechanic Repair: +" + repairAmount + " HP", {
size: 32,
fill: 0x00ff99,
align: "center"
});
label.anchor.set(0.5, 0.5);
label.x = ship.x;
label.y = ship.y - 100;
if (typeof game !== "undefined" && game.addChild) {
game.addChild(label);
}
LK.setTimeout(function () {
if (label.parent) {
label.parent.removeChild(label);
}
}, 1200);
updateUI && updateUI();
}
// Float animation
var floatY = Math.sin(LK.ticks * 0.04 + self._floatPhase) * 6;
var floatX = Math.cos(LK.ticks * 0.03 + self._floatPhaseX) * 3;
self.x = self._floatBaseX + floatX;
self.y = self._floatBaseY + floatY;
};
return self;
});
// Helper: open Crew panel
var NPC = Container.expand(function () {
var self = Container.call(this);
// Randomly select one of 2 NPC images
var npcImageIndex = Math.floor(Math.random() * 2) + 1;
var npcAssetName = 'npc' + npcImageIndex;
var npcGraphics = self.attachAsset(npcAssetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.isAI = true; // AI-driven
self.baseY = 0;
self.moveSpeed = 1 + Math.random() * 2;
self.moveAngle = Math.random() * Math.PI * 2;
self.wanderTimer = 0;
self.lastAIAction = 0;
self.targetX = null;
self.targetY = null;
self.rpgName = "NPC-" + Math.floor(Math.random() * 10000);
// --- NPC Level System ---
// Level range: wave 1 => 1-3, wave 2 => 3-5, wave 3 => 5-7, etc.
var minLevel = 1 + (typeof waveNumber !== "undefined" ? waveNumber : 0) * 2;
var maxLevel = minLevel + 2;
self.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1));
self.health = 100 + (self.level - 1) * 10;
// --- Level Text ---
self._levelText = new Text2("Lv." + self.level, {
size: 22,
fill: 0xcccccc,
align: "center"
});
self._levelText.anchor.set(0.5, 0);
self._levelText.x = 0;
self._levelText.y = 40; // Under the NPC sprite
self.addChild(self._levelText);
// Only floating AI logic, no quests/events/trade
// Add dummy interact method to prevent TypeError
self.interact = function () {
// No interaction for basic NPCs, return empty string
return "";
};
self.update = function () {
// Update level text position in case of movement
if (self._levelText) {
self._levelText.x = 0;
self._levelText.y = npcGraphics.height / 2 + 4;
self._levelText.setText("Lv." + self.level);
}
// Pause logic: If pauseUntil is set and not expired, skip movement
if (self.pauseUntil && LK.ticks < self.pauseUntil) {
// Still paused, only float animation
self.y += Math.sin(LK.ticks * 0.05) * 0.5;
return;
}
// AI: Sometimes move toward a random point, sometimes wander
self.wanderTimer++;
if (self.wanderTimer > 120 + Math.random() * 120) {
if (Math.random() < 0.5) {
// Pick a random point to move toward (simulate AI goal seeking)
self.targetX = self.x + (Math.random() - 0.5) * 800;
self.targetY = self.y + (Math.random() - 0.5) * 800;
} else {
self.targetX = null;
self.targetY = null;
}
self.moveAngle = Math.random() * Math.PI * 2;
self.wanderTimer = 0;
}
// Move toward target if set, else wander
if (self.targetX !== null && self.targetY !== null) {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 10) {
self.x += dx / dist * self.moveSpeed;
self.y += dy / dist * self.moveSpeed;
self.moveAngle = Math.atan2(dy, dx);
self._targetRotation = self.moveAngle + Math.PI / 2;
} else {
self.targetX = null;
self.targetY = null;
}
} else {
// Wander randomly with smooth gliding motion (no sharp turns)
if (typeof self._wanderVX === "undefined") {
self._wanderVX = Math.cos(self.moveAngle) * self.moveSpeed;
}
if (typeof self._wanderVY === "undefined") {
self._wanderVY = Math.sin(self.moveAngle) * self.moveSpeed;
}
// Target velocity for this wander direction
var targetVX = Math.cos(self.moveAngle) * self.moveSpeed;
var targetVY = Math.sin(self.moveAngle) * self.moveSpeed;
// Smoothly interpolate velocity for gliding effect
self._wanderVX += (targetVX - self._wanderVX) * 0.03;
self._wanderVY += (targetVY - self._wanderVY) * 0.03;
self.x += self._wanderVX;
self.y += self._wanderVY;
self._targetRotation = Math.atan2(self._wanderVY, self._wanderVX) + Math.PI / 2;
}
// Smoothly rotate toward target rotation
if (typeof self._targetRotation === "number") {
var diff = self._targetRotation - self.rotation;
while (diff > Math.PI) {
diff -= Math.PI * 2;
}
while (diff < -Math.PI) {
diff += Math.PI * 2;
}
var lerp = 0.05;
self.rotation += diff * lerp;
}
// Float animation on top of movement
self.y += Math.sin(LK.ticks * 0.05) * 0.5;
};
return self;
});
var Pirate = Container.expand(function () {
var self = Container.call(this);
// Randomly select one of 20 pirate images
var pirateImageIndex = Math.floor(Math.random() * 20) + 1;
var pirateAssetName = 'pirate' + pirateImageIndex;
var pirateGraphics = self.attachAsset(pirateAssetName, {
anchorX: 0.5,
anchorY: 0.5
});
// --- Pirate Level & Scaling ---
self.level = typeof waveNumber !== "undefined" ? 1 + waveNumber : 1;
self.health = 30 + (self.level - 1) * 10;
self.speed = 2 + Math.floor((self.level - 1) / 10) * 0.2; // Slight speed up every 10 waves
self.fireRate = 60 - Math.min((self.level - 1) * 2, 30); // Faster fire at higher levels
self.lastFire = 0;
self.loot = Math.floor(Math.random() * 30) + 10 + (self.level - 1) * 2;
self.moveAngle = Math.random() * Math.PI * 2;
self.patrolTimer = 0;
self.aggroRange = 600;
// Korsan saldırı ve savunma gücü seviyeye göre orantılı artar
self.damage = 5 + Math.floor((self.level - 1) * 1.5); // Saldırı gücü daha hızlı artar
self.defense = 2 + Math.floor((self.level - 1) * 1.2); // Savunma gücü de seviyeye göre artar
// --- Enemy Text ---
self._enemyText = new Text2("Enemy", {
size: 18,
fill: 0xff4444,
align: "center"
});
self._enemyText.anchor.set(0.5, 0);
self._enemyText.x = 0;
self._enemyText.y = -50; // Above the pirate sprite
self.addChild(self._enemyText);
// --- Level Text ---
self._levelText = new Text2("Lv." + self.level, {
size: 22,
fill: 0xcccccc,
align: "center"
});
self._levelText.anchor.set(0.5, 0);
self._levelText.x = 0;
self._levelText.y = 40; // Under the pirate sprite
self.addChild(self._levelText);
// Katil korsan için "Ödül:" yazısı ekle
self._rewardText = null;
if (self.rpgName === "Katil" || self._isKatil) {
self._rewardText = new Text2("Reward:", {
size: 22,
fill: 0xffcc00,
align: "center"
});
self._rewardText.anchor.set(0.5, 0);
self._rewardText.x = 0;
self._rewardText.y = self._levelText.y + 28; // Seviye yazısının hemen altında, aynı büyüklükte
self.addChild(self._rewardText);
}
// Level text only, defense text removed
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xffffff, 100);
};
self.update = function () {
// Update enemy text position in case of movement
if (self._enemyText) {
self._enemyText.x = 0;
self._enemyText.y = -50;
}
// Update level text position in case of movement
if (self._levelText) {
self._levelText.x = 0;
self._levelText.y = pirateGraphics.height / 2 + 4;
self._levelText.setText("Lv." + self.level);
}
// Katil korsan için ödül yazısı pozisyonunu güncel tut
if (self.rpgName === "Katil" || self._isKatil) {
if (!self._rewardText) {
self._rewardText = new Text2("Ödül:", {
size: 22,
fill: 0xffcc00,
align: "center"
});
self._rewardText.anchor.set(0.5, 0);
self.addChild(self._rewardText);
}
self._rewardText.x = 0;
self._rewardText.y = self._levelText.y + 28;
self._rewardText.visible = true;
// Katil korsan için coin miktarı yazısı ekle/güncelle
var rewardCoinValue = 0;
// Katil korsan için coin değeri, öldüğünde verilecek coin miktarı ile aynı olmalı
// Oyun kodunda coin.value = coin.value * (1 + Math.floor(waveNumber / 3)) * 10;
// Burada coin.value rastgele 5-14 arası, ama göstermek için ortalama değer (ör: 10) kullanılabilir
// Alternatif: self.level ve waveNumber'a göre hesapla
var baseCoin = 10; // ortalama coin
var waveNum = typeof waveNumber !== "undefined" ? waveNumber : 0;
rewardCoinValue = baseCoin * (1 + Math.floor(waveNum / 3)) * 10;
if (!self._rewardCoinText) {
self._rewardCoinText = new Text2(rewardCoinValue + " coin", {
size: 22,
fill: 0xffcc00,
align: "center"
});
self._rewardCoinText.anchor.set(0.5, 0);
self.addChild(self._rewardCoinText);
}
self._rewardCoinText.x = 0;
self._rewardCoinText.y = self._rewardText.y + 28;
self._rewardCoinText.setText(rewardCoinValue + " coin");
self._rewardCoinText.visible = true;
} else if (self._rewardText) {
self._rewardText.visible = false;
if (self._rewardCoinText) {
self._rewardCoinText.visible = false;
}
}
// Defense text removed
// Movement AI
self.patrolTimer++;
if (self.patrolTimer > 180) {
self.moveAngle = Math.random() * Math.PI * 2;
self.patrolTimer = 0;
}
// Patrol movement
if (self._squadTargetNPC && self._squadTargetNPC.questType === 'defend' && self._squadTargetNPC.questActive) {
// Korsan, NPC'yi hedefle
var target = self._squadTargetNPC;
// Eğer defend quest'te yardım kabul edildiyse, oyuncuya da saldırabilir
if (target._defendHelpAccepted === true && Math.random() < 0.5) {
// %50 ihtimalle oyuncuya saldır
target = Math.random() < 0.5 ? self._squadTargetNPC : ship;
}
var dx = target.x - self.x;
var dy = target.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 10) {
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
self.moveAngle = Math.atan2(dy, dx);
self._targetRotation = self.moveAngle + Math.PI / 2;
} else {
// Hedefe çok yaklaştıysa sadece dön
var angle = Math.atan2(target.y - self.y, target.x - self.x);
self._targetRotation = angle + Math.PI / 2;
}
// Ateş et
if (LK.ticks - self.lastFire > self.fireRate) {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y;
var angle = Math.atan2(target.y - self.y, target.x - self.x);
bullet.directionX = Math.cos(angle);
bullet.directionY = Math.sin(angle);
bullet.rotation = angle + Math.PI / 2;
enemyBullets.push(bullet);
if (game && game.addChild) {
game.addChild(bullet);
}
self.lastFire = LK.ticks;
// Keep pirate rotated to attack direction after firing
self._targetRotation = angle + Math.PI / 2;
}
} else {
// Wander randomly with smooth gliding motion (no sharp turns)
if (typeof self._wanderVX === "undefined") {
self._wanderVX = Math.cos(self.moveAngle) * self.speed * 0.5;
}
if (typeof self._wanderVY === "undefined") {
self._wanderVY = Math.sin(self.moveAngle) * self.speed * 0.5;
}
// Target velocity for this wander direction
var targetVX = Math.cos(self.moveAngle) * self.speed * 0.5;
var targetVY = Math.sin(self.moveAngle) * self.speed * 0.5;
// Smoothly interpolate velocity for gliding effect
self._wanderVX += (targetVX - self._wanderVX) * 0.03;
self._wanderVY += (targetVY - self._wanderVY) * 0.03;
self.x += self._wanderVX;
self.y += self._wanderVY;
self._targetRotation = Math.atan2(self._wanderVY, self._wanderVX) + Math.PI / 2;
}
// Smoothly rotate toward target rotation
if (typeof self._targetRotation === "number") {
var diff = self._targetRotation - self.rotation;
while (diff > Math.PI) {
diff -= Math.PI * 2;
}
while (diff < -Math.PI) {
diff += Math.PI * 2;
}
var lerp = 0.05;
self.rotation += diff * lerp;
}
};
return self;
});
// Pirate Base Class
var PirateBase = Container.expand(function () {
var self = Container.call(this);
// Use randomly selected pirate asset for base appearance
var pirateImageIndex = Math.floor(Math.random() * 20) + 1;
var pirateAssetName = 'pirate' + pirateImageIndex;
var baseGraphics = self.attachAsset(pirateAssetName, {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x990000,
scaleX: 3,
scaleY: 3
});
self.health = 300;
self.maxHealth = 300;
self.attackPower = 10;
self.defensePower = 10;
self.spawnRadius = 350;
self.pirates = [];
self.lastPirateSpawn = 0;
self.maxPirates = 10;
self._destroyed = false;
// Health bar
self._healthBar = new HealthBar();
self._healthBar.set({
maxValue: function maxValue() {
return self.maxHealth;
},
getValueFn: function getValueFn() {
return self.health;
},
getMaxFn: function getMaxFn() {
return self.maxHealth;
},
width: 120,
height: 16,
color: 0xff0000
});
self._healthBar.y = -120;
self.addChild(self._healthBar);
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xff0000, 120);
if (self.health <= 0 && !self._destroyed) {
self.health = 0;
self._destroyed = true;
// --- BAŞARIM: Korsan üssü yok etme ---
if (typeof checkPirateBaseAchievements === "function") {
checkPirateBaseAchievements();
}
// Remove all pirates around base
for (var i = 0; i < self.pirates.length; i++) {
if (self.pirates[i] && !self.pirates[i]._destroyed) {
self.pirates[i].destroy();
}
}
// Explosion effect
createExplosion(self.x, self.y);
// Floating label
var label = new Text2("Pirate Base Destroyed!", {
size: 48,
fill: 0xffcc00,
align: "center"
});
label.anchor.set(0.5, 0.5);
label.x = self.x;
label.y = self.y - 180;
if (game && game.addChild) {
game.addChild(label);
}
self._destroyed = true;
self.destroy();
}
};
self.update = function () {
// Health bar update
if (self._healthBar && self._healthBar.update) {
self._healthBar.update();
}
// Remove dead pirates from list
for (var i = self.pirates.length - 1; i >= 0; i--) {
if (!self.pirates[i] || self.pirates[i]._destroyed) {
self.pirates.splice(i, 1);
}
}
// Spawn pirates if less than max
if (!self._destroyed && self.pirates.length < self.maxPirates && LK.ticks - self.lastPirateSpawn > 90) {
var pirate = new Pirate();
var angle = Math.random() * Math.PI * 2;
pirate.x = self.x + Math.cos(angle) * (self.spawnRadius + Math.random() * 60);
pirate.y = self.y + Math.sin(angle) * (self.spawnRadius + Math.random() * 60);
pirate.health = 30 + self.attackPower * 2;
pirate.damage = 5 + self.attackPower;
pirate.rpgName = "BaseKorsan-" + Math.floor(Math.random() * 10000);
pirate._baseRef = self;
// Add health bar to pirate
pirate._healthBar = new HealthBar();
pirate._healthBar.set({
maxValue: function maxValue() {
return pirate.health > 0 ? pirate.health : 0;
},
getValueFn: function getValueFn() {
return pirate.health > 0 ? pirate.health : 0;
},
getMaxFn: function getMaxFn() {
return 30 + self.attackPower * 2;
},
width: 60,
height: 10
});
pirate._healthBar.y = -60;
pirate.addChild(pirate._healthBar);
pirates.push(pirate);
self.pirates.push(pirate);
if (game && game.addChild) {
game.addChild(pirate);
}
self.lastPirateSpawn = LK.ticks;
}
// Base defense: shoot at player if close
var distToShip = Math.sqrt(Math.pow(ship.x - self.x, 2) + Math.pow(ship.y - self.y, 2));
if (!self._destroyed && distToShip < self.spawnRadius + 80 && LK.ticks % 30 === 0) {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y;
var angle = Math.atan2(ship.y - self.y, ship.x - self.x);
bullet.directionX = Math.cos(angle);
bullet.directionY = Math.sin(angle);
bullet.rotation = angle + Math.PI / 2;
bullet.damage = 10 + self.attackPower;
enemyBullets.push(bullet);
if (game && game.addChild) {
game.addChild(bullet);
}
}
};
return self;
});
var Resource = Container.expand(function () {
var self = Container.call(this);
var resourceGraphics = self.attachAsset('resource', {
anchorX: 0.5,
anchorY: 0.5
});
// 10 farklı hammadde tipi
var resourceTypes = ['metal', 'energy', 'crystal', 'gas', 'ice', 'uranium', 'silicon', 'carbon', 'plasma', 'antimatter'];
// --- Değerli hammaddelerin daha az çıkmasını sağlamak için ağırlıklı seçim ---
// Market kaldırıldı, sabit fiyatlar kullanılıyor
var prices = [100, 80, 120, 90, 70, 200, 110, 60, 150, 300];
// Her hammaddenin ağırlığı: 1 / (fiyat^0.7) ile ters orantılı (daha değerli = daha az)
// 0.7 üssüyle daha yumuşak bir dağılım, istenirse 1.0 yapılabilir
var weights = [];
var totalWeight = 0;
for (var i = 0; i < prices.length; i++) {
var w = 1 / Math.pow(prices[i], 0.7);
weights.push(w);
totalWeight += w;
}
// Rastgele ağırlıklı seçim
var r = Math.random() * totalWeight;
var acc = 0,
idx = 0;
for (var i = 0; i < weights.length; i++) {
acc += weights[i];
if (r <= acc) {
idx = i;
break;
}
}
self.type = resourceTypes[idx];
// Miktar: Değerli hammaddelerde miktar da az olsun
// maxAmount = 1 + 4 * (ucuzluk ağırlığı)
var maxAmount = Math.max(1, Math.round(1 + 4 * (weights[idx] / totalWeight * prices.length)));
self.amount = Math.floor(Math.random() * maxAmount) + 1;
// --- Timed fade-out system ---
self._spawnTick = LK.ticks;
self._fadeStartTick = self._spawnTick + 180; // 3 seconds visible
self._expireTick = self._fadeStartTick + 120; // 2 seconds fade (total 5s)
self.update = function () {
self.rotation += 0.03;
// Timed fade-out logic
if (typeof self._expireTick === "number" && typeof self._fadeStartTick === "number") {
var now = LK.ticks;
if (now >= self._fadeStartTick) {
// Start fading out
var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick));
self.alpha = 1 - fadeProgress;
if (now >= self._expireTick) {
// Mark for removal (actual removal in game.update)
self._shouldRemove = true;
}
} else {
self.alpha = 1;
}
}
};
return self;
});
var Ship = Container.expand(function () {
var self = Container.call(this);
var shipGraphics = self.attachAsset('ship', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.shield = 50;
self.maxShield = 50;
self.shieldRegenDelay = 0;
self.speed = 5;
self.fireRate = 10;
self.damage = 10;
self.lastFire = 0;
self.takeDamage = function (amount) {
// Shield absorbs damage first
if (self.shield > 0) {
var shieldDamage = Math.min(amount, self.shield);
self.shield -= shieldDamage;
amount -= shieldDamage;
LK.effects.flashObject(self, 0x0088ff, 100);
self.shieldRegenDelay = 180; // 3 seconds delay before shield regen
}
if (amount > 0) {
self.health -= amount;
LK.effects.flashObject(self, 0xff0000, 200);
}
if (self.health <= 0) {
// --- Clear all world entities on respawn ---
var _destroyAll = function _destroyAll(arr) {
if (!arr) {
return;
}
for (var i = arr.length - 1; i >= 0; i--) {
if (arr[i] && typeof arr[i].destroy === "function") {
arr[i].destroy();
}
arr.splice(i, 1);
}
}; // Remove all pirates, npcs, asteroids, coins, resources, enemyBullets, bullets, stars, explosions, upgradeStations, pirateBases
self.health = 0;
// --- Respawn logic instead of Game Over ---
// 1. Lose 50% coins and 50% of each resource
var lostCoins = Math.floor(playerCoins * 0.5);
playerCoins = Math.floor(playerCoins * 0.5);
var lostResources = [];
for (var i = 0; i < playerResources.length; i++) {
var lost = Math.floor(playerResources[i] * 0.5);
lostResources.push(lost);
playerResources[i] = Math.floor(playerResources[i] * 0.5);
}
// 2. Respawn ship at a random far location, restore health/shield
var respawnRadius = 1200 + Math.random() * 1800; // 1200-3000 px away from last position
var respawnAngle = Math.random() * Math.PI * 2;
self.x = self.x + Math.cos(respawnAngle) * respawnRadius;
self.y = self.y + Math.sin(respawnAngle) * respawnRadius;
self.health = self.maxHealth;
self.shield = self.maxShield;
self.shieldRegenDelay = 180;
_destroyAll(pirates);
_destroyAll(npcs);
_destroyAll(asteroids);
_destroyAll(coins);
_destroyAll(resources);
_destroyAll(enemyBullets);
_destroyAll(bullets);
_destroyAll(stars);
_destroyAll(explosions);
_destroyAll(upgradeStations);
_destroyAll(pirateBases);
// Also clear any katil korsan respawn timer
if (typeof window._katilPirateRespawnTimer !== "undefined" && window._katilPirateRespawnTimer) {
LK.clearTimeout(window._katilPirateRespawnTimer);
window._katilPirateRespawnTimer = null;
}
window._katilPirateSpawned = false;
// Katil korsan öldü ve dünya sıfırlandıktan sonra tekrar spawn et
if (typeof window._katilPirateRespawnTimer !== "undefined" && window._katilPirateRespawnTimer) {
LK.clearTimeout(window._katilPirateRespawnTimer);
window._katilPirateRespawnTimer = null;
}
window._katilPirateRespawnTimer = LK.setTimeout(function () {
if (!window._katilPirateSpawned) {
var pirate = new Pirate();
// Katil korsan statlarını tekrar ayarla
var minLevel = 1 + waveNumber * 2;
pirate.level = minLevel + 10;
pirate.health = (30 + (pirate.level - 1) * 10) * 10;
pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10;
pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10;
pirate.rpgName = "Katil";
pirate._isKatil = true;
pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3));
pirate._attackRange = Math.floor(600 * 0.9); // Katil korsan için %10 daha kısa menzil
window._katilPirateSpawned = true;
// Diğer korsanlar gibi kenar spawn algoritmasıyla doğsun
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y - 1400;
break;
case 1:
pirate.x = ship.x + 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
case 2:
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y + 1400;
break;
case 3:
pirate.x = ship.x - 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
}
// Add health bar to katil pirate
pirate._healthBar = new HealthBar();
pirate._healthBar.set({
maxValue: function maxValue() {
return pirate.health > 0 ? pirate.health : 0;
},
getValueFn: function getValueFn() {
return pirate.health > 0 ? pirate.health : 0;
},
getMaxFn: function getMaxFn() {
return 30 + waveNumber * 10;
},
width: 60,
height: 10
});
pirate._healthBar.y = -60;
pirate.addChild(pirate._healthBar);
pirates.push(pirate);
if (typeof game !== "undefined" && game.addChild) {
game.addChild(pirate);
}
}
window._katilPirateRespawnTimer = null;
}, 6000);
// Optionally reset wave progress
enemiesKilled = 0;
// Respawn initial world elements
for (var i = 0; i < 135; i++) {
spawnStar(true);
}
for (var i = 0; i < 6; i++) {
spawnNPC();
}
for (var i = 0; i < 6; i++) {
spawnPirate();
}
for (var i = 0; i < 2; i++) {
spawnAsteroid();
}
// Track respawn tick for input blocking
window._lastRespawnTick = LK.ticks;
// 3. Show floating label for penalty
var lostText = "-%50 coin & hammadde kaybı! Yeniden doğdun.";
var label = new Text2(lostText, {
size: 48,
fill: 0xff4444,
align: "center"
});
label.anchor.set(0.5, 0.5);
label.x = 1024;
label.y = 1366 - 200;
if (typeof game !== "undefined" && game.addChild) {
game.addChild(label);
}
LK.setTimeout(function () {
if (label.parent) {
label.parent.removeChild(label);
}
}, 1800);
// 4. Update UI and save progress
if (typeof updateUI === "function") {
updateUI();
}
if (typeof saveProgress === "function") {
saveProgress();
}
}
};
self.update = function () {
// Shield regeneration
if (self.shieldRegenDelay > 0) {
self.shieldRegenDelay--;
} else if (self.shield < self.maxShield) {
self.shield = Math.min(self.shield + 0.1, self.maxShield);
}
// --- SHIP ROTATION: Keep ship rotated to last movement or attack direction, but turn smoothly ---
// If joystick is being dragged, rotate to movement direction
if (typeof isDragging !== "undefined" && isDragging && typeof joystickHandle !== "undefined" && typeof joystickBase !== "undefined") {
var dx = joystickHandle.x - joystickBase.x;
var dy = joystickHandle.y - joystickBase.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 10) {
self._lastMoveAngle = Math.atan2(dy, dx);
self._targetRotation = self._lastMoveAngle + Math.PI / 2;
}
} else if (typeof game !== "undefined" && typeof game._lastShipAttackAngle !== "undefined") {
// If not moving, but last attack angle exists, keep ship rotated to last attack direction
self._targetRotation = game._lastShipAttackAngle + Math.PI / 2;
}
// Smoothly rotate toward target rotation
if (typeof self._targetRotation === "number") {
// Calculate shortest angle difference
var diff = self._targetRotation - self.rotation;
while (diff > Math.PI) {
diff -= Math.PI * 2;
}
while (diff < -Math.PI) {
diff += Math.PI * 2;
}
// Lerp factor: lower = softer (0.05 = very soft, 0.2 = sharp)
var lerp = 0.05;
self.rotation += diff * lerp;
}
};
return self;
});
var Star = Container.expand(function () {
var self = Container.call(this);
var starGraphics = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = Math.random() * 2 + 0.5;
starGraphics.alpha = Math.random() * 0.8 + 0.2;
self.vx = (Math.random() - 0.5) * 0.5;
self.vy = (Math.random() - 0.5) * 0.5;
self.update = function () {
// Slow floating movement
self.x += self.vx;
self.y += self.vy;
};
return self;
});
// Trade Station Class
var TradeStation = Container.expand(function () {
var self = Container.call(this);
// Randomly select one of 2 NPC images for trade station appearance
var npcImageIndex = Math.floor(Math.random() * 2) + 1;
var npcAssetName = 'npc' + npcImageIndex;
var tradeGraphics = self.attachAsset(npcAssetName, {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00ff99,
scaleX: 2,
scaleY: 2
});
self.type = 'trade';
self.rotationSpeed = 0.01;
self.offerType = Math.random() < 0.5 ? 'buy' : 'sell'; // buy: station buys from player, sell: station sells to player
self.resourceType = Math.random() < 0.5 ? 'metal' : 'energy';
self.amount = Math.floor(Math.random() * 10) + 5;
self.price = Math.floor(Math.random() * 30) + 10;
self.specialOffer = Math.random() < 0.2; // 20% chance for special upgrade offer
self.specialUpgrade = null;
if (self.specialOffer) {
var upgrades = [{
name: 'maxHealth',
label: 'Max Health +30',
cost: 200
}, {
name: 'maxShield',
label: 'Max Shield +20',
cost: 180
}, {
name: 'damage',
label: 'Damage +5',
cost: 150
}, {
name: 'speed',
label: 'Speed +1',
cost: 120
}];
self.specialUpgrade = upgrades[Math.floor(Math.random() * upgrades.length)];
}
self.update = function () {
self.rotation += self.rotationSpeed;
// Pulse effect
var scale = 2 + Math.sin(LK.ticks * 0.05) * 0.1;
tradeGraphics.scaleX = scale;
tradeGraphics.scaleY = scale;
};
return self;
});
// Wormhole Teleporter Class
var Wormhole = Container.expand(function () {
var self = Container.call(this);
var whGraphics = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 5,
scaleY: 5,
tint: 0x00ffff
});
self.radius = 120;
self.cooldown = 0;
self.update = function () {
whGraphics.rotation += 0.07;
// Teleport ship if close and not on cooldown
var dx = ship.x - self.x;
var dy = ship.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < self.radius && self.cooldown <= 0) {
// Teleport ship to a random far location
var tx = ship.x + (Math.random() - 0.5) * 4000;
var ty = ship.y + (Math.random() - 0.5) * 4000;
var dx2 = tx - ship.x;
var dy2 = ty - ship.y;
ship.x = tx;
ship.y = ty;
// Move all objects to keep camera effect
for (var i = 0; i < stars.length; i++) {
stars[i].x -= dx2;
stars[i].y -= dy2;
}
for (var i = 0; i < npcs.length; i++) {
npcs[i].x -= dx2;
npcs[i].y -= dy2;
}
for (var i = 0; i < pirates.length; i++) {
pirates[i].x -= dx2;
pirates[i].y -= dy2;
}
for (var i = 0; i < coins.length; i++) {
coins[i].x -= dx2;
coins[i].y -= dy2;
}
for (var i = 0; i < resources.length; i++) {
resources[i].x -= dx2;
resources[i].y -= dy2;
}
for (var i = 0; i < bullets.length; i++) {
bullets[i].x -= dx2;
bullets[i].y -= dy2;
}
for (var i = 0; i < enemyBullets.length; i++) {
enemyBullets[i].x -= dx2;
enemyBullets[i].y -= dy2;
}
for (var i = 0; i < asteroids.length; i++) {
asteroids[i].x -= dx2;
asteroids[i].y -= dy2;
}
for (var i = 0; i < upgradeStations.length; i++) {
upgradeStations[i].x -= dx2;
upgradeStations[i].y -= dy2;
}
// boostParticles removed (no boost system)
for (var i = 0; i < explosions.length; i++) {
explosions[i].x -= dx2;
explosions[i].y -= dy2;
}
for (var i = 0; i < blackHoles.length; i++) {
blackHoles[i].x -= dx2;
blackHoles[i].y -= dy2;
}
for (var i = 0; i < wormholes.length; i++) {
if (wormholes[i] !== self) {
wormholes[i].x -= dx2;
wormholes[i].y -= dy2;
}
}
self.cooldown = 180;
LK.effects.flashScreen(0x00ffff, 500);
}
if (self.cooldown > 0) {
self.cooldown--;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000033
});
/****
* Game Code
****/
// Her friend, en yakın pirate'ı (veya asteroid) bulup saldıracak, ship gibi
// --- FRIENDS: Dost birlikler NPC gibi düşmana karşı savaşa girsin ---
if (typeof friends !== "undefined" && Array.isArray(friends)) {
for (var i = 0; i < friends.length; i++) {
var friend = friends[i];
if (!friend) {
continue;
}
// En yakın pirate'ı bul
var nearestTarget = null;
var nearestDistance = Infinity;
for (var j = 0; j < pirates.length; j++) {
var pirate = pirates[j];
if (!pirate) {
continue;
}
var dist = Math.sqrt(Math.pow(pirate.x - friend.x, 2) + Math.pow(pirate.y - friend.y, 2));
if (dist < nearestDistance) {
nearestDistance = dist;
nearestTarget = pirate;
}
}
// Asteroidleri de hedef olarak eklemek isterseniz, aşağıdaki kodu açabilirsiniz:
// for (var j = 0; j < asteroids.length; j++) {
// var asteroid = asteroids[j];
// var dist = Math.sqrt(Math.pow(asteroid.x - friend.x, 2) + Math.pow(asteroid.y - friend.y, 2));
// if (dist < nearestDistance) {
// nearestDistance = dist;
// nearestTarget = asteroid;
// }
// }
// Saldırı menzili ship ile aynı olsun (ATTACK_RANGE)
var friendRange = typeof ATTACK_RANGE !== "undefined" ? ATTACK_RANGE : 220;
if (nearestTarget && nearestDistance < friendRange) {
// Eğer friend zaten bu hedefe saldırmıyorsa, saldırıyı başlat
if (!friend._attacking || friend._attackTarget !== nearestTarget) {
if (typeof friend.startAttack === "function") {
friend.startAttack(nearestTarget);
}
}
} else {
// Hedef yoksa saldırıyı bırak
if (typeof friend.stopAttack === "function") {
friend.stopAttack();
}
}
}
}
var ship;
var joystickBase;
var joystickHandle;
var isDragging = false;
var bullets = [];
var enemyBullets = [];
var npcs = [];
var pirates = [];
var coins = [];
var resources = [];
var stars = [];
var asteroids = [];
var upgradeStations = [];
var explosions = [];
var blackHoles = [];
var wormholes = [];
var tradeStations = [];
var pirateBases = [];
var tradeText = null; // No trade text needed
// Helper: spawn a pirate base at a random edge, with pirates around it
function spawnPirateBase() {
var base = new PirateBase();
// Spawn from edges of visible area
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
// Top
base.x = ship.x + (Math.random() - 0.5) * 2000;
base.y = ship.y - 1600;
break;
case 1:
// Right
base.x = ship.x + 1600;
base.y = ship.y + (Math.random() - 0.5) * 2000;
break;
case 2:
// Bottom
base.x = ship.x + (Math.random() - 0.5) * 2000;
base.y = ship.y + 1600;
break;
case 3:
// Left
base.x = ship.x - 1600;
base.y = ship.y + (Math.random() - 0.5) * 2000;
break;
}
;
pirateBases.push(base);
game.addChild(base);
// Spawn initial pirates around base
for (var i = 0; i < 10; i++) {
var pirate = new Pirate();
var angle = Math.PI * 2 * (i / 10);
pirate.x = base.x + Math.cos(angle) * (base.spawnRadius + Math.random() * 60);
pirate.y = base.y + Math.sin(angle) * (base.spawnRadius + Math.random() * 60);
// Level range: wave 1 => 1-3, wave 2 => 3-5, wave 3 => 5-7, etc.
var minLevel = 1 + waveNumber * 2;
var maxLevel = minLevel + 2;
pirate.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1));
pirate.health = 30 + (pirate.level - 1) * 10 + base.attackPower * 2;
pirate.damage = 5 + Math.floor((pirate.level - 1) * 1.5) + base.attackPower;
pirate.defense = 2 + Math.floor((pirate.level - 1) * 1.2) + base.defensePower;
pirate.rpgName = "BaseKorsan-" + Math.floor(Math.random() * 10000);
pirate._baseRef = base;
// Add health bar to pirate
pirate._healthBar = new HealthBar();
pirate._healthBar.set({
maxValue: function maxValue() {
return pirate.health > 0 ? pirate.health : 0;
},
getValueFn: function getValueFn() {
return pirate.health > 0 ? pirate.health : 0;
},
getMaxFn: function getMaxFn() {
return 30 + base.attackPower * 2;
},
width: 60,
height: 10
});
pirate._healthBar.y = -60;
pirate.addChild(pirate._healthBar);
pirates.push(pirate);
base.pirates.push(pirate);
game.addChild(pirate);
}
}
// tradeText UI removed (no tradeText in this version)
var camera = {
x: 0,
y: 0
};
// --- Helper for multi-pirate attack quest ---
// If groupCount >= 2, spawn GrupKorsan squad with reward label under each
function spawnPirateSquad(centerX, centerY, count, targetNPC) {
var squad = [];
if (count >= 2) {
// GrupKorsan: all move together, same target/angle
var minLevel = 1 + waveNumber * 2 + 5;
var maxLevel = minLevel + 2;
var groupAngle = Math.random() * Math.PI * 2;
var groupTarget = targetNPC || {
x: centerX + Math.cos(groupAngle) * 400,
y: centerY + Math.sin(groupAngle) * 400
};
for (var i = 0; i < count; i++) {
var gpirate = new GrupKorsan();
gpirate.groupIndex = i;
gpirate.groupSize = count;
gpirate.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1));
// Katil Korsan'ın statları: level = minLevel+10, health = (30 + (level-1)*10)*10, damage = (5 + ...)*10, defense = (2 + ...)*10
// GrupKorsan, Katil Korsan'ın statlarının %20 daha düşüğü olacak şekilde ayarlanır
var katilHealth = (30 + (gpirate.level - 1) * 10) * 10;
var katilDamage = (5 + Math.floor((gpirate.level - 1) * 1.5)) * 10;
var katilDefense = (2 + Math.floor((gpirate.level - 1) * 1.2)) * 10;
gpirate.health = Math.round(katilHealth * 0.8);
gpirate.damage = Math.round(katilDamage * 0.8);
gpirate.defense = Math.round(katilDefense * 0.8);
gpirate.loot = 200 + (gpirate.level - 1) * 50;
// --- GrupKorsan arası mesafe %15 oranında açıldı (200px yerine 200*1.15=230px) ---
// Her bir GrupKorsan'ın pozisyonu, merkezden eşit aralıklı ve %15 daha uzak olacak şekilde ayarlanır
var grupDistance = 200 * 1.15;
var angleOffset = Math.PI * 2 * i / count;
gpirate.x = centerX + Math.cos(groupAngle + angleOffset) * grupDistance + (Math.random() - 0.5) * 60;
gpirate.y = centerY + Math.sin(groupAngle + angleOffset) * grupDistance + (Math.random() - 0.5) * 60;
gpirate._groupTarget = groupTarget;
gpirate._groupAngle = groupAngle;
gpirate._groupMoveSpeed = 2 + Math.random() * 0.5;
gpirate.rpgName = "GrupKorsan-" + (i + 1) + "/" + count;
gpirate._isGrupKorsan = true;
pirates.push(gpirate);
game.addChild(gpirate);
squad.push(gpirate);
}
} else {
// Fallback: single pirate
for (var i = 0; i < count; i++) {
var pirate = new Pirate();
var angle = Math.PI * 2 * (i / count);
pirate.x = centerX + Math.cos(angle) * 200 + (Math.random() - 0.5) * 60;
pirate.y = centerY + Math.sin(angle) * 200 + (Math.random() - 0.5) * 60;
// Level range: wave 1 => 1-3, wave 2 => 3-5, wave 3 => 5-7, etc.
var minLevel = 1 + waveNumber * 2;
var maxLevel = minLevel + 2;
pirate.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1));
pirate.health = 30 + (pirate.level - 1) * 10;
pirate.damage = 5 + Math.floor((pirate.level - 1) * 1.5);
pirate.defense = 2 + Math.floor((pirate.level - 1) * 1.2);
pirate.rpgName = "Korsan-" + Math.floor(Math.random() * 10000);
pirate._squadTargetNPC = targetNPC;
pirates.push(pirate);
game.addChild(pirate);
squad.push(pirate);
}
}
return squad;
}
// Trade station tap detection (RPG/ticaret)
for (var i = 0; i < tradeStations.length; i++) {
var ts = tradeStations[i];
var dist = Math.sqrt(Math.pow(ts.x - ship.x, 2) + Math.pow(ts.y - ship.y, 2));
if (dist < 180) {
game._lastTradeStationTap = LK.ticks;
break;
}
}
var waveNumber = 0;
var enemiesKilled = 0;
var totalDistance = 0;
// Player stats
var playerCoins = storage.coins || 0;
// 10 hammadde için oyuncu envanteri
var playerResources = [storage.metal || 0,
// metal
storage.energy || 0,
// energy
storage.crystal || 0,
// crystal
storage.gas || 0,
// gas
storage.ice || 0,
// ice
storage.uranium || 0,
// uranium
storage.silicon || 0,
// silicon
storage.carbon || 0,
// carbon
storage.plasma || 0,
// plasma
storage.antimatter || 0 // antimatter
];
// Aliases for compatibility with old code
var playerMetal = playerResources[0];
var playerEnergy = playerResources[1];
var questsCompleted = storage.questsCompleted || 0;
// --- Player Level/EXP ---
var playerLevel = typeof storage.playerLevel !== "undefined" ? storage.playerLevel : 1;
var playerEXP = typeof storage.playerEXP !== "undefined" ? storage.playerEXP : 0;
function expToNext(level) {
// Simple EXP curve: next = 10 + 5*(level-1)
return 10 + 5 * (level - 1);
}
// UI Elements
// Calculate the unified font size (30% smaller than expText, then %10 büyüt)
var unifiedFontSize = Math.round(38 * 0.7 * 1.1);
// Add coin icon
var coinIcon = LK.getAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
x: 100,
y: 35,
scaleX: 1.2,
scaleY: 1.2
});
LK.gui.topLeft.addChild(coinIcon);
var coinText = new Text2('Coins: ' + playerCoins, {
size: unifiedFontSize,
fill: 0xFFFF00
});
coinText.anchor.set(0, 0);
coinText.x = 120;
coinText.y = 20;
LK.gui.topLeft.addChild(coinText);
// --- NPC ile savaş butonu ---
var npcFightBtnFont = Math.round(unifiedFontSize * 1.1);
window.npcFightBtn = new Text2("Fight NPCs", {
size: npcFightBtnFont,
fill: 0xff4444,
align: "center"
});
window.npcFightBtn.anchor.set(0.5, 0.5);
// Place at bottom center, above the bottom edge
window.npcFightBtn.x = 2048 / 2;
window.npcFightBtn.y = 2732 - 300; // moved 2 lines (about 120px) higher
LK.gui.bottom.addChild(window.npcFightBtn);
window.npcFightMode = false;
// --- UZAY İSTASYONU TESLİMAT BUTTON (above Reset, 2 rows up) ---
if (!window.stationDeliveryBtn) {
var deliveryBtnFont = Math.round(unifiedFontSize * 1.1);
// window.stationDeliveryBtn = new Text2("", { ... }); // BUTTON IS HIDDEN, DO NOT CREATE
// window.stationDeliveryBtn.anchor.set(1, 1);
// window.stationDeliveryBtn.x = -40;
// window.stationDeliveryBtn.y = -40 - 2 * 90;
// Add image above the button, 10% higher (yukarıya %10)
if (!window.stationDeliveryBtnImage) {
// Use the special image for the transparent button area
var imgAsset = LK.getAsset('stationDeliveryBtnAreaImg', {
anchorX: 1,
anchorY: 1,
scaleX: 0.7,
scaleY: 0.7
});
// Place image at the same x, but y is 10% higher (10% of image height above the button)
// Keep the station image fixed (do not move it up)
imgAsset.x = -40;
imgAsset.y = -40 - 2 * 90 - imgAsset.height * 1.1;
window.stationDeliveryBtnImage = imgAsset;
LK.gui.bottomRight.addChild(window.stationDeliveryBtnImage);
// Add a transparent button area exactly the size and position of the image for İstasyon Teslimat
var stationDeliveryBtnArea = new Text2("", {
size: 1,
fill: 0xffffff,
align: "center"
});
stationDeliveryBtnArea.anchor.set(1, 1);
// Place the button area exactly behind the image, matching its position and size
stationDeliveryBtnArea.x = imgAsset.x;
// Move the button area 1 row (imgAsset.height) above the image
stationDeliveryBtnArea.y = imgAsset.y - imgAsset.height;
stationDeliveryBtnArea.width = imgAsset.width;
stationDeliveryBtnArea.height = imgAsset.height;
stationDeliveryBtnArea.alpha = 0; // fully transparent, but still receives taps
window.stationDeliveryBtnArea = stationDeliveryBtnArea;
// Add the button area BEFORE the image so it is behind
LK.gui.bottomRight.addChild(stationDeliveryBtnArea);
LK.gui.bottomRight.addChild(window.stationDeliveryBtnImage);
}
// DO NOT ADD THE BUTTON TO THE GUI (HIDDEN)
// Add 'İstasyona Hammadde Gönder' text 2 rows below the button
if (!window.stationSendResourceText) {
var sendTextFont = Math.max(8, Math.round(unifiedFontSize * 1.0) - 2);
window.stationSendResourceText = new Text2("", {
size: sendTextFont,
fill: 0xffffff,
align: "center"
});
window.stationSendResourceText.anchor.set(1, 1);
// 2 rows below the stationDeliveryBtn (row = 90px)
// Use the same x/y as the image, but offset as if the button was there
window.stationSendResourceText.x = -40;
window.stationSendResourceText.y = -40 - 2 * 90 + 1 * 90;
LK.gui.bottomRight.addChild(window.stationSendResourceText);
}
}
// --- RESET BUTTON (bottom right) ---
if (!game._resetBtn) {
game._resetBtn = new Text2("Reset", {
size: Math.round(unifiedFontSize * 1.1),
fill: 0xff4444,
align: "center"
});
game._resetBtn.anchor.set(1, 1);
// Place at bottom right, with margin from edge
game._resetBtn.x = -40;
game._resetBtn.y = -40;
LK.gui.bottomRight.addChild(game._resetBtn);
}
// --- UZAY İSTASYONU TESLİMAT PANEL SYSTEM ---
// State for delivery system
if (!window.stationDeliveryState) {
// 10 hammadde için rastgele 50-200 arası istek, bonus %2-5 arası
window.stationDeliveryState = {
requests: [],
delivered: [],
completed: false,
bonus: 0
};
for (var i = 0; i < 10; i++) {
var req = 50 + Math.floor(Math.random() * 151); // 50-200
window.stationDeliveryState.requests.push(req);
window.stationDeliveryState.delivered.push(0);
}
window.stationDeliveryState.bonus = 2 + Math.floor(Math.random() * 4); // 2-5
window.stationDeliveryState.completed = false;
window.stationDeliveryState.rewardClaimed = false;
}
// Helper to open/close panel
window.openStationDeliveryPanel = function () {
if (window.stationDeliveryPanel) {
return;
}
// Increase font size by 2 for all panel texts/buttons
var panelFontSize = unifiedFontSize + 4;
var panelBtnFontSize = panelFontSize;
var labelFontSize = panelFontSize;
var closeBtnFontSize = panelFontSize;
var claimBtnFontSize = panelFontSize;
var bonusFontSize = panelFontSize;
var infoFontSize = panelFontSize;
var titleFontSize = panelFontSize;
// Calculate new panel size to fit enlarged text/buttons
var rowH = 80 + 8; // add 8px for extra font size
var panelW = 900 + 120; // add width for larger text/buttons
var panelH = 1100 + 2 * rowH; // add height for larger text/buttons
var px = 2048 / 2,
py = 2732 / 2;
// Panel BG
window.stationDeliveryPanelBG = LK.getAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
x: px,
y: py,
scaleX: panelW / 40,
scaleY: panelH / 40,
tint: 0x223355,
alpha: 0.97
});
// Title
window.stationDeliveryPanelTitle = new Text2("Space Station Delivery", {
size: titleFontSize,
fill: 0x00ffcc,
align: "center"
});
window.stationDeliveryPanelTitle.anchor.set(0.5, 0);
window.stationDeliveryPanelTitle.x = px;
window.stationDeliveryPanelTitle.y = py - panelH / 2 + 40;
// Info text
window.stationDeliveryPanelInfoText = new Text2("Complete the station's special resource requests!\nDeliver all to sell at +" + window.stationDeliveryState.bonus + "% bonus price.", {
size: infoFontSize,
fill: 0xffffff,
align: "center"
});
window.stationDeliveryPanelInfoText.anchor.set(0.5, 0);
window.stationDeliveryPanelInfoText.x = px;
window.stationDeliveryPanelInfoText.y = window.stationDeliveryPanelTitle.y + 60;
// Close button
window.stationDeliveryPanelCloseBtn = new Text2("Close", {
size: Math.round((unifiedFontSize + 4) * 1.3),
// 2 font larger than Coins, then 30% larger
fill: 0xff4444,
align: "center"
});
window.stationDeliveryPanelCloseBtn.anchor.set(0.5, 0.5);
// Move close button above the panel title, with extra margin
window.stationDeliveryPanelCloseBtn.x = px;
window.stationDeliveryPanelCloseBtn.y = window.stationDeliveryPanelTitle.y - 110; // further above title
// Resource request labels and delivery buttons
window.stationDeliveryLabels = [];
window.stationDeliveryDeliverBtns = [];
window.stationDeliveryStockLabels = [];
// Move all resource rows down by 3% of the panel height
var startY = window.stationDeliveryPanelInfoText.y + 80 + Math.round(panelH * 0.03);
for (var i = 0; i < window.resourceLabelOrder.length; i++) {
var resObj = window.resourceLabelOrder[i];
var req = window.stationDeliveryState.requests[resObj.origIdx];
var delivered = window.stationDeliveryState.delivered[resObj.origIdx];
var label = new Text2(resObj.name + ": " + delivered + " / " + req, {
size: labelFontSize,
fill: 0xffffff,
align: "left"
});
label.anchor.set(0, 0.5);
label.x = px - panelW / 2 + 60;
label.y = startY + i * rowH;
window.stationDeliveryLabels.push(label);
// Oyuncu stok durumu etiketi
var stockAmount = typeof playerResources[resObj.origIdx] !== "undefined" ? playerResources[resObj.origIdx] : 0;
var stockLabel = new Text2("Stock: " + stockAmount, {
size: Math.max(18, Math.round(labelFontSize * 0.85)),
fill: 0xcccccc,
align: "left"
});
stockLabel.anchor.set(0, 0.5);
stockLabel.x = label.x + 340;
stockLabel.y = label.y;
window.stationDeliveryStockLabels.push(stockLabel);
// Deliver button
var teslimBtn = new Text2("Deliver", {
size: panelBtnFontSize,
fill: 0x00ff99,
align: "center"
});
teslimBtn.anchor.set(0.5, 0.5);
teslimBtn.x = px + 220;
teslimBtn.y = label.y;
teslimBtn._stationResIdx = resObj.origIdx;
window.stationDeliveryDeliverBtns.push(teslimBtn);
}
// Bonus info
window.stationDeliveryPanelBonusText = new Text2("Complete all requests to sell at +" + window.stationDeliveryState.bonus + "% bonus price.", {
size: bonusFontSize,
fill: 0xffff00,
align: "center"
});
window.stationDeliveryPanelBonusText.anchor.set(0.5, 0);
window.stationDeliveryPanelBonusText.x = px;
window.stationDeliveryPanelBonusText.y = startY + rowH * 10 + 30;
// Complete delivery (claim reward) button
window.stationDeliveryPanelClaimBtn = new Text2("Deliver All & Claim Reward", {
size: claimBtnFontSize,
fill: 0xffcc00,
align: "center"
});
window.stationDeliveryPanelClaimBtn.anchor.set(0.5, 0.5);
window.stationDeliveryPanelClaimBtn.x = px;
window.stationDeliveryPanelClaimBtn.y = window.stationDeliveryPanelBonusText.y + 90;
// Add to game
game.addChild(window.stationDeliveryPanelBG);
game.addChild(window.stationDeliveryPanelTitle);
game.addChild(window.stationDeliveryPanelInfoText);
game.addChild(window.stationDeliveryPanelCloseBtn);
for (var i = 0; i < window.stationDeliveryLabels.length; i++) {
game.addChild(window.stationDeliveryLabels[i]);
}
for (var i = 0; i < window.stationDeliveryStockLabels.length; i++) {
game.addChild(window.stationDeliveryStockLabels[i]);
}
for (var i = 0; i < window.stationDeliveryDeliverBtns.length; i++) {
game.addChild(window.stationDeliveryDeliverBtns[i]);
}
game.addChild(window.stationDeliveryPanelBonusText);
game.addChild(window.stationDeliveryPanelClaimBtn);
window.stationDeliveryPanel = true;
};
window.closeStationDeliveryPanel = function () {
if (!window.stationDeliveryPanel) {
return;
}
if (window.stationDeliveryPanelBG && window.stationDeliveryPanelBG.parent) {
window.stationDeliveryPanelBG.parent.removeChild(window.stationDeliveryPanelBG);
}
if (window.stationDeliveryPanelTitle && window.stationDeliveryPanelTitle.parent) {
window.stationDeliveryPanelTitle.parent.removeChild(window.stationDeliveryPanelTitle);
}
if (window.stationDeliveryPanelInfoText && window.stationDeliveryPanelInfoText.parent) {
window.stationDeliveryPanelInfoText.parent.removeChild(window.stationDeliveryPanelInfoText);
}
if (window.stationDeliveryPanelCloseBtn && window.stationDeliveryPanelCloseBtn.parent) {
window.stationDeliveryPanelCloseBtn.parent.removeChild(window.stationDeliveryPanelCloseBtn);
}
if (window.stationDeliveryLabels) {
for (var i = 0; i < window.stationDeliveryLabels.length; i++) {
if (window.stationDeliveryLabels[i] && window.stationDeliveryLabels[i].parent) {
window.stationDeliveryLabels[i].parent.removeChild(window.stationDeliveryLabels[i]);
}
}
}
if (window.stationDeliveryDeliverBtns) {
for (var i = 0; i < window.stationDeliveryDeliverBtns.length; i++) {
if (window.stationDeliveryDeliverBtns[i] && window.stationDeliveryDeliverBtns[i].parent) {
window.stationDeliveryDeliverBtns[i].parent.removeChild(window.stationDeliveryDeliverBtns[i]);
}
}
}
if (window.stationDeliveryPanelBonusText && window.stationDeliveryPanelBonusText.parent) {
window.stationDeliveryPanelBonusText.parent.removeChild(window.stationDeliveryPanelBonusText);
}
if (window.stationDeliveryPanelClaimBtn && window.stationDeliveryPanelClaimBtn.parent) {
window.stationDeliveryPanelClaimBtn.parent.removeChild(window.stationDeliveryPanelClaimBtn);
}
window.stationDeliveryPanel = null;
window.stationDeliveryPanelBG = null;
window.stationDeliveryPanelTitle = null;
window.stationDeliveryPanelInfoText = null;
window.stationDeliveryPanelCloseBtn = null;
window.stationDeliveryLabels = null;
window.stationDeliveryDeliverBtns = null;
window.stationDeliveryPanelBonusText = null;
window.stationDeliveryPanelClaimBtn = null;
// Hide station stock labels when panel is closed
if (window.stationDeliveryStockLabels) {
for (var i = 0; i < window.stationDeliveryStockLabels.length; i++) {
if (window.stationDeliveryStockLabels[i] && window.stationDeliveryStockLabels[i].parent) {
window.stationDeliveryStockLabels[i].parent.removeChild(window.stationDeliveryStockLabels[i]);
}
}
window.stationDeliveryStockLabels = null;
}
};
// --- GAME TIME LABEL ---
// Track game start time in ticks
var gameStartTick = LK.ticks;
var gameTimeText = new Text2('Game Time: 0:00', {
size: unifiedFontSize,
fill: 0xcccccc
});
gameTimeText.anchor.set(0, 0);
gameTimeText.x = coinText.x + coinText.width + 40;
gameTimeText.y = coinText.y;
LK.gui.topLeft.addChild(gameTimeText);
// Add an image below the Game Time label
if (!window.gameTimeImage) {
// Use an existing image asset, e.g. 'teslimEtBtnBg' as a placeholder
// Move the Global Panel image and button to the row above the station image
var imgScale = 0.7;
var imgAsset = LK.getAsset('teslimEtBtnBg', {
anchorX: 1,
anchorY: 1,
scaleX: imgScale,
scaleY: imgScale
});
// Place the image at the same x as the stationDeliveryBtnImage, but 1 row above it
// If stationDeliveryBtnImage exists, align to it
var refImg = window.stationDeliveryBtnImage;
if (refImg) {
imgAsset.x = refImg.x;
imgAsset.y = refImg.y - refImg.height; // 1 row above station image
} else {
// fallback: 10% right from left edge, and 10px below the Game Time label
imgAsset.x = Math.round(2048 * 0.10);
imgAsset.y = gameTimeText.y + gameTimeText.height + 10;
}
window.gameTimeImage = imgAsset;
LK.gui.bottomRight.addChild(window.gameTimeImage);
// --- Add a new independent image file at the position 2 rows above Hammadde Takası (Resource Trade) image ---
// Remove the previously created image if it exists
if (window.resourceTradePanelImage && window.resourceTradePanelImage.parent) {
window.resourceTradePanelImage.parent.removeChild(window.resourceTradePanelImage);
window.resourceTradePanelImage = null;
}
// Create a new independent image at the same position (now 1 row above Hammadde Takası image)
if (!window.resourceTradePanelIndependentImage) {
var independentImgScale = 0.7;
var independentImgAsset = LK.getAsset('resourceTradePanelIndependentCus', {
anchorX: 1,
anchorY: 1,
scaleX: independentImgScale,
scaleY: independentImgScale
});
// Place 1 row above the Hammadde Takası (Resource Trade) image
independentImgAsset.x = imgAsset.x;
independentImgAsset.y = imgAsset.y - imgAsset.height * 1;
window.resourceTradePanelIndependentImage = independentImgAsset;
LK.gui.bottomRight.addChild(independentImgAsset);
// Move the Hammadde Stoğu panel button (tap area) 2 rows above the image, image stays fixed
if (!window.resourceTradePanelIndependentBtn) {
var btn = new Text2("", {
size: 1,
fill: 0xffffff,
align: "center"
});
btn.anchor.set(1, 1);
btn.x = independentImgAsset.x;
btn.y = independentImgAsset.y - independentImgAsset.height * 2; // 2 rows above the image
btn.width = independentImgAsset.width;
btn.height = independentImgAsset.height;
btn.alpha = 0; // fully transparent, but still receives taps
window.resourceTradePanelIndependentBtn = btn;
LK.gui.bottomRight.addChild(btn);
}
}
// Add a transparent button area exactly the size and position of the image for Global Market
var marketBtn = new Text2("Global Market", {
size: 1,
fill: 0xffffff,
align: "center"
});
marketBtn.anchor.set(1, 1);
marketBtn.x = imgAsset.x;
// Move the button area 2 rows (imgAsset.height * 2) above the image, image stays fixed
marketBtn.y = imgAsset.y - imgAsset.height * 2;
// Grow the button area 10% downward and shrink it 5% from the top
marketBtn.width = imgAsset.width;
marketBtn.height = imgAsset.height * 1.10 * 0.95; // first grow 10%, then shrink 5% from top
marketBtn.alpha = 0; // fully transparent, but still receives taps
window.globalMarketBtn = marketBtn;
LK.gui.bottomRight.addChild(marketBtn);
}
// --- RESOURCE TRADE PANEL STATE ---
if (!window.resourceTradePanelState) {
// 10 hammadde için değerler (1-10 arası, rastgele başlat)
window.resourceTradeValues = [];
for (var i = 0; i < 10; i++) {
window.resourceTradeValues.push(1 + Math.floor(Math.random() * 10));
}
window.resourceTradePanelState = {
open: false,
lastValueUpdateTick: LK.ticks
};
}
// Timer: her 5 saniyede bir (300 tick) değerleri değiştir
if (!window._resourceTradeValueTimer) {
window._resourceTradeValueTimer = LK.setInterval(function () {
if (!window.resourceTradeValues) {
return;
}
for (var i = 0; i < window.resourceTradeValues.length; i++) {
// Değeri -3/+3 arası değiştir, 1-10 aralığında tut
var delta = Math.floor(Math.random() * 7) - 3;
window.resourceTradeValues[i] = Math.max(1, Math.min(10, window.resourceTradeValues[i] + delta));
}
// Panel açıksa UI güncelle
if (window.resourceTradePanelState && window.resourceTradePanelState.open && typeof window.updateResourceTradePanel === "function") {
window.updateResourceTradePanel();
}
}, 5000);
}
// Resource UI: create 10 vertically stacked resource labels, left-aligned
// Sort resourceTypes by name length descending, keep original index for mapping to playerResources
var resourceTypes = ['Metal', 'Energy', 'Crystal', 'Gas', 'Ice', 'Uranium', 'Silicon', 'Carbon', 'Plasma', 'Antimatter'];
var resourceTypeObjs = [];
for (var i = 0; i < resourceTypes.length; i++) {
resourceTypeObjs.push({
name: resourceTypes[i],
origIdx: i
});
}
resourceTypeObjs.sort(function (a, b) {
// Sort by name length descending, then alphabetically for ties
if (b.name.length !== a.name.length) {
return b.name.length - a.name.length;
}
return a.name.localeCompare(b.name);
});
window.resourceLabels = [];
window.resourceLabelOrder = resourceTypeObjs; // Save for updateUI
var resourceIconMargin = 8;
var resourceLabelStartY = Math.round(2732 * 0.05); // 5% down from the top
for (var i = 0; i < resourceTypeObjs.length; i++) {
var label = new Text2(resourceTypeObjs[i].name + ': 0', {
size: unifiedFontSize,
fill: 0xFFFFFF
});
label.anchor.set(0, 0);
label.x = 20; // flush to left edge, but leave margin for menu icon
label.y = resourceLabelStartY + i * (unifiedFontSize + resourceIconMargin);
// Do NOT add to GUI, so resource stock text is hidden on main screen
// LK.gui.topLeft.addChild(label);
window.resourceLabels.push(label);
}
// --- RESOURCE TRADE PANEL LOGIC ---
window.openResourceTradePanel = function () {
if (window.resourceTradePanelState.open) {
return;
}
window.resourceTradePanelState.open = true;
// Increase font size by 2 for all panel texts/buttons
var panelFontSize = unifiedFontSize + 4;
var panelBtnFontSize = panelFontSize;
var labelFontSize = panelFontSize;
var closeBtnFontSize = panelFontSize;
var infoFontSize = panelFontSize;
var titleFontSize = panelFontSize;
// Panel boyutları ve konum (resize for larger text/buttons)
var rowH = 80 + 8;
var panelW = 1200 + 120;
var panelH = 1200 + 2 * rowH;
var px = 2048 / 2,
py = 2732 / 2;
// Panel BG
window.resourceTradePanelBG = LK.getAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
x: px,
y: py,
scaleX: panelW / 40,
scaleY: panelH / 40,
tint: 0x223355,
alpha: 0.97
});
// Başlık
window.resourceTradePanelTitle = new Text2("Resource Trading", {
size: titleFontSize,
fill: 0x00ffcc,
align: "center"
});
window.resourceTradePanelTitle.anchor.set(0.5, 0);
window.resourceTradePanelTitle.x = px;
window.resourceTradePanelTitle.y = py - panelH / 2 + 40;
// Info text
window.resourceTradePanelInfoText = new Text2("You can trade your resources.\nEach resource value changes between 5-30 and updates every 5 seconds.", {
size: infoFontSize,
fill: 0xffffff,
align: "center"
});
window.resourceTradePanelInfoText.anchor.set(0.5, 0);
window.resourceTradePanelInfoText.x = px;
window.resourceTradePanelInfoText.y = window.resourceTradePanelTitle.y + 60;
// Kapat butonu
window.resourceTradePanelCloseBtn = new Text2("Close", {
size: Math.round((unifiedFontSize + 4) * 1.3),
// 2 font larger than Coins, then 30% larger
fill: 0xff4444,
align: "center"
});
window.resourceTradePanelCloseBtn.anchor.set(0.5, 0.5);
window.resourceTradePanelCloseBtn.x = px;
window.resourceTradePanelCloseBtn.y = window.resourceTradePanelTitle.y - 110; // further above title
// Hammadde satırları ve butonları
window.resourceTradeLabels = [];
window.resourceTradeSellBtns = [];
window.resourceTradeSellAllBtns = [];
window.resourceTradeBuyBtns = [];
var startY = window.resourceTradePanelInfoText.y + 80 + rowH * 2; // move all rows down by 2 rows (1 row further)
for (var i = 0; i < window.resourceLabelOrder.length; i++) {
var resObj = window.resourceLabelOrder[i];
var idx = resObj.origIdx;
var label = new Text2(resObj.name + ": " + (playerResources[idx] || 0) + " | Value: " + (window.resourceTradeValues ? window.resourceTradeValues[idx] : 0), {
size: labelFontSize,
fill: 0xffffff,
align: "left"
});
label.anchor.set(0, 0.5);
label.x = px - panelW / 2 + 60;
label.y = startY + i * rowH;
window.resourceTradeLabels.push(label);
// Sell button
var sellBtn = new Text2("Sell", {
size: panelBtnFontSize,
fill: 0x00ff99,
align: "center"
});
sellBtn.anchor.set(0.5, 0.5);
sellBtn.x = px + 220;
sellBtn.y = label.y;
sellBtn._resourceIdx = idx;
window.resourceTradeSellBtns.push(sellBtn);
// Sell All button
var sellAllBtn = new Text2("Sell All", {
size: panelBtnFontSize,
fill: 0x00cc99,
align: "center"
});
sellAllBtn.anchor.set(0.5, 0.5);
sellAllBtn.x = px + 400;
sellAllBtn.y = label.y;
sellAllBtn._resourceIdx = idx;
window.resourceTradeSellAllBtns.push(sellAllBtn);
// Buy button
var buyBtn = new Text2("Buy", {
size: panelBtnFontSize,
fill: 0x0099ff,
align: "center"
});
buyBtn.anchor.set(0.5, 0.5);
buyBtn.x = px + 580;
buyBtn.y = label.y;
buyBtn._resourceIdx = idx;
window.resourceTradeBuyBtns.push(buyBtn);
}
// Add to game
game.addChild(window.resourceTradePanelBG);
game.addChild(window.resourceTradePanelTitle);
game.addChild(window.resourceTradePanelInfoText);
game.addChild(window.resourceTradePanelCloseBtn);
for (var i = 0; i < window.resourceTradeLabels.length; i++) {
game.addChild(window.resourceTradeLabels[i]);
}
for (var i = 0; i < window.resourceTradeSellBtns.length; i++) {
game.addChild(window.resourceTradeSellBtns[i]);
}
for (var i = 0; i < window.resourceTradeSellAllBtns.length; i++) {
game.addChild(window.resourceTradeSellAllBtns[i]);
}
for (var i = 0; i < window.resourceTradeBuyBtns.length; i++) {
game.addChild(window.resourceTradeBuyBtns[i]);
}
// Panel güncelleme fonksiyonu
window.updateResourceTradePanel = function () {
for (var i = 0; i < window.resourceLabelOrder.length; i++) {
var resObj = window.resourceLabelOrder[i];
var idx = resObj.origIdx;
if (window.resourceTradeLabels[i]) {
window.resourceTradeLabels[i].setText(resObj.name + ": " + (playerResources[idx] || 0) + " | Değer: " + (window.resourceTradeValues ? window.resourceTradeValues[idx] : 0));
}
}
};
window.updateResourceTradePanel();
};
window.closeResourceTradePanel = function () {
if (!window.resourceTradePanelState.open) {
return;
}
window.resourceTradePanelState.open = false;
if (window.resourceTradePanelBG && window.resourceTradePanelBG.parent) {
window.resourceTradePanelBG.parent.removeChild(window.resourceTradePanelBG);
}
if (window.resourceTradePanelTitle && window.resourceTradePanelTitle.parent) {
window.resourceTradePanelTitle.parent.removeChild(window.resourceTradePanelTitle);
}
if (window.resourceTradePanelInfoText && window.resourceTradePanelInfoText.parent) {
window.resourceTradePanelInfoText.parent.removeChild(window.resourceTradePanelInfoText);
}
if (window.resourceTradePanelCloseBtn && window.resourceTradePanelCloseBtn.parent) {
window.resourceTradePanelCloseBtn.parent.removeChild(window.resourceTradePanelCloseBtn);
}
if (window.resourceTradeLabels) {
for (var i = 0; i < window.resourceTradeLabels.length; i++) {
if (window.resourceTradeLabels[i] && window.resourceTradeLabels[i].parent) {
window.resourceTradeLabels[i].parent.removeChild(window.resourceTradeLabels[i]);
}
}
}
if (window.resourceTradeSellBtns) {
for (var i = 0; i < window.resourceTradeSellBtns.length; i++) {
if (window.resourceTradeSellBtns[i] && window.resourceTradeSellBtns[i].parent) {
window.resourceTradeSellBtns[i].parent.removeChild(window.resourceTradeSellBtns[i]);
}
}
}
if (window.resourceTradeSellAllBtns) {
for (var i = 0; i < window.resourceTradeSellAllBtns.length; i++) {
if (window.resourceTradeSellAllBtns[i] && window.resourceTradeSellAllBtns[i].parent) {
window.resourceTradeSellAllBtns[i].parent.removeChild(window.resourceTradeSellAllBtns[i]);
}
}
}
if (window.resourceTradeBuyBtns) {
for (var i = 0; i < window.resourceTradeBuyBtns.length; i++) {
if (window.resourceTradeBuyBtns[i] && window.resourceTradeBuyBtns[i].parent) {
window.resourceTradeBuyBtns[i].parent.removeChild(window.resourceTradeBuyBtns[i]);
}
}
}
window.resourceTradePanelBG = null;
window.resourceTradePanelTitle = null;
window.resourceTradePanelInfoText = null;
window.resourceTradePanelCloseBtn = null;
window.resourceTradeLabels = null;
window.resourceTradeSellBtns = null;
window.resourceTradeSellAllBtns = null;
window.resourceTradeBuyBtns = null;
};
var healthText = new Text2('Health: 100/100', {
size: unifiedFontSize,
fill: 0x00FF00
});
healthText.anchor.set(0.5, 0);
healthText._isHealthBar = true; // Mark for tap detection
LK.gui.top.addChild(healthText);
var shieldText = new Text2('Shield: 50/50', {
size: unifiedFontSize,
fill: 0x0088FF
});
shieldText.anchor.set(0.5, 0);
shieldText.y = 60;
LK.gui.top.addChild(shieldText);
// --- Add waveText and expText UI for wave/level display ---
// --- REPAIR PANEL WARNING SUPPRESSION ---
// When repair panel is closed, set a suppression timer for 30 seconds (1800 ticks)
if (window.repairPanel && !window._repairPanelSuppressUntil) {
window._repairPanelSuppressUntil = LK.ticks + 1800;
}
// (removed duplicate handler, see main game.down below)
var waveText = new Text2('Wave: 1', {
size: unifiedFontSize,
fill: 0xffffff
});
waveText.anchor.set(0.5, 0);
waveText.x = 1024;
waveText.y = 60; // Move down to make space for market button
LK.gui.top.addChild(waveText);
var expText = new Text2('Level: 1 EXP: 0/10', {
size: unifiedFontSize,
fill: 0xffffff
});
expText.anchor.set(0.5, 0);
expText.x = 1024;
expText.y = 70;
LK.gui.top.addChild(expText);
// --- Add image to right edge, 3 rows below waveText ---
if (!window.levelPanelBelowImg) {
// Use the 'levelPanelBelowImg' asset as the image
var imgAsset = LK.getAsset('levelPanelBelowImg', {
anchorX: 1,
// right edge
anchorY: 0,
// top
scaleX: 0.7,
scaleY: 0.7
});
// Place at right edge (x=0 in bottomRight, so x=-margin from right)
// LK.gui.topRight is anchored at (1,0), so x=0 is right edge, y=0 is top
// waveText.y is 60, so 3 rows below is 60 + 3*unifiedFontSize + 3*10 (10px margin per row)
var rowH = unifiedFontSize + 10;
imgAsset.x = -40; // 40px margin from right edge
imgAsset.y = waveText.y + 3 * rowH;
window.levelPanelBelowImg = imgAsset;
LK.gui.topRight.addChild(imgAsset);
// --- Achievements Panel Tap Area (transparent, over Achievements image) ---
if (!window.achievementsPanelBtn) {
var achBtn = new Text2("", {
size: 1,
fill: 0xffffff,
align: "center"
});
achBtn.anchor.set(1, 0);
achBtn.x = imgAsset.x;
achBtn.y = imgAsset.y;
achBtn.width = imgAsset.width;
achBtn.height = imgAsset.height;
achBtn.alpha = 0; // fully transparent, but receives taps
window.achievementsPanelBtn = achBtn;
LK.gui.topRight.addChild(achBtn);
}
// --- Add new image just below Achievements image (right edge, keep all others fixed) ---
if (!window.newImageAboveStorage) {
var newImgAsset = LK.getAsset('newImageAboveStorage', {
anchorX: 1,
anchorY: 0,
scaleX: 0.7,
scaleY: 0.7
});
// Place just below Achievements image (levelPanelBelowImg)
if (window.levelPanelBelowImg) {
newImgAsset.x = window.levelPanelBelowImg.x;
newImgAsset.y = window.levelPanelBelowImg.y + window.levelPanelBelowImg.height + 10;
} else if (typeof imgAsset !== "undefined") {
// fallback: just below the previous image
newImgAsset.x = imgAsset.x;
newImgAsset.y = imgAsset.y + imgAsset.height + 10;
} else {
// fallback: right edge, 2 rows from top
newImgAsset.x = -40;
newImgAsset.y = 10 + newImgAsset.height * 2 + 20;
}
window.newImageAboveStorage = newImgAsset;
LK.gui.topRight.addChild(newImgAsset);
}
// --- Add upg image to right edge, 3 rows below waveText, just below the previous image ---
if (!window.upgPanelBelowImg) {
var upgImgAsset = LK.getAsset('upg', {
anchorX: 1,
anchorY: 0,
scaleX: 0.7,
scaleY: 0.7
});
upgImgAsset.x = -40;
upgImgAsset.y = imgAsset.y + imgAsset.height + 10 + upgImgAsset.height + 10; // move 1 row (image height + 10px) further down
window.upgPanelBelowImg = upgImgAsset;
LK.gui.topRight.addChild(upgImgAsset);
// Add a transparent tap area over the upg image for opening the Upgrades panel
if (!window.upgPanelBtn) {
var upgBtn = new Text2("", {
size: 1,
fill: 0xffffff,
align: "center"
});
upgBtn.anchor.set(1, 0);
upgBtn.x = upgImgAsset.x;
// Move the button 1 row (image height + 10px) further down, image stays fixed
upgBtn.y = upgImgAsset.y + upgImgAsset.height + 10;
// Make the button area 1.5x wider and 1.5x taller than the image, centered on the image's position
upgBtn.width = upgImgAsset.width * 1.5;
upgBtn.height = upgImgAsset.height * 1.5;
// Shift the button left and up so it stays centered on the image
upgBtn.x = upgImgAsset.x - (upgBtn.width - upgImgAsset.width) / 2;
upgBtn.y = upgImgAsset.y + upgImgAsset.height + 10 - (upgBtn.height - upgImgAsset.height) / 2;
upgBtn.alpha = 0; // fully transparent, but receives taps
window.upgPanelBtn = upgBtn;
LK.gui.topRight.addChild(upgBtn);
}
}
}
// --- Achievements Panel Logic ---
// 50 achievements, with difficulty and rewards
if (!window.achievementsList) {
window.achievementsList = [
// id, name, desc, reward, difficulty
{
id: 1,
name: "First Blood",
desc: "Destroy your first pirate.",
reward: 50,
difficulty: "Easy"
}, {
id: 2,
name: "Miner",
desc: "Collect resources from an asteroid.",
reward: 30,
difficulty: "Easy"
}, {
id: 3,
name: "Trader",
desc: "Trade a resource.",
reward: 40,
difficulty: "Easy"
}, {
id: 5,
name: "Killer Pirate",
desc: "Destroy the killer pirate.",
reward: 500,
difficulty: "Hard"
}, {
id: 6,
name: "Rich",
desc: "Accumulate 1000 coins.",
reward: 100,
difficulty: "Medium"
}, {
id: 7,
name: "Mechanic",
desc: "Repair your ship.",
reward: 30,
difficulty: "Easy"
}, {
id: 8,
name: "Level Up",
desc: "Reach level 5.",
reward: 80,
difficulty: "Medium"
}, {
id: 9,
name: "Shield Master",
desc: "Fully charge your shield.",
reward: 40,
difficulty: "Easy"
}, {
id: 11,
name: "Pirate Hunter",
desc: "Destroy 10 pirates.",
reward: 120,
difficulty: "Medium"
}, {
id: 12,
name: "Asteroid Destroyer",
desc: "Destroy 5 asteroids.",
reward: 80,
difficulty: "Medium"
}, {
id: 13,
name: "Trade Master",
desc: "Complete 10 trades total.",
reward: 150,
difficulty: "Hard"
}, {
id: 14,
name: "Explorer",
desc: "Travel a total of 10,000 distance units.",
reward: 100,
difficulty: "Medium"
}, {
id: 15,
name: "Pirate King",
desc: "Destroy 50 pirates.",
reward: 300,
difficulty: "Hard"
}, {
id: 16,
name: "Resource King",
desc: "Collect 100 units of all resources.",
reward: 200,
difficulty: "Hard"
}, {
id: 17,
name: "Upgrader",
desc: "Purchase an upgrade.",
reward: 60,
difficulty: "Easy"
}, {
id: 18,
name: "Repair Specialist",
desc: "Repair 5 times.",
reward: 100,
difficulty: "Medium"
}, {
id: 19,
name: "Wave Passer",
desc: "Reach wave 5.",
reward: 80,
difficulty: "Medium"
}, {
id: 20,
name: "Wave Master",
desc: "Reach wave 10.",
reward: 200,
difficulty: "Hard"
}, {
id: 21,
name: "Shield King",
desc: "Upgrade shield to 200.",
reward: 120,
difficulty: "Hard"
}, {
id: 22,
name: "Beast",
desc: "Upgrade health to 300.",
reward: 120,
difficulty: "Hard"
}, {
id: 23,
name: "Fast",
desc: "Upgrade speed to 10.",
reward: 100,
difficulty: "Hard"
}, {
id: 24,
name: "Bullet Storm",
desc: "Reduce fire rate to 5.",
reward: 100,
difficulty: "Hard"
}, {
id: 25,
name: "Energy Master",
desc: "Collect a total of 500 energy.",
reward: 100,
difficulty: "Medium"
}, {
id: 26,
name: "Metal Worker",
desc: "Collect a total of 500 metal.",
reward: 100,
difficulty: "Medium"
}, {
id: 27,
name: "Ice Man",
desc: "Collect a total of 100 ice.",
reward: 80,
difficulty: "Medium"
}, {
id: 28,
name: "Plasma Hunter",
desc: "Collect a total of 50 plasma.",
reward: 80,
difficulty: "Medium"
}, {
id: 29,
name: "Antimatter Collector",
desc: "Collect a total of 10 antimatter.",
reward: 120,
difficulty: "Hard"
}, {
id: 33,
name: "Space Station",
desc: "Make a delivery to the station.",
reward: 100,
difficulty: "Medium"
}, {
id: 34,
name: "Big Delivery",
desc: "Complete all station requests.",
reward: 300,
difficulty: "Hard"
}, {
id: 35,
name: "Pirate Base",
desc: "Destroy a pirate base.",
reward: 200,
difficulty: "Hard"
}, {
id: 36,
name: "Pirate Base Hunter",
desc: "Destroy 3 pirate bases.",
reward: 400,
difficulty: "Very Hard"
}, {
id: 38,
name: "Resource Tycoon",
desc: "Collect 500 units of all resources.",
reward: 400,
difficulty: "Very Hard"
}, {
id: 39,
name: "Trade Tycoon",
desc: "Complete a total of 100 trades.",
reward: 400,
difficulty: "Very Hard"
}, {
id: 40,
name: "Level Master",
desc: "Reach level 20.",
reward: 400,
difficulty: "Very Hard"
}, {
id: 41,
name: "Wave Legend",
desc: "Reach wave 20.",
reward: 500,
difficulty: "Very Hard"
}, {
id: 42,
name: "Space Conqueror",
desc: "Travel a total of 100,000 distance units.",
reward: 500,
difficulty: "Very Hard"
}, {
id: 43,
name: "Pirate Slayer",
desc: "Destroy 100 pirates.",
reward: 600,
difficulty: "Very Hard"
}, {
id: 44,
name: "Asteroid King",
desc: "Destroy 50 asteroids.",
reward: 400,
difficulty: "Very Hard"
}, {
id: 45,
name: "Upgrade King",
desc: "Max out all upgrades.",
reward: 600,
difficulty: "Very Hard"
}, {
id: 46,
name: "Repair King",
desc: "Repair 20 times.",
reward: 300,
difficulty: "Very Hard"
},
// NEW: Active gameplay feature achievements
{
id: 51,
name: "Asteroid Hunter",
desc: "Destroy 20 asteroids.",
reward: 150,
difficulty: "Medium"
}, {
id: 52,
name: "Pirate Raider",
desc: "Destroy 10 pirates in one wave.",
reward: 200,
difficulty: "Hard"
}, {
id: 53,
name: "Fast Collector",
desc: "Collect 10 resources in one minute.",
reward: 120,
difficulty: "Medium"
}, {
id: 54,
name: "Rich Miner",
desc: "Have 1000 coins and 100 resources at the same time.",
reward: 250,
difficulty: "Hard"
}, {
id: 55,
name: "Upgrade Series",
desc: "Buy 3 upgrades in a row.",
reward: 180,
difficulty: "Medium"
}, {
id: 56,
name: "Achievement Hunter",
desc: "Complete all achievements.",
reward: 1000,
difficulty: "Legendary"
},
// --- NEW 10 ACHIEVEMENTS (latest features) ---
{
id: 57,
name: "GroupPirate Hunter",
desc: "Completely destroy a GroupPirate squad.",
reward: 250,
difficulty: "Hard"
}, {
id: 58,
name: "CoinBox Hunter",
desc: "Collect a CoinBox.",
reward: 80,
difficulty: "Easy"
}, {
id: 59,
name: "CoinBox Tycoon",
desc: "Collect a total of 10 CoinBoxes.",
reward: 300,
difficulty: "Hard"
}, {
id: 60,
name: "Wreck Hunter",
desc: "Collect a Derelict Wreck.",
reward: 60,
difficulty: "Easy"
}, {
id: 61,
name: "Wreck Tycoon",
desc: "Collect a total of 10 Derelict Wrecks.",
reward: 250,
difficulty: "Medium"
}, {
id: 62,
name: "Double Pirate",
desc: "Fight 2 pirates at the same time.",
reward: 120,
difficulty: "Medium"
}, {
id: 63,
name: "Triple GroupPirate",
desc: "Fight 3 GroupPirates at the same time.",
reward: 200,
difficulty: "Hard"
}, {
id: 64,
name: "Asteroid Rain",
desc: "Destroy 5 asteroids in one wave.",
reward: 180,
difficulty: "Hard"
}, {
id: 65,
name: "Resource Burst",
desc: "Collect more than 20 resources at once.",
reward: 220,
difficulty: "Hard"
}, {
id: 66,
name: "Pirate Swarm",
desc: "Fight more than 5 pirates in one wave.",
reward: 250,
difficulty: "Very Hard"
}];
// Save progress
if (!storage.achievements) {
storage.achievements = [];
}
window.achievementsProgress = storage.achievements;
}
// --- BAŞARIM MEKANİĞİ EKLENDİ ---
// Yardımcı fonksiyon: başarıma sahip mi?
function hasAchievement(id) {
return window.achievementsProgress && window.achievementsProgress.indexOf(id) !== -1;
}
// Yardımcı fonksiyon: başarıma ekle ve ödül ver
function grantAchievement(id) {
if (!window.achievementsProgress) {
window.achievementsProgress = [];
}
if (window.achievementsProgress.indexOf(id) !== -1) {
return;
} // Zaten kazanıldı
window.achievementsProgress.push(id);
storage.achievements = window.achievementsProgress;
// Ödül ver
var ach = null;
for (var i = 0; i < window.achievementsList.length; i++) {
if (window.achievementsList[i].id === id) {
ach = window.achievementsList[i];
break;
}
}
if (ach && typeof playerCoins !== "undefined") {
playerCoins += ach.reward;
updateUI && updateUI();
saveProgress && saveProgress();
// Show a short notification
var label = new Text2("Achievement Earned!\n" + ach.name + "\n+" + ach.reward + " coin", {
size: 44,
fill: 0x00ffcc,
align: "center"
});
label.anchor.set(0.5, 0.5);
label.x = 1024;
label.y = 400;
if (typeof game !== "undefined" && game.addChild) {
game.addChild(label);
}
LK.setTimeout(function () {
if (label.parent) {
label.parent.removeChild(label);
}
}, 1800);
}
// Tüm başarımlar kazanıldıysa 50. başarıma bak
if (window.achievementsProgress.length === 50 && !hasAchievement(50)) {
grantAchievement(50);
}
}
// --- BAŞARIMLARIN TAKİBİ İÇİN SAYACLAR ---
// Oyun içi sayaçlar
if (!window._achPirateKills) {
window._achPirateKills = 0;
}
if (!window._achAsteroidKills) {
window._achAsteroidKills = 0;
}
if (!window._achTradeCount) {
window._achTradeCount = 0;
}
if (!window._achRepairCount) {
window._achRepairCount = 0;
}
if (!window._achQuestComplete) {
window._achQuestComplete = 0;
}
if (!window._achNPCKills) {
window._achNPCKills = 0;
}
if (!window._achStationDeliver) {
window._achStationDeliver = 0;
}
if (!window._achStationAllDeliver) {
window._achStationAllDeliver = 0;
}
if (!window._achPirateBaseKills) {
window._achPirateBaseKills = 0;
}
if (!window._achPirateBaseKills3) {
window._achPirateBaseKills3 = 0;
}
if (!window._achUpgradeCount) {
window._achUpgradeCount = 0;
}
if (!window._achKatilEscape) {
window._achKatilEscape = false;
}
if (!window._achKatilKill) {
window._achKatilKill = false;
}
if (!window._achWormhole) {
window._achWormhole = false;
}
if (!window._achPirateTrade) {
window._achPirateTrade = false;
}
if (!window._achProtectNPC) {
window._achProtectNPC = false;
}
if (!window._ach3PirateFight) {
window._ach3PirateFight = false;
}
// --- BAŞARIM KONTROLLERİ ---
// 1. Korsan öldürme (İlk Kan, Korsan Avcısı, Korsan Kralı, Korsan Katili)
function checkPirateKillAchievements() {
window._achPirateKills++;
if (window._achPirateKills === 1) {
grantAchievement(1);
} // İlk Kan
if (window._achPirateKills === 10) {
grantAchievement(11);
} // Korsan Avcısı
if (window._achPirateKills === 50) {
grantAchievement(15);
} // Korsan Kralı
if (window._achPirateKills === 100) {
grantAchievement(43);
} // Korsan Katili
}
// 2. Katil korsan öldürme
function checkKatilKillAchievement() {
if (!window._achKatilKill) {
window._achKatilKill = true;
grantAchievement(5); // Katil Korsan
}
}
// 3. Asteroit patlatma
function checkAsteroidKillAchievements() {
window._achAsteroidKills++;
if (window._achAsteroidKills === 5) {
grantAchievement(12);
} // Asteroit Patlatıcı
if (window._achAsteroidKills === 50) {
grantAchievement(44);
} // Asteroit Kralı
}
// 4. Hammadde toplama (Madenci, Hammadde Kralı, Hammadde Zengini, Enerji Ustası, Metalci, Buz Adam, Plazma Avcısı, Antimaddeci)
function checkResourceCollectAchievements() {
// Madenci
grantAchievement(2);
// Enerji Ustası, Metalci, Buz Adam, Plazma Avcısı, Antimaddeci, Hammadde Kralı, Hammadde Zengini
var res = playerResources;
if (res[1] >= 500) {
grantAchievement(25);
} // Enerji Ustası
if (res[0] >= 500) {
grantAchievement(26);
} // Metalci
if (res[4] >= 100) {
grantAchievement(27);
} // Buz Adam
if (res[8] >= 50) {
grantAchievement(28);
} // Plazma Avcısı
if (res[9] >= 10) {
grantAchievement(29);
} // Antimaddeci
var all100 = true,
all500 = true;
for (var i = 0; i < res.length; i++) {
if (res[i] < 100) {
all100 = false;
}
if (res[i] < 500) {
all500 = false;
}
}
if (all100) {
grantAchievement(16);
} // Hammadde Kralı
if (all500) {
grantAchievement(38);
} // Hammadde Zengini
}
// 5. Takas (Tüccar, Ticaret Ustası, Takas Zengini)
function checkTradeAchievements() {
window._achTradeCount++;
if (window._achTradeCount === 1) {
grantAchievement(3);
} // Tüccar
if (window._achTradeCount === 10) {
grantAchievement(13);
} // Ticaret Ustası
if (window._achTradeCount === 100) {
grantAchievement(39);
} // Takas Zengini
}
// 6. Seviye atlama (Seviye Atla, Seviye Ustası)
function checkLevelAchievements() {
if (playerLevel >= 5) {
grantAchievement(8);
} // Seviye Atla
if (playerLevel >= 20) {
grantAchievement(40);
} // Seviye Ustası
}
// 7. Coin biriktirme (Zengin)
function checkCoinAchievements() {
if (playerCoins >= 1000) {
grantAchievement(6);
} // Zengin
}
// 8. Tamir (Mekanik, Tamirci, Tamir Kralı)
function checkRepairAchievements() {
window._achRepairCount++;
if (window._achRepairCount === 1) {
grantAchievement(7);
} // Mekanik
if (window._achRepairCount === 5) {
grantAchievement(18);
} // Tamirci
if (window._achRepairCount === 20) {
grantAchievement(46);
} // Tamir Kralı
}
// 9. Kalkanı doldurma (Kalkan Ustası, Kalkan Kralı)
function checkShieldAchievements() {
if (ship.shield >= ship.maxShield && ship.shield >= 50) {
grantAchievement(9);
} // Kalkan Ustası
if (ship.maxShield >= 200) {
grantAchievement(21);
} // Kalkan Kralı
}
// 10. Canı yükseltme (Canavar)
function checkHealthAchievements() {
if (ship.maxHealth >= 300) {
grantAchievement(22);
} // Canavar
}
// 11. Hızı yükseltme (Hızlı)
function checkSpeedAchievements() {
if (ship.speed >= 10) {
grantAchievement(23);
} // Hızlı
}
// 12. Atış hızını düşürme (Ateş Yağmuru)
function checkFireRateAchievements() {
if (ship.fireRate <= 5) {
grantAchievement(24);
} // Ateş Yağmuru
}
// 13. Görev tamamlama (Görev Adamı)
function checkQuestAchievements() {
window._achQuestComplete++;
if (window._achQuestComplete === 1) {
grantAchievement(10);
} // Görev Adamı
}
// 14. Dalga geçme (Dalga Geç, Dalga Ustası, Dalga Efsanesi)
function checkWaveAchievements() {
if (waveNumber + 1 >= 5) {
grantAchievement(19);
} // Dalga Geç
if (waveNumber + 1 >= 10) {
grantAchievement(20);
} // Dalga Ustası
if (waveNumber + 1 >= 20) {
grantAchievement(41);
} // Dalga Efsanesi
}
// 15. Yol katetme (Gezgin, Uzay Fatihi)
function checkDistanceAchievements() {
if (totalDistance >= 10000) {
grantAchievement(14);
} // Gezgin
if (totalDistance >= 100000) {
grantAchievement(42);
} // Uzay Fatihi
}
// 16. Upgrade satın alma (Upgradeci, Upgrade Kralı)
function checkUpgradeAchievements() {
window._achUpgradeCount++;
if (window._achUpgradeCount === 1) {
grantAchievement(17);
} // Upgradeci
// Tüm upgrade'ler maksimuma çıkarıldıysa
if (ship.maxHealth >= 300 && ship.maxShield >= 200 && ship.damage >= 50 && ship.speed >= 10 && ship.fireRate <= 5) {
grantAchievement(45); // Upgrade Kralı
}
}
// 17. Korsan üssü yok etme (Korsan Üssü, Korsan Üssü Avcısı)
function checkPirateBaseAchievements() {
window._achPirateBaseKills++;
if (window._achPirateBaseKills === 1) {
grantAchievement(35);
} // Korsan Üssü
if (window._achPirateBaseKills === 3) {
grantAchievement(36);
} // Korsan Üssü Avcısı
}
// 18. NPC öldürme (NPC Avcısı)
function checkNPCKillAchievements() {
window._achNPCKills++;
if (window._achNPCKills === 10) {
grantAchievement(48);
} // NPC Avcısı
}
// 19. İstasyon teslimatı (Uzay İstasyonu, Büyük Teslimat)
function checkStationDeliveryAchievements(allDone) {
window._achStationDeliver++;
grantAchievement(33); // Uzay İstasyonu
if (allDone) {
window._achStationAllDeliver++;
grantAchievement(34); // Büyük Teslimat
}
}
// 20. Solucan deliğinden geçme (Kaşif)
function checkWormholeAchievement() {
if (!window._achWormhole) {
window._achWormhole = true;
grantAchievement(4); // Kaşif
}
}
// 21. Katil korsandan kaçma (Katil Kaçışı)
function checkKatilEscapeAchievement() {
if (!window._achKatilEscape) {
window._achKatilEscape = true;
grantAchievement(37); // Katil Kaçışı
}
}
// 22. Bir korsandan kaçma (Korsan Kaçakçısı)
function checkPirateEscapeAchievement() {
grantAchievement(30); // Korsan Kaçakçısı
}
// 23. Bir NPC'yi koruma (Dost Canlısı)
function checkProtectNPCAchievement() {
if (!window._achProtectNPC) {
window._achProtectNPC = true;
grantAchievement(31); // Dost Canlısı
}
}
// 24. Aynı anda 3 korsanla savaş (Korsan Saldırısı)
function check3PirateFightAchievement() {
if (!window._ach3PirateFight) {
window._ach3PirateFight = true;
grantAchievement(32); // Korsan Saldırısı
}
}
// 25. Bir korsanla ticaret yapma (Korsan Dostu)
function checkPirateTradeAchievement() {
if (!window._achPirateTrade) {
window._achPirateTrade = true;
grantAchievement(47); // Korsan Dostu
}
}
// 26. 30 dakika hayatta kalma (Uzayda Hayatta Kal)
function checkSurvive30MinAchievement(elapsedSeconds) {
if (elapsedSeconds >= 1800) {
grantAchievement(49);
}
}
// --- Achievements Panel Open/Close ---
window.openAchievementsPanel = function (page) {
if (window.achievementsPanelBG) {
return;
}
var pageSize = 10;
var total = window.achievementsList.length;
var totalPages = Math.ceil(total / pageSize);
var currentPage = typeof page === "number" ? Math.max(1, Math.min(page, totalPages)) : 1;
window._achievementsPanelPage = currentPage;
var panelW = 1100,
panelH = 1200;
var px = 2048 / 2,
py = 2732 / 2;
// BG
var bg = LK.getAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
x: px,
y: py,
scaleX: panelW / 40,
scaleY: panelH / 40,
tint: 0x222244,
alpha: 0.97
});
// Title
var title = new Text2("Achievements", {
size: Math.round(unifiedFontSize * 1.3) + 2,
fill: 0x00ffcc,
align: "center"
});
title.anchor.set(0.5, 0);
title.x = px;
title.y = py - panelH / 2 + 40;
// Page info
var pageInfo = new Text2("Page " + currentPage + "/" + totalPages, {
size: Math.round(unifiedFontSize * 0.9) + 2,
fill: 0xffffff,
align: "center"
});
pageInfo.anchor.set(0.5, 0);
pageInfo.x = px;
pageInfo.y = title.y + 60;
// Achievements rows
var startY = pageInfo.y + 60;
var rowH = 90;
var achRows = [];
for (var i = 0; i < pageSize; i++) {
var idx = (currentPage - 1) * pageSize + i;
if (idx >= total) {
break;
}
var ach = window.achievementsList[idx];
var unlocked = window.achievementsProgress.indexOf(ach.id) !== -1;
var text = (unlocked ? "✅ " : "⬜ ") + ach.name + " - " + ach.desc + " [" + ach.difficulty + "]\nÖdül: " + ach.reward + " coin";
var color = unlocked ? 0x00ff99 : 0xffffff;
var row = new Text2(text, {
size: Math.round(unifiedFontSize * 0.9) + 2,
fill: color,
align: "left"
});
row.anchor.set(0, 0.5);
row.x = px - panelW / 2 + 40;
row.y = startY + i * rowH;
achRows.push(row);
}
// Page number buttons (1-2-3...)
var btnW = 45,
btnH = 35;
var pageBtns = [];
var pageBtnY = py + panelH / 2 - btnH / 2 - 20;
var pageBtnStartX = px - (totalPages - 1) * (btnW + 10) / 2;
for (var p = 1; p <= totalPages; p++) {
var isCurrent = p === currentPage;
var btn = new Text2("" + p, {
size: Math.round(unifiedFontSize * 0.6) + 2,
fill: isCurrent ? 0x00ffcc : 0xffffff,
align: "center"
});
btn.anchor.set(0.5, 0.5);
btn.x = pageBtnStartX + (p - 1) * (btnW + 10);
btn.y = pageBtnY;
btn.width = btnW;
btn.height = btnH;
btn._achPageBtn = p;
if (isCurrent) {
btn.fill = 0x00ffcc;
btn.setText("[" + p + "]");
}
pageBtns.push(btn);
}
// Close button
var closeBtn = new Text2("Close", {
size: Math.round((unifiedFontSize + 4) * 1.3),
fill: 0xff4444,
align: "center"
});
closeBtn.anchor.set(0.5, 0.5);
closeBtn.x = px;
closeBtn.y = title.y - 80;
closeBtn._achCloseBtn = true;
// Add to game
game.addChild(bg);
game.addChild(title);
game.addChild(pageInfo);
for (var i = 0; i < achRows.length; i++) {
game.addChild(achRows[i]);
}
for (var i = 0; i < pageBtns.length; i++) {
game.addChild(pageBtns[i]);
}
game.addChild(closeBtn);
// Store refs for tap logic/cleanup
window.achievementsPanelBG = bg;
window.achievementsPanelTitle = title;
window.achievementsPanelPageInfo = pageInfo;
window.achievementsPanelRows = achRows;
window.achievementsPanelPageBtns = pageBtns;
window.achievementsPanelCloseBtn = closeBtn;
};
window.closeAchievementsPanel = function () {
if (!window.achievementsPanelBG) {
return;
}
if (window.achievementsPanelBG.parent) {
window.achievementsPanelBG.parent.removeChild(window.achievementsPanelBG);
}
if (window.achievementsPanelTitle && window.achievementsPanelTitle.parent) {
window.achievementsPanelTitle.parent.removeChild(window.achievementsPanelTitle);
}
if (window.achievementsPanelPageInfo && window.achievementsPanelPageInfo.parent) {
window.achievementsPanelPageInfo.parent.removeChild(window.achievementsPanelPageInfo);
}
if (window.achievementsPanelRows) {
for (var i = 0; i < window.achievementsPanelRows.length; i++) {
if (window.achievementsPanelRows[i] && window.achievementsPanelRows[i].parent) {
window.achievementsPanelRows[i].parent.removeChild(window.achievementsPanelRows[i]);
}
}
}
if (window.achievementsPanelPageBtns) {
for (var i = 0; i < window.achievementsPanelPageBtns.length; i++) {
if (window.achievementsPanelPageBtns[i] && window.achievementsPanelPageBtns[i].parent) {
window.achievementsPanelPageBtns[i].parent.removeChild(window.achievementsPanelPageBtns[i]);
}
}
}
if (window.achievementsPanelCloseBtn && window.achievementsPanelCloseBtn.parent) {
window.achievementsPanelCloseBtn.parent.removeChild(window.achievementsPanelCloseBtn);
}
window.achievementsPanelBG = null;
window.achievementsPanelTitle = null;
window.achievementsPanelPageInfo = null;
window.achievementsPanelRows = null;
window.achievementsPanelPageBtns = null;
window.achievementsPanelCloseBtn = null;
window._achievementsPanelPage = null;
};
// --- CREW PANEL SYSTEM ---
// Global arrays for friends, dron, and mechanic
var friends = [];
var dron = null;
var mechanic = null;
// Helper: open Crew panel
window.openCrewPanel = function () {
if (window.crewPanelBG) {
return;
}
var px = 2048 / 2,
py = 2732 / 2;
var panelW = 900,
panelH = 900;
var rowH = 110;
var fontSize = Math.round(unifiedFontSize * 1.1) + 2;
// BG
var bg = LK.getAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
x: px,
y: py,
scaleX: panelW / 40,
scaleY: panelH / 40,
tint: 0x1a2dcb,
alpha: 0.97
});
// Title
var title = new Text2("Crew (Friends & Dron)", {
size: fontSize + 8,
fill: 0x00ffcc,
align: "center"
});
title.anchor.set(0.5, 0);
title.x = px;
title.y = py - panelH / 2 + 40;
// Info
var info = new Text2("You can buy up to 3 Friends. Each Friend attacks with 50% of your attack power.\nDron collects nearby resources/coins.\nMechanic automatically repairs when health drops below 50%.", {
size: fontSize - 2,
fill: 0xffffff,
align: "center"
});
info.anchor.set(0.5, 0);
info.x = px;
info.y = title.y + 60;
// Friend satın al butonları ve etiketleri
window.crewFriendBtns = [];
window.crewFriendLabels = [];
for (var i = 0; i < 3; i++) {
var label = new Text2("Friend #" + (i + 1) + (friends[i] ? " (Purchased)" : " (Empty)"), {
size: fontSize,
fill: friends[i] ? 0x00ff99 : 0xffffff,
align: "left"
});
label.anchor.set(0, 0.5);
label.x = px - panelW / 2 + 60;
label.y = info.y + 100 + rowH + i * rowH;
window.crewFriendLabels.push(label);
var btn = new Text2(friends[i] ? "Purchased" : "Buy (500 coin)", {
size: fontSize,
fill: friends[i] ? 0x888888 : 0x00ff99,
align: "center"
});
btn.anchor.set(0.5, 0.5);
btn.x = px + 220;
btn.y = label.y;
btn._friendIdx = i;
window.crewFriendBtns.push(btn);
}
// Dron satın al etiketi ve butonu
var dronLabel = new Text2("Dron: " + (dron ? "Purchased" : "None"), {
size: fontSize,
fill: dron ? 0x00ff99 : 0xffffff,
align: "left"
});
dronLabel.anchor.set(0, 0.5);
dronLabel.x = px - panelW / 2 + 60;
dronLabel.y = info.y + 100 + rowH + 3 * rowH;
var dronBtn = new Text2(dron ? "Purchased" : "Buy (1200 coin)", {
size: fontSize,
fill: dron ? 0x888888 : 0x00ff99,
align: "center"
});
dronBtn.anchor.set(0.5, 0.5);
dronBtn.x = px + 220;
dronBtn.y = dronLabel.y;
// Mechanic satın al etiketi ve butonu
var mechanicLabel = new Text2("Mechanic: " + (window.mechanic ? "Purchased" : "None"), {
size: fontSize,
fill: window.mechanic ? 0x00ff99 : 0xffffff,
align: "left"
});
mechanicLabel.anchor.set(0, 0.5);
mechanicLabel.x = px - panelW / 2 + 60;
mechanicLabel.y = info.y + 100 + rowH + 4 * rowH;
var mechanicBtn = new Text2(window.mechanic ? "Purchased" : "Buy (800 coin)", {
size: fontSize,
fill: window.mechanic ? 0x888888 : 0x00ff99,
align: "center"
});
mechanicBtn.anchor.set(0.5, 0.5);
mechanicBtn.x = px + 220;
mechanicBtn.y = mechanicLabel.y;
// Close button
var closeBtn = new Text2("Close", {
size: fontSize + 6,
fill: 0xff4444,
align: "center"
});
closeBtn.anchor.set(0.5, 0.5);
closeBtn.x = px;
closeBtn.y = title.y - 80;
// Add to game
game.addChild(bg);
game.addChild(title);
game.addChild(info);
for (var i = 0; i < window.crewFriendLabels.length; i++) {
game.addChild(window.crewFriendLabels[i]);
}
for (var i = 0; i < window.crewFriendBtns.length; i++) {
game.addChild(window.crewFriendBtns[i]);
}
game.addChild(dronLabel);
game.addChild(dronBtn);
game.addChild(mechanicLabel);
game.addChild(mechanicBtn);
game.addChild(closeBtn);
// Store refs for tap logic/cleanup
window.crewPanelBG = bg;
window.crewPanelTitle = title;
window.crewPanelInfo = info;
window.crewPanelDronLabel = dronLabel;
window.crewPanelDronBtn = dronBtn;
window.crewPanelMechanicLabel = mechanicLabel;
window.crewPanelMechanicBtn = mechanicBtn;
window.crewPanelCloseBtn = closeBtn;
};
window.closeCrewPanel = function () {
if (!window.crewPanelBG) {
return;
}
if (window.crewPanelBG.parent) {
window.crewPanelBG.parent.removeChild(window.crewPanelBG);
}
if (window.crewPanelTitle && window.crewPanelTitle.parent) {
window.crewPanelTitle.parent.removeChild(window.crewPanelTitle);
}
if (window.crewPanelInfo && window.crewPanelInfo.parent) {
window.crewPanelInfo.parent.removeChild(window.crewPanelInfo);
}
if (window.crewFriendLabels) {
for (var i = 0; i < window.crewFriendLabels.length; i++) {
if (window.crewFriendLabels[i] && window.crewFriendLabels[i].parent) {
window.crewFriendLabels[i].parent.removeChild(window.crewFriendLabels[i]);
}
}
}
if (window.crewFriendBtns) {
for (var i = 0; i < window.crewFriendBtns.length; i++) {
if (window.crewFriendBtns[i] && window.crewFriendBtns[i].parent) {
window.crewFriendBtns[i].parent.removeChild(window.crewFriendBtns[i]);
}
}
}
if (window.crewPanelDronLabel && window.crewPanelDronLabel.parent) {
window.crewPanelDronLabel.parent.removeChild(window.crewPanelDronLabel);
}
if (window.crewPanelDronBtn && window.crewPanelDronBtn.parent) {
window.crewPanelDronBtn.parent.removeChild(window.crewPanelDronBtn);
}
if (window.crewPanelMechanicLabel && window.crewPanelMechanicLabel.parent) {
window.crewPanelMechanicLabel.parent.removeChild(window.crewPanelMechanicLabel);
}
if (window.crewPanelMechanicBtn && window.crewPanelMechanicBtn.parent) {
window.crewPanelMechanicBtn.parent.removeChild(window.crewPanelMechanicBtn);
}
if (window.crewPanelCloseBtn && window.crewPanelCloseBtn.parent) {
window.crewPanelCloseBtn.parent.removeChild(window.crewPanelCloseBtn);
}
window.crewPanelBG = null;
window.crewPanelTitle = null;
window.crewPanelInfo = null;
window.crewFriendLabels = null;
window.crewFriendBtns = null;
window.crewPanelDronLabel = null;
window.crewPanelDronBtn = null;
window.crewPanelMechanicLabel = null;
window.crewPanelMechanicBtn = null;
window.crewPanelCloseBtn = null;
};
// --- Upgrades Panel Logic ---
if (!window.openUpgradesPanel) {
window.openUpgradesPanel = function () {
if (window.upgradesPanel) {
return;
}
// Panel size and position
var panelW = 900,
panelH = 1100;
// Center the panel on the screen
var px = 2048 / 2;
var py = 2732 / 2;
// Panel BG
var bg = LK.getAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
x: px,
y: py,
scaleX: panelW / 40,
scaleY: panelH / 40,
tint: 0x222244,
alpha: 0.97
});
// Title
var title = new Text2("Upgrades", {
size: Math.round(unifiedFontSize * 1.3),
fill: 0x00ffcc,
align: "center"
});
title.anchor.set(0.5, 0);
title.x = px;
title.y = py - panelH / 2 + 40;
// Upgrade options (example: maxHealth, maxShield, damage, speed, fireRate)
var upgFont = unifiedFontSize + 2;
var upgSpacing = 120;
var upgStartY = title.y + 120;
var upgBtns = [];
var upgDefs = [{
label: "Attack Range +5%\n(300 coin)",
type: "range",
fill: 0x99ff99,
cost: {
coins: 300,
metal: 0,
energy: 0
}
}, {
label: "Heal 5%\n(50 coin)",
type: "heal5",
fill: 0xff6699,
cost: {
coins: 50,
metal: 0,
energy: 0
}
}, {
label: "Max Health +20\n(600 coin, 60 metal)",
type: "maxHealth",
fill: 0x00ff99,
cost: {
coins: 600,
metal: 60,
energy: 0
}
}, {
label: "Max Shield +10\n(720 coin, 72 metal)",
type: "maxShield",
fill: 0x00ccff,
cost: {
coins: 720,
metal: 72,
energy: 0
}
}, {
label: "Damage +5\n(900 coin, 90 energy)",
type: "damage",
fill: 0xffcc00,
cost: {
coins: 900,
metal: 0,
energy: 90
}
}, {
label: "Speed +10%\n(1000 coin)",
type: "speed",
fill: 0x00ffff,
cost: {
coins: 1000,
metal: 0,
energy: 0
}
}, {
label: "Fire Rate +10%\n(1200 coin)",
type: "fireRate",
fill: 0xff99ff,
cost: {
coins: 1200,
metal: 0,
energy: 0
}
}];
for (var i = 0; i < upgDefs.length; i++) {
var upg = upgDefs[i];
var btn = new Text2(upg.label, {
size: upgFont,
fill: upg.fill,
align: "center"
});
btn.anchor.set(0.5, 0.5);
btn.x = px;
btn.y = upgStartY + i * upgSpacing;
btn._isUpgradesPanelBtn = true;
btn._upgradeType = upg.type;
btn._upgradeCost = upg.cost;
upgBtns.push(btn);
}
// Close button
var closeBtn = new Text2("Close", {
size: Math.round((unifiedFontSize + 4) * 1.3),
fill: 0xff4444,
align: "center"
});
closeBtn.anchor.set(0.5, 0.5);
closeBtn.x = px;
closeBtn.y = title.y - 80;
closeBtn._isUpgradesPanelCloseBtn = true;
// Add to game
game.addChild(bg);
game.addChild(title);
for (var i = 0; i < upgBtns.length; i++) {
game.addChild(upgBtns[i]);
}
game.addChild(closeBtn);
// Store refs for tap logic/cleanup
window.upgradesPanel = bg;
window.upgradesPanelTitle = title;
window.upgradesPanelBtns = upgBtns;
window.upgradesPanelCloseBtn = closeBtn;
};
window.closeUpgradesPanel = function () {
if (!window.upgradesPanel) {
return;
}
if (window.upgradesPanel && window.upgradesPanel.parent) {
window.upgradesPanel.parent.removeChild(window.upgradesPanel);
}
if (window.upgradesPanelTitle && window.upgradesPanelTitle.parent) {
window.upgradesPanelTitle.parent.removeChild(window.upgradesPanelTitle);
}
if (window.upgradesPanelBtns) {
for (var i = 0; i < window.upgradesPanelBtns.length; i++) {
if (window.upgradesPanelBtns[i] && window.upgradesPanelBtns[i].parent) {
window.upgradesPanelBtns[i].parent.removeChild(window.upgradesPanelBtns[i]);
}
}
}
if (window.upgradesPanelCloseBtn && window.upgradesPanelCloseBtn.parent) {
window.upgradesPanelCloseBtn.parent.removeChild(window.upgradesPanelCloseBtn);
}
window.upgradesPanel = null;
window.upgradesPanelTitle = null;
window.upgradesPanelBtns = null;
window.upgradesPanelCloseBtn = null;
};
}
// No wave, minimap, exp, or coordinate UI in this version
// Create joystick
joystickBase = game.addChild(LK.getAsset('joystickBase', {
anchorX: 0.5,
anchorY: 0.5,
x: 300,
y: 2432,
scaleX: 1.3,
scaleY: 1.3
}));
joystickBase.alpha = 0.5;
joystickHandle = game.addChild(LK.getAsset('joystickHandle', {
anchorX: 0.5,
anchorY: 0.5,
x: 300,
y: 2432,
scaleX: 1.3,
scaleY: 1.3
}));
joystickHandle.alpha = 0.7;
// Create ship
ship = game.addChild(new Ship());
ship.x = 1024;
ship.y = 1366;
// Add health bar to ship
ship._healthBar = new HealthBar();
ship._healthBar.set({
maxValue: function maxValue() {
return ship.maxHealth;
},
getValueFn: function getValueFn() {
return ship.health;
},
getMaxFn: function getMaxFn() {
return ship.maxHealth;
},
width: 80,
height: 12
});
ship._healthBar.y = -70;
ship.addChild(ship._healthBar);
// Start background music
LK.playMusic('bgmusic');
// Initialize star field (increased by 50%) and make them colorful
for (var i = 0; i < 135; i++) {
// 90 * 1.5 = 135
spawnStar(true); // pass true to enable colorful stars
}
// Spawn more map elements at start (20% more)
for (var i = 0; i < 6; i++) {
// was 5, now 6
spawnNPC();
}
for (var i = 0; i < 6; i++) {
// was 5, now 6
spawnPirate();
}
// Ensure at least one Katil korsan exists at game start
if (typeof window._katilPirateSpawned === "undefined" || !window._katilPirateSpawned) {
var pirate = new Pirate();
var minLevel = 1 + waveNumber * 2;
pirate.level = minLevel + 10;
pirate.health = (30 + (pirate.level - 1) * 10) * 10;
pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10;
pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10;
pirate.rpgName = "Katil";
pirate._isKatil = true;
pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3));
pirate._attackRange = Math.floor(600 * 0.9); // Katil korsan için %10 daha kısa menzil
window._katilPirateSpawned = true;
// Spawn from edge like other pirates
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y - 1400;
break;
case 1:
pirate.x = ship.x + 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
case 2:
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y + 1400;
break;
case 3:
pirate.x = ship.x - 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
}
// Add health bar to katil pirate
pirate._healthBar = new HealthBar();
pirate._healthBar.set({
maxValue: function maxValue() {
return pirate.health > 0 ? pirate.health : 0;
},
getValueFn: function getValueFn() {
return pirate.health > 0 ? pirate.health : 0;
},
getMaxFn: function getMaxFn() {
return 30 + waveNumber * 10;
},
width: 60,
height: 10
});
pirate._healthBar.y = -60;
pirate.addChild(pirate._healthBar);
pirates.push(pirate);
game.addChild(pirate);
}
for (var i = 0; i < 2; i++) {
// was 4, now 2 (50% reduction)
spawnAsteroid();
}
// Helper functions
function spawnStar(colorful) {
var star = new Star();
// If colorful is true, assign a random color
if (colorful) {
// Pick a random color from a vibrant palette
var palette = [0xffe066,
// yellow
0xff66cc,
// pink
0x66ffcc,
// aqua
0x66ccff,
// blue
0xcc66ff,
// purple
0xff6666,
// red
0x66ff66,
// green
0xffffff,
// white
0xffcc66,
// orange
0x66ffff // cyan
];
var color = palette[Math.floor(Math.random() * palette.length)];
if (star.children && star.children.length > 0 && star.children[0]) {
star.children[0].tint = color;
}
}
// Spawn at a random position within a large area around the ship
var radius = 1800 + Math.random() * 1200; // 1800-3000 px from ship
var angle = Math.random() * Math.PI * 2;
star.x = ship.x + Math.cos(angle) * radius;
star.y = ship.y + Math.sin(angle) * radius;
stars.push(star);
game.addChild(star);
}
function spawnNPC() {
var npc = new NPC();
// Spawn from edges of visible area
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
// Top
npc.x = ship.x + (Math.random() - 0.5) * 2000;
npc.y = ship.y - 1500;
break;
case 1:
// Right
npc.x = ship.x + 1500;
npc.y = ship.y + (Math.random() - 0.5) * 2000;
break;
case 2:
// Bottom
npc.x = ship.x + (Math.random() - 0.5) * 2000;
npc.y = ship.y + 1500;
break;
case 3:
// Left
npc.x = ship.x - 1500;
npc.y = ship.y + (Math.random() - 0.5) * 2000;
break;
}
npc.baseY = npc.y;
// --- Set NPC level and health to match pirate scaling ---
var minLevel = 1 + waveNumber * 2;
var maxLevel = minLevel + 2;
npc.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1));
npc.health = 100 + (npc.level - 1) * 10;
// Add health bar to NPC (for defend/hunt/escort/delivery/explore)
npc._healthBar = new HealthBar();
npc._healthBar.set({
maxValue: function maxValue() {
return npc.health !== undefined ? npc.health : 100;
},
getValueFn: function getValueFn() {
return npc.health !== undefined ? npc.health : 100;
},
getMaxFn: function getMaxFn() {
return 100 + (npc.level - 1) * 10;
},
width: 60,
height: 10
});
npc._healthBar.y = -60;
npc.addChild(npc._healthBar);
npcs.push(npc);
game.addChild(npc);
}
function spawnPirate() {
var pirate = new Pirate();
// Spawn from edges of visible area
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
// Top
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y - 1400;
break;
case 1:
// Right
pirate.x = ship.x + 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
case 2:
// Bottom
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y + 1400;
break;
case 3:
// Left
pirate.x = ship.x - 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
}
// Set pirate level and scale stats based on wave
// Level range: wave 1 => 1-3, wave 2 => 3-5, wave 3 => 5-7, etc.
var minLevel = 1 + waveNumber * 2;
var maxLevel = minLevel + 2;
// Katil korsan spawn etme şansı: Eğer henüz yoksa ve %20 şansla, bu korsanı Katil yap
if (typeof window._katilPirateSpawned === "undefined") {
window._katilPirateSpawned = false;
}
if (!window._katilPirateSpawned && Math.random() < 0.2) {
// Katil korsan!
pirate.level = minLevel + 10;
pirate.health = (30 + (pirate.level - 1) * 10) * 10;
pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10;
pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10;
pirate.rpgName = "Katil";
pirate._isKatil = true;
window._katilPirateSpawned = true;
// Katil korsan saldırı oranını 3 kat arttır
pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3));
pirate._attackRange = Math.floor(600 * 0.9); // Katil korsan için %10 daha kısa menzil
// Add Enemy text to Katil Korsan
if (!pirate._enemyText) {
pirate._enemyText = new Text2("Enemy", {
size: 18,
fill: 0xff4444,
align: "center"
});
pirate._enemyText.anchor.set(0.5, 0);
pirate._enemyText.x = 0;
pirate._enemyText.y = -50;
pirate.addChild(pirate._enemyText);
}
// Katil korsan için özel bir renk veya efekt istenirse burada eklenebilir
} else {
pirate.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1));
pirate.health = 30 + (pirate.level - 1) * 10;
pirate.damage = 5 + Math.floor((pirate.level - 1) * 1.5);
pirate.defense = 2 + Math.floor((pirate.level - 1) * 1.2);
}
// Add health bar to pirate
pirate._healthBar = new HealthBar();
pirate._healthBar.set({
maxValue: function maxValue() {
return pirate.health > 0 ? pirate.health : 0;
},
getValueFn: function getValueFn() {
return pirate.health > 0 ? pirate.health : 0;
},
getMaxFn: function getMaxFn() {
return 30 + waveNumber * 10;
},
width: 60,
height: 10
});
pirate._healthBar.y = -60;
pirate.addChild(pirate._healthBar);
pirates.push(pirate);
game.addChild(pirate);
}
function spawnAsteroid() {
var asteroid = new Asteroid();
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
asteroid.x = ship.x + (Math.random() - 0.5) * 2200;
asteroid.y = ship.y - 1600;
break;
case 1:
asteroid.x = ship.x + 1600;
asteroid.y = ship.y + (Math.random() - 0.5) * 2200;
break;
case 2:
asteroid.x = ship.x + (Math.random() - 0.5) * 2200;
asteroid.y = ship.y + 1600;
break;
case 3:
asteroid.x = ship.x - 1600;
asteroid.y = ship.y + (Math.random() - 0.5) * 2200;
break;
}
// Add health bar to asteroid
asteroid._healthBar = new HealthBar();
asteroid._healthBar.set({
maxValue: function maxValue() {
return asteroid.health;
},
getValueFn: function getValueFn() {
return asteroid.health;
},
getMaxFn: function getMaxFn() {
return 50;
},
width: 50,
height: 8
});
asteroid._healthBar.y = -50;
asteroid.addChild(asteroid._healthBar);
asteroids.push(asteroid);
game.addChild(asteroid);
}
// Spawn an upgrade station at a random edge and add it to the world
function spawnUpgradeStation() {
// Artık UpgradeStation yerine DerelictDebris (enkaz) spawn ediyoruz
var debris = new DerelictDebris();
// Spawn from edges of visible area
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
debris.x = ship.x + (Math.random() - 0.5) * 1800;
debris.y = ship.y - 1400;
break;
case 1:
debris.x = ship.x + 1400;
debris.y = ship.y + (Math.random() - 0.5) * 1800;
break;
case 2:
debris.x = ship.x + (Math.random() - 0.5) * 1800;
debris.y = ship.y + 1400;
break;
case 3:
debris.x = ship.x - 1400;
debris.y = ship.y + (Math.random() - 0.5) * 1800;
break;
}
upgradeStations.push(debris);
game.addChild(debris);
}
// No trade stations in this version
function createExplosion(x, y) {
// %95 küçült: 8 * 0.05 = 0.4, en az 1 partikül bırakıyoruz
for (var i = 0; i < 1; i++) {
var particle = LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: y,
scaleX: 0.22,
// %95 küçült (1 - 0.95 = 0.05, yani 0.05x, ama çok küçük olur, 0.22 ile 8 partikülün toplam alanı korunur)
scaleY: 0.22
});
particle.vx = (Math.random() - 0.5) * 2; // Hızı da azalt, daha küçük efekt için
particle.vy = (Math.random() - 0.5) * 2;
particle.life = 12; // Daha kısa ömür
explosions.push(particle);
game.addChild(particle);
}
}
// No black holes or wormholes in this version
function updateUI() {
coinText.setText('Coins: ' + playerCoins);
// Keep playerMetal/playerEnergy in sync with playerResources
playerMetal = playerResources[0];
playerEnergy = playerResources[1];
// Use sorted resource label order for display
if (window.resourceLabels && window.resourceLabelOrder && window.resourceLabels.length === window.resourceLabelOrder.length) {
for (var i = 0; i < window.resourceLabelOrder.length; i++) {
var resObj = window.resourceLabelOrder[i];
window.resourceLabels[i].setText(resObj.name + ': ' + (playerResources[resObj.origIdx] || 0));
}
}
// (No market price display)
var healthPercent = Math.round(ship.health / ship.maxHealth * 100);
healthText.setText('Health: %' + healthPercent);
var shieldPercent = Math.round(ship.shield / ship.maxShield * 100);
shieldText.setText('Shield: %' + shieldPercent);
// Restore wave and exp UI
if (typeof waveText !== "undefined") {
waveText.setText('Wave: ' + (waveNumber + 1));
}
if (typeof expText !== "undefined") {
expText.setText('Level: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel));
}
waveText.setText('Wave: ' + (waveNumber + 1));
expText.setText('Level: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel));
// Show player level/exp in GUI (top right, above repText)
if (!window.levelText) {
window.levelText = new Text2('Seviye: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel), {
size: unifiedFontSize,
fill: 0xffffff
});
window.levelText.anchor.set(1, 0);
window.levelText.x = -20;
window.levelText.y = 20; // moved up by one row (40px)
LK.gui.topRight.addChild(window.levelText);
}
window.levelText.setText('Seviye: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel));
// Update player level/exp in top right
if (window.levelText) {
window.levelText.setText('Seviye: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel));
}
// Update health text color
if (ship.health > 60) {
healthText.fill = 0x00ff00;
} else if (ship.health > 30) {
healthText.fill = 0xffff00;
} else {
healthText.fill = 0xff0000;
}
// Update shield text color
if (ship.shield > 30) {
shieldText.fill = 0x0088ff;
} else if (ship.shield > 10) {
shieldText.fill = 0xffff00;
} else {
shieldText.fill = 0xff0000;
}
storage.playerLevel = playerLevel;
storage.playerEXP = playerEXP;
}
function saveProgress() {
storage.coins = playerCoins;
var resourceKeys = ['metal', 'energy', 'crystal', 'gas', 'ice', 'uranium', 'silicon', 'carbon', 'plasma', 'antimatter'];
for (var i = 0; i < resourceKeys.length; i++) {
storage[resourceKeys[i]] = playerResources[i] || 0;
}
// Keep playerMetal/playerEnergy in sync with playerResources
playerMetal = playerResources[0];
playerEnergy = playerResources[1];
storage.questsCompleted = questsCompleted;
}
// Event handlers
game.down = function (x, y, obj) {
// --- BLOCK ALL TAPS IF ANY PANEL IS OPEN (fixes tap blocking bug) ---
// --- ALSO BLOCK ALL INPUT IF SHIP IS DEAD ---
if (window.stationDeliveryPanel && window.stationDeliveryPanelBG && window.stationDeliveryPanelBG.parent || window.resourceTradePanelState && window.resourceTradePanelState.open || window._resourceStockPanel && window._resourceStockPanel.bg && window._resourceStockPanel.bg.parent || window.shipUpgradePanel && window.shipUpgradePanel.parent || window.repairPanel && window.repairPanel.parent || ship && ship.health <= 0) {
// If a panel is open or ship is dead, only allow taps on that panel's close/confirm buttons (handled below), block all other taps
// (Panel-specific tap logic is handled further down, so just return here to block all other UI/joystick/game taps)
// Exception: let panel close/confirm buttons work (handled in their own blocks)
// If ship is dead, block all input until respawn label is gone
if (ship && ship.health <= 0) {
return;
}
// (Do not return here, let the rest of the handler run for panel close/confirm buttons)
} else {
// --- PAUSE BUTTON (top left) tap detection: always allow pause tap, never block it ---
if (x < 100 && y < 100) {
// This is the reserved area for the platform pause/menu button.
// Let the LK engine handle the pause/menu tap, do not block it.
// Do not return here, let the engine handle it.
}
// --- GLOBAL MARKET BUTTON HANDLER (exact image area) ---
if (window.globalMarketBtn && window.globalMarketBtn.parent) {
var btn = window.globalMarketBtn;
var bx = btn.x,
by = btn.y;
var bw = btn.width || 320,
bh = btn.height || 80;
// LK.gui.bottomRight is anchored at (1,1), so x,y negative from bottom right
// The area is anchored at (1,1), so its (x,y) is the bottom right corner of the area
// So the area covers (bx-bw, by-bh) to (bx, by)
if (x > 2048 + bx - bw && x < 2048 + bx && y > 2732 + by - bh && y < 2732 + by) {
window.openResourceTradePanel();
return;
}
}
// --- CREW PANEL BUTTON HANDLER (just below Achievements image) ---
if (window.newImageAboveStorage) {
// Place a transparent tap area over the newImageAboveStorage image if not already present
if (!window.crewPanelBtn) {
var btn = new Text2("", {
size: 1,
fill: 0xffffff,
align: "center"
});
btn.anchor.set(1, 0);
btn.x = window.newImageAboveStorage.x;
// Move the Crew panel button to exactly the same y as the image
btn.y = window.newImageAboveStorage.y;
// Grow the button downward by the image's height (so it covers 2x the image height, starting at image.y)
btn.width = window.newImageAboveStorage.width;
btn.height = window.newImageAboveStorage.height * 2;
btn.alpha = 0;
window.crewPanelBtn = btn;
LK.gui.topRight.addChild(btn);
}
var btn = window.crewPanelBtn;
var bx = btn.x,
by = btn.y;
var bw = btn.width,
bh = btn.height;
// LK.gui.topRight is anchored at (1,0), so x negative from right, y positive from top
if (x > 2048 + bx - bw && x < 2048 + bx && y > by && y < by + bh) {
window.openCrewPanel();
return;
}
}
// --- ACHIEVEMENTS PANEL BUTTON HANDLER (Achievements image tap) ---
if (window.achievementsPanelBtn && window.achievementsPanelBtn.parent) {
var btn = window.achievementsPanelBtn;
var bx = btn.x,
by = btn.y;
var bw = btn.width,
bh = btn.height;
// LK.gui.topRight is anchored at (1,0), so x negative from right, y positive from top
// So the area covers (2048+bx-bw, by) to (2048+bx, by+bh)
if (x > 2048 + bx - bw && x < 2048 + bx && y > by && y < by + bh) {
window.openAchievementsPanel(1);
return;
}
}
// --- CREW PANEL HANDLER ---
if (window.crewPanelBG) {
// Close btn
if (window.crewPanelCloseBtn) {
var bx = window.crewPanelCloseBtn.x,
by = window.crewPanelCloseBtn.y;
var bw = window.crewPanelCloseBtn.width || 300,
bh = window.crewPanelCloseBtn.height || 80;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
window.closeCrewPanel();
return;
}
}
// Friend satın al btns
if (window.crewFriendBtns) {
for (var i = 0; i < window.crewFriendBtns.length; i++) {
var btn = window.crewFriendBtns[i];
var bx = btn.x,
by = btn.y;
var bw = btn.width || 220,
bh = btn.height || 60;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
var idx = btn._friendIdx;
if (!friends[idx] && playerCoins >= 500) {
playerCoins -= 500;
// Create Friend instance (see class below)
var friend = new Friend();
friend.x = ship.x + 80 + 60 * idx;
friend.y = ship.y + 80;
friends[idx] = friend;
game.addChild(friend);
btn.setText("Purchased");
btn.fill = 0x888888;
if (window.crewFriendLabels && window.crewFriendLabels[idx]) {
window.crewFriendLabels[idx].setText("Friend #" + (idx + 1) + " (Purchased)");
window.crewFriendLabels[idx].fill = 0x00ff99;
}
updateUI && updateUI();
saveProgress && saveProgress();
} else if (friends[idx]) {
btn.setText("Already Purchased");
btn.fill = 0x888888;
} else if (playerCoins < 500) {
btn.setText("Insufficient coins!");
btn.fill = 0xff4444;
}
// Reset label after short delay
(function (b, orig, origFill) {
LK.setTimeout(function () {
b.setText(friends[idx] ? "Purchased" : "Buy (500 coin)");
b.fill = friends[idx] ? 0x888888 : 0x00ff99;
}, 900);
})(btn, "Buy (500 coin)", 0x00ff99);
return;
}
}
}
// Dron satın al btn
if (window.crewPanelDronBtn) {
var btn = window.crewPanelDronBtn;
var bx = btn.x,
by = btn.y;
var bw = btn.width || 220,
bh = btn.height || 60;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
if (!dron && playerCoins >= 1200) {
playerCoins -= 1200;
dron = new Dron();
dron.x = ship.x - 100;
dron.y = ship.y + 80;
game.addChild(dron);
btn.setText("Purchased");
btn.fill = 0x888888;
if (window.crewPanelDronLabel) {
window.crewPanelDronLabel.setText("Dron: Purchased");
window.crewPanelDronLabel.fill = 0x00ff99;
}
updateUI && updateUI();
saveProgress && saveProgress();
} else if (dron) {
btn.setText("Already Purchased");
btn.fill = 0x888888;
} else if (playerCoins < 1200) {
btn.setText("Insufficient coins!");
btn.fill = 0xff4444;
}
(function (b, orig, origFill) {
LK.setTimeout(function () {
b.setText(dron ? "Purchased" : "Buy (1200 coin)");
b.fill = dron ? 0x888888 : 0x00ff99;
}, 900);
})(btn, "Buy (1200 coin)", 0x00ff99);
return;
}
}
// Mechanic satın al btn
if (window.crewPanelMechanicBtn) {
var btn = window.crewPanelMechanicBtn;
var bx = btn.x,
by = btn.y;
var bw = btn.width || 220,
bh = btn.height || 60;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
if (!window.mechanic && playerCoins >= 800) {
playerCoins -= 800;
window.mechanic = new Mechanic();
window.mechanic.x = ship.x - 120;
window.mechanic.y = ship.y - 80;
game.addChild(window.mechanic);
btn.setText("Purchased");
btn.fill = 0x888888;
if (window.crewPanelMechanicLabel) {
window.crewPanelMechanicLabel.setText("Mechanic: Purchased");
window.crewPanelMechanicLabel.fill = 0x00ff99;
}
updateUI && updateUI();
saveProgress && saveProgress();
} else if (window.mechanic) {
btn.setText("Already Purchased");
btn.fill = 0x888888;
} else if (playerCoins < 800) {
btn.setText("Insufficient coins!");
btn.fill = 0xff4444;
}
(function (b, orig, origFill) {
LK.setTimeout(function () {
b.setText(window.mechanic ? "Purchased" : "Buy (800 coin)");
b.fill = window.mechanic ? 0x888888 : 0x00ff99;
}, 900);
})(btn, "Buy (800 coin)", 0x00ff99);
return;
}
}
// Block all other taps when panel is open
return;
}
// --- UPGRADE PANEL BUTTON HANDLER (upg image tap) ---
if (window.upgPanelBtn && window.upgPanelBtn.parent) {
var btn = window.upgPanelBtn;
var bx = btn.x,
by = btn.y;
var bw = btn.width,
bh = btn.height;
// LK.gui.topRight is anchored at (1,0), so x negative from right, y positive from top
// So the area covers (2048+bx-bw, by) to (2048+bx, by+bh)
if (x > 2048 + bx - bw && x < 2048 + bx && y > by && y < by + bh) {
window.openUpgradesPanel();
return;
}
}
}
// --- RESOURCE TRADE PANEL INDEPENDENT BUTTON HANDLER ---
// If the independent button exists, check tap
if (window.resourceTradePanelIndependentBtn && window.resourceTradePanelIndependentBtn.parent) {
var btn = window.resourceTradePanelIndependentBtn;
var bx = btn.x,
by = btn.y;
var bw = btn.width,
bh = btn.height;
// LK.gui.bottomRight is anchored at (1,1), so x,y negative from bottom right
// The area is anchored at (1,1), so its (x,y) is the bottom right corner of the area
// So the area covers (bx-bw, by-bh) to (bx, by)
if (x > 2048 + bx - bw && x < 2048 + bx && y > 2732 + by - bh && y < 2732 + by) {
// Open 'Hammadde Stoğu' panel
if (typeof window.openResourceStockPanel === "function") {
window.openResourceStockPanel();
} else {
// Fallback: show a simple panel with player's resource stock
if (window._resourceStockPanel) {
return;
}
var panelW = 900,
panelH = 900;
var px = 2048 / 2,
py = 2732 / 2;
// Increase font size by 2 for all panel texts/buttons
var panelFontSize = unifiedFontSize + 4;
var labelFontSize = panelFontSize;
var closeBtnFontSize = panelFontSize;
var titleFontSize = panelFontSize;
// Resize panel background to fit enlarged text/buttons
var rowH = 70 + 8;
var panelW = 900 + 120;
var panelH = 900 + 2 * rowH;
var bg = LK.getAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
x: px,
y: py,
scaleX: panelW / 40,
scaleY: panelH / 40,
tint: 0x223355,
alpha: 0.97
});
var title = new Text2("Hammadde Stoğu", {
size: titleFontSize,
fill: 0x00ffcc,
align: "center"
});
title.anchor.set(0.5, 0);
title.x = px;
title.y = py - panelH / 2 + 40;
// List resources
var labels = [];
var startY = title.y + 80;
for (var i = 0; i < window.resourceLabelOrder.length; i++) {
var resObj = window.resourceLabelOrder[i];
var idx = resObj.origIdx;
var label = new Text2(resObj.name + ": " + (playerResources[idx] || 0), {
size: labelFontSize,
fill: 0xffffff,
align: "left"
});
label.anchor.set(0, 0.5);
label.x = px - panelW / 2 + 60;
label.y = startY + i * rowH;
labels.push(label);
}
// Close button
var closeBtn = new Text2("Kapat", {
size: Math.round((unifiedFontSize + 4) * 1.3),
// 2 font larger than Coins, then 30% larger
fill: 0xff4444,
align: "center"
});
closeBtn.anchor.set(0.5, 0.5);
closeBtn.x = px;
closeBtn.y = title.y - 110; // above the title
// Add to game
game.addChild(bg);
game.addChild(title);
for (var i = 0; i < labels.length; i++) {
game.addChild(labels[i]);
}
game.addChild(closeBtn);
// Store refs for tap logic/cleanup
window._resourceStockPanel = {
bg: bg,
title: title,
labels: labels,
closeBtn: closeBtn
};
// Add close logic
closeBtn._isResourceStockCloseBtn = true;
}
return;
}
}
// --- RESOURCE TRADE PANEL HANDLER ---
if (window._resourceStockPanel && window._resourceStockPanel.closeBtn) {
var btn = window._resourceStockPanel.closeBtn;
var bx = btn.x,
by = btn.y;
var bw = btn.width || 300,
bh = btn.height || 80;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
// Remove all panel elements
if (window._resourceStockPanel.bg && window._resourceStockPanel.bg.parent) {
window._resourceStockPanel.bg.parent.removeChild(window._resourceStockPanel.bg);
}
if (window._resourceStockPanel.title && window._resourceStockPanel.title.parent) {
window._resourceStockPanel.title.parent.removeChild(window._resourceStockPanel.title);
}
if (window._resourceStockPanel.labels) {
for (var i = 0; i < window._resourceStockPanel.labels.length; i++) {
if (window._resourceStockPanel.labels[i] && window._resourceStockPanel.labels[i].parent) {
window._resourceStockPanel.labels[i].parent.removeChild(window._resourceStockPanel.labels[i]);
}
}
}
if (window._resourceStockPanel.closeBtn && window._resourceStockPanel.closeBtn.parent) {
window._resourceStockPanel.closeBtn.parent.removeChild(window._resourceStockPanel.closeBtn);
}
window._resourceStockPanel = null;
return;
}
}
if (window.resourceTradePanelState && window.resourceTradePanelState.open) {
// Close btn
if (window.resourceTradePanelCloseBtn) {
var bx = window.resourceTradePanelCloseBtn.x,
by = window.resourceTradePanelCloseBtn.y;
var bw = window.resourceTradePanelCloseBtn.width || 300,
bh = window.resourceTradePanelCloseBtn.height || 80;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
window.closeResourceTradePanel();
return;
}
}
// Sat btns
if (window.resourceTradeSellBtns) {
for (var i = 0; i < window.resourceTradeSellBtns.length; i++) {
var btn = window.resourceTradeSellBtns[i];
var bx = btn.x,
by = btn.y;
var bw = btn.width || 120,
bh = btn.height || 60;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
var idx = btn._resourceIdx;
var amount = playerResources[idx] || 0;
var value = window.resourceTradeValues ? window.resourceTradeValues[idx] : 0;
if (amount > 0 && value > 0) {
playerCoins += value * amount;
playerResources[idx] = 0;
// --- BAŞARIM: Takas yapınca ---
checkTradeAchievements();
btn.setText("Satıldı!");
btn.fill = 0x00ffcc;
if (window.updateResourceTradePanel) {
window.updateResourceTradePanel();
}
updateUI();
saveProgress();
} else {
btn.setText("Yok!");
btn.fill = 0xff4444;
}
// Reset label after short delay
(function (b, orig, origFill) {
LK.setTimeout(function () {
b.setText("Sat");
b.fill = 0x00ff99;
}, 700);
})(btn, "Sat", 0x00ff99);
return;
}
}
}
// Tümünü Sat btns
if (window.resourceTradeSellAllBtns) {
for (var i = 0; i < window.resourceTradeSellAllBtns.length; i++) {
var btn = window.resourceTradeSellAllBtns[i];
var bx = btn.x,
by = btn.y;
var bw = btn.width || 180,
bh = btn.height || 60;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
var idx = btn._resourceIdx;
var amount = playerResources[idx] || 0;
var value = window.resourceTradeValues ? window.resourceTradeValues[idx] : 0;
if (amount > 0 && value > 0) {
playerCoins += value * amount;
playerResources[idx] = 0;
btn.setText("Hepsi Satıldı!");
btn.fill = 0x00ffcc;
if (window.updateResourceTradePanel) {
window.updateResourceTradePanel();
}
updateUI();
saveProgress();
} else {
btn.setText("Yok!");
btn.fill = 0xff4444;
}
(function (b, orig, origFill) {
LK.setTimeout(function () {
b.setText("Tümünü Sat");
b.fill = 0x00cc99;
}, 700);
})(btn, "Tümünü Sat", 0x00cc99);
return;
}
}
}
// Al btns
if (window.resourceTradeBuyBtns) {
for (var i = 0; i < window.resourceTradeBuyBtns.length; i++) {
var btn = window.resourceTradeBuyBtns[i];
var bx = btn.x,
by = btn.y;
var bw = btn.width || 120,
bh = btn.height || 60;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
var idx = btn._resourceIdx;
var value = window.resourceTradeValues ? window.resourceTradeValues[idx] : 0;
// Satın alma miktarı: 1 (veya istenirse artırılabilir)
var buyAmount = 1;
var totalCost = value * buyAmount;
if (playerCoins >= totalCost && value > 0) {
playerCoins -= totalCost;
playerResources[idx] = (playerResources[idx] || 0) + buyAmount;
btn.setText("Alındı!");
btn.fill = 0x00ffcc;
if (window.updateResourceTradePanel) {
window.updateResourceTradePanel();
}
updateUI();
saveProgress();
} else {
btn.setText("Yetersiz coin!");
btn.fill = 0xff4444;
}
(function (b, orig, origFill) {
LK.setTimeout(function () {
b.setText("Al");
b.fill = 0x0099ff;
}, 700);
})(btn, "Al", 0x0099ff);
return;
}
}
}
// Block all other taps when panel is open
return;
}
// --- UZAY İSTASYONU TESLİMAT BUTTON HANDLER ---
// Handle tap on stationDeliveryBtnArea (transparent area, exactly image size)
if (window.stationDeliveryBtnArea && window.stationDeliveryBtnArea.parent) {
var btn = window.stationDeliveryBtnArea;
var bx = btn.x,
by = btn.y;
var bw = btn.width,
bh = btn.height;
// LK.gui.bottomRight is anchored at (1,1), so x,y negative from bottom right
// The area is anchored at (1,1), so its (x,y) is the bottom right corner of the area
// So the area covers (bx-bw, by-bh) to (bx, by)
if (x > 2048 + bx - bw && x < 2048 + bx && y > 2732 + by - bh && y < 2732 + by) {
window.openStationDeliveryPanel();
return;
}
}
// LK.gui.bottomRight is anchored at (1,1), so x,y negative from bottom right
if (window.stationDeliveryBtn && window.stationDeliveryBtn.parent) {
var btn = window.stationDeliveryBtn;
var bx = btn.x,
by = btn.y;
// Button is about 180x80 px, so check if x is within 200px of right edge and y within 300-480px of bottom
if (x > 2048 - 200 && y > 2732 - 300 && y < 2732 - 100) {
window.openStationDeliveryPanel();
return;
}
}
// --- UZAY İSTASYONU TESLİMAT PANEL HANDLER ---
if (window.stationDeliveryPanel) {
// Close btn
if (window.stationDeliveryPanelCloseBtn) {
var bx = window.stationDeliveryPanelCloseBtn.x,
by = window.stationDeliveryPanelCloseBtn.y;
var bw = window.stationDeliveryPanelCloseBtn.width || 300,
bh = window.stationDeliveryPanelCloseBtn.height || 80;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
window.closeStationDeliveryPanel();
return;
}
}
// Teslim Et btns
if (window.stationDeliveryDeliverBtns) {
for (var i = 0; i < window.stationDeliveryDeliverBtns.length; i++) {
var btn = window.stationDeliveryDeliverBtns[i];
var bx = btn.x,
by = btn.y;
var bw = btn.width || 120,
bh = btn.height || 60;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
var idx = btn._stationResIdx;
var req = window.stationDeliveryState.requests[idx];
var delivered = window.stationDeliveryState.delivered[idx];
var canDeliver = Math.min(playerResources[idx], req - delivered);
if (canDeliver > 0) {
playerResources[idx] -= canDeliver;
window.stationDeliveryState.delivered[idx] += canDeliver;
btn.setText("Teslim Edildi!");
btn.fill = 0x00ffcc;
// Update label
if (window.stationDeliveryLabels && window.stationDeliveryLabels[i]) {
window.stationDeliveryLabels[i].setText(window.resourceLabelOrder[i].name + ": " + window.stationDeliveryState.delivered[idx] + " / " + req);
}
// Update stock label
if (window.stationDeliveryStockLabels && window.stationDeliveryStockLabels[i]) {
var stockAmount = typeof playerResources[idx] !== "undefined" ? playerResources[idx] : 0;
window.stationDeliveryStockLabels[i].setText("Stok: " + stockAmount);
}
updateUI();
saveProgress();
} else {
btn.setText("Yok!");
btn.fill = 0xff4444;
}
// Reset label after short delay
(function (b, orig, origFill) {
LK.setTimeout(function () {
b.setText(orig);
b.fill = origFill;
}, 700);
})(btn, "Teslim Et", 0x00ff99);
return;
}
}
}
// Teslimatı Tamamla (ödül al) butonu
if (window.stationDeliveryPanelClaimBtn) {
var btn = window.stationDeliveryPanelClaimBtn;
var bx = btn.x,
by = btn.y;
var bw = btn.width || 400,
bh = btn.height || 80;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
// Check if all delivered
var allDone = true;
for (var i = 0; i < 10; i++) {
if (window.stationDeliveryState.delivered[i] < window.stationDeliveryState.requests[i]) {
allDone = false;
break;
}
}
if (allDone && !window.stationDeliveryState.rewardClaimed) {
// Calculate total value (normal market price * delivered) * (1+bonus%)
var totalValue = 0;
for (var i = 0; i < 10; i++) {
var price = 1; // Market removed, use static price
totalValue += price * window.stationDeliveryState.requests[i];
}
var bonus = window.stationDeliveryState.bonus;
var reward = Math.round(totalValue * (1 + bonus / 100));
playerCoins += reward;
window.stationDeliveryState.rewardClaimed = true;
btn.setText("Ödül Alındı! +" + reward + " coin");
btn.fill = 0x00ffcc;
// Show floating label
var label = new Text2("İstasyon Teslimatı Tamamlandı!\n+" + reward + " coin", {
size: 48,
fill: 0xffcc00,
align: "center"
});
label.anchor.set(0.5, 0.5);
label.x = 1024;
label.y = 600;
game.addChild(label);
LK.setTimeout(function () {
if (label.parent) {
label.parent.removeChild(label);
}
}, 1800);
updateUI();
saveProgress();
} else if (window.stationDeliveryState.rewardClaimed) {
btn.setText("Zaten Alındı!");
btn.fill = 0x888888;
} else {
btn.setText("Eksik Teslimat!");
btn.fill = 0xff4444;
}
// Reset label after short delay
(function (b, orig, origFill) {
LK.setTimeout(function () {
b.setText("Tümünü Teslim Et & Ödülü Al");
b.fill = 0xffcc00;
}, 1200);
})(btn, "Tümünü Teslim Et & Ödülü Al", 0xffcc00);
return;
}
}
// Block all other taps when panel is open
return;
}
// --- RESET BUTTON HANDLER ---
if (game._resetBtn && game._resetBtn.parent) {
// Convert GUI coordinates for bottomRight
// LK.gui.bottomRight is anchored at (1,1) so x,y are negative from bottom right
var btn = game._resetBtn;
var bx = btn.x,
by = btn.y;
// Button is about 180x80 px, but let's use a safe area
// Since anchor is (1,1), bottom right, and x/y are negative from bottom right
// So, for a tap, check if x is within 200px of right edge and y within 100px of bottom
// LK.gui.bottomRight: x=0,y=0 is bottom right, so x,y negative
if (x > 2048 - 200 && y > 2732 - 100) {
// Clear all persistent storage, including achievements and all custom progress
storage.coins = 200; // Başlangıçta 200 coin ile başla
storage.metal = 0;
storage.energy = 0;
storage.crystal = 0;
storage.gas = 0;
storage.ice = 0;
storage.uranium = 0;
storage.silicon = 0;
storage.carbon = 0;
storage.plasma = 0;
storage.antimatter = 0;
storage.questsCompleted = 0;
storage.playerLevel = 1;
storage.playerEXP = 0;
// Clear all achievements
storage.achievements = [];
// Clear all custom progress keys
storage.rangeUpgradeLevel = 0;
storage.fireRateUpgradeLevel = 0;
// Remove any other custom keys that may have been set
// (defensive: remove all keys except built-in ones)
for (var k in storage) {
if (storage.hasOwnProperty(k) && ["coins", "metal", "energy", "crystal", "gas", "ice", "uranium", "silicon", "carbon", "plasma", "antimatter", "questsCompleted", "playerLevel", "playerEXP", "achievements", "rangeUpgradeLevel", "fireRateUpgradeLevel"].indexOf(k) === -1) {
try {
storage[k] = 0;
} catch (e) {}
}
}
// Show a quick feedback
btn.setText("Reset!");
btn.fill = 0x00ff99;
// Reload game (reset all progress)
LK.showGameOver();
return;
}
}
// --- NPC ile savaş butonu handler ---
if (window.npcFightBtn) {
var bx = window.npcFightBtn.x,
by = window.npcFightBtn.y;
var bw = window.npcFightBtn.width || 400,
bh = window.npcFightBtn.height || 100;
// Accept generous tap area
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
window.npcFightMode = !window.npcFightMode;
if (window.npcFightMode) {
window.npcFightBtn.setText("NPC Savaş: AÇIK");
window.npcFightBtn.fill = 0xff0000;
} else {
window.npcFightBtn.setText("NPC ile Savaş");
window.npcFightBtn.fill = 0xff4444;
}
return;
}
}
if (window.repairPanel && window.repairBtn && window.repairBtn.parent) {
// Check tap on repair button
var btn = window.repairBtn;
var bx = btn.x,
by = btn.y;
if (x > bx - 90 && x < bx + 90 && y > by - 40 && y < by + 40) {
// Try to repair
var costCoins = btn._repairCostCoins,
costMetal = btn._repairCostMetal,
costEnergy = btn._repairCostEnergy;
if (playerCoins >= costCoins && playerMetal >= costMetal && playerEnergy >= costEnergy) {
playerCoins -= costCoins;
playerMetal -= costMetal;
playerEnergy -= costEnergy;
ship.health = ship.maxHealth;
// --- BAŞARIM: Tamir ---
checkRepairAchievements();
updateUI();
saveProgress();
// Show feedback
btn.setText("Tamir Edildi!");
btn.fill = 0x00ffcc;
// Remove panel after short delay
var expire = LK.ticks + 40;
var origUpdate = game.update;
game.update = function () {
if (LK.ticks > expire) {
if (window.repairPanel && window.repairPanel.parent) {
window.repairPanel.parent.removeChild(window.repairPanel);
}
if (window.repairText && window.repairText.parent) {
window.repairText.parent.removeChild(window.repairText);
}
if (window.repairBtn && window.repairBtn.parent) {
window.repairBtn.parent.removeChild(window.repairBtn);
}
if (window.cancelRepairBtn && window.cancelRepairBtn.parent) {
window.cancelRepairBtn.parent.removeChild(window.cancelRepairBtn);
}
window.repairPanel = null;
window.repairText = null;
window.repairBtn = null;
window.cancelRepairBtn = null;
game.update = origUpdate;
}
if (origUpdate) {
origUpdate.apply(game, arguments);
}
};
} else {
// Not enough resources
btn.setText("Yetersiz kaynak!");
btn.fill = 0xff4444;
}
return;
}
// Check tap on cancel button
var cbtn = window.cancelRepairBtn;
if (cbtn) {
var cbx = cbtn.x,
cby = cbtn.y;
if (x > cbx - 90 && x < cbx + 90 && y > cby - 40 && y < cby + 40) {
// Remove all repair panel elements, allow closing even if not repaired
if (window.repairPanel && window.repairPanel.parent) {
window.repairPanel.parent.removeChild(window.repairPanel);
}
if (window.repairText && window.repairText.parent) {
window.repairText.parent.removeChild(window.repairText);
}
if (window.repairBtn && window.repairBtn.parent) {
window.repairBtn.parent.removeChild(window.repairBtn);
}
if (window.cancelRepairBtn && window.cancelRepairBtn.parent) {
window.cancelRepairBtn.parent.removeChild(window.cancelRepairBtn);
}
window.repairPanel = null;
window.repairText = null;
window.repairBtn = null;
window.cancelRepairBtn = null;
window._showRepairPanel = false;
return;
}
}
}
// --- Health bar tap to show repair panel ---
if (healthText && healthText.parent && healthText._isHealthBar && ship.health < ship.maxHealth && ship.health > 0) {
// Get healthText bounds in screen coordinates
// healthText is anchored at (0.5, 0), at top center
// Let's estimate its bounds
var hx = healthText.x,
hy = healthText.y;
var hw = healthText.width || 300;
var hh = healthText.height || 60;
// Accept generous tap area
if (x > hx - hw / 2 && x < hx + hw / 2 && y > hy && y < hy + hh) {
window._showRepairPanel = true;
return;
}
}
// --- SHIP TAP: Show upgrade/can-buy-health panel ---
// Oyuncu gemisi resmi üzerindeki buton GİZLENDİ. Artık bu panel gemi resmine tıklanarak açılamaz.
// (Bu alanı bilerek boş bırakıyoruz, panel açılmayacak.)
// --- Only allow joystick drag if ship is alive ---
if (ship && ship.health > 0 && Math.sqrt(Math.pow(x - joystickBase.x, 2) + Math.pow(y - joystickBase.y, 2)) < 130) {
isDragging = true;
joystickHandle.x = x;
joystickHandle.y = y;
return; // Prevent other interactions when dragging joystick
}
// --- If any panel is open, block all other game/ship input (except panel close/confirm buttons) ---
if (window.stationDeliveryPanel && window.stationDeliveryPanelBG && window.stationDeliveryPanelBG.parent || window.resourceTradePanelState && window.resourceTradePanelState.open || window._resourceStockPanel && window._resourceStockPanel.bg && window._resourceStockPanel.bg.parent || window.shipUpgradePanel && window.shipUpgradePanel.parent || window.repairPanel && window.repairPanel.parent) {
// Only allow panel close/confirm buttons (handled below), block all other game/ship input
// (Do not return here, let the rest of the handler run for panel close/confirm buttons)
} else {
// --- SHIP UPGRADE PANEL BUTTONS ---
if (window.shipUpgradePanel) {
// (existing ship upgrade panel logic unchanged)
// ... (no change here)
}
// --- ACHIEVEMENTS PANEL BUTTONS ---
if (window.achievementsPanelBG) {
// Close button
var cb = window.achievementsPanelCloseBtn;
if (cb) {
var bx = cb.x,
by = cb.y;
var bw = cb.width || 300,
bh = cb.height || 80;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
window.closeAchievementsPanel();
return;
}
}
// Page number buttons
var pageBtns = window.achievementsPanelPageBtns;
if (pageBtns) {
for (var i = 0; i < pageBtns.length; i++) {
var btn = pageBtns[i];
var bx = btn.x,
by = btn.y;
var bw = btn.width || 90,
bh = btn.height || 70;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
if (window._achievementsPanelPage !== btn._achPageBtn) {
window.closeAchievementsPanel();
window.openAchievementsPanel(btn._achPageBtn);
}
return;
}
}
}
// Block all other taps when panel is open
return;
}
// --- UPGRADE PANEL BUTTONS ---
if (window.upgradesPanel) {
// Upgrade buttons
var btns = window.upgradesPanelBtns || [];
for (var i = 0; i < btns.length; i++) {
var btn = btns[i];
if (!btn) {
continue;
}
var bx = btn.x,
by = btn.y;
var bw = btn.width || 400,
bh = btn.height || 80;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
// Try to buy upgrade
var upg = btn._upgradeType;
var cost = btn._upgradeCost;
if (upg === "range" && playerCoins >= cost.coins) {
// Increase attack range by 5%
if (typeof window._rangeUpgradeLevel === "undefined") {
window._rangeUpgradeLevel = 0;
}
window._rangeUpgradeLevel++;
// Store in storage for persistence
storage.rangeUpgradeLevel = window._rangeUpgradeLevel;
playerCoins -= cost.coins;
LK.effects.flashObject(ship, 0x99ff99, 400);
btn.setText("Alındı!");
btn.fill = 0x00ffcc;
// --- BAŞARIM: Upgrade satın alma ---
checkUpgradeAchievements();
updateUI();
saveProgress();
} else if (upg === "heal5" && playerCoins >= cost.coins) {
// Heal 5% of max health, but not above max
var healAmount = Math.round(ship.maxHealth * 0.05);
ship.health = Math.min(ship.health + healAmount, ship.maxHealth);
playerCoins -= cost.coins;
LK.effects.flashObject(ship, 0xff6699, 400);
btn.setText("Yenilendi!");
btn.fill = 0x00ffcc;
// --- BAŞARIM: Tamir ---
checkRepairAchievements();
updateUI();
saveProgress();
} else if (upg === "maxHealth" && playerCoins >= cost.coins && playerResources[0] >= cost.metal) {
ship.maxHealth += 20;
ship.health = ship.maxHealth;
playerCoins -= cost.coins;
playerResources[0] -= cost.metal;
LK.effects.flashObject(ship, 0x00ff99, 400);
btn.setText("Alındı!");
btn.fill = 0x00ffcc;
// --- BAŞARIM: Upgrade satın alma ---
checkUpgradeAchievements();
updateUI();
saveProgress();
} else if (upg === "maxShield" && playerCoins >= cost.coins && playerResources[0] >= cost.metal) {
ship.maxShield += 10;
ship.shield = ship.maxShield;
playerCoins -= cost.coins;
playerResources[0] -= cost.metal;
LK.effects.flashObject(ship, 0x00ccff, 400);
btn.setText("Alındı!");
btn.fill = 0x00ffcc;
// --- BAŞARIM: Upgrade satın alma ---
checkUpgradeAchievements();
updateUI();
saveProgress();
} else if (upg === "damage" && playerCoins >= cost.coins && playerResources[1] >= cost.energy) {
ship.damage += 5;
playerCoins -= cost.coins;
playerResources[1] -= cost.energy;
LK.effects.flashObject(ship, 0xffcc00, 400);
btn.setText("Alındı!");
btn.fill = 0x00ffcc;
// --- BAŞARIM: Upgrade satın alma ---
checkUpgradeAchievements();
updateUI();
saveProgress();
} else if (upg === "speed" && playerCoins >= cost.coins) {
ship.speed = Math.round(ship.speed * 1.1 * 100) / 100;
playerCoins -= cost.coins;
LK.effects.flashObject(ship, 0x00ffff, 400);
btn.setText("Alındı!");
btn.fill = 0x00ffcc;
// --- BAŞARIM: Upgrade satın alma ---
checkUpgradeAchievements();
updateUI();
saveProgress();
} else if (upg === "fireRate") {
if (typeof window._fireRateUpgradeLevel === "undefined") {
window._fireRateUpgradeLevel = 0;
}
if (window._fireRateUpgradeLevel >= 5) {
btn.setText("Maks. seviye!");
btn.fill = 0xff4444;
LK.setTimeout(function () {
btn.setText("Atış Hızı +10%\n(1200 coin)");
btn.fill = 0xff99ff;
}, 900);
} else if (playerCoins >= cost.coins) {
ship.fireRate = Math.max(2, Math.round(ship.fireRate * 0.9));
playerCoins -= cost.coins;
window._fireRateUpgradeLevel++;
storage.fireRateUpgradeLevel = window._fireRateUpgradeLevel;
LK.effects.flashObject(ship, 0xff99ff, 400);
btn.setText("Alındı!");
btn.fill = 0x00ffcc;
updateUI();
saveProgress();
} else {
btn.setText("Yetersiz kaynak!");
btn.fill = 0xff4444;
LK.setTimeout(function () {
btn.setText("Atış Hızı +10%\n(1200 coin)");
btn.fill = 0xff99ff;
}, 900);
}
} else {
var origText = "";
var origFill = btn.fill;
if (upg === "maxHealth") {
origText = "Max Can +20\n(600 coin, 60 metal)";
origFill = 0x00ff99;
} else if (upg === "maxShield") {
origText = "Max Kalkan +10\n(720 coin, 72 metal)";
origFill = 0x00ccff;
} else if (upg === "damage") {
origText = "Hasar +5\n(900 coin, 90 enerji)";
origFill = 0xffcc00;
} else if (upg === "speed") {
origText = "Hız +10%\n(1000 coin)";
origFill = 0x00ffff;
} else if (upg === "fireRate") {
origText = "Atış Hızı +10%\n(1200 coin)";
origFill = 0xff99ff;
} else {
origText = btn.text;
origFill = btn.fill;
}
btn.setText("Yetersiz kaynak!");
btn.fill = 0xff4444;
// Reset label after short delay
LK.setTimeout(function () {
btn.setText(origText);
btn.fill = origFill;
}, 900);
}
return;
}
}
// Close button
var cb = window.upgradesPanelCloseBtn;
if (cb) {
var bx = cb.x,
by = cb.y;
var bw = cb.width || 300,
bh = cb.height || 80;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
window.closeUpgradesPanel();
return;
}
}
}
// --- Handle Accept/Reject quest button taps ---
if (game._questDecisionAcceptBtn && game._questDecisionAcceptBtn.parent) {
var btn = game._questDecisionAcceptBtn;
var bx = btn.x,
by = btn.y;
// Button is about 180x80 px
if (x > bx - 90 && x < bx + 90 && y > by - 40 && y < by + 40) {
// Accept quest
var npc = btn._questNPC;
if (npc) {
// Defend quest özel: yardım kabul edildi
if (npc.questType === 'defend') {
npc._defendHelpAccepted = true;
// Show floating label for accepted
var acceptLabel = new Text2("Yardım kabul edildi!", {
size: 36,
fill: 0x00ff99,
align: "center"
});
acceptLabel.anchor.set(0.5, 0.5);
acceptLabel.x = npc.x;
acceptLabel.y = npc.y + 110;
game.addChild(acceptLabel);
npc._npcInteractionLabel = acceptLabel;
npc._npcInteractionLabelExpire = LK.ticks + 120;
npc.pauseUntil = LK.ticks + 120;
npc._npcText && npc._npcText.parent && npc._npcText.parent.removeChild(npc._npcText);
npc._npcText = new Text2(npc.rpgName + ": Teşekkürler! Beni korsanlardan koru!", {
size: 32,
fill: 0x00ffcc,
align: "center"
});
npc._npcText.anchor.set(0.5, 1);
npc._npcText.x = npc.x;
npc._npcText.y = npc.y - 70;
game.addChild(npc._npcText);
} else {
// Mark quest as accepted (progress 1 means started)
npc.questProgress = 1;
// Show NPC response
var acceptLabel = new Text2("Görev kabul edildi!", {
size: 36,
fill: 0x00ff99,
align: "center"
});
acceptLabel.anchor.set(0.5, 0.5);
acceptLabel.x = npc.x;
acceptLabel.y = npc.y + 110;
game.addChild(acceptLabel);
npc._npcInteractionLabel = acceptLabel;
npc._npcInteractionLabelExpire = LK.ticks + 120;
npc.pauseUntil = LK.ticks + 120;
npc._npcText && npc._npcText.parent && npc._npcText.parent.removeChild(npc._npcText);
npc._npcText = new Text2(npc.rpgName + ": Harika! Görev başladı.", {
size: 32,
fill: 0x00ffcc,
align: "center"
});
npc._npcText.anchor.set(0.5, 1);
npc._npcText.x = npc.x;
npc._npcText.y = npc.y - 70;
game.addChild(npc._npcText);
}
// Remove quest panel
if (npc._questDecisionPanel && npc._questDecisionPanel.parent) {
npc._questDecisionPanel.parent.removeChild(npc._questDecisionPanel);
}
if (npc._questDecisionText && npc._questDecisionText.parent) {
npc._questDecisionText.parent.removeChild(npc._questDecisionText);
}
if (npc._questDecisionAcceptBtn && npc._questDecisionAcceptBtn.parent) {
npc._questDecisionAcceptBtn.parent.removeChild(npc._questDecisionAcceptBtn);
}
if (npc._questDecisionRejectBtn && npc._questDecisionRejectBtn.parent) {
npc._questDecisionRejectBtn.parent.removeChild(npc._questDecisionRejectBtn);
}
npc._questDecisionPanel = null;
npc._questDecisionText = null;
npc._questDecisionAcceptBtn = null;
npc._questDecisionRejectBtn = null;
game._questDecisionPanel = null;
game._questDecisionAcceptBtn = null;
game._questDecisionRejectBtn = null;
game._questDecisionText = null;
game._questDecisionNPC = null;
}
return;
}
}
if (game._questDecisionRejectBtn && game._questDecisionRejectBtn.parent) {
var btn = game._questDecisionRejectBtn;
var bx = btn.x,
by = btn.y;
if (x > bx - 90 && x < bx + 90 && y > by - 40 && y < by + 40) {
// Reject quest
var npc = btn._questNPC;
if (npc) {
if (npc.questType === 'defend') {
npc._defendHelpAccepted = false;
// Show floating label for rejected
var rejectLabel = new Text2("Yardım edilmedi.", {
size: 36,
fill: 0xff4444,
align: "center"
});
rejectLabel.anchor.set(0.5, 0.5);
rejectLabel.x = npc.x;
rejectLabel.y = npc.y + 110;
game.addChild(rejectLabel);
npc._npcInteractionLabel = rejectLabel;
npc._npcInteractionLabelExpire = LK.ticks + 120;
npc.pauseUntil = LK.ticks + 120;
npc._npcText && npc._npcText.parent && npc._npcText.parent.removeChild(npc._npcText);
npc._npcText = new Text2(npc.rpgName + ": Anlaşıldı, kendi başıma savaşacağım!", {
size: 32,
fill: 0x00ffcc,
align: "center"
});
npc._npcText.anchor.set(0.5, 1);
npc._npcText.x = npc.x;
npc._npcText.y = npc.y - 70;
game.addChild(npc._npcText);
} else {
// Mark quest as inactive
npc.questActive = false;
// Show floating label for rejected
var rejectLabel = new Text2("Görev reddedildi.", {
size: 36,
fill: 0xff4444,
align: "center"
});
rejectLabel.anchor.set(0.5, 0.5);
rejectLabel.x = npc.x;
rejectLabel.y = npc.y + 110;
game.addChild(rejectLabel);
npc._npcInteractionLabel = rejectLabel;
npc._npcInteractionLabelExpire = LK.ticks + 120;
npc.pauseUntil = LK.ticks + 120;
npc._npcText && npc._npcText.parent && npc._npcText.parent.removeChild(npc._npcText);
npc._npcText = new Text2(npc.rpgName + ": Belki başka zaman!", {
size: 32,
fill: 0x00ffcc,
align: "center"
});
npc._npcText.anchor.set(0.5, 1);
npc._npcText.x = npc.x;
npc._npcText.y = npc.y - 70;
game.addChild(npc._npcText);
}
// Remove quest panel
if (npc._questDecisionPanel && npc._questDecisionPanel.parent) {
npc._questDecisionPanel.parent.removeChild(npc._questDecisionPanel);
}
if (npc._questDecisionText && npc._questDecisionText.parent) {
npc._questDecisionText.parent.removeChild(npc._questDecisionText);
}
if (npc._questDecisionAcceptBtn && npc._questDecisionAcceptBtn.parent) {
npc._questDecisionAcceptBtn.parent.removeChild(npc._questDecisionAcceptBtn);
}
if (npc._questDecisionRejectBtn && npc._questDecisionRejectBtn.parent) {
npc._questDecisionRejectBtn.parent.removeChild(npc._questDecisionRejectBtn);
}
npc._questDecisionPanel = null;
npc._questDecisionText = null;
npc._questDecisionAcceptBtn = null;
npc._questDecisionRejectBtn = null;
game._questDecisionPanel = null;
game._questDecisionAcceptBtn = null;
game._questDecisionRejectBtn = null;
game._questDecisionText = null;
game._questDecisionNPC = null;
}
return;
}
}
// Check for NPC tap (RPG/adventure/trade)
for (var i = 0; i < npcs.length; i++) {
var npc = npcs[i];
var dist = Math.sqrt(Math.pow(npc.x - ship.x, 2) + Math.pow(npc.y - ship.y, 2));
if (dist < 180) {
game._lastNPCTap = LK.ticks;
break;
}
}
// --- Distress event tap detection ---
if (window.distressEvent && window.distressEvent.helpBtn && window.distressEvent.ignoreBtn) {
game._lastDistressTapTick = LK.ticks;
game._lastDistressTapX = x;
game._lastDistressTapY = y;
}
}
};
game.move = function (x, y, obj) {
// Only allow joystick drag if ship is alive
if (isDragging && ship && ship.health > 0) {
var dx = x - joystickBase.x;
var dy = y - joystickBase.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Clamp handle to joystick base radius (130px for 30% larger size)
if (distance > 130) {
dx = dx / distance * 130;
dy = dy / distance * 130;
}
joystickHandle.x = joystickBase.x + dx;
joystickHandle.y = joystickBase.y + dy;
}
};
game.up = function (x, y, obj) {
// Only allow joystick release if ship is alive
isDragging = false;
if (ship && ship.health > 0) {
// Snap handle back to base
joystickHandle.x = joystickBase.x;
joystickHandle.y = joystickBase.y;
}
};
// Main game loop
game.update = function () {
// --- PERIODIC WORLD CLEANUP TO PREVENT LAG ---
// Every 600 ticks (~10 seconds), remove excess or orphaned entities
if (typeof window._lastWorldCleanupTick === "undefined") {
window._lastWorldCleanupTick = LK.ticks;
}
if (LK.ticks - window._lastWorldCleanupTick > 600) {
// Defensive: Remove destroyed, undefined, or offscreen objects from all arrays
var cleanupArray = function cleanupArray(arr, maxCount, offscreenDist) {
if (!arr) {
return;
}
for (var i = arr.length - 1; i >= 0; i--) {
var obj = arr[i];
// Remove if destroyed, undefined, or missing x/y
if (!obj || typeof obj.x !== "number" || typeof obj.y !== "number" || obj._destroyed) {
if (obj && typeof obj.destroy === "function") {
obj.destroy();
}
arr.splice(i, 1);
continue;
}
// Remove if far offscreen (relative to ship)
if (typeof ship !== "undefined" && ship && typeof ship.x === "number" && typeof ship.y === "number" && typeof offscreenDist === "number") {
var dx = obj.x - ship.x;
var dy = obj.y - ship.y;
if (Math.sqrt(dx * dx + dy * dy) > offscreenDist) {
if (typeof obj.destroy === "function") {
obj.destroy();
}
arr.splice(i, 1);
continue;
}
}
}
// Clamp array to maxCount (remove oldest if too many)
if (typeof maxCount === "number" && arr.length > maxCount) {
for (var j = arr.length - 1; j >= maxCount; j--) {
if (arr[j] && typeof arr[j].destroy === "function") {
arr[j].destroy();
}
arr.splice(j, 1);
}
}
}; // Clean up all major arrays
// Remove any nulls from arrays (defensive)
var compactArray = function compactArray(arr) {
if (!arr) {
return;
}
for (var i = arr.length - 1; i >= 0; i--) {
if (!arr[i]) {
arr.splice(i, 1);
}
}
};
cleanupArray(bullets, 100, 3000);
cleanupArray(enemyBullets, 100, 3000);
cleanupArray(pirates, 30, 4000);
cleanupArray(npcs, 20, 4000);
cleanupArray(asteroids, 10, 4000);
cleanupArray(coins, 30, 4000);
cleanupArray(resources, 30, 4000);
cleanupArray(stars, 200, 6000);
cleanupArray(upgradeStations, 5, 6000);
cleanupArray(explosions, 30, 4000);
cleanupArray(pirateBases, 5, 6000);
compactArray(bullets);
compactArray(enemyBullets);
compactArray(pirates);
compactArray(npcs);
compactArray(asteroids);
compactArray(coins);
compactArray(resources);
compactArray(stars);
compactArray(upgradeStations);
compactArray(explosions);
compactArray(pirateBases);
window._lastWorldCleanupTick = LK.ticks;
}
// --- SHIP MOVEMENT SYSTEM ---
// Defensive: always reset deltas
var shipDX = 0;
var shipDY = 0;
// Block all ship movement and input if ship is dead or just respawned
if (ship && ship.health <= 0) {
// Do not move ship, do not process joystick, do not update camera, do not fire, etc.
// Optionally, you could add a "dead" animation or effect here.
// (Handled by respawn logic)
return;
} else if (typeof window._lastRespawnTick !== "undefined" && LK.ticks - window._lastRespawnTick < 60) {
// Block all input/movement for 1 second after respawn
return;
} else {
// Block ship movement if ANY panel is open (but always allow joystick to move)
if (!(window.stationDeliveryPanel && window.stationDeliveryPanelBG && window.stationDeliveryPanelBG.parent) && !(window.resourceTradePanelState && window.resourceTradePanelState.open) && !(window._resourceStockPanel && window._resourceStockPanel.bg && window._resourceStockPanel.bg.parent) && !(window.shipUpgradePanel && window.shipUpgradePanel.parent) && !(window.repairPanel && window.repairPanel.parent)) {
// Only move if dragging joystick
if (isDragging) {
var dx = joystickHandle.x - joystickBase.x;
var dy = joystickHandle.y - joystickBase.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Only move if joystick is moved enough
if (distance > 10) {
// Normalize direction, scale by ship speed and joystick displacement
var norm = Math.min(distance, 130) / 130;
var effectiveSpeed = ship.speed * norm;
shipDX = dx / distance * effectiveSpeed;
shipDY = dy / distance * effectiveSpeed;
// Rotate ship to face movement direction
ship.rotation = Math.atan2(dy, dx) + Math.PI / 2;
}
}
}
// Actually move the ship's logical position, but keep it visually centered
ship.x += shipDX;
ship.y += shipDY;
}
// Track total distance traveled
totalDistance += Math.sqrt(shipDX * shipDX + shipDY * shipDY);
// --- Keep ship always visually centered ---
// Calculate the offset needed to keep the ship at the center of the screen
var centerX = 2048 / 2;
var centerY = 2732 / 2;
var offsetX = centerX - ship.x;
var offsetY = centerY - ship.y;
// Visually place the ship at the center
ship.x = centerX;
ship.y = centerY;
// Move all world objects by the offset so the ship appears to move through the world
for (var i = 0; i < stars.length; i++) {
stars[i].x += offsetX;
stars[i].y += offsetY;
}
for (var i = 0; i < npcs.length; i++) {
npcs[i].x += offsetX;
npcs[i].y += offsetY;
}
for (var i = 0; i < pirates.length; i++) {
pirates[i].x += offsetX;
pirates[i].y += offsetY;
}
for (var i = 0; i < coins.length; i++) {
coins[i].x += offsetX;
coins[i].y += offsetY;
}
for (var i = 0; i < resources.length; i++) {
resources[i].x += offsetX;
resources[i].y += offsetY;
}
for (var i = 0; i < bullets.length; i++) {
bullets[i].x += offsetX;
bullets[i].y += offsetY;
}
for (var i = 0; i < enemyBullets.length; i++) {
enemyBullets[i].x += offsetX;
enemyBullets[i].y += offsetY;
}
for (var i = 0; i < asteroids.length; i++) {
asteroids[i].x += offsetX;
asteroids[i].y += offsetY;
}
for (var i = 0; i < upgradeStations.length; i++) {
upgradeStations[i].x += offsetX;
upgradeStations[i].y += offsetY;
}
// --- DerelictDebris (enkaz) toplama ve fade-out sistemi ---
for (var i = upgradeStations.length - 1; i >= 0; i--) {
var debris = upgradeStations[i];
if (debris && debris.type === 'derelict' && !debris._collected && debris.intersects(ship)) {
debris._collected = true;
// 10 hammadde için rastgele bir tip seç
var resourceTypes = ['metal', 'energy', 'crystal', 'gas', 'ice', 'uranium', 'silicon', 'carbon', 'plasma', 'antimatter'];
var idx = Math.floor(Math.random() * resourceTypes.length);
if (debris._resourceAmount > 0) {
playerResources[idx] += debris._resourceAmount;
// Başarım kontrolü
checkResourceCollectAchievements && checkResourceCollectAchievements();
// Kısa bir label göster
var label = new Text2("+" + debris._resourceAmount + " " + resourceTypes[idx], {
size: 36,
fill: 0x00ff00,
align: "center"
});
label.anchor.set(0.5, 0.5);
label.x = ship.x;
label.y = ship.y - 120;
game.addChild(label);
LK.setTimeout(function () {
if (label.parent) {
label.parent.removeChild(label);
}
}, 900);
updateUI && updateUI();
saveProgress && saveProgress();
}
// Fade-out başlat
debris._fadeStartTick = LK.ticks;
debris._expireTick = debris._fadeStartTick + 60; // 1 saniyede kaybolsun
}
// Fade-out tamamlandıysa sil
if (debris && debris._shouldRemove) {
debris.destroy();
upgradeStations.splice(i, 1);
}
}
// boostParticles removed (no boost system)
for (var i = 0; i < explosions.length; i++) {
explosions[i].x += offsetX;
explosions[i].y += offsetY;
}
// Auto fire at very close pirates or asteroids
// Saldırı mesafesi %10 arttırıldı (200 -> 220)
// +%5 menzil yükseltmeleri uygula
var baseRange = 350;
var rangeBonus = (typeof window._rangeUpgradeLevel !== "undefined" ? window._rangeUpgradeLevel : storage.rangeUpgradeLevel || 0) * 0.05;
var ATTACK_RANGE = Math.round(baseRange * (1 + rangeBonus));
// %10 daha fazla menzil Katil Korsan ve GrupKorsan'a karşı
var ATTACK_RANGE_PIRATE_SPECIAL = Math.round(ATTACK_RANGE * 1.1);
if (LK.ticks - ship.lastFire > ship.fireRate && (pirates.length > 0 || asteroids.length > 0)) {
// Find nearest pirate or asteroid within 220 pixels
var nearestTarget = null;
var nearestDistance = Infinity;
var isPirate = false;
// Check pirates
for (var i = 0; i < pirates.length; i++) {
var pirate = pirates[i];
var dist = Math.sqrt(Math.pow(pirate.x - ship.x, 2) + Math.pow(pirate.y - ship.y, 2));
var isSpecialPirate = pirate._isKatil === true || pirate._isGrupKorsan === true;
var rangeToUse = isSpecialPirate ? ATTACK_RANGE_PIRATE_SPECIAL : ATTACK_RANGE;
if (dist < rangeToUse && dist < nearestDistance) {
nearestDistance = dist;
nearestTarget = pirate;
isPirate = true;
}
}
// Check asteroids
for (var i = 0; i < asteroids.length; i++) {
var dist = Math.sqrt(Math.pow(asteroids[i].x - ship.x, 2) + Math.pow(asteroids[i].y - ship.y, 2));
if (dist < ATTACK_RANGE && dist < nearestDistance) {
nearestDistance = dist;
nearestTarget = asteroids[i];
isPirate = false;
}
}
if (nearestTarget) {
var bullet = new Bullet();
bullet.x = ship.x;
bullet.y = ship.y;
var angle = Math.atan2(nearestTarget.y - ship.y, nearestTarget.x - ship.x);
bullet.directionX = Math.cos(angle);
bullet.directionY = Math.sin(angle);
bullet.rotation = angle + Math.PI / 2;
// Store last attack angle for ship rotation
game._lastShipAttackAngle = angle;
if (typeof ship !== "undefined") {
ship._lastMoveAngle = angle;
}
bullets.push(bullet);
game.addChild(bullet);
ship.lastFire = LK.ticks;
// Calculate distance from ship to bullet spawn point and adjust volume
var dx = bullet.x - ship.x;
var dy = bullet.y - ship.y;
var distanceFromShip = Math.sqrt(dx * dx + dy * dy);
// Calculate volume based on distance (closer = louder, farther = quieter)
// Max distance for sound falloff is 500 pixels
var maxSoundDistance = 500;
var volumeMultiplier = Math.max(0.1, Math.min(1.0, 1.0 - distanceFromShip / maxSoundDistance));
// Get the sound and play with adjusted volume
var shootSound = LK.getSound('shoot');
var originalVolume = shootSound.volume || 0.3; // fallback to default volume
shootSound.volume = originalVolume * volumeMultiplier;
shootSound.play();
// Restore original volume for next play
shootSound.volume = originalVolume;
// --- FRIENDS: Saldırı başlat ---
// Her friend, 1 saniye sonra saldırmaya başlar ve oyuncu saldırıyı bırakana kadar devam eder
for (var i = 0; i < friends.length; i++) {
if (friends[i] && typeof friends[i].startAttack === "function") {
// Eğer zaten bu hedefe saldırıyorsa, tekrar başlatma
if (!friends[i]._attacking || friends[i]._attackTarget !== nearestTarget) {
friends[i]._attackDelay = 60; // 1 saniye gecikme (her saldırı başlatıldığında sıfırla)
friends[i].startAttack(nearestTarget);
}
}
}
} else {
// --- FRIENDS: Saldırı bırak ---
for (var i = 0; i < friends.length; i++) {
if (friends[i] && typeof friends[i].stopAttack === "function") {
friends[i].stopAttack();
}
}
}
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
if (!bullet || typeof bullet.x === "undefined" || typeof bullet.y === "undefined") {
// Defensive: remove undefined or destroyed bullet
if (bullet && typeof bullet.destroy === "function") {
bullet.destroy();
}
bullets.splice(i, 1);
continue;
}
// Remove if marked for removal by fade-out
if (bullet._shouldRemove) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// (No longer destroy instantly when out of range, fade-out will handle it)
// Check collision with pirates
for (var j = pirates.length - 1; j >= 0; j--) {
if (bullet.intersects(pirates[j])) {
// Small explosion effect at collision
createExplosion(bullet.x, bullet.y);
pirates[j].takeDamage(bullet.damage);
var killedByPlayer = !bullet._npcBullet;
var killedByNPC = !!bullet._npcBullet;
if (pirates[j].health <= 0) {
enemiesKilled++;
createExplosion(pirates[j].x, pirates[j].y);
// --- BAŞARIM: Korsan öldürme ---
if (killedByPlayer) {
checkPirateKillAchievements();
// Katil korsan öldürme
if (pirates[j]._isKatil) {
checkKatilKillAchievement();
}
}
// Drop loot
var coin = new Coin();
coin.x = pirates[j].x;
coin.y = pirates[j].y;
// Katil korsan ise 10 kat coin ver
if (pirates[j]._isKatil) {
coin.value = coin.value * (1 + Math.floor(waveNumber / 3)) * 10;
// Katil korsan öldü, tekrar spawn edilebilir
window._katilPirateSpawned = false;
// Katil korsan öldükten veya yok olduğunda tekrar spawn için timer başlat
if (typeof window._katilPirateRespawnTimer !== "undefined" && window._katilPirateRespawnTimer) {
LK.clearTimeout(window._katilPirateRespawnTimer);
}
window._katilPirateRespawnTimer = LK.setTimeout(function () {
if (!window._katilPirateSpawned) {
// Katil korsanı tekrar spawn et
var pirate = new Pirate();
// Katil korsan statlarını tekrar ayarla
var minLevel = 1 + waveNumber * 2;
pirate.level = minLevel + 10;
pirate.health = (30 + (pirate.level - 1) * 10) * 10;
pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10;
pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10;
pirate.rpgName = "Katil";
pirate._isKatil = true;
pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3));
pirate._attackRange = Math.floor(600 * 0.9); // Katil korsan için %10 daha kısa menzil
window._katilPirateSpawned = true;
// Diğer korsanlar gibi kenar spawn algoritmasıyla doğsun
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
// Top
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y - 1400;
break;
case 1:
// Right
pirate.x = ship.x + 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
case 2:
// Bottom
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y + 1400;
break;
case 3:
// Left
pirate.x = ship.x - 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
}
// Add health bar to katil pirate
pirate._healthBar = new HealthBar();
pirate._healthBar.set({
maxValue: function maxValue() {
return pirate.health > 0 ? pirate.health : 0;
},
getValueFn: function getValueFn() {
return pirate.health > 0 ? pirate.health : 0;
},
getMaxFn: function getMaxFn() {
return 30 + waveNumber * 10;
},
width: 60,
height: 10
});
pirate._healthBar.y = -60;
pirate.addChild(pirate._healthBar);
pirates.push(pirate);
game.addChild(pirate);
}
window._katilPirateRespawnTimer = null;
}, 6000); // 6 saniye sonra tekrar spawn
// Katil korsan yok olduğunda da tekrar spawn edilmesini sağla
for (var i = pirates.length - 1; i >= 0; i--) {
var p = pirates[i];
if (p && p._isKatil && (p._destroyed || typeof p.x === "undefined" || typeof p.y === "undefined")) {
// Katil korsan yok olduysa, tekrar spawn için timer başlat
window._katilPirateSpawned = false;
if (typeof window._katilPirateRespawnTimer !== "undefined" && window._katilPirateRespawnTimer) {
LK.clearTimeout(window._katilPirateRespawnTimer);
}
window._katilPirateRespawnTimer = LK.setTimeout(function () {
if (!window._katilPirateSpawned) {
var pirate = new Pirate();
var minLevel = 1 + waveNumber * 2;
pirate.level = minLevel + 10;
pirate.health = (30 + (pirate.level - 1) * 10) * 10;
pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10;
pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10;
pirate.rpgName = "Katil";
pirate._isKatil = true;
pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3));
pirate._attackRange = Math.floor(600 * 0.9); // Katil korsan için %10 daha kısa menzil
window._katilPirateSpawned = true;
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y - 1400;
break;
case 1:
pirate.x = ship.x + 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
case 2:
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y + 1400;
break;
case 3:
pirate.x = ship.x - 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
}
pirate._healthBar = new HealthBar();
pirate._healthBar.set({
maxValue: function maxValue() {
return pirate.health > 0 ? pirate.health : 0;
},
getValueFn: function getValueFn() {
return pirate.health > 0 ? pirate.health : 0;
},
getMaxFn: function getMaxFn() {
return 30 + waveNumber * 10;
},
width: 60,
height: 10
});
pirate._healthBar.y = -60;
pirate.addChild(pirate._healthBar);
pirates.push(pirate);
game.addChild(pirate);
}
window._katilPirateRespawnTimer = null;
}, 6000);
break; // Sadece bir kere tetiklenmeli
}
}
// Katil etiketiyle özel bir label göster
var katilLabel = new Text2("KATİL KORSAN ÖLDÜRÜLDÜ!\n+10x coin", {
size: 40,
fill: 0xff0000,
align: "center"
});
katilLabel.anchor.set(0.5, 0.5);
katilLabel.x = pirates[j].x;
katilLabel.y = pirates[j].y - 120;
game.addChild(katilLabel);
LK.setTimeout(function () {
if (katilLabel.parent) {
katilLabel.parent.removeChild(katilLabel);
}
}, 1500);
} else {
coin.value = coin.value * (1 + Math.floor(waveNumber / 3));
}
coins.push(coin);
game.addChild(coin);
// GrupKorsan öldüyse %25 ihtimalle CoinBox düşür
if (pirates[j]._isGrupKorsan && Math.random() < 0.25) {
var coinBox = new CoinBox();
coinBox.x = pirates[j].x + (Math.random() - 0.5) * 40;
coinBox.y = pirates[j].y + (Math.random() - 0.5) * 40;
coins.push(coinBox); // CoinBox da coins array'ine eklenir, toplama ve fade-out için
game.addChild(coinBox);
// Kısa bir label göster
var boxLabel = new Text2("COINBOX!", {
size: 32,
fill: 0xffd700,
align: "center"
});
boxLabel.anchor.set(0.5, 0.5);
boxLabel.x = coinBox.x;
boxLabel.y = coinBox.y - 60;
game.addChild(boxLabel);
LK.setTimeout(function () {
if (boxLabel.parent) {
boxLabel.parent.removeChild(boxLabel);
}
}, 900);
}
if (Math.random() < 0.3) {
var resource = new Resource();
resource.x = pirates[j].x + (Math.random() - 0.5) * 50;
resource.y = pirates[j].y + (Math.random() - 0.5) * 50;
resources.push(resource);
game.addChild(resource);
}
// Remove KILL label after 2 seconds if present
if (pirates[j]._killLabel) {
// If not already expiring, set expire tick
if (!pirates[j]._killLabel._expireTick) {
pirates[j]._killLabel._expireTick = LK.ticks + 120;
}
}
// Remove KILL label if expired
if (pirates[j]._killLabel && pirates[j]._killLabel._expireTick && LK.ticks > pirates[j]._killLabel._expireTick) {
if (pirates[j]._killLabel.parent) {
pirates[j]._killLabel.parent.removeChild(pirates[j]._killLabel);
}
pirates[j]._killLabel = null;
}
if (killedByPlayer) {
// Oyuncu öldürdü: sadece exp kazan
var pirateExp = 3 + Math.floor(Math.random() * 3);
playerEXP += pirateExp;
// Level up if enough EXP
while (playerEXP >= expToNext(playerLevel)) {
playerEXP -= expToNext(playerLevel);
playerLevel++;
LK.effects.flashObject(ship, 0x00ffcc, 800);
checkLevelAchievements();
}
} else if (killedByNPC) {
// NPC öldürdü: yoluna devam etsin, rep/exp yok
// Kısa bir etiket göster (isteğe bağlı)
var npcLabel = new Text2("NPC KILL", {
size: 28,
fill: 0xccccff,
align: "center"
});
npcLabel.anchor.set(0.5, 0.5);
npcLabel.x = pirates[j].x;
npcLabel.y = pirates[j].y - 80;
game.addChild(npcLabel);
// Fade out and remove after 700ms
LK.setTimeout(function () {
if (npcLabel.parent) {
npcLabel.parent.removeChild(npcLabel);
}
}, 700);
}
// Update UI after EXP gain
updateUI();
pirates[j].destroy();
pirates.splice(j, 1);
// Check for wave completion
if (enemiesKilled >= 10 + waveNumber * 5) {
waveNumber++;
enemiesKilled = 0;
// --- BAŞARIM: Dalga geçme ---
checkWaveAchievements();
// Spawn upgrade station every 3 waves
if (waveNumber % 3 === 0) {
spawnUpgradeStation();
}
}
}
bullet.destroy();
bullets.splice(i, 1);
LK.getSound('hit').play();
break;
}
}
// --- PLAYER CAN ATTACK NPCs IF FIGHT MODE IS ON ---
if (window.npcFightMode) {
for (var j = npcs.length - 1; j >= 0; j--) {
var npc = npcs[j];
if (bullet.intersects(npc)) {
// Small explosion effect at collision
createExplosion(bullet.x, bullet.y);
if (typeof npc.health === "undefined") {
npc.health = 100;
}
npc.health -= bullet.damage;
LK.effects.flashObject(npc, 0xff0000, 120);
// Floating label for NPC hit
var npcHitLabel = new Text2("NPC HIT", {
size: 24,
fill: 0xff8888,
align: "center"
});
npcHitLabel.anchor.set(0.5, 0.5);
npcHitLabel.x = npc.x;
npcHitLabel.y = npc.y - 60;
game.addChild(npcHitLabel);
LK.setTimeout(function (label) {
if (label.parent) {
label.parent.removeChild(label);
}
}.bind(null, npcHitLabel), 600);
// Remove NPC if dead
if (npc.health <= 0) {
// Floating label for NPC kill
var npcKillLabel = new Text2("NPC KILL", {
size: 28,
fill: 0xccccff,
align: "center"
});
npcKillLabel.anchor.set(0.5, 0.5);
npcKillLabel.x = npc.x;
npcKillLabel.y = npc.y - 80;
game.addChild(npcKillLabel);
LK.setTimeout(function (label) {
if (label.parent) {
label.parent.removeChild(label);
}
}.bind(null, npcKillLabel), 700);
// Oyuncu NPC öldürdü: coin kaybı uygula
var coinLoss = 10 + Math.floor(Math.random() * 41); // 10-50 arası coin kaybı
playerCoins = Math.max(0, playerCoins - coinLoss);
// Kısa bir etiket göster
var coinLossLabel = new Text2("-" + coinLoss + " coin", {
size: 28,
fill: 0xff4444,
align: "center"
});
coinLossLabel.anchor.set(0.5, 0.5);
coinLossLabel.x = npc.x;
coinLossLabel.y = npc.y - 120;
game.addChild(coinLossLabel);
LK.setTimeout(function (label) {
if (label.parent) {
label.parent.removeChild(label);
}
}, 900, coinLossLabel);
updateUI();
// Remove hostile label if present
if (npc._hostileLabel && npc._hostileLabel.parent) {
npc._hostileLabel.parent.removeChild(npc._hostileLabel);
npc._hostileLabel = null;
}
// Instantly remove Düşman! label if it exists (defensive, in case above missed)
if (npc._hostileLabel && npc._hostileLabel.parent) {
npc._hostileLabel.parent.removeChild(npc._hostileLabel);
npc._hostileLabel = null;
}
npc.destroy();
npcs.splice(j, 1);
}
bullet.destroy();
bullets.splice(i, 1);
LK.getSound('hit').play();
break;
}
}
}
// Check collision with asteroids
for (var j = asteroids.length - 1; j >= 0; j--) {
if (bullet.intersects(asteroids[j])) {
// Small explosion effect at collision
createExplosion(bullet.x, bullet.y);
asteroids[j].takeDamage(bullet.damage);
if (asteroids[j].health <= 0) {
// --- BAŞARIM: Asteroit patlatma ---
checkAsteroidKillAchievements();
// Drop multiple resources (reduced by 80%)
var dropCount = Math.max(1, Math.floor(asteroids[j].resources * 0.2));
for (var k = 0; k < dropCount; k++) {
var resource = new Resource();
resource.x = asteroids[j].x + (Math.random() - 0.5) * 100;
resource.y = asteroids[j].y + (Math.random() - 0.5) * 100;
resource.amount = Math.floor(Math.random() * 3) + 1;
resources.push(resource);
game.addChild(resource);
}
createExplosion(asteroids[j].x, asteroids[j].y);
asteroids[j].destroy();
asteroids.splice(j, 1);
}
bullet.destroy();
bullets.splice(i, 1);
LK.getSound('hit').play();
break;
}
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
if (!bullet || typeof bullet.x === "undefined" || typeof bullet.y === "undefined") {
// Defensive: remove undefined or destroyed bullet
if (bullet && typeof bullet.destroy === "function") {
bullet.destroy();
}
enemyBullets.splice(i, 1);
continue;
}
// Remove if marked for removal by fade-out
if (bullet._shouldRemove) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// (No longer destroy instantly when out of range, fade-out will handle it)
if (bullet.intersects(ship)) {
// Small explosion effect at collision
createExplosion(bullet.x, bullet.y);
ship.takeDamage(bullet.damage);
updateUI();
bullet.destroy();
enemyBullets.splice(i, 1);
LK.getSound('hit').play();
continue;
}
// --- NEW: Pirate bullets can hit NPCs ---
for (var n = npcs.length - 1; n >= 0; n--) {
var npc = npcs[n];
if (bullet.intersects(npc)) {
// Small explosion effect at collision
createExplosion(bullet.x, bullet.y);
// Damage NPC
if (typeof npc.health === "undefined") {
npc.health = 100;
}
npc.health -= bullet.damage;
LK.effects.flashObject(npc, 0xff0000, 120);
// Floating label for NPC hit (optional)
var npcHitLabel = new Text2("NPC HIT", {
size: 24,
fill: 0xff8888,
align: "center"
});
npcHitLabel.anchor.set(0.5, 0.5);
npcHitLabel.x = npc.x;
npcHitLabel.y = npc.y - 60;
game.addChild(npcHitLabel);
LK.setTimeout(function (label) {
if (label.parent) {
label.parent.removeChild(label);
}
}.bind(null, npcHitLabel), 600);
// Remove NPC if dead
if (npc.health <= 0) {
// Floating label for NPC kill
var npcKillLabel = new Text2("NPC KILL", {
size: 28,
fill: 0xccccff,
align: "center"
});
npcKillLabel.anchor.set(0.5, 0.5);
npcKillLabel.x = npc.x;
npcKillLabel.y = npc.y - 80;
game.addChild(npcKillLabel);
LK.setTimeout(function (label) {
if (label.parent) {
label.parent.removeChild(label);
}
}.bind(null, npcKillLabel), 700);
// Remove hostile label if present
if (npc._hostileLabel && npc._hostileLabel.parent) {
npc._hostileLabel.parent.removeChild(npc._hostileLabel);
npc._hostileLabel = null;
}
// Remove NPC from world
npc.destroy();
npcs.splice(n, 1);
}
bullet.destroy();
enemyBullets.splice(i, 1);
LK.getSound('hit').play();
break;
}
}
}
// Update pirates
for (var i = 0; i < pirates.length; i++) {
var pirate = pirates[i];
if (pirate._healthBar && pirate._healthBar.update) {
pirate._healthBar.update();
}
// --- Pirate AI: Always attack the nearest target (NPC or player) ---
var nearestTarget = null;
var minDist = Infinity;
// Find nearest NPC
for (var n = 0; n < npcs.length; n++) {
var npc = npcs[n];
var distToNPC = Math.sqrt(Math.pow(npc.x - pirate.x, 2) + Math.pow(npc.y - pirate.y, 2));
if (distToNPC < minDist) {
minDist = distToNPC;
nearestTarget = npc;
}
}
// Compare with player distance
var distToPlayer = Math.sqrt(Math.pow(ship.x - pirate.x, 2) + Math.pow(ship.y - pirate.y, 2));
if (distToPlayer < minDist) {
minDist = distToPlayer;
nearestTarget = ship;
}
// If there is a target, move and attack
var pirateAttackRange = typeof pirate._attackRange === "number" ? pirate._attackRange : 600;
if (nearestTarget && minDist < pirateAttackRange) {
// --- 10% mesafe kuralı: hedefe %10 mesafe bırak, iç içe girme ---
var targetRadius = 40; // Varsayılan hedef yarıçapı (NPC/ship/pirate boyutu)
if (nearestTarget._healthBar && nearestTarget._healthBar._width) {
targetRadius = Math.max(targetRadius, nearestTarget._healthBar._width / 2);
}
var selfRadius = 35; // Korsan yarıçapı
if (pirate._healthBar && pirate._healthBar._width) {
selfRadius = Math.max(selfRadius, pirate._healthBar._width / 2);
}
var minBuffer = (minDist + selfRadius + targetRadius) * 0.15; // %15 buffer
var minAllowedDist = selfRadius + targetRadius + minBuffer;
var angle = Math.atan2(nearestTarget.y - pirate.y, nearestTarget.x - pirate.x);
// Sadece minAllowedDist'ten uzaktaysa yaklaş
if (minDist > minAllowedDist) {
var moveDist = Math.min(pirate.speed, minDist - minAllowedDist);
pirate.x += Math.cos(angle) * moveDist;
pirate.y += Math.sin(angle) * moveDist;
pirate.rotation = angle + Math.PI / 2;
} else {
// Hedefe çok yaklaştıysa sadece dön, hareket etme
pirate.rotation = angle + Math.PI / 2;
}
// Shoot at target
if (LK.ticks - pirate.lastFire > pirate.fireRate) {
var bullet = new EnemyBullet();
bullet.x = pirate.x;
bullet.y = pirate.y;
bullet.directionX = Math.cos(angle);
bullet.directionY = Math.sin(angle);
bullet.rotation = angle + Math.PI / 2;
bullet._pirateBullet = true;
enemyBullets.push(bullet);
game.addChild(bullet);
pirate.lastFire = LK.ticks;
// Calculate distance from ship to pirate bullet spawn point and adjust volume
var dx = bullet.x - ship.x;
var dy = bullet.y - ship.y;
var distanceFromShip = Math.sqrt(dx * dx + dy * dy);
// Calculate volume based on distance (closer = louder, farther = quieter)
// Max distance for sound falloff is 500 pixels
var maxSoundDistance = 500;
var volumeMultiplier = Math.max(0.1, Math.min(1.0, 1.0 - distanceFromShip / maxSoundDistance));
// Get the sound and play with adjusted volume
var shootSound = LK.getSound('shoot');
var originalVolume = shootSound.volume || 0.3; // fallback to default volume
shootSound.volume = originalVolume * volumeMultiplier;
shootSound.play();
// Restore original volume for next play
shootSound.volume = originalVolume;
}
} else {
// Wander randomly
pirate.x += Math.cos(pirate.moveAngle) * pirate.speed * 0.5;
pirate.y += Math.sin(pirate.moveAngle) * pirate.speed * 0.5;
}
// Remove pirates that are too far away
var dist = Math.sqrt(Math.pow(ship.x - pirate.x, 2) + Math.pow(ship.y - pirate.y, 2));
if (dist > 2000) {
// Katil korsan ise tekrar spawn için timer başlat
if (pirate._isKatil) {
window._katilPirateSpawned = false;
if (typeof window._katilPirateRespawnTimer !== "undefined" && window._katilPirateRespawnTimer) {
LK.clearTimeout(window._katilPirateRespawnTimer);
}
window._katilPirateRespawnTimer = LK.setTimeout(function () {
if (!window._katilPirateSpawned) {
var pirate = new Pirate();
var minLevel = 1 + waveNumber * 2;
pirate.level = minLevel + 10;
pirate.health = (30 + (pirate.level - 1) * 10) * 10;
pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10;
pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10;
pirate.rpgName = "Katil";
pirate._isKatil = true;
pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3));
pirate._attackRange = Math.floor(600 * 0.9);
window._katilPirateSpawned = true;
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y - 1400;
break;
case 1:
pirate.x = ship.x + 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
case 2:
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y + 1400;
break;
case 3:
pirate.x = ship.x - 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
}
pirate._healthBar = new HealthBar();
pirate._healthBar.set({
maxValue: function maxValue() {
return pirate.health > 0 ? pirate.health : 0;
},
getValueFn: function getValueFn() {
return pirate.health > 0 ? pirate.health : 0;
},
getMaxFn: function getMaxFn() {
return 30 + waveNumber * 10;
},
width: 60,
height: 10
});
pirate._healthBar.y = -60;
pirate.addChild(pirate._healthBar);
pirates.push(pirate);
game.addChild(pirate);
}
window._katilPirateRespawnTimer = null;
}, 6000);
}
pirate.destroy();
pirates.splice(i, 1);
i--;
}
}
// Update NPCs
for (var i = npcs.length - 1; i >= 0; i--) {
var npc = npcs[i];
if (npc._healthBar && npc._healthBar.update) {
npc._healthBar.update();
}
// Remove NPCs that are too far away
var dist = Math.sqrt(Math.pow(npc.x - ship.x, 2) + Math.pow(npc.y - ship.y, 2));
if (dist > 2500) {
npc.destroy();
npcs.splice(i, 1);
continue;
}
// Remove any floating text if player is not close
if (npc._npcText && npc._npcText.parent) {
npc._npcText.parent.removeChild(npc._npcText);
npc._npcText = null;
}
// --- NPC ATTITUDE: Rep sistemi kaldırıldı, sadece NPC Saldırı butonuna göre saldırı ---
// Eğer NPC Saldırı aktifse, her durumda NPC'ler oyuncuya saldırır
var npcHostile = false;
if (window.npcSaldiriBtn && window.npcSaldiriBtn._active) {
npcHostile = true;
} else {
npcHostile = false;
}
// --- NPC AI: Always attack the nearest target (pirate or player) ---
// If NPC Saldırı is PASİF, NPCs never attack the player
var nearestTarget = null;
var minDist = Infinity;
// Find nearest pirate
for (var j = 0; j < pirates.length; j++) {
var p = pirates[j];
if (p._destroyed) {
continue;
}
var d = Math.sqrt(Math.pow(p.x - npc.x, 2) + Math.pow(p.y - npc.y, 2));
if (d < minDist) {
minDist = d;
nearestTarget = p;
}
}
// Compare with player distance only if NPC Saldırı is AKTİF
var npcSaldiriActive = window.npcSaldiriBtn && window.npcSaldiriBtn._active;
if (npcSaldiriActive) {
var distToPlayer = Math.sqrt(Math.pow(ship.x - npc.x, 2) + Math.pow(ship.y - npc.y, 2));
if (distToPlayer < minDist) {
minDist = distToPlayer;
nearestTarget = ship;
}
}
// If there is a target, move and attack
if (nearestTarget && minDist < 600) {
// --- 10% mesafe kuralı: hedefe %10 mesafe bırak, iç içe girme ---
var targetRadius = 40;
if (nearestTarget._healthBar && nearestTarget._healthBar._width) {
targetRadius = Math.max(targetRadius, nearestTarget._healthBar._width / 2);
}
var selfRadius = 30;
if (npc._healthBar && npc._healthBar._width) {
selfRadius = Math.max(selfRadius, npc._healthBar._width / 2);
}
var minBuffer = (minDist + selfRadius + targetRadius) * 0.15;
var minAllowedDist = selfRadius + targetRadius + minBuffer;
var dx = nearestTarget.x - npc.x;
var dy = nearestTarget.y - npc.y;
var d = Math.sqrt(dx * dx + dy * dy);
if (d > minAllowedDist) {
var moveDist = Math.min(npc.moveSpeed * 1.2, d - minAllowedDist);
npc.x += dx / d * moveDist;
npc.y += dy / d * moveDist;
}
// Shoot at target (use NPC bullet fire rate)
if (typeof npc._aiAttackTimer === "undefined") {
npc._aiAttackTimer = 0;
}
npc._aiAttackTimer++;
// Use the same fire rate as pirates (EnemyBullet speed)
var npcFireRate = 8;
if (typeof npc.fireRate === "number") {
npcFireRate = npc.fireRate;
} else {
npcFireRate = 40;
}
if (d < 300 && npc._aiAttackTimer > npcFireRate) {
var bullet;
var angle = Math.atan2(nearestTarget.y - npc.y, nearestTarget.x - npc.x);
if (nearestTarget === ship) {
bullet = new EnemyBullet();
bullet._npcBullet = true;
} else {
bullet = new Bullet();
bullet._npcBullet = true;
}
bullet.x = npc.x;
bullet.y = npc.y;
bullet.directionX = Math.cos(angle);
bullet.directionY = Math.sin(angle);
bullet.rotation = angle + Math.PI / 2;
if (nearestTarget === ship) {
enemyBullets.push(bullet);
} else {
bullets.push(bullet);
}
game.addChild(bullet);
npc._aiAttackTimer = 0;
// Calculate distance from ship to NPC bullet spawn point and adjust volume
var dx = bullet.x - ship.x;
var dy = bullet.y - ship.y;
var distanceFromShip = Math.sqrt(dx * dx + dy * dy);
// Calculate volume based on distance (closer = louder, farther = quieter)
// Max distance for sound falloff is 500 pixels
var maxSoundDistance = 500;
var volumeMultiplier = Math.max(0.1, Math.min(1.0, 1.0 - distanceFromShip / maxSoundDistance));
// Get the sound and play with adjusted volume
var shootSound = LK.getSound('shoot');
var originalVolume = shootSound.volume || 0.3; // fallback to default volume
shootSound.volume = originalVolume * volumeMultiplier;
shootSound.play();
// Restore original volume for next play
shootSound.volume = originalVolume;
}
// Show angry label above NPC if attacking player
if (nearestTarget === ship) {
if (!npc._hostileLabel || !npc._hostileLabel.parent) {
npc._hostileLabel = new Text2("Düşman!", {
size: 32,
fill: 0xff4444,
align: "center"
});
npc._hostileLabel.anchor.set(0.5, 0.5);
npc._hostileLabel.x = npc.x;
npc._hostileLabel.y = npc.y - 80;
npc._hostileLabel.alpha = 1;
game.addChild(npc._hostileLabel);
}
if (npc._hostileLabel) {
npc._hostileLabel.x = npc.x;
npc._hostileLabel.y = npc.y - 80;
npc._hostileLabel.alpha = 1;
npc._hostileLabel._expireTick = LK.ticks + 60; // 1 second
}
if (npc._hostileLabel && npc._hostileLabel._expireTick && LK.ticks > npc._hostileLabel._expireTick) {
if (npc._hostileLabel.parent) {
npc._hostileLabel.parent.removeChild(npc._hostileLabel);
}
npc._hostileLabel = null;
} else if (npc._hostileLabel && npc._hostileLabel._expireTick) {
var fadeStart = npc._hostileLabel._expireTick - 20;
if (LK.ticks > fadeStart) {
npc._hostileLabel.alpha = Math.max(0, (npc._hostileLabel._expireTick - LK.ticks) / 20);
} else {
npc._hostileLabel.alpha = 1;
}
}
} else {
// Remove hostile label if present
if (npc._hostileLabel && npc._hostileLabel.parent) {
npc._hostileLabel.parent.removeChild(npc._hostileLabel);
npc._hostileLabel = null;
}
}
} else {
// Remove hostile label if present
if (npc._hostileLabel && npc._hostileLabel.parent) {
npc._hostileLabel.parent.removeChild(npc._hostileLabel);
npc._hostileLabel = null;
}
}
}
// Update asteroids
for (var i = asteroids.length - 1; i >= 0; i--) {
var asteroid = asteroids[i];
if (asteroid._healthBar && asteroid._healthBar.update) {
asteroid._healthBar.update();
}
var dist = Math.sqrt(Math.pow(asteroid.x - ship.x, 2) + Math.pow(asteroid.y - ship.y, 2));
if (dist > 2500) {
asteroid.destroy();
asteroids.splice(i, 1);
}
}
// Update explosion particles
for (var i = explosions.length - 1; i >= 0; i--) {
var particle = explosions[i];
particle.x += particle.vx;
particle.y += particle.vy;
particle.vx *= 0.9;
particle.vy *= 0.9;
particle.life--;
particle.alpha = particle.life / 30;
particle.scaleX = particle.scaleY = 1 + (30 - particle.life) / 10;
if (particle.life <= 0) {
particle.destroy();
explosions.splice(i, 1);
}
}
// Collect items
for (var i = coins.length - 1; i >= 0; i--) {
// Remove if marked for removal by fade-out
if (coins[i]._shouldRemove) {
coins[i].destroy();
coins.splice(i, 1);
continue;
}
if (coins[i].intersects(ship)) {
// CoinBox ise özel toplama efekti ve 50-100 coin ver
if (typeof coins[i].value === "number" && coins[i].value >= 50 && coins[i].value <= 100 && coins[i].constructor && coins[i].constructor.name === "CoinBox") {
playerCoins += coins[i].value;
// Kısa bir label göster
var boxLabel = new Text2("+" + coins[i].value + " coin!", {
size: 36,
fill: 0xffd700,
align: "center"
});
boxLabel.anchor.set(0.5, 0.5);
boxLabel.x = ship.x;
boxLabel.y = ship.y - 120;
game.addChild(boxLabel);
LK.setTimeout(function () {
if (boxLabel.parent) {
boxLabel.parent.removeChild(boxLabel);
}
}, 900);
coins[i].destroy();
coins.splice(i, 1);
LK.getSound('collect').play();
updateUI();
saveProgress();
continue;
}
// Normal coin ise
playerCoins += coins[i].value;
coins[i].destroy();
coins.splice(i, 1);
LK.getSound('collect').play();
updateUI();
saveProgress();
}
}
for (var i = resources.length - 1; i >= 0; i--) {
// Remove if marked for removal by fade-out
if (resources[i]._shouldRemove) {
resources[i].destroy();
resources.splice(i, 1);
continue;
}
if (resources[i].intersects(ship)) {
// 10 tip için index bul
var resourceTypes = ['metal', 'energy', 'crystal', 'gas', 'ice', 'uranium', 'silicon', 'carbon', 'plasma', 'antimatter'];
var idx = resourceTypes.indexOf(resources[i].type);
if (idx >= 0) {
playerResources[idx] += resources[i].amount;
// --- BAŞARIM: Hammadde toplama ---
checkResourceCollectAchievements();
}
resources[i].destroy();
resources.splice(i, 1);
LK.getSound('collect').play();
updateUI();
saveProgress();
}
}
// --- BAŞARIM: Coin biriktirme ---
checkCoinAchievements();
// --- BAŞARIM: Seviye atlama ---
checkLevelAchievements();
// --- BAŞARIM: Kalkan doldurma ---
checkShieldAchievements();
// --- BAŞARIM: Can yükseltme ---
checkHealthAchievements();
// --- BAŞARIM: Hız yükseltme ---
checkSpeedAchievements();
// --- BAŞARIM: Atış hızı yükseltme ---
checkFireRateAchievements();
// --- BAŞARIM: Yol katetme ---
checkDistanceAchievements();
// --- BAŞARIM: 30 dakika hayatta kalma ---
var elapsedTicks = LK.ticks - gameStartTick;
var elapsedSeconds = Math.floor(elapsedTicks / 60);
checkSurvive30MinAchievement(elapsedSeconds);
// Update stars
for (var i = stars.length - 1; i >= 0; i--) {
var dist = Math.sqrt(Math.pow(stars[i].x - ship.x, 2) + Math.pow(stars[i].y - ship.y, 2));
if (dist > 2000) {
stars[i].destroy();
stars.splice(i, 1);
}
}
// Spawn entities (star spawn frequency is now random interval)
if (typeof window._nextStarSpawnTick === "undefined") {
window._nextStarSpawnTick = LK.ticks + Math.floor(20 + Math.random() * 60); // 20-80 ticks
}
if (LK.ticks >= window._nextStarSpawnTick) {
spawnStar(true);
window._nextStarSpawnTick = LK.ticks + Math.floor(20 + Math.random() * 60); // 20-80 ticks
}
// Spawn pirates and NPCs at intervals
if (pirates.length < 6 && Math.random() < 0.3) {
spawnPirate();
}
if (npcs.length < 4 && Math.random() < 0.2) {
spawnNPC();
}
// --- ASTEROID SYSTEM RESTORED ---
// Spawn asteroids if less than 3 in the world (50% reduction from 6)
// Also halve the spawn rate
if (asteroids.length < 3 && Math.random() < 0.0125) {
spawnAsteroid();
}
// --- GRUP KORSAN SQUAD SYSTEM (at least 1 squad always present, Katil Korsan mantığı ile) ---
if (typeof window._grupKorsanSpawned === "undefined") {
window._grupKorsanSpawned = false;
}
if (typeof window._grupKorsanRespawnTimer === "undefined") {
window._grupKorsanRespawnTimer = null;
}
// Count living GrupKorsan pirates
var grupKorsanCount = 0;
for (var i = 0; i < pirates.length; i++) {
if (pirates[i] && pirates[i]._isGrupKorsan) {
grupKorsanCount++;
}
}
// If none exist, spawn a new squad (2-4 members) after 6 seconds, like Katil Korsan
if (grupKorsanCount === 0 && !window._grupKorsanSpawned && !window._grupKorsanRespawnTimer) {
window._grupKorsanRespawnTimer = LK.setTimeout(function () {
if (!window._grupKorsanSpawned) {
// Pick squad size 2-4
var squadSize = 2 + Math.floor(Math.random() * 3); // 2,3,4
// Spawn from edge like Katil Korsan
var edge = Math.floor(Math.random() * 4);
var centerX = ship.x,
centerY = ship.y;
switch (edge) {
case 0:
centerX = ship.x + (Math.random() - 0.5) * 1800;
centerY = ship.y - 1400;
break;
case 1:
centerX = ship.x + 1400;
centerY = ship.y + (Math.random() - 0.5) * 1800;
break;
case 2:
centerX = ship.x + (Math.random() - 0.5) * 1800;
centerY = ship.y + 1400;
break;
case 3:
centerX = ship.x - 1400;
centerY = ship.y + (Math.random() - 0.5) * 1800;
break;
}
spawnPirateSquad(centerX, centerY, squadSize);
window._grupKorsanSpawned = true;
}
window._grupKorsanRespawnTimer = null;
}, 6000);
}
// If all GrupKorsan pirates are dead or removed, allow respawn again
if (grupKorsanCount === 0 && window._grupKorsanSpawned) {
window._grupKorsanSpawned = false;
// If timer is running, clear it to avoid double spawn
if (window._grupKorsanRespawnTimer) {
LK.clearTimeout(window._grupKorsanRespawnTimer);
window._grupKorsanRespawnTimer = null;
}
}
// Update score
LK.setScore(playerCoins + questsCompleted * 100 + enemiesKilled * 10 + Math.floor(totalDistance / 100));
if (ship._healthBar && ship._healthBar.update) {
ship._healthBar.update();
}
updateUI();
// --- AUTO-REPAIR PANEL WHEN HEALTH IS LOW OR HEALTH BAR TAPPED ---
// Show repair panel ONLY if explicitly requested (not automatically when health decreases)
if (!window.repairPanel && ship.health < ship.maxHealth && ship.health > 0 && window._showRepairPanel && (!window._repairPanelSuppressUntil || LK.ticks > window._repairPanelSuppressUntil)) {
// Do nothing: do not auto-open repair panel when health decreases
// The repair panel will only open if window._showRepairPanel is set by explicit user action (e.g. health bar tap)
} else if (window.repairPanel && ship.health >= ship.maxHealth * 0.7) {
// Remove repair panel if health is restored
if (window.repairPanel.parent) {
window.repairPanel.parent.removeChild(window.repairPanel);
}
if (window.repairText && window.repairText.parent) {
window.repairText.parent.removeChild(window.repairText);
}
if (window.repairBtn && window.repairBtn.parent) {
window.repairBtn.parent.removeChild(window.repairBtn);
}
if (window.cancelRepairBtn && window.cancelRepairBtn.parent) {
window.cancelRepairBtn.parent.removeChild(window.cancelRepairBtn);
}
window.repairPanel = null;
window.repairText = null;
window.repairBtn = null;
window.cancelRepairBtn = null;
window._showRepairPanel = false;
}
// If health < 50% and not dead, do NOT auto-show repair panel (disabled as per new requirement)
// (window._showRepairPanel is only set by explicit user action, e.g. health bar tap)
// --- GAME TIME UPDATE ---
// Calculate elapsed time in seconds
var elapsedTicks = LK.ticks - gameStartTick;
var elapsedSeconds = Math.floor(elapsedTicks / 60);
var minutes = Math.floor(elapsedSeconds / 60);
var seconds = elapsedSeconds % 60;
if (typeof gameTimeText !== "undefined") {
// Format as mm:ss
var timeStr = "Game Time: " + minutes + ":" + (seconds < 10 ? "0" : "") + seconds;
gameTimeText.setText(timeStr);
// Always keep gameTimeText to the right of coinText
gameTimeText.x = coinText.x + coinText.width + 40;
gameTimeText.y = coinText.y;
}
// --- Synchronize LK.ticks with real game time ---
// This ensures all time-based systems use real elapsed time, not just frame count
LK.ticks = gameStartTick + Math.floor((Date.now() - (window._gameStartRealTime || (window._gameStartRealTime = Date.now()))) / (1000 / 60));
};
Uzay temalı global Cevher ticareti, gerçekçi, Altında "Trade" yazsın. In-Game asset. High contrast. No shadows
Uzay temalı global Cevher ticareti, uzay istasyonu, gerçekçi, Altında "Space Stations" yazsın. In-Game asset. High contrast. No shadows
Uzay temalı global Cevher deposu, uzay kargosu, gerçekçi, Altında "Storage" yazsın. In-Game asset. High contrast. No shadows
Uzay temalı Gemi Geliştirme, Tamirci İstasyonu, gerçekçi, Altında "Upgrade" yazsın. In-Game asset. High contrast. No shadows
Uzay temalı global Achievements, gerçekçi, Altında "Achievements" yazsın. In-Game asset. High contrast. No shadows
Uzay temalı, içi altın dolu kapağı açık kasa, gerçekçi, In-Game asset. High contrast. No shadows
Uzay temalı Spaceship Crew, gerçekçi, Altında "crew" yazsın. In-Game asset. High contrast. No shadows
Uzay temalı Savaş Gemisi, gerçekçi, yazısız üstten görünüm. In-Game asset. High contrast. No shadows
Uzay temalı Savaş Gemisi, gerçekçi, yazısız üstten görünüm. In-Game asset. High contrast. No shadows
Uzay temalı altın coin, gerçekçi, yazısız üstten görünüm. In-Game asset. High contrast. No shadows
Uzay temalı Savaş Gemisi, gerçekçi, yazısız üstten görünüm. In-Game asset. High contrast. No shadows
Uzay temalı Savaş Gemisi, gerçekçi, yazısız üstten görünüm. In-Game asset. High contrast. No shadows
Uzay temalı Savaş Gemisi, gerçekçi, yazısız üstten görünüm. In-Game asset. High contrast. No shadows
Uzay temalı Savaş Gemisi, gerçekçi, yazısız üstten görünüm. In-Game asset. High contrast. No shadows
Uzay temalı Savaş Gemisi, gerçekçi, yazısız üstten görünüm. In-Game asset. High contrast. No shadows
Uzay temalı Savaş Gemisi, gerçekçi, yazısız üstten görünüm. In-Game asset. High contrast. No shadows
Uzay temalı Savaş Gemisi, gerçekçi, yazısız üstten görünüm. In-Game asset. High contrast. No shadows
Uzay temalı asteroid, üzerinde cevherler bulunsun, gerçekçi, yazısız üstten görünüm. In-Game asset. High contrast. No shadows