User prompt
Oyun tick süresini, game time ile eşitle.
User prompt
Coins yazısının 1 satır sağına, "Game Time" yazılı gerçek zamanlı oyun süresi ekle. Oyunda zaman bazlı uzaklık ve sistemler bu süreye göre ayarlansın.
User prompt
Ekranda askeri öldürünce kill yazısı da kalıyor. O da kaybolsun.
User prompt
Rep ve exp yazıları ekranda görünmeye devam ediyor.
User prompt
Korsan askeri öldürünce geminin üstünde çıkan yazı 2 saniye sonra kaybolsun.
User prompt
Korsan öldürünce çıkan rep exp yazısı en fazla 2 saniye spawn olup gitsin.
User prompt
NPC'ler yapay zeka kontrolünde olsunlar. Bazen korsanlara saldırsınlar. Saldırdıları korsan üzerinde "KILL" Yazsın. Onu ben öldürürsem bana +rep ve belli miktar exp versin. Npc öldürür ise yoluna devam etsin.
User prompt
NPC'ler ile ilgili etkinlikleri oyundan tamamen sil.
User prompt
Please fix the bug: 'TypeError: npc.interact is not a function' in or related to this line: 'var msg = npc.interact();' Line Number: 2897
User prompt
Panel kapanınca, yazılar otomatik gizlensin.
User prompt
Panel arka plan resmini, içeriğine göre büyüt.
User prompt
Bütün etkinlik tarzı şeyler, olacağı zaman ekranın herhangi bir köşesinde spawn olsun.
User prompt
NPC'lerde ki Bütün görev ve etkinlikleri kaldır ve sil.
User prompt
Panel arka plan resmini %25 aşağı doğru uzat
User prompt
Yükseltme panelinin, yükseltmelerinin en altına, gemi hızını artıracak özellik ekle. Her seviye de %10 hızımız artsın.
User prompt
Çok uzağa olacak şekilde yapma bu tür etkinlikleri. Giderken Hemen ekranın biraz ileri ucunda spawn olsun.
User prompt
Çok uzağa olacak şekilde yapma bu tür etkinlikleri. Giderken Hemen ekranın biraz ileri ucunda spawn olsun.
User prompt
Çok uzağa olacak şekilde yapma bu tür etkinlikleri.
User prompt
Çok uzağa olacak şekilde yapma bu tür etkinlikleri.
User prompt
5 dakikadır gidiyorum, hala ulaşamadım. Çok uzağa olacak şekilde yapma bu tür etkinlikleri.
User prompt
Yenilenecek can miktarı kadarıyla, orantılı olarak istenen hammadde ve maliyet oranı artsın.
User prompt
Minimap, karakterin alt tarafında açılsın.
User prompt
Minimap tıklıyorum ama bir minimap açılmıyor.
User prompt
Reset butonu üzerine "Mini Map" ekle. Koordinat ve hedef görevleri, etkinlikleri orada spawn olsun. Oradan bakarak gidebilelim ilgili yere.
User prompt
Hedef olan korsanın üzerinde "KILL" yazsın.
/**** * 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; } for (var i = 0; i < boostParticles.length; i++) { boostParticles[i].x -= dx / dist * pull; boostParticles[i].y -= dy / dist * pull; } 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.update = function () { self.x += self.directionX * self.speed; self.y += self.directionY * self.speed; }; 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; self.update = function () { self.rotation += 0.05; }; 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.update = function () { self.x += self.directionX * self.speed; self.y += self.directionY * self.speed; }; 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; }); var NPC = Container.expand(function () { var self = Container.call(this); var npcGraphics = self.attachAsset('npc', { anchorX: 0.5, anchorY: 0.5 }); var questTypes = ['trade', 'delivery', 'explore', 'escort', 'hunt', 'puzzle', 'defend']; // 10% şansla yeni 'defend' questType ata, yoksa normal random if (Math.random() < 0.10) { self.questType = 'defend'; } else { self.questType = questTypes[Math.floor(Math.random() * (questTypes.length - 1))]; } self.questActive = true; self.questProgress = 0; self.questTarget = null; self.reward = Math.floor(Math.random() * 80) + 30; self.dialogueStep = 0; self.hasTraded = false; 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); self.rpgMood = Math.random() < 0.5 ? "friendly" : "neutral"; self.rpgInventory = { metal: Math.floor(Math.random() * 10), energy: Math.floor(Math.random() * 10), coins: Math.floor(Math.random() * 30) }; self.rpgTradeOffer = { give: Math.random() < 0.5 ? "metal" : "energy", giveAmount: Math.floor(Math.random() * 5) + 1, want: Math.random() < 0.5 ? "energy" : "metal", wantAmount: Math.floor(Math.random() * 5) + 1 }; // --- AI/Quest logic --- self.update = function () { // 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); } else { self.targetX = null; self.targetY = null; } } else { self.x += Math.cos(self.moveAngle) * self.moveSpeed; self.y += Math.sin(self.moveAngle) * self.moveSpeed; } // Float animation on top of movement self.y += Math.sin(LK.ticks * 0.05) * 0.5; // --- Defend quest: spawn pirates, request help, handle accept/reject, rep and reward --- if (self.questActive && self.questType === 'defend') { // 1. Eğer henüz korsan saldırısı başlatılmadıysa, başlat if (!self._defendStarted) { // 3-5 korsan spawn et, NPC'yi hedeflesin var squadCount = 3 + Math.floor(Math.random() * 3); self._defendPirates = spawnPirateSquad(self.x, self.y, squadCount, self); self._defendStarted = true; self._defendHelpRequested = false; self._defendHelpAccepted = undefined; // undefined: karar verilmedi, true: kabul, false: reddedildi self._defendHelpPanelShown = false; self._defendPiratesAlive = squadCount; self._defendRewardGiven = false; self._defendPirateDestroyed = 0; self._defendPirateDestroyedLast = 0; self._defendPirateDestroyedTicks = 0; self._defendPirateDestroyedTotal = squadCount; self._defendPirateDestroyedList = []; self._defendPirateDestroyedRepGiven = false; } // 2. Eğer oyuncu yakınsa ve yardım isteği henüz gösterilmediyse, Accept/Reject panelini göster var playerDist = Math.sqrt(Math.pow(ship.x - self.x, 2) + Math.pow(ship.y - self.y, 2)); if (!self._defendHelpPanelShown && playerDist < 180 && self._defendHelpAccepted === undefined) { // Accept/Reject paneli göster if (!self._questDecisionPanel) { // Remove any previous quest panel if (game._questDecisionPanel && game._questDecisionPanel.parent) { game._questDecisionPanel.parent.removeChild(game._questDecisionPanel); game._questDecisionPanel = null; } if (game._questDecisionAcceptBtn && game._questDecisionAcceptBtn.parent) { game._questDecisionAcceptBtn.parent.removeChild(game._questDecisionAcceptBtn); game._questDecisionAcceptBtn = null; } if (game._questDecisionRejectBtn && game._questDecisionRejectBtn.parent) { game._questDecisionRejectBtn.parent.removeChild(game._questDecisionRejectBtn); game._questDecisionRejectBtn = null; } // Panel background var panel = LK.getAsset('resource', { anchorX: 0.5, anchorY: 0.5, x: ship.x, y: ship.y - 180, scaleX: 7, scaleY: 2, tint: 0x222244 }); // Quest text var questText = new Text2("Korsanlar saldırıyor! Yardım eder misin?", { size: 38, fill: 0xffffff, align: "center" }); questText.anchor.set(0.5, 0.5); questText.x = ship.x; questText.y = ship.y - 200; // Accept button var acceptBtn = new Text2("Yardım Et", { size: 44, fill: 0x00ff99, align: "center" }); acceptBtn.anchor.set(0.5, 0.5); acceptBtn.x = ship.x - 100; acceptBtn.y = ship.y - 120; // Reject button var rejectBtn = new Text2("Yardım Etme", { size: 44, fill: 0xff4444, align: "center" }); rejectBtn.anchor.set(0.5, 0.5); rejectBtn.x = ship.x + 100; rejectBtn.y = ship.y - 120; // Add to game game.addChild(panel); game.addChild(questText); game.addChild(acceptBtn); game.addChild(rejectBtn); // Store references for cleanup self._questDecisionPanel = panel; self._questDecisionText = questText; self._questDecisionAcceptBtn = acceptBtn; self._questDecisionRejectBtn = rejectBtn; game._questDecisionPanel = panel; game._questDecisionAcceptBtn = acceptBtn; game._questDecisionRejectBtn = rejectBtn; game._questDecisionText = questText; game._questDecisionNPC = self; acceptBtn._isQuestAcceptBtn = true; rejectBtn._isQuestRejectBtn = true; acceptBtn._questNPC = self; rejectBtn._questNPC = self; acceptBtn._questType = self.questType; rejectBtn._questType = self.questType; } self._defendHelpPanelShown = true; } // 3. Oyuncu Accept/Reject panelinden karar verirse, _defendHelpAccepted güncellenir (game.down'da) // 4. Eğer yardım kabul edildiyse, korsanlar oyuncuya ve NPC'ye saldırır, oyuncu korsanları öldürmeli if (self._defendHelpAccepted === true) { // Korsanlar hem NPC'ye hem oyuncuya saldırır, oyuncu korsanları öldürmeli // Korsanlar update fonksiyonunda zaten NPC'yi hedefliyor // Eğer tüm korsanlar öldüyse, ödül ver ve rep artır var alive = 0; for (var i = 0; i < self._defendPirates.length; i++) { if (!self._defendPirates[i]._destroyed) alive++; } if (alive === 0 && !self._defendRewardGiven) { // Görev tamamlandı, ödül ver playerCoins += self.reward + 50; playerGoodRep += 10; self.questProgress = 1; self._defendRewardGiven = true; questsCompleted++; // Görev tamamlandı mesajı göster var completeLabel = new Text2("NPC kurtarıldı! Ödül: " + (self.reward + 50) + " coin\n+10 İyi Rep", { size: 32, fill: 0x00ff99, align: "center" }); completeLabel.anchor.set(0.5, 0.5); completeLabel.x = self.x; completeLabel.y = self.y + 110; game.addChild(completeLabel); self._npcInteractionLabel = completeLabel; self._npcInteractionLabelExpire = LK.ticks + 150; self.pauseUntil = LK.ticks + 120; self.questActive = false; self._questCompleteShown = true; updateUI(); saveProgress(); } } // 5. Eğer yardım reddedildiyse, korsanlar sadece NPC'ye saldırır, NPC yok edilirse kötü rep ver if (self._defendHelpAccepted === false) { // Korsanlar sadece NPC'yi hedefler var alive = 0; for (var i = 0; i < self._defendPirates.length; i++) { if (!self._defendPirates[i]._destroyed) alive++; } // NPC öldüyse ve rep verilmediyse if (self.health !== undefined && self.health <= 0 && !self._defendPirateDestroyedRepGiven) { playerBadRep += 10; self._defendPirateDestroyedRepGiven = true; // Kötü rep mesajı var failLabel = new Text2("NPC yok edildi!\n+10 Kötü Rep", { size: 32, fill: 0xff4444, align: "center" }); failLabel.anchor.set(0.5, 0.5); failLabel.x = self.x; failLabel.y = self.y + 110; game.addChild(failLabel); self._npcInteractionLabel = failLabel; self._npcInteractionLabelExpire = LK.ticks + 150; self.pauseUntil = LK.ticks + 120; self.questActive = false; self._questCompleteShown = true; updateUI(); saveProgress(); } } } // --- RPG/Quest/Adventure logic: update quest progress if active --- if (self.questActive && self.questType === 'explore' && self.questTarget) { // If player is close to quest target, complete quest var dx = ship.x - self.questTarget.x; var dy = ship.y - self.questTarget.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 80) { self.questProgress = 1; } } if (self.questActive && self.questType === 'escort' && self.questTarget) { // Oyuncu NPC'ye yakınsa, NPC hedefe doğru hareket etmeye başlasın, uzaksa beklesin var playerDist = Math.sqrt(Math.pow(ship.x - self.x, 2) + Math.pow(ship.y - self.y, 2)); if (self.questProgress === 0) { if (playerDist < 220) { // Hedefe doğru hareket et var tdx = self.questTarget.x - self.x; var tdy = self.questTarget.y - self.y; var tdist = Math.sqrt(tdx * tdx + tdy * tdy); if (tdist > 10) { self.x += tdx / tdist * self.moveSpeed; self.y += tdy / tdist * self.moveSpeed; self.moveAngle = Math.atan2(tdy, tdx); } self._escortWaiting = false; } else { // Oyuncu uzaksa bekle, "Bekliyorum..." mesajı göster if (!self._escortWaiting) { if (self._npcText && self._npcText.parent) { self._npcText.parent.removeChild(self._npcText); } self._npcText = new Text2(self.rpgName + ": Seni bekliyorum, lütfen yakınımda kal!", { size: 32, fill: 0x00ffcc, align: "center" }); self._npcText.anchor.set(0.5, 1); self._npcText.x = self.x; self._npcText.y = self.y - 70; if (game && game.addChild) game.addChild(self._npcText); self._escortWaiting = true; } } } // Eğer NPC hedefe ulaştıysa görevi tamamla var dx = self.x - self.questTarget.x; var dy = self.y - self.questTarget.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 80) { self.questProgress = 1; // Hedefe ulaşınca bekleme mesajını kaldır if (self._npcText && self._npcText.parent) { self._npcText.parent.removeChild(self._npcText); self._npcText = null; } self._escortWaiting = false; } } if (self.questActive && self.questType === 'hunt') { // Avcılık görevi başlatıldıysa, görev süresi ve hedef sayısı belirle if (!self._huntStarted) { // Görev kabul edildiğinde başlatılır self._huntStarted = true; // Görev süresi (ör: 30 saniye = 1800 tick) self._huntTimeLimit = 1800 + Math.floor(Math.random() * 600); // 30-40 sn arası self._huntTimeStart = LK.ticks; // Kaç korsan öldürülmesi gerektiği self._huntTargetCount = 2 + Math.floor(Math.random() * 3); // 2-4 arası self._huntKilled = 0; self._huntTargetList = []; // Hedef korsanları seç var availablePirates = []; for (var i = 0; i < pirates.length; i++) { if (!pirates[i]._destroyed) availablePirates.push(pirates[i]); } // Eğer yeterli korsan yoksa, yeni korsan spawn et while (availablePirates.length < self._huntTargetCount) { spawnPirate(); for (var i = 0; i < pirates.length; i++) { if (!pirates[i]._destroyed && availablePirates.indexOf(pirates[i]) === -1) { availablePirates.push(pirates[i]); } } } // Hedef korsanları seç for (var i = 0; i < self._huntTargetCount; i++) { var p = availablePirates[i % availablePirates.length]; // Limit pirate spawn distance to max 2 minutes of travel var maxTravelTime = 120; var maxDistance = (ship && ship.speed ? ship.speed : 5) * 60 * maxTravelTime; var distToNPC = Math.sqrt(Math.pow(p.x - self.x, 2) + Math.pow(p.y - self.y, 2)); if (distToNPC > maxDistance) { // Move pirate closer to NPC var angle = Math.atan2(p.y - self.x, p.x - self.x); p.x = self.x + Math.cos(angle) * (Math.random() * (maxDistance * 0.5) + 400); p.y = self.y + Math.sin(angle) * (Math.random() * (maxDistance * 0.5) + 400); } self._huntTargetList.push(p); } self.questTarget = self._huntTargetList[0]; self._huntCurrentTargetIndex = 0; self._huntComplete = false; self._huntFailed = false; } // Görev süresi kontrolü var elapsed = LK.ticks - self._huntTimeStart; if (!self._huntComplete && !self._huntFailed && elapsed > self._huntTimeLimit) { // Süre doldu, görev başarısız self._huntFailed = true; self.questActive = false; self.questProgress = -1; // Kısa süreli başarısızlık mesajı var failLabel = new Text2("Av görevi başarısız! Süre doldu.", { size: 32, fill: 0xff4444, align: "center" }); failLabel.anchor.set(0.5, 0.5); failLabel.x = self.x; failLabel.y = self.y + 110; game.addChild(failLabel); self._npcInteractionLabel = failLabel; self._npcInteractionLabelExpire = LK.ticks + 150; self.pauseUntil = LK.ticks + 120; } // Görev başarısızsa, başka işlem yapma if (self._huntFailed) return; // Hedef korsan öldürüldüyse, bir sonrakine geç if (self._huntTargetList && self._huntCurrentTargetIndex < self._huntTargetList.length) { var currentTarget = self._huntTargetList[self._huntCurrentTargetIndex]; if (currentTarget && currentTarget._destroyed) { self._huntKilled++; self._huntCurrentTargetIndex++; if (self._huntCurrentTargetIndex < self._huntTargetList.length) { self.questTarget = self._huntTargetList[self._huntCurrentTargetIndex]; } else { self.questTarget = null; } } } // Görev tamamlandı mı? if (!self._huntComplete && self._huntKilled >= self._huntTargetCount) { self._huntComplete = true; self.questProgress = 1; self.questActive = false; playerCoins += self.reward + 30; questsCompleted++; var completeLabel = new Text2("Av görevi tamamlandı! Ödül: " + (self.reward + 30) + " coin", { size: 32, fill: 0x00ff99, align: "center" }); completeLabel.anchor.set(0.5, 0.5); completeLabel.x = self.x; completeLabel.y = self.y + 110; game.addChild(completeLabel); self._npcInteractionLabel = completeLabel; self._npcInteractionLabelExpire = LK.ticks + 150; self.pauseUntil = LK.ticks + 120; updateUI(); saveProgress(); } // Hedef korsan varsa, ona saldır if (self.questTarget && !self.questTarget._destroyed) { var dx = self.questTarget.x - self.x; var dy = self.questTarget.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); // Saldırı mesafesi %10 arttırıldı (120 -> 132) if (dist > 132) { self.x += dx / dist * self.moveSpeed * 1.2; self.y += dy / dist * self.moveSpeed * 1.2; self.moveAngle = Math.atan2(dy, dx); } // Saldırı: NPC ateş etsin (her 40 tickte bir) if (!self._lastAttack || LK.ticks - self._lastAttack > 40) { var bullet = new Bullet(); bullet.x = self.x; bullet.y = self.y; var angle = Math.atan2(self.questTarget.y - self.y, self.questTarget.x - self.x); bullet.directionX = Math.cos(angle); bullet.directionY = Math.sin(angle); bullet.rotation = angle + Math.PI / 2; bullets.push(bullet); if (game && game.addChild) game.addChild(bullet); self._lastAttack = LK.ticks; } // Eğer oyuncu da yakınsa, birlikte saldırı efekti // Saldırı mesafesi %10 arttırıldı (200 -> 220) var playerDist = Math.sqrt(Math.pow(ship.x - self.questTarget.x, 2) + Math.pow(ship.y - self.questTarget.y, 2)); if (playerDist < 220 && dist < 220) { self.questTarget.moveAngle = Math.atan2(ship.y - self.questTarget.y, ship.x - self.questTarget.x); } } } if (self.questActive && self.questType === 'puzzle') { // Simulate puzzle solved after some time if (LK.ticks % 600 === 0) { self.questProgress = 1; } } }; // --- RPG/Adventure/Trade: interaction method for player tap/close --- self.interact = function () { // Dialogue/quest/trade logic if (!self.questActive) return "I'm just exploring the stars!"; // Helper for EXP gain and level up function gainEXP(amount) { playerEXP += amount; var leveledUp = false; while (playerEXP >= expToNext(playerLevel)) { playerEXP -= expToNext(playerLevel); playerLevel++; leveledUp = true; } updateUI(); if (leveledUp) { LK.effects.flashObject(ship, 0x00ffcc, 800); return "Level Up! Now Level " + playerLevel + "!"; } return null; } if (self.questType === 'trade') { if (!self.hasTraded) { // Offer trade if (self.rpgTradeOffer.give === 'metal' && playerMetal >= self.rpgTradeOffer.wantAmount) { playerMetal -= self.rpgTradeOffer.wantAmount; playerEnergy += self.rpgTradeOffer.giveAmount; self.hasTraded = true; var expMsg = gainEXP(10 + Math.floor(Math.random() * 10)); return self.rpgName + ": Thanks for the trade! +" + self.rpgTradeOffer.giveAmount + " energy!" + (expMsg ? "\n" + expMsg : ""); } else if (self.rpgTradeOffer.give === 'energy' && playerEnergy >= self.rpgTradeOffer.wantAmount) { playerEnergy -= self.rpgTradeOffer.wantAmount; playerMetal += self.rpgTradeOffer.giveAmount; self.hasTraded = true; var expMsg = gainEXP(10 + Math.floor(Math.random() * 10)); return self.rpgName + ": Thanks for the trade! +" + self.rpgTradeOffer.giveAmount + " metal!" + (expMsg ? "\n" + expMsg : ""); } else { return self.rpgName + ": I can trade " + self.rpgTradeOffer.giveAmount + " " + self.rpgTradeOffer.give + " for " + self.rpgTradeOffer.wantAmount + " " + self.rpgTradeOffer.want + "."; } } else { return self.rpgName + ": Good luck out there!"; } } if (self.questType === 'delivery') { if (self.questProgress === 0) { // Assign delivery quest: go to a random point if (!self.questTarget) { // Limit quest target distance to max 2 minutes of travel (approx 60*ship.speed*2 px) var maxTravelTime = 120; // seconds var maxDistance = (ship && ship.speed ? ship.speed : 5) * 60 * maxTravelTime; // px var angle = Math.random() * Math.PI * 2; var dist = Math.random() * (maxDistance * 0.5) + 400; // 400px min, up to maxDistance/2 self.questTarget = { x: self.x + Math.cos(angle) * dist, y: self.y + Math.sin(angle) * dist }; return self.rpgName + ": Can you deliver this package to (" + Math.floor(self.questTarget.x) + "," + Math.floor(self.questTarget.y) + ")?"; } else { var dx = ship.x - self.questTarget.x; var dy = ship.y - self.questTarget.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 80) { self.questProgress = 1; playerCoins += self.reward; questsCompleted++; var expMsg = gainEXP(40 + Math.floor(Math.random() * 20)); return self.rpgName + ": Delivery complete! Reward: " + self.reward + " coins!" + (expMsg ? "\n" + expMsg : ""); } else { return self.rpgName + ": Please deliver to (" + Math.floor(self.questTarget.x) + "," + Math.floor(self.questTarget.y) + ")"; } } } else { return self.rpgName + ": Thank you for your help!"; } } if (self.questType === 'explore') { if (self.questProgress === 0) { if (!self.questTarget) { // Limit quest target distance to max 2 minutes of travel var maxTravelTime = 120; var maxDistance = (ship && ship.speed ? ship.speed : 5) * 60 * maxTravelTime; var angle = Math.random() * Math.PI * 2; var dist = Math.random() * (maxDistance * 0.5) + 400; self.questTarget = { x: self.x + Math.cos(angle) * dist, y: self.y + Math.sin(angle) * dist }; return self.rpgName + ": Can you explore the area at (" + Math.floor(self.questTarget.x) + "," + Math.floor(self.questTarget.y) + ")?"; } else { var dx = ship.x - self.questTarget.x; var dy = ship.y - self.questTarget.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 80) { self.questProgress = 1; playerCoins += self.reward; questsCompleted++; var expMsg = gainEXP(30 + Math.floor(Math.random() * 15)); return self.rpgName + ": Thanks for exploring! Reward: " + self.reward + " coins!" + (expMsg ? "\n" + expMsg : ""); } else { return self.rpgName + ": Please explore (" + Math.floor(self.questTarget.x) + "," + Math.floor(self.questTarget.y) + ")"; } } } else { return self.rpgName + ": You are a true explorer!"; } } if (self.questType === 'escort') { if (self.questProgress === 0) { if (!self.questTarget) { // Limit quest target distance to max 2 minutes of travel var maxTravelTime = 120; var maxDistance = (ship && ship.speed ? ship.speed : 5) * 60 * maxTravelTime; var angle = Math.random() * Math.PI * 2; var dist = Math.random() * (maxDistance * 0.5) + 400; self.questTarget = { x: self.x + Math.cos(angle) * dist, y: self.y + Math.sin(angle) * dist }; return self.rpgName + ": Can you escort me to (" + Math.floor(self.questTarget.x) + "," + Math.floor(self.questTarget.y) + ")?"; } else { var dx = self.x - self.questTarget.x; var dy = self.y - self.questTarget.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 80) { self.questProgress = 1; playerCoins += self.reward; questsCompleted++; var expMsg = gainEXP(35 + Math.floor(Math.random() * 15)); return self.rpgName + ": Safe arrival! Reward: " + self.reward + " coins!" + (expMsg ? "\n" + expMsg : ""); } else { // Move toward target if player is close var pdx = ship.x - self.x; var pdy = ship.y - self.y; var pdist = Math.sqrt(pdx * pdx + pdy * pdy); if (pdist < 200) { var tdx = self.questTarget.x - self.x; var tdy = self.questTarget.y - self.y; var tdist = Math.sqrt(tdx * tdx + tdy * tdy); if (tdist > 10) { self.x += tdx / tdist * self.moveSpeed; self.y += tdy / tdist * self.moveSpeed; } } return self.rpgName + ": Please escort me to (" + Math.floor(self.questTarget.x) + "," + Math.floor(self.questTarget.y) + ")"; } } } else { return self.rpgName + ": Thank you for the escort!"; } } if (self.questType === 'hunt') { if (self.questProgress === 0) { if (!self.questTarget) { // Assign a random pirate as target, and give pirates random names if not already set if (pirates.length > 0) { // Pirate name pool var pirateNames = ["Kaptan Kara", "Uzay Korsanı Zed", "Astro Jack", "Korsan Nova", "Kaptan Yıldız", "Korsan Rex", "Korsan Luna", "Korsan Vex", "Korsan Orion", "Korsan Vega"]; // Assign names to pirates if not already named for (var i = 0; i < pirates.length; i++) { if (!pirates[i].rpgName || pirates[i].rpgName.indexOf("Korsan-") === 0 || pirates[i].rpgName.indexOf("BaseKorsan-") === 0) { pirates[i].rpgName = pirateNames[Math.floor(Math.random() * pirateNames.length)]; } } self.questTarget = pirates[Math.floor(Math.random() * pirates.length)]; return self.rpgName + ": " + self.questTarget.rpgName + " adlı korsanı yenebilir misin?"; } else { return self.rpgName + ": Yakında korsan yok, sonra tekrar gel!"; } } else { if (self.questTarget._destroyed) { self.questProgress = 1; playerCoins += self.reward; questsCompleted++; var expMsg = gainEXP(50 + Math.floor(Math.random() * 20)); return self.rpgName + ": Korsan yenildi! Ödül: " + self.reward + " coin!" + (expMsg ? "\n" + expMsg : ""); } else { return self.rpgName + ": Lütfen " + (self.questTarget.rpgName || "bilinmeyen korsan") + " adlı korsanı yen."; } } } else { return self.rpgName + ": Gerçek bir kahramansın!"; } } if (self.questType === 'puzzle') { if (self.questProgress === 0) { return self.rpgName + ": Solve my puzzle! (Wait a bit...)"; } else { playerCoins += self.reward; questsCompleted++; self.questProgress = 2; var expMsg = gainEXP(25 + Math.floor(Math.random() * 10)); return self.rpgName + ": Puzzle solved! Reward: " + self.reward + " coins!" + (expMsg ? "\n" + expMsg : ""); } } return self.rpgName + ": Safe travels!"; }; return self; }); var Pirate = Container.expand(function () { var self = Container.call(this); var pirateGraphics = self.attachAsset('pirate', { 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 // --- 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); // Level text only, defense text removed self.takeDamage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xffffff, 100); }; self.update = function () { // 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); } // 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); } // 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; } } else { self.x += Math.cos(self.moveAngle) * self.speed * 0.5; self.y += Math.sin(self.moveAngle) * self.speed * 0.5; } }; return self; }); // Pirate Base Class var PirateBase = Container.expand(function () { var self = Container.call(this); var baseGraphics = self.attachAsset('pirate', { 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; // 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("Korsan Üssü Yok Edildi!", { 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 }); self.type = Math.random() < 0.5 ? 'metal' : 'energy'; self.amount = Math.floor(Math.random() * 5) + 1; self.update = function () { self.rotation += 0.03; }; 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.boostSpeed = 0; self.boostCooldown = 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) { self.health = 0; LK.showGameOver(); } }; 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); } // Boost cooldown if (self.boostCooldown > 0) { self.boostCooldown--; } if (self.boostSpeed > 0) { self.boostSpeed -= 0.2; } }; 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); var tradeGraphics = self.attachAsset('npc', { 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; }); var UpgradeStation = Container.expand(function () { var self = Container.call(this); var stationGraphics = self.attachAsset('pirate', { anchorX: 0.5, anchorY: 0.5, tint: 0x00ffff, scaleX: 1.5, scaleY: 1.5 }); self.type = 'upgrade'; self.upgrades = [{ name: 'health', cost: 100, metalCost: 10 }, { name: 'shield', cost: 150, metalCost: 15 }, { name: 'damage', cost: 200, energyCost: 20 }, { name: 'speed', cost: 120, energyCost: 10 }]; self.currentUpgrade = 0; self.rotationSpeed = 0.01; self.update = function () { self.rotation += self.rotationSpeed; // Pulse effect var scale = 1.5 + Math.sin(LK.ticks * 0.05) * 0.1; stationGraphics.scaleX = scale; stationGraphics.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; } for (var i = 0; i < boostParticles.length; i++) { boostParticles[i].x -= dx2; boostParticles[i].y -= dy2; } 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 ****/ // Game variables 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 boostParticles = []; var explosions = []; var blackHoles = []; var wormholes = []; var tradeStations = []; var pirateBases = []; // NEW: array for pirate bases var tradeText = new Text2('', { size: 40, fill: 0x00ff99 }); // 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.anchor.set(0.5, 0); tradeText.y = 180; LK.gui.top.addChild(tradeText); var camera = { x: 0, y: 0 }; // --- Reputation system --- var playerGoodRep = storage.playerGoodRep || 0; var playerBadRep = storage.playerBadRep || 0; // --- Helper for multi-pirate attack quest --- function spawnPirateSquad(centerX, centerY, count, targetNPC) { var squad = []; 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; var playerMetal = storage.metal || 0; var playerEnergy = storage.energy || 0; var questsCompleted = storage.questsCompleted || 0; // 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); 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); var resourceText = new Text2('Metal: ' + playerMetal + ' Energy: ' + playerEnergy, { size: unifiedFontSize, fill: 0xFFFFFF }); resourceText.anchor.set(0, 0); resourceText.x = 120; resourceText.y = 80; LK.gui.topLeft.addChild(resourceText); 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); var boostText = new Text2('BOOST READY', { size: unifiedFontSize, fill: 0x00FFFF }); boostText.anchor.set(0.5, 0); boostText.y = 110; LK.gui.top.addChild(boostText); var waveText = new Text2('Wave: 1', { size: unifiedFontSize, fill: 0xFFFFFF }); waveText.anchor.set(1, 0); waveText.x = -20; waveText.y = 20; LK.gui.topRight.addChild(waveText); // --- RESET BUTTON (bottom right) --- var resetBtn = new Text2('Reset', { size: 44, fill: 0xff4444, font: "'Heath', Impact, 'Arial Black', Tahoma", // Heath-like font align: "center" }); resetBtn.anchor.set(1, 1); // bottom right resetBtn.x = -40; resetBtn.y = -40; LK.gui.bottomRight.addChild(resetBtn); // Touch/click handler for reset button resetBtn._isResetBtn = true; game._resetBtn = resetBtn; // Level/EXP system var playerLevel = storage.playerLevel || 1; var playerEXP = storage.playerEXP || 0; var expToNext = function expToNext(lv) { return 100 + (lv - 1) * 50; }; var expText = new Text2('Level: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel), { size: unifiedFontSize, fill: 0x00ffcc }); expText.anchor.set(1, 0); expText.x = -20; expText.y = 80; LK.gui.topRight.addChild(expText); // Move coordinateText to the left of waveText, decrease its size by 30%, and update its position and size in real time var coordinateText = new Text2('X: 0 Y: 0', { size: unifiedFontSize, // 30% smaller than Level text fill: 0xcccccc }); coordinateText.anchor.set(1, 0.5); // right center of the text // Position coordinateText to the left of waveText coordinateText.x = waveText.x - waveText.width - 30; coordinateText.y = waveText.y + waveText.height / 2; LK.gui.topRight.addChild(coordinateText); // Create joystick joystickBase = game.addChild(LK.getAsset('joystickBase', { anchorX: 0.5, anchorY: 0.5, x: 300, y: 2432 })); joystickBase.alpha = 0.5; joystickHandle = game.addChild(LK.getAsset('joystickHandle', { anchorX: 0.5, anchorY: 0.5, x: 300, y: 2432 })); 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 for (var i = 0; i < 50; i++) { spawnStar(); } // Helper functions function spawnStar() { var star = new Star(); // Spawn from edges of visible area var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: // Top star.x = ship.x + (Math.random() - 0.5) * 2500; star.y = ship.y - 1500; break; case 1: // Right star.x = ship.x + 1500; star.y = ship.y + (Math.random() - 0.5) * 2500; break; case 2: // Bottom star.x = ship.x + (Math.random() - 0.5) * 2500; star.y = ship.y + 1500; break; case 3: // Left star.x = ship.x - 1500; star.y = ship.y + (Math.random() - 0.5) * 2500; break; } 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; // 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; }, 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; 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); } function spawnUpgradeStation() { var station = new UpgradeStation(); station.x = ship.x + (Math.random() - 0.5) * 3000; station.y = ship.y + (Math.random() - 0.5) * 3000; upgradeStations.push(station); game.addChild(station); } function spawnTradeStation() { var ts = new TradeStation(); // Spawn from edges of visible area var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: ts.x = ship.x + (Math.random() - 0.5) * 2000; ts.y = ship.y - 1500; break; case 1: ts.x = ship.x + 1500; ts.y = ship.y + (Math.random() - 0.5) * 2000; break; case 2: ts.x = ship.x + (Math.random() - 0.5) * 2000; ts.y = ship.y + 1500; break; case 3: ts.x = ship.x - 1500; ts.y = ship.y + (Math.random() - 0.5) * 2000; break; } tradeStations.push(ts); game.addChild(ts); } function createExplosion(x, y) { for (var i = 0; i < 8; i++) { var particle = LK.getAsset('explosion', { anchorX: 0.5, anchorY: 0.5, x: x, y: y }); particle.vx = (Math.random() - 0.5) * 10; particle.vy = (Math.random() - 0.5) * 10; particle.life = 30; explosions.push(particle); game.addChild(particle); } } function spawnBlackHole() { var bh = new BlackHole(); // Spawn far from ship, random edge var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: bh.x = ship.x + (Math.random() - 0.5) * 2000; bh.y = ship.y - 1700; break; case 1: bh.x = ship.x + 1700; bh.y = ship.y + (Math.random() - 0.5) * 2000; break; case 2: bh.x = ship.x + (Math.random() - 0.5) * 2000; bh.y = ship.y + 1700; break; case 3: bh.x = ship.x - 1700; bh.y = ship.y + (Math.random() - 0.5) * 2000; break; } blackHoles.push(bh); game.addChild(bh); } function spawnWormhole() { var wh = new Wormhole(); // Spawn far from ship, random edge var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: wh.x = ship.x + (Math.random() - 0.5) * 2000; wh.y = ship.y - 1700; break; case 1: wh.x = ship.x + 1700; wh.y = ship.y + (Math.random() - 0.5) * 2000; break; case 2: wh.x = ship.x + (Math.random() - 0.5) * 2000; wh.y = ship.y + 1700; break; case 3: wh.x = ship.x - 1700; wh.y = ship.y + (Math.random() - 0.5) * 2000; break; } wormholes.push(wh); game.addChild(wh); } function createBoostParticle() { var particle = LK.getAsset('boostParticle', { anchorX: 0.5, anchorY: 0.5, x: ship.x - Math.cos(ship.rotation - Math.PI / 2) * 50, y: ship.y - Math.sin(ship.rotation - Math.PI / 2) * 50 }); particle.vx = -Math.cos(ship.rotation - Math.PI / 2) * 5 + (Math.random() - 0.5) * 2; particle.vy = -Math.sin(ship.rotation - Math.PI / 2) * 5 + (Math.random() - 0.5) * 2; particle.life = 20; boostParticles.push(particle); game.addChild(particle); } function updateUI() { coinText.setText('Coins: ' + playerCoins); resourceText.setText('Metal: ' + playerMetal + ' Energy: ' + playerEnergy); healthText.setText('Health: ' + ship.health + '/' + ship.maxHealth); shieldText.setText('Shield: ' + Math.floor(ship.shield) + '/' + ship.maxShield); waveText.setText('Wave: ' + (waveNumber + 1)); expText.setText('Level: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel)); // Show rep in GUI if (!window.repText) { window.repText = new Text2('Rep: +' + playerGoodRep + ' / -' + playerBadRep, { size: unifiedFontSize, fill: 0xffffff }); window.repText.anchor.set(1, 0); window.repText.x = -20; window.repText.y = 140; LK.gui.topRight.addChild(window.repText); } window.repText.setText('Rep: +' + playerGoodRep + ' / -' + playerBadRep); // 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; } // Update boost text if (ship.boostCooldown <= 0) { boostText.setText('BOOST READY'); boostText.fill = 0x00ffff; } else { boostText.setText('BOOST: ' + Math.ceil(ship.boostCooldown / 60) + 's'); boostText.fill = 0x888888; } storage.playerLevel = playerLevel; storage.playerEXP = playerEXP; } function saveProgress() { storage.coins = playerCoins; storage.metal = playerMetal; storage.energy = playerEnergy; storage.questsCompleted = questsCompleted; storage.playerGoodRep = playerGoodRep; storage.playerBadRep = playerBadRep; } // Event handlers game.down = function (x, y, obj) { // --- 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 storage.coins = 0; storage.metal = 0; storage.energy = 0; storage.questsCompleted = 0; storage.playerLevel = 1; storage.playerEXP = 0; storage.playerGoodRep = 0; storage.playerBadRep = 0; // Optionally clear any other custom progress keys // Show a quick feedback btn.setText("Reset!"); btn.fill = 0x00ff99; // Reload game (reset all progress) LK.showGameOver(); 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; 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; var cbx = cbtn.x, cby = cbtn.y; if (x > cbx - 90 && x < cbx + 90 && y > cby - 40 && y < cby + 40) { 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; 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 --- if (ship && ship.parent) { // Ship is at (ship.x, ship.y), size is 80x100 (see asset) var sx = ship.x, sy = ship.y; var sw = 80, sh = 100; // Accept generous tap area if (x > sx - sw / 2 && x < sx + sw / 2 && y > sy - sh / 2 && y < sy + sh / 2) { // Only show if not already shown if (!window.shipUpgradePanel) { // Panel background var panelWidth = 700; var panelHeight = 340; panelWidth = Math.round(panelWidth * 1.25); // %25 yana büyüt panelHeight = Math.round(panelHeight * 1.5); // %50 yukarı ve aşağı büyüt panelHeight = Math.round(panelHeight * 1.5); // %50 tekrar yukarı ve aşağı büyüt (toplamda %50+%50=2x) var panel = LK.getAsset('resource', { anchorX: 0.5, anchorY: 0.5, x: sx, y: sy - 220, scaleX: panelWidth / 40, // asset is 40px wide scaleY: panelHeight / 40, // asset is 40px tall tint: 0x224488 }); // Title var titleFont = Math.round(62 * 1.1); var title = new Text2("Yükseltmeler & Can Satın Al", { size: titleFont, fill: 0xffffff, align: "center" }); title.anchor.set(0.5, 0.5); title.x = sx; title.y = sy - 220 - panelHeight / 2 + 100; // Upgrade buttons, dikeyde aralıklı ve panelin içine sığacak şekilde var upgBtnY = title.y + 110; var upgBtnSpacingY = 140; // Paneldeki yazı ve butonların font boyutunu, Wave font boyutu ile aynı yap var upgBtnFont = unifiedFontSize; var upgBtnWidth = 340; var upgBtnHeight = 90; var colSpacing = 80; var colXOffset = upgBtnWidth / 2 + colSpacing / 2; // Normal upgrades var upg1 = new Text2("Max Can +20\n(100 coin, 10 metal)", { size: upgBtnFont, fill: 0x00ff99, align: "center" }); upg1.anchor.set(0.5, 0.5); upg1.x = sx - colXOffset; upg1.y = upgBtnY; upg1._isShipUpgradeBtn = true; upg1._upgradeType = "maxHealth"; upg1._upgradeCost = { coins: 100, metal: 10, energy: 0 }; var upg1x2 = new Text2("Max Can +20\n(200 coin, 20 metal)", { size: upgBtnFont, fill: 0x00cc99, align: "center" }); upg1x2.anchor.set(0.5, 0.5); upg1x2.x = sx + colXOffset; upg1x2.y = upgBtnY; upg1x2._isShipUpgradeBtn = true; upg1x2._upgradeType = "maxHealth"; upg1x2._upgradeCost = { coins: 200, metal: 20, energy: 0 }; upg1x2._isDoubleCost = true; var upg2 = new Text2("Max Kalkan +10\n(120 coin, 12 metal)", { size: upgBtnFont, fill: 0x00ccff, align: "center" }); upg2.anchor.set(0.5, 0.5); upg2.x = sx - colXOffset; upg2.y = upgBtnY + upgBtnSpacingY; upg2._isShipUpgradeBtn = true; upg2._upgradeType = "maxShield"; upg2._upgradeCost = { coins: 120, metal: 12, energy: 0 }; var upg2x2 = new Text2("Max Kalkan +10\n(240 coin, 24 metal)", { size: upgBtnFont, fill: 0x00aaff, align: "center" }); upg2x2.anchor.set(0.5, 0.5); upg2x2.x = sx + colXOffset; upg2x2.y = upgBtnY + upgBtnSpacingY; upg2x2._isShipUpgradeBtn = true; upg2x2._upgradeType = "maxShield"; upg2x2._upgradeCost = { coins: 240, metal: 24, energy: 0 }; upg2x2._isDoubleCost = true; var upg3 = new Text2("Hasar +5\n(150 coin, 15 enerji)", { size: upgBtnFont, fill: 0xffcc00, align: "center" }); upg3.anchor.set(0.5, 0.5); upg3.x = sx - colXOffset; upg3.y = upgBtnY + upgBtnSpacingY * 2; upg3._isShipUpgradeBtn = true; upg3._upgradeType = "damage"; upg3._upgradeCost = { coins: 150, metal: 0, energy: 15 }; var upg3x2 = new Text2("Hasar +5\n(300 coin, 30 enerji)", { size: upgBtnFont, fill: 0xff9900, align: "center" }); upg3x2.anchor.set(0.5, 0.5); upg3x2.x = sx + colXOffset; upg3x2.y = upgBtnY + upgBtnSpacingY * 2; upg3x2._isShipUpgradeBtn = true; upg3x2._upgradeType = "damage"; upg3x2._upgradeCost = { coins: 300, metal: 0, energy: 30 }; upg3x2._isDoubleCost = true; // Buy health button (if not full) var canBuyHealth = ship.health < ship.maxHealth; var buyHealthBtnFont = unifiedFontSize; var buyHealthBtn = new Text2("Canı Doldur (" + Math.max(30, Math.round((ship.maxHealth - ship.health) * 1.5)) + " coin, " + Math.max(2, Math.round((ship.maxHealth - ship.health) / 20)) + " metal, " + Math.max(2, Math.round((ship.maxHealth - ship.health) / 25)) + " enerji)", { size: buyHealthBtnFont, fill: canBuyHealth ? 0x00ffcc : 0x888888, align: "center" }); buyHealthBtn.anchor.set(0.5, 0.5); buyHealthBtn.x = sx; buyHealthBtn.y = upgBtnY + upgBtnSpacingY * 3 + 30; buyHealthBtn._isShipBuyHealthBtn = true; buyHealthBtn._repairCostCoins = Math.max(30, Math.round((ship.maxHealth - ship.health) * 1.5)); buyHealthBtn._repairCostMetal = Math.max(2, Math.round((ship.maxHealth - ship.health) / 20)); buyHealthBtn._repairCostEnergy = Math.max(2, Math.round((ship.maxHealth - ship.health) / 25)); // Close button var closeBtnFont = unifiedFontSize; var closeBtn = new Text2("Kapat", { size: closeBtnFont, fill: 0xff4444, align: "center" }); closeBtn.anchor.set(0.5, 0.5); // Move close button above the panel title, with extra margin closeBtn.x = sx; closeBtn.y = title.y - 90; closeBtn._isShipUpgradeCloseBtn = true; // Add to game game.addChild(panel); game.addChild(title); game.addChild(upg1); game.addChild(upg1x2); game.addChild(upg2); game.addChild(upg2x2); game.addChild(upg3); game.addChild(upg3x2); game.addChild(buyHealthBtn); game.addChild(closeBtn); // Store refs for tap logic/cleanup window.shipUpgradePanel = panel; window.shipUpgradeTitle = title; window.shipUpgradeBtn1 = upg1; window.shipUpgradeBtn1x2 = upg1x2; window.shipUpgradeBtn2 = upg2; window.shipUpgradeBtn2x2 = upg2x2; window.shipUpgradeBtn3 = upg3; window.shipUpgradeBtn3x2 = upg3x2; window.shipBuyHealthBtn = buyHealthBtn; window.shipUpgradeCloseBtn = closeBtn; } return; } } if (Math.sqrt(Math.pow(x - joystickBase.x, 2) + Math.pow(y - joystickBase.y, 2)) < 100) { isDragging = true; joystickHandle.x = x; joystickHandle.y = y; } else if (y < 300 && ship.boostCooldown <= 0) { ship.boostSpeed = 10; ship.boostCooldown = 300; // 5 seconds cooldown if (LK.getSound('boost')) { LK.getSound('boost').play(); } } else { // --- SHIP UPGRADE PANEL BUTTONS --- if (window.shipUpgradePanel) { // Upgrade buttons var btns = [window.shipUpgradeBtn1, window.shipUpgradeBtn1x2, window.shipUpgradeBtn2, window.shipUpgradeBtn2x2, window.shipUpgradeBtn3, window.shipUpgradeBtn3x2]; for (var i = 0; i < btns.length; i++) { var btn = btns[i]; if (!btn) continue; var bx = btn.x, by = btn.y; if (x > bx - 120 && x < bx + 120 && y > by - 50 && y < by + 50) { // Try to buy upgrade var upg = btn._upgradeType; var cost = btn._upgradeCost; var isDouble = !!btn._isDoubleCost; if (upg === "maxHealth" && playerCoins >= cost.coins && playerMetal >= cost.metal) { ship.maxHealth += 20; ship.health = ship.maxHealth; playerCoins -= cost.coins; playerMetal -= cost.metal; LK.effects.flashObject(ship, isDouble ? 0x00cc99 : 0x00ff99, 400); btn.setText("Alındı!"); btn.fill = 0x00ffcc; updateUI(); saveProgress(); } else if (upg === "maxShield" && playerCoins >= cost.coins && playerMetal >= cost.metal) { ship.maxShield += 10; ship.shield = ship.maxShield; playerCoins -= cost.coins; playerMetal -= cost.metal; LK.effects.flashObject(ship, isDouble ? 0x00aaff : 0x00ccff, 400); btn.setText("Alındı!"); btn.fill = 0x00ffcc; updateUI(); saveProgress(); } else if (upg === "damage" && playerCoins >= cost.coins && playerEnergy >= cost.energy) { ship.damage += 5; playerCoins -= cost.coins; playerEnergy -= cost.energy; LK.effects.flashObject(ship, isDouble ? 0xff9900 : 0xffcc00, 400); btn.setText("Alındı!"); btn.fill = 0x00ffcc; updateUI(); saveProgress(); } else { btn.setText("Yetersiz kaynak!"); btn.fill = 0xff4444; } return; } } // Buy health button var bhb = window.shipBuyHealthBtn; if (bhb) { var bx = bhb.x, by = bhb.y; if (x > bx - 180 && x < bx + 180 && y > by - 50 && y < by + 50) { if (ship.health < ship.maxHealth) { var costCoins = bhb._repairCostCoins, costMetal = bhb._repairCostMetal, costEnergy = bhb._repairCostEnergy; if (playerCoins >= costCoins && playerMetal >= costMetal && playerEnergy >= costEnergy) { playerCoins -= costCoins; playerMetal -= costMetal; playerEnergy -= costEnergy; ship.health = ship.maxHealth; updateUI(); saveProgress(); bhb.setText("Can Dolduruldu!"); bhb.fill = 0x00ffcc; } else { bhb.setText("Yetersiz kaynak!"); bhb.fill = 0xff4444; } } else { bhb.setText("Can zaten tam!"); bhb.fill = 0x888888; } return; } } // Close button var cb = window.shipUpgradeCloseBtn; if (cb) { var bx = cb.x, by = cb.y; if (x > bx - 120 && x < bx + 120 && y > by - 50 && y < by + 50) { // Remove all panel elements if (window.shipUpgradePanel && window.shipUpgradePanel.parent) window.shipUpgradePanel.parent.removeChild(window.shipUpgradePanel); if (window.shipUpgradeTitle && window.shipUpgradeTitle.parent) window.shipUpgradeTitle.parent.removeChild(window.shipUpgradeTitle); if (window.shipUpgradeBtn1 && window.shipUpgradeBtn1.parent) window.shipUpgradeBtn1.parent.removeChild(window.shipUpgradeBtn1); if (window.shipUpgradeBtn1x2 && window.shipUpgradeBtn1x2.parent) window.shipUpgradeBtn1x2.parent.removeChild(window.shipUpgradeBtn1x2); if (window.shipUpgradeBtn2 && window.shipUpgradeBtn2.parent) window.shipUpgradeBtn2.parent.removeChild(window.shipUpgradeBtn2); if (window.shipUpgradeBtn2x2 && window.shipUpgradeBtn2x2.parent) window.shipUpgradeBtn2x2.parent.removeChild(window.shipUpgradeBtn2x2); if (window.shipUpgradeBtn3 && window.shipUpgradeBtn3.parent) window.shipUpgradeBtn3.parent.removeChild(window.shipUpgradeBtn3); if (window.shipUpgradeBtn3x2 && window.shipUpgradeBtn3x2.parent) window.shipUpgradeBtn3x2.parent.removeChild(window.shipUpgradeBtn3x2); if (window.shipBuyHealthBtn && window.shipBuyHealthBtn.parent) window.shipBuyHealthBtn.parent.removeChild(window.shipBuyHealthBtn); if (window.shipUpgradeCloseBtn && window.shipUpgradeCloseBtn.parent) window.shipUpgradeCloseBtn.parent.removeChild(window.shipUpgradeCloseBtn); window.shipUpgradePanel = null; window.shipUpgradeTitle = null; window.shipUpgradeBtn1 = null; window.shipUpgradeBtn1x2 = null; window.shipUpgradeBtn2 = null; window.shipUpgradeBtn2x2 = null; window.shipUpgradeBtn3 = null; window.shipUpgradeBtn3x2 = null; window.shipBuyHealthBtn = null; window.shipUpgradeCloseBtn = null; 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) { if (isDragging) { var dx = x - joystickBase.x; var dy = y - joystickBase.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 100) { dx = dx / distance * 100; dy = dy / distance * 100; } joystickHandle.x = joystickBase.x + dx; joystickHandle.y = joystickBase.y + dy; } }; game.up = function (x, y, obj) { isDragging = false; joystickHandle.x = joystickBase.x; joystickHandle.y = joystickBase.y; }; // Main game loop game.update = function () { // Ship movement var shipDX = 0; var shipDY = 0; if (isDragging) { var dx = joystickHandle.x - joystickBase.x; var dy = joystickHandle.y - joystickBase.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 10) { var effectiveSpeed = ship.speed + ship.boostSpeed; shipDX = dx / 100 * effectiveSpeed; shipDY = dy / 100 * effectiveSpeed; ship.rotation = Math.atan2(dy, dx) + Math.PI / 2; // Create boost particles when boosting if (ship.boostSpeed > 0 && LK.ticks % 2 === 0) { createBoostParticle(); } } } // Track total distance traveled totalDistance += Math.sqrt(shipDX * shipDX + shipDY * shipDY); // Update camera to follow ship movement camera.x += shipDX; camera.y += shipDY; // Move all world objects opposite to ship movement to create camera effect for (var i = 0; i < stars.length; i++) { stars[i].x -= shipDX; stars[i].y -= shipDY; } for (var i = 0; i < npcs.length; i++) { npcs[i].x -= shipDX; npcs[i].y -= shipDY; } for (var i = 0; i < pirates.length; i++) { pirates[i].x -= shipDX; pirates[i].y -= shipDY; } for (var i = 0; i < coins.length; i++) { coins[i].x -= shipDX; coins[i].y -= shipDY; } for (var i = 0; i < resources.length; i++) { resources[i].x -= shipDX; resources[i].y -= shipDY; } for (var i = 0; i < bullets.length; i++) { bullets[i].x -= shipDX; bullets[i].y -= shipDY; } for (var i = 0; i < enemyBullets.length; i++) { enemyBullets[i].x -= shipDX; enemyBullets[i].y -= shipDY; } for (var i = 0; i < asteroids.length; i++) { asteroids[i].x -= shipDX; asteroids[i].y -= shipDY; } for (var i = 0; i < upgradeStations.length; i++) { upgradeStations[i].x -= shipDX; upgradeStations[i].y -= shipDY; } for (var i = 0; i < boostParticles.length; i++) { boostParticles[i].x -= shipDX; boostParticles[i].y -= shipDY; } for (var i = 0; i < explosions.length; i++) { explosions[i].x -= shipDX; explosions[i].y -= shipDY; } // Auto fire at very close pirates or asteroids // Saldırı mesafesi %10 arttırıldı (200 -> 220) var ATTACK_RANGE = 220; 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 dist = Math.sqrt(Math.pow(pirates[i].x - ship.x, 2) + Math.pow(pirates[i].y - ship.y, 2)); if (dist < ATTACK_RANGE && dist < nearestDistance) { nearestDistance = dist; nearestTarget = pirates[i]; 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; bullets.push(bullet); game.addChild(bullet); ship.lastFire = LK.ticks; LK.getSound('shoot').play(); } } // Update bullets for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; if (bullet.x < -50 || bullet.x > 2098 || bullet.y < -50 || bullet.y > 2782) { bullet.destroy(); bullets.splice(i, 1); continue; } // Check collision with pirates for (var j = pirates.length - 1; j >= 0; j--) { if (bullet.intersects(pirates[j])) { pirates[j].takeDamage(bullet.damage); if (pirates[j].health <= 0) { enemiesKilled++; createExplosion(pirates[j].x, pirates[j].y); // Drop loot var coin = new Coin(); coin.x = pirates[j].x; coin.y = pirates[j].y; coin.value = coin.value * (1 + Math.floor(waveNumber / 3)); coins.push(coin); game.addChild(coin); 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); } // Give 1-3 EXP for each pirate killed var pirateExp = 1 + 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); } // 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; // Spawn upgrade station every 3 waves if (waveNumber % 3 === 0) { spawnUpgradeStation(); } } } 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])) { asteroids[j].takeDamage(bullet.damage); if (asteroids[j].health <= 0) { // Drop multiple resources for (var k = 0; k < asteroids[j].resources; 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.x < -50 || bullet.x > 2098 || bullet.y < -50 || bullet.y > 2782) { bullet.destroy(); enemyBullets.splice(i, 1); continue; } if (bullet.intersects(ship)) { ship.takeDamage(bullet.damage); updateUI(); bullet.destroy(); enemyBullets.splice(i, 1); LK.getSound('hit').play(); } } // Update pirates for (var i = 0; i < pirates.length; i++) { var pirate = pirates[i]; if (pirate._healthBar && pirate._healthBar.update) pirate._healthBar.update(); // --- Show "KILL" label above quest target pirate for hunt quests --- var isHuntTarget = false; for (var n = 0; n < npcs.length; n++) { var npc = npcs[n]; if (npc.questType === 'hunt' && npc.questActive && npc.questTarget === pirate && !pirate._destroyed) { isHuntTarget = true; break; } } if (isHuntTarget) { if (!pirate._killLabel) { var killLabel = new Text2("KILL", { size: 38, fill: 0xff4444, align: "center" }); killLabel.anchor.set(0.5, 0); killLabel.x = pirate.x; killLabel.y = pirate.y - (pirate.children[0] ? pirate.children[0].height / 2 + 38 : 90); pirate._killLabel = killLabel; game.addChild(killLabel); } else { pirate._killLabel.x = pirate.x; pirate._killLabel.y = pirate.y - (pirate.children[0] ? pirate.children[0].height / 2 + 38 : 90); pirate._killLabel.visible = true; pirate._killLabel.alpha = 0.95 + Math.sin(LK.ticks * 0.12) * 0.05; pirate._killLabel.zIndex = 9999; } } else if (pirate._killLabel) { if (pirate._killLabel.parent) pirate._killLabel.parent.removeChild(pirate._killLabel); pirate._killLabel = null; } // Update pirate AI var dist = Math.sqrt(Math.pow(ship.x - pirate.x, 2) + Math.pow(ship.y - pirate.y, 2)); // Eğer bir NPC'nin hunt görevi varsa ve bu korsan hedefse, birlikte saldırı efekti var huntedByNPC = false; for (var n = 0; n < npcs.length; n++) { var npc = npcs[n]; if (npc.questType === 'hunt' && npc.questActive && npc.questTarget === pirate && !pirate._destroyed) { huntedByNPC = true; // NPC'nin saldırı animasyonu için efekt if (!pirate._lastHuntFlash || LK.ticks - pirate._lastHuntFlash > 30) { LK.effects.flashObject(pirate, 0xffcccc, 100); pirate._lastHuntFlash = LK.ticks; } // Eğer oyuncu da yakınsa, korsan hem NPC'ye hem oyuncuya saldırabilir var distToNPC = Math.sqrt(Math.pow(npc.x - pirate.x, 2) + Math.pow(npc.y - pirate.y, 2)); if (distToNPC < 200 && dist < 200) { // Rastgele oyuncuya veya NPC'ye saldır var target = Math.random() < 0.5 ? ship : npc; var angle = Math.atan2(target.y - pirate.y, target.x - pirate.x); pirate.x += Math.cos(angle) * pirate.speed; pirate.y += Math.sin(angle) * pirate.speed; pirate.rotation = angle + Math.PI / 2; // Pirate shooting 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); enemyBullets.push(bullet); game.addChild(bullet); pirate.lastFire = LK.ticks; } continue; } } } // Only chase and attack when very close (within 220 pixels) // Saldırı mesafesi %10 arttırıldı (200 -> 220) if (!huntedByNPC && dist < 220) { // Chase ship when very close var angle = Math.atan2(ship.y - pirate.y, ship.x - pirate.x); pirate.x += Math.cos(angle) * pirate.speed; pirate.y += Math.sin(angle) * pirate.speed; pirate.rotation = angle + Math.PI / 2; // Pirate shooting when very close 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); enemyBullets.push(bullet); game.addChild(bullet); pirate.lastFire = LK.ticks; } } if (dist > 2000) { pirate.destroy(); pirates.splice(i, 1); i--; } } // Update NPCs var npcActive = false; 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; } // Görev tamamlandıysa, NPC'nin etkileşim mesajı ve durumu güncellensin if (npc.questActive && npc.questProgress === 1 && !npc._questCompleteShown) { // Görev tamamlandı mesajı göster var completeLabel = new Text2("Görev tamamlandı!", { size: 36, fill: 0x00ff99, align: "center" }); completeLabel.anchor.set(0.5, 0.5); completeLabel.x = npc.x; completeLabel.y = npc.y + 110; game.addChild(completeLabel); npc._npcInteractionLabel = completeLabel; npc._npcInteractionLabelExpire = LK.ticks + 120; npc._questCompleteShown = true; // NPC kısa süre dursun npc.pauseUntil = LK.ticks + 120; // NPC'nin questActive'ini pasif yap (tekrar etkileşim olmasın) npc.questActive = false; } // RPG/Adventure: If player is close, show interaction UI if (dist < 180) { npcActive = true; // Remove any previous floating text if (npc._npcText && npc._npcText.parent) { npc._npcText.parent.removeChild(npc._npcText); npc._npcText = null; } var npcMsg = ""; if (game._lastNPCTap !== undefined && game._lastNPCTap + 30 > LK.ticks && game._lastNPC !== npc) { // Player tapped recently, interact var msg = npc.interact(); npcMsg = msg; updateUI(); saveProgress(); game._lastNPC = npc; // Pause NPC for 2 seconds (120 ticks) after interaction npc.pauseUntil = LK.ticks + 120; // Also clear tradeText in GUI tradeText.setText(''); // --- Show floating interaction label under NPC for a short duration --- if (msg && typeof msg === "string" && msg.length > 0) { // Remove previous interaction label if any if (npc._npcInteractionLabel && npc._npcInteractionLabel.parent) { npc._npcInteractionLabel.parent.removeChild(npc._npcInteractionLabel); npc._npcInteractionLabel = null; } var interactionLabel = new Text2(msg.split('\n')[0], { size: 30, fill: 0xffffff, align: "center" }); interactionLabel.anchor.set(0.5, 0); interactionLabel.x = npc.x; interactionLabel.y = npc.y + 70; npc._npcInteractionLabel = interactionLabel; game.addChild(interactionLabel); // Set a timer to remove after 2.5 seconds (150 ticks) npc._npcInteractionLabelExpire = LK.ticks + 150; } // --- Show Accept/Reject quest buttons above the ship if NPC is a quest-giver and quest is not yet accepted --- if (npc.questActive && npc.questType !== 'trade' && npc.questProgress === 0 && !npc._questDecisionPanel) { // Remove any previous quest panel if (game._questDecisionPanel && game._questDecisionPanel.parent) { game._questDecisionPanel.parent.removeChild(game._questDecisionPanel); game._questDecisionPanel = null; } if (game._questDecisionAcceptBtn && game._questDecisionAcceptBtn.parent) { game._questDecisionAcceptBtn.parent.removeChild(game._questDecisionAcceptBtn); game._questDecisionAcceptBtn = null; } if (game._questDecisionRejectBtn && game._questDecisionRejectBtn.parent) { game._questDecisionRejectBtn.parent.removeChild(game._questDecisionRejectBtn); game._questDecisionRejectBtn = null; } // Panel background var panel = LK.getAsset('resource', { anchorX: 0.5, anchorY: 0.5, x: ship.x, y: ship.y - 180, scaleX: 7, scaleY: 2, tint: 0x222244 }); // Quest text var questText = new Text2("Görev: " + (npcMsg ? npcMsg.split('\n')[0] : "Bir görev var!"), { size: 38, fill: 0xffffff, align: "center" }); questText.anchor.set(0.5, 0.5); questText.x = ship.x; questText.y = ship.y - 200; // Accept button var acceptBtn = new Text2("Kabul Et", { size: 44, fill: 0x00ff99, align: "center" }); acceptBtn.anchor.set(0.5, 0.5); acceptBtn.x = ship.x - 100; acceptBtn.y = ship.y - 120; // Reject button var rejectBtn = new Text2("Reddet", { size: 44, fill: 0xff4444, align: "center" }); rejectBtn.anchor.set(0.5, 0.5); rejectBtn.x = ship.x + 100; rejectBtn.y = ship.y - 120; // Add to game game.addChild(panel); game.addChild(questText); game.addChild(acceptBtn); game.addChild(rejectBtn); // Store references for cleanup npc._questDecisionPanel = panel; npc._questDecisionText = questText; npc._questDecisionAcceptBtn = acceptBtn; npc._questDecisionRejectBtn = rejectBtn; game._questDecisionPanel = panel; game._questDecisionAcceptBtn = acceptBtn; game._questDecisionRejectBtn = rejectBtn; game._questDecisionText = questText; game._questDecisionNPC = npc; // Accept/Reject tap logic acceptBtn._isQuestAcceptBtn = true; rejectBtn._isQuestRejectBtn = true; acceptBtn._questNPC = npc; rejectBtn._questNPC = npc; acceptBtn._questType = npc.questType; rejectBtn._questType = npc.questType; } // --- NPC Trade Panel Logic --- if (npc.questType === 'trade' && npc.questActive && !npc.hasTraded) { // Show a trade panel above the NPC with offer and tap-to-trade var offer = npc.rpgTradeOffer; var canTrade = false; var tradeMsg = ""; if (offer.give === 'metal' && playerMetal >= offer.wantAmount) { canTrade = true; tradeMsg = "Trade: Give " + offer.wantAmount + " " + offer.want + " for " + offer.giveAmount + " " + offer.give + " (Tap to confirm)"; } else if (offer.give === 'energy' && playerEnergy >= offer.wantAmount) { canTrade = true; tradeMsg = "Trade: Give " + offer.wantAmount + " " + offer.want + " for " + offer.giveAmount + " " + offer.give + " (Tap to confirm)"; } else { tradeMsg = "Trade: Need " + offer.wantAmount + " " + offer.want + " to trade for " + offer.giveAmount + " " + offer.give; } // Remove previous trade panel if any if (npc._npcTradePanel && npc._npcTradePanel.parent) { npc._npcTradePanel.parent.removeChild(npc._npcTradePanel); npc._npcTradePanel = null; } var tradePanel = new Text2(tradeMsg, { size: 36, fill: canTrade ? 0x00ff99 : 0xff8888, align: "center" }); tradePanel.anchor.set(0.5, 0); tradePanel.x = npc.x; tradePanel.y = npc.y + 80; npc._npcTradePanel = tradePanel; game.addChild(tradePanel); // If tapped again within window, perform trade if (canTrade && game._lastNPCTap !== undefined && game._lastNPCTap + 15 > LK.ticks && game._lastNPCTrade !== npc) { // Actually perform trade if (offer.give === 'metal') { playerMetal -= offer.wantAmount; playerEnergy += offer.giveAmount; } else { playerEnergy -= offer.wantAmount; playerMetal += offer.giveAmount; } npc.hasTraded = true; var expMsg = "EXP +" + (10 + Math.floor(Math.random() * 10)); playerEXP += 10 + Math.floor(Math.random() * 10); updateUI(); saveProgress(); // Show confirmation tradePanel.setText("Trade complete!\n" + expMsg); tradePanel.fill = 0x00ffcc; game._lastNPCTrade = npc; } } else { // Remove trade panel if not trading if (npc._npcTradePanel && npc._npcTradePanel.parent) { npc._npcTradePanel.parent.removeChild(npc._npcTradePanel); npc._npcTradePanel = null; } } } else if (!npc.questActive || npc.questProgress > 0) { npcMsg = npc.rpgName + ": " + (npc.questActive ? "Thank you!" : "Safe travels!"); // Remove trade panel if not trading if (npc._npcTradePanel && npc._npcTradePanel.parent) { npc._npcTradePanel.parent.removeChild(npc._npcTradePanel); npc._npcTradePanel = null; } } else { // Show quest/trade offer if (npc.questType === 'trade') { npcMsg = npc.rpgName + ": Trade? Tap to interact."; } else if (npc.questType === 'delivery') { npcMsg = npc.rpgName + ": Delivery quest! Tap to interact."; } else if (npc.questType === 'explore') { npcMsg = npc.rpgName + ": Exploration quest! Tap to interact."; } else if (npc.questType === 'escort') { npcMsg = npc.rpgName + ": Escort quest! Tap to interact."; } else if (npc.questType === 'hunt') { npcMsg = npc.rpgName + ": Hunt quest! Tap to interact."; } else if (npc.questType === 'puzzle') { npcMsg = npc.rpgName + ": Puzzle quest! Tap to interact."; } // Remove trade panel if not trading if (npc._npcTradePanel && npc._npcTradePanel.parent) { npc._npcTradePanel.parent.removeChild(npc._npcTradePanel); npc._npcTradePanel = null; } } // Show floating text above NPC if (npcMsg) { var npcText = new Text2(npcMsg, { size: 38, fill: 0x00ffcc, align: "center" }); npcText.anchor.set(0.5, 1); npcText.x = npc.x; npcText.y = npc.y - 70; // Remove previous if any if (npc._npcText && npc._npcText.parent) { npc._npcText.parent.removeChild(npc._npcText); } npc._npcText = npcText; game.addChild(npcText); } // Remove tradeText from GUI tradeText.setText(''); } else { // Remove floating text if player is not close if (npc._npcText && npc._npcText.parent) { npc._npcText.parent.removeChild(npc._npcText); npc._npcText = null; } } // Remove quest Accept/Reject panel if player moves away from NPC if (npc._questDecisionPanel && dist >= 220) { if (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; } // Remove floating interaction label if expired if (npc._npcInteractionLabel && npc._npcInteractionLabelExpire !== undefined && LK.ticks > npc._npcInteractionLabelExpire) { if (npc._npcInteractionLabel.parent) { npc._npcInteractionLabel.parent.removeChild(npc._npcInteractionLabel); } npc._npcInteractionLabel = null; npc._npcInteractionLabelExpire = undefined; } } if (!npcActive) { game._lastNPC = 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); } } // --- Space Debris Field Event --- // Every 18000 ticks, 10% chance, only one at a time if (!window.debrisField && LK.ticks % 18000 === 0 && Math.random() < 0.1) { var cx = ship.x + (Math.random() - 0.5) * 3000; var cy = ship.y + (Math.random() - 0.5) * 3000; var debris = []; for (var i = 0; i < 15; i++) { var a = new Asteroid(); var angle = Math.random() * Math.PI * 2; var radius = 200 + Math.random() * 200; a.x = cx + Math.cos(angle) * radius; a.y = cy + Math.sin(angle) * radius; a.health = 10 + Math.floor(Math.random() * 20); a.resources = 1 + Math.floor(Math.random() * 3); asteroids.push(a); game.addChild(a); debris.push(a); } var debrisLabel = new Text2("Enkaz Alanı", { size: 36, fill: 0xcccccc, align: "center" }); debrisLabel.anchor.set(0.5, 0); debrisLabel.x = cx; debrisLabel.y = cy + 250; game.addChild(debrisLabel); window.debrisField = { debris: debris, label: debrisLabel, x: cx, y: cy }; } // Remove debris field if all asteroids gone or too far if (window.debrisField) { var allGone = true; for (var i = 0; i < window.debrisField.debris.length; i++) { if (asteroids.indexOf(window.debrisField.debris[i]) !== -1) { allGone = false; break; } } var dist = Math.sqrt(Math.pow(window.debrisField.x - ship.x, 2) + Math.pow(window.debrisField.y - ship.y, 2)); if (allGone || dist > 4000) { if (window.debrisField.label && window.debrisField.label.parent) window.debrisField.label.parent.removeChild(window.debrisField.label); window.debrisField = null; } else { // Keep label above field if (window.debrisField.label) { window.debrisField.label.x = window.debrisField.x; window.debrisField.label.y = window.debrisField.y + 250; } } } // Update upgrade stations for (var i = upgradeStations.length - 1; i >= 0; i--) { var station = upgradeStations[i]; var dist = Math.sqrt(Math.pow(station.x - ship.x, 2) + Math.pow(station.y - ship.y, 2)); if (dist < 100 && playerCoins >= 100) { // Apply random upgrade var upgrade = station.upgrades[Math.floor(Math.random() * station.upgrades.length)]; if (upgrade.name === 'health' && playerMetal >= (upgrade.metalCost || 0)) { ship.maxHealth += 20; ship.health = ship.maxHealth; playerCoins -= upgrade.cost; playerMetal -= upgrade.metalCost || 0; } else if (upgrade.name === 'shield' && playerMetal >= (upgrade.metalCost || 0)) { ship.maxShield += 10; ship.shield = ship.maxShield; playerCoins -= upgrade.cost; playerMetal -= upgrade.metalCost || 0; } else if (upgrade.name === 'damage' && playerEnergy >= (upgrade.energyCost || 0)) { ship.damage += 5; playerCoins -= upgrade.cost; playerEnergy -= upgrade.energyCost || 0; } else if (upgrade.name === 'speed' && playerEnergy >= (upgrade.energyCost || 0)) { ship.speed += 1; playerCoins -= upgrade.cost; playerEnergy -= upgrade.energyCost || 0; } if (LK.getSound('upgrade')) { LK.getSound('upgrade').play(); } updateUI(); saveProgress(); station.destroy(); upgradeStations.splice(i, 1); } if (dist > 3000) { station.destroy(); upgradeStations.splice(i, 1); } } // Update boost particles for (var i = boostParticles.length - 1; i >= 0; i--) { var particle = boostParticles[i]; particle.x += particle.vx; particle.y += particle.vy; particle.life--; particle.alpha = particle.life / 20; if (particle.life <= 0) { particle.destroy(); boostParticles.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--) { if (coins[i].intersects(ship)) { 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--) { if (resources[i].intersects(ship)) { if (resources[i].type === 'metal') { playerMetal += resources[i].amount; } else { playerEnergy += resources[i].amount; } resources[i].destroy(); resources.splice(i, 1); LK.getSound('collect').play(); updateUI(); saveProgress(); } } // 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); } } // Update pirate bases for (var i = pirateBases.length - 1; i >= 0; i--) { var base = pirateBases[i]; if (base.update) base.update(); var dist = Math.sqrt(Math.pow(base.x - ship.x, 2) + Math.pow(base.y - ship.y, 2)); // Remove base if too far or destroyed if (dist > 3500 || base._destroyed) { if (base._healthBar && base._healthBar.parent) base._healthBar.parent.removeChild(base._healthBar); base.destroy(); pirateBases.splice(i, 1); } } // Update black holes for (var i = blackHoles.length - 1; i >= 0; i--) { var bh = blackHoles[i]; if (bh.update) bh.update(); var dist = Math.sqrt(Math.pow(bh.x - ship.x, 2) + Math.pow(bh.y - ship.y, 2)); if (dist > 3000) { bh.destroy(); blackHoles.splice(i, 1); } } // Update wormholes for (var i = wormholes.length - 1; i >= 0; i--) { var wh = wormholes[i]; if (wh.update) wh.update(); var dist = Math.sqrt(Math.pow(wh.x - ship.x, 2) + Math.pow(wh.y - ship.y, 2)); if (dist > 3000) { wh.destroy(); wormholes.splice(i, 1); } } // Update trade stations (RPG/ticaret) var tradeActive = false; for (var i = tradeStations.length - 1; i >= 0; i--) { var ts = tradeStations[i]; if (ts.update) ts.update(); var dist = Math.sqrt(Math.pow(ts.x - ship.x, 2) + Math.pow(ts.y - ship.y, 2)); if (dist < 180) { tradeActive = true; // Show trade offer if (ts.specialOffer && ts.specialUpgrade) { tradeText.setText('Special: ' + ts.specialUpgrade.label + ' for ' + ts.specialUpgrade.cost + ' coins\n(Tap to buy)'); // Buy special upgrade on tap if (game._lastTradeStation !== ts && game._lastTradeStationTap !== undefined && game._lastTradeStationTap + 30 > LK.ticks) { if (playerCoins >= ts.specialUpgrade.cost) { playerCoins -= ts.specialUpgrade.cost; if (ts.specialUpgrade.name === 'maxHealth') { ship.maxHealth += 30; ship.health = ship.maxHealth; } else if (ts.specialUpgrade.name === 'maxShield') { ship.maxShield += 20; ship.shield = ship.maxShield; } else if (ts.specialUpgrade.name === 'damage') { ship.damage += 5; } else if (ts.specialUpgrade.name === 'speed') { ship.speed += 1; } LK.effects.flashObject(ship, 0x00ff99, 400); updateUI(); saveProgress(); ts.destroy(); tradeStations.splice(i, 1); tradeText.setText('Upgrade purchased!'); game._lastTradeStation = null; break; } else { tradeText.setText('Not enough coins!'); } } } else if (ts.offerType === 'buy') { tradeText.setText('Trade: Sell ' + ts.amount + ' ' + ts.resourceType + ' for ' + ts.price + ' coins\n(Tap to sell)'); // Sell resource on tap if (game._lastTradeStation !== ts && game._lastTradeStationTap !== undefined && game._lastTradeStationTap + 30 > LK.ticks) { if (ts.resourceType === 'metal' && playerMetal >= ts.amount) { playerMetal -= ts.amount; playerCoins += ts.price; LK.effects.flashObject(ship, 0x00ff99, 200); updateUI(); saveProgress(); ts.destroy(); tradeStations.splice(i, 1); tradeText.setText('Sold!'); game._lastTradeStation = null; break; } else if (ts.resourceType === 'energy' && playerEnergy >= ts.amount) { playerEnergy -= ts.amount; playerCoins += ts.price; LK.effects.flashObject(ship, 0x00ff99, 200); updateUI(); saveProgress(); ts.destroy(); tradeStations.splice(i, 1); tradeText.setText('Sold!'); game._lastTradeStation = null; break; } else { tradeText.setText('Not enough ' + ts.resourceType + '!'); } } } else if (ts.offerType === 'sell') { tradeText.setText('Trade: Buy ' + ts.amount + ' ' + ts.resourceType + ' for ' + ts.price + ' coins\n(Tap to buy)'); // Buy resource on tap if (game._lastTradeStation !== ts && game._lastTradeStationTap !== undefined && game._lastTradeStationTap + 30 > LK.ticks) { if (playerCoins >= ts.price) { playerCoins -= ts.price; if (ts.resourceType === 'metal') { playerMetal += ts.amount; } else { playerEnergy += ts.amount; } LK.effects.flashObject(ship, 0x00ff99, 200); updateUI(); saveProgress(); ts.destroy(); tradeStations.splice(i, 1); tradeText.setText('Purchased!'); game._lastTradeStation = null; break; } else { tradeText.setText('Not enough coins!'); } } } game._lastTradeStation = ts; } if (dist > 2500) { ts.destroy(); tradeStations.splice(i, 1); } } if (!tradeActive) { tradeText.setText(''); game._lastTradeStation = null; } // Spawn entities (reduced by 70%) if (LK.ticks % 100 === 0) { if (Math.random() < 0.5) { // 50% chance spawnStar(); } } // --- Anomaly Events: Space Storm, Nebula, Time Warp --- // Only one anomaly at a time, random chance every 9000 ticks if (!window.activeAnomaly && LK.ticks % 9000 === 0 && Math.random() < 0.33) { var anomalyType = Math.random(); if (anomalyType < 0.34) { // Space Storm: disables boost and slows ship for 20 seconds window.activeAnomaly = "storm"; window.anomalyExpire = LK.ticks + 1200; ship.speed = Math.max(2, ship.speed - 2); ship.boostCooldown = 9999; var anomalyFontSize = Math.round(unifiedFontSize * 1.1); var stormLabel = new Text2("UZAY FIRTINASI! Hız ve boost devre dışı!", { size: anomalyFontSize, fill: 0x00ffff, align: "center" }); stormLabel.anchor.set(0.5, 0.5); stormLabel.x = ship.x; stormLabel.y = ship.y - 200; game.addChild(stormLabel); window.anomalyLabel = stormLabel; } else if (anomalyType < 0.67) { // Nebula: disables auto-fire and reduces vision (star alpha) window.activeAnomaly = "nebula"; window.anomalyExpire = LK.ticks + 1200; window._oldStarAlpha = []; for (var i = 0; i < stars.length; i++) { window._oldStarAlpha[i] = stars[i].children[0].alpha; stars[i].children[0].alpha = 0.1; } window._oldAutoFire = ship.autoFireDisabled; ship.autoFireDisabled = true; var nebulaLabel = new Text2("NEBULA! Otomatik ateş ve görüş kısıtlı!", { size: anomalyFontSize, fill: 0xcc99ff, align: "center" }); nebulaLabel.anchor.set(0.5, 0.5); nebulaLabel.x = ship.x; nebulaLabel.y = ship.y - 200; game.addChild(nebulaLabel); window.anomalyLabel = nebulaLabel; } else { // Time Warp: all pirates/asteroids move double speed, player gets double EXP window.activeAnomaly = "timewarp"; window.anomalyExpire = LK.ticks + 1200; for (var i = 0; i < pirates.length; i++) pirates[i].speed *= 2; for (var i = 0; i < asteroids.length; i++) asteroids[i].vx *= 2, asteroids[i].vy *= 2; window._expMultiplier = 2; var warpLabel = new Text2("ZAMAN YAMULMASI! Düşmanlar hızlı, EXP x2!", { size: anomalyFontSize, fill: 0xffcc00, align: "center" }); warpLabel.anchor.set(0.5, 0.5); warpLabel.x = ship.x; warpLabel.y = ship.y - 200; game.addChild(warpLabel); window.anomalyLabel = warpLabel; } } // Anomaly expire logic if (window.activeAnomaly && LK.ticks > window.anomalyExpire) { if (window.activeAnomaly === "storm") { ship.speed = Math.min(ship.speed + 2, 10); ship.boostCooldown = 0; } else if (window.activeAnomaly === "nebula") { for (var i = 0; i < stars.length; i++) { if (window._oldStarAlpha && window._oldStarAlpha[i] !== undefined) stars[i].children[0].alpha = window._oldStarAlpha[i]; } ship.autoFireDisabled = window._oldAutoFire || false; } else if (window.activeAnomaly === "timewarp") { for (var i = 0; i < pirates.length; i++) pirates[i].speed /= 2; for (var i = 0; i < asteroids.length; i++) asteroids[i].vx /= 2, asteroids[i].vy /= 2; window._expMultiplier = 1; } if (window.anomalyLabel && window.anomalyLabel.parent) window.anomalyLabel.parent.removeChild(window.anomalyLabel); window.activeAnomaly = null; window.anomalyExpire = null; window.anomalyLabel = null; } // Spawn black holes occasionally if (LK.ticks % 3600 === 0 && Math.random() < 0.6) { spawnBlackHole(); } // Spawn wormholes occasionally if (LK.ticks % 4200 === 0 && Math.random() < 0.6) { spawnWormhole(); } // Spawn trade stations occasionally (RPG/ticaret) if (LK.ticks % 4800 === 0 && Math.random() < 0.6) { spawnTradeStation(); } // Spawn pirate bases occasionally if (LK.ticks % 6000 === 0 && Math.random() < 0.5 && pirateBases.length < 3) { spawnPirateBase(); } if (LK.ticks % 900 === 0) { var spawnRoll = Math.random(); if (spawnRoll < 0.15) { // 15% chance (pirates reduced by 85%) spawnPirate(); } else if (spawnRoll < 0.31) { // 16% chance (NPCs increased by 10%) spawnNPC(); } else if (spawnRoll < 0.33) { // 2% chance // Spawn multiple entities occasionally spawnPirate(); spawnNPC(); } } // Spawn asteroids (reduced by 70%) if (LK.ticks % 1500 === 0) { if (Math.random() < 0.5) { // 50% chance spawnAsteroid(); } } // --- Derelict Ship Event --- // Every 12000 ticks, 20% chance, only one at a time if (!window.derelictShip && LK.ticks % 12000 === 0 && Math.random() < 0.2) { // Limit derelict ship spawn distance to max 2 minutes of travel var maxTravelTime = 120; var maxDistance = (ship && ship.speed ? ship.speed : 5) * 60 * maxTravelTime; var angle = Math.random() * Math.PI * 2; var dist = Math.random() * (maxDistance * 0.5) + 400; var dx = Math.cos(angle) * dist; var dy = Math.sin(angle) * dist; var derelict = LK.getAsset('ship', { anchorX: 0.5, anchorY: 0.5, x: ship.x + dx, y: ship.y + dy, tint: 0x888888, scaleX: 1.2, scaleY: 1.2, alpha: 0.7 }); derelict._isDerelict = true; derelict._loot = { coins: 50 + Math.floor(Math.random() * 100), metal: 5 + Math.floor(Math.random() * 10), energy: 5 + Math.floor(Math.random() * 10) }; derelict._label = new Text2("Terkedilmiş Gemi", { size: 32, fill: 0xcccccc, align: "center" }); derelict._label.anchor.set(0.5, 0); derelict._label.x = derelict.x; derelict._label.y = derelict.y + 70; game.addChild(derelict); game.addChild(derelict._label); window.derelictShip = derelict; } // Derelict ship interaction if (window.derelictShip) { var d = window.derelictShip; var dist = Math.sqrt(Math.pow(d.x - ship.x, 2) + Math.pow(d.y - ship.y, 2)); if (dist < 120) { // Collect loot playerCoins += d._loot.coins; playerMetal += d._loot.metal; playerEnergy += d._loot.energy; var lootLabel = new Text2("Gemi yağmalandı!\n+" + d._loot.coins + " coin, +" + d._loot.metal + " metal, +" + d._loot.energy + " enerji", { size: 36, fill: 0x00ffcc, align: "center" }); lootLabel.anchor.set(0.5, 0.5); lootLabel.x = d.x; lootLabel.y = d.y - 80; game.addChild(lootLabel); if (d.parent) d.parent.removeChild(d); if (d._label && d._label.parent) d._label.parent.removeChild(d._label); window.derelictShip = null; updateUI(); saveProgress(); // Remove loot label after 2 seconds (function (lbl) { var expire = LK.ticks + 120; var removeLootLabel = function removeLootLabel() { if (LK.ticks > expire && lbl.parent) { lbl.parent.removeChild(lbl); game.update = origUpdate; } }; var origUpdate = game.update; game.update = function () { removeLootLabel(); if (origUpdate) origUpdate.apply(game, arguments); }; })(lootLabel); } else if (dist > 3000) { if (d.parent) d.parent.removeChild(d); if (d._label && d._label.parent) d._label.parent.removeChild(d._label); window.derelictShip = null; } else { // Keep label above derelict if (d._label) { d._label.x = d.x; d._label.y = d.y + 70; } } } // Wave-based pirate spawning if (LK.ticks % (180 - Math.min(waveNumber * 10, 100)) === 0) { var piratesInWave = 1 + Math.floor(waveNumber / 2); for (var j = 0; j < piratesInWave; j++) { spawnPirate(); } } // --- Distress Signal Event --- // Every 15000 ticks, 15% chance, only one at a time if (!window.distressEvent && LK.ticks % 15000 === 0 && Math.random() < 0.15) { // Limit distress event spawn distance to max 2 minutes of travel var maxTravelTime = 120; var maxDistance = (ship && ship.speed ? ship.speed : 5) * 60 * maxTravelTime; var angle = Math.random() * Math.PI * 2; var dist = Math.random() * (maxDistance * 0.5) + 400; var sx = ship.x + Math.cos(angle) * dist; var sy = ship.y + Math.sin(angle) * dist; var signal = LK.getAsset('star', { anchorX: 0.5, anchorY: 0.5, x: sx, y: sy, tint: 0xff00ff, scaleX: 4, scaleY: 4, alpha: 0.8 }); var label = new Text2("İmdat Sinyali", { size: 36, fill: 0xff00ff, align: "center" }); label.anchor.set(0.5, 0); label.x = sx; label.y = sy + 70; game.addChild(signal); game.addChild(label); window.distressEvent = { signal: signal, label: label, x: sx, y: sy, state: "waiting" }; } // Distress event logic if (window.distressEvent) { var e = window.distressEvent; var dist = Math.sqrt(Math.pow(e.x - ship.x, 2) + Math.pow(e.y - ship.y, 2)); if (e.state === "waiting" && dist < 120) { // Show choice: rescue or ignore (ambush possibility) if (!e.panel) { var panel = LK.getAsset('resource', { anchorX: 0.5, anchorY: 0.5, x: e.x, y: e.y - 180, scaleX: 7, scaleY: 2, tint: 0x222244 }); var text = new Text2("İmdat sinyali! Yardım et veya uzaklaş?", { size: 38, fill: 0xffffff, align: "center" }); text.anchor.set(0.5, 0.5); text.x = e.x; text.y = e.y - 200; var helpBtn = new Text2("Yardım Et", { size: 44, fill: 0x00ff99, align: "center" }); helpBtn.anchor.set(0.5, 0.5); helpBtn.x = e.x - 100; helpBtn.y = e.y - 120; var ignoreBtn = new Text2("Uzaklaş", { size: 44, fill: 0xff4444, align: "center" }); ignoreBtn.anchor.set(0.5, 0.5); ignoreBtn.x = e.x + 100; ignoreBtn.y = e.y - 120; game.addChild(panel); game.addChild(text); game.addChild(helpBtn); game.addChild(ignoreBtn); e.panel = panel; e.text = text; e.helpBtn = helpBtn; e.ignoreBtn = ignoreBtn; helpBtn._isDistressHelp = true; ignoreBtn._isDistressIgnore = true; helpBtn._distressEvent = e; ignoreBtn._distressEvent = e; } // Tap logic for help/ignore if (game._lastDistressTap !== e && game._lastDistressTapTick !== undefined && game._lastDistressTapTick + 30 > LK.ticks) { var tapX = game._lastDistressTapX, tapY = game._lastDistressTapY; if (tapX > e.helpBtn.x - 90 && tapX < e.helpBtn.x + 90 && tapY > e.helpBtn.y - 40 && tapY < e.helpBtn.y + 40) { // Help: 70% chance for reward, 30% ambush if (Math.random() < 0.7) { var reward = 100 + Math.floor(Math.random() * 100); playerCoins += reward; var msg = new Text2("Kurtarılan gemi! Ödül: " + reward + " coin", { size: 36, fill: 0x00ffcc, align: "center" }); msg.anchor.set(0.5, 0.5); msg.x = e.x; msg.y = e.y - 80; game.addChild(msg); updateUI(); saveProgress(); } else { // Ambush: spawn 3 pirates for (var i = 0; i < 3; i++) { var pirate = new Pirate(); pirate.x = e.x + (Math.random() - 0.5) * 100; pirate.y = e.y + (Math.random() - 0.5) * 100; pirates.push(pirate); game.addChild(pirate); } var msg = new Text2("Pusu! Korsanlar saldırıyor!", { size: 36, fill: 0xff4444, align: "center" }); msg.anchor.set(0.5, 0.5); msg.x = e.x; msg.y = e.y - 80; game.addChild(msg); } // Clean up if (e.signal && e.signal.parent) e.signal.parent.removeChild(e.signal); if (e.label && e.label.parent) e.label.parent.removeChild(e.label); if (e.panel && e.panel.parent) e.panel.parent.removeChild(e.panel); if (e.text && e.text.parent) e.text.parent.removeChild(e.text); if (e.helpBtn && e.helpBtn.parent) e.helpBtn.parent.removeChild(e.helpBtn); if (e.ignoreBtn && e.ignoreBtn.parent) e.ignoreBtn.parent.removeChild(e.ignoreBtn); window.distressEvent = null; } else if (tapX > e.ignoreBtn.x - 90 && tapX < e.ignoreBtn.x + 90 && tapY > e.ignoreBtn.y - 40 && tapY < e.ignoreBtn.y + 40) { // Ignore: nothing happens, clean up if (e.signal && e.signal.parent) e.signal.parent.removeChild(e.signal); if (e.label && e.label.parent) e.label.parent.removeChild(e.label); if (e.panel && e.panel.parent) e.panel.parent.removeChild(e.panel); if (e.text && e.text.parent) e.text.parent.removeChild(e.text); if (e.helpBtn && e.helpBtn.parent) e.helpBtn.parent.removeChild(e.helpBtn); if (e.ignoreBtn && e.ignoreBtn.parent) e.ignoreBtn.parent.removeChild(e.ignoreBtn); window.distressEvent = null; } game._lastDistressTap = e; } } else if (dist > 3000) { if (e.signal && e.signal.parent) e.signal.parent.removeChild(e.signal); if (e.label && e.label.parent) e.label.parent.removeChild(e.label); if (e.panel && e.panel.parent) e.panel.parent.removeChild(e.panel); if (e.text && e.text.parent) e.text.parent.removeChild(e.text); if (e.helpBtn && e.helpBtn.parent) e.helpBtn.parent.removeChild(e.helpBtn); if (e.ignoreBtn && e.ignoreBtn.parent) e.ignoreBtn.parent.removeChild(e.ignoreBtn); window.distressEvent = null; } else { // Keep label above signal if (e.label) { e.label.x = e.x; e.label.y = e.y + 70; } } } // Boss wave every 5 waves if (waveNumber > 0 && waveNumber % 5 === 0 && pirates.length === 0) { var bossPirate = new Pirate(); bossPirate.x = ship.x + 1000; bossPirate.y = ship.y; // Level range: wave 1 => 1-3, wave 2 => 3-5, wave 3 => 5-7, etc. var minLevel = 1 + waveNumber * 2; var maxLevel = minLevel + 2; bossPirate.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1)); bossPirate.health = 200 + (bossPirate.level - 1) * 20; bossPirate.damage = 10 + Math.floor(bossPirate.level * 1.5); bossPirate.defense = 2 + Math.floor(bossPirate.level * 1.2); bossPirate.loot = 200 + bossPirate.level * 50; bossPirate.speed = 3; var bossGraphics = bossPirate.children[0]; bossGraphics.tint = 0xff00ff; bossGraphics.scaleX = 2; bossGraphics.scaleY = 2; pirates.push(bossPirate); game.addChild(bossPirate); } // 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 if health < 100% and not dead, and not already shown if (!window.repairPanel && ship.health < ship.maxHealth && ship.health > 0 && window._showRepairPanel) { var repairCostCoins = Math.max(30, Math.round((ship.maxHealth - ship.health) * 1.5)); var repairCostMetal = Math.max(2, Math.round((ship.maxHealth - ship.health) / 20)); var repairCostEnergy = Math.max(2, Math.round((ship.maxHealth - ship.health) / 25)); // Panel background var panel = LK.getAsset('resource', { anchorX: 0.5, anchorY: 0.5, x: ship.x, y: ship.y - 220, scaleX: 7, scaleY: 2, tint: 0x224444 }); // Repair text var repairText = new Text2("Gemi hasarlı! Tamir: " + repairCostCoins + " coin, " + repairCostMetal + " metal, " + repairCostEnergy + " enerji", { size: 38, fill: 0xffffff, align: "center" }); repairText.anchor.set(0.5, 0.5); repairText.x = ship.x; repairText.y = ship.y - 240; // Repair button var repairBtn = new Text2("Tamir Et", { size: 44, fill: 0x00ff99, align: "center" }); repairBtn.anchor.set(0.5, 0.5); repairBtn.x = ship.x - 100; repairBtn.y = ship.y - 160; // Cancel button var cancelBtn = new Text2("Kapat", { size: 44, fill: 0xff4444, align: "center" }); cancelBtn.anchor.set(0.5, 0.5); cancelBtn.x = ship.x + 100; cancelBtn.y = ship.y - 160; // Add to game game.addChild(panel); game.addChild(repairText); game.addChild(repairBtn); game.addChild(cancelBtn); // Store references for tap logic and cleanup window.repairPanel = panel; window.repairText = repairText; window.repairBtn = repairBtn; window.cancelRepairBtn = cancelBtn; repairBtn._isRepairBtn = true; cancelBtn._isCancelRepairBtn = true; repairBtn._repairCostCoins = repairCostCoins; repairBtn._repairCostMetal = repairCostMetal; repairBtn._repairCostEnergy = repairCostEnergy; window._showRepairPanel = false; // Reset flag } 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, auto-show repair panel (legacy behavior) if (!window.repairPanel && ship.health < ship.maxHealth * 0.5 && ship.health > 0) { window._showRepairPanel = true; } // Update coordinateText with current ship coordinates (rounded) in real time to the left of waveText if (typeof coordinateText !== "undefined" && ship && typeof waveText !== "undefined") { // Show the actual current coordinates of the ship coordinateText.setText('X: ' + Math.round(ship.x) + ' Y: ' + Math.round(ship.y)); // Always keep coordinateText to the left of waveText, and keep size 30% smaller than expText coordinateText.size = Math.round(expText.size * 0.7); coordinateText.x = waveText.x - waveText.width - 30; coordinateText.y = waveText.y + waveText.height / 2; } // --- QUEST TARGET ARROW INDICATOR --- // Only one arrow at a time, create if needed if (!window.questArrow) { // Use a yellow triangle as arrow var arrow = LK.getAsset('bullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 2.5, scaleY: 1.2, tint: 0xffff00, alpha: 0.85 }); arrow.visible = false; window.questArrow = arrow; game.addChild(arrow); } var showArrow = false; var arrowX = 0, arrowY = 0, arrowRot = 0; var questTarget = null; var minDist = Infinity; // Find the nearest active coordinate-based quest target for (var i = 0; i < npcs.length; i++) { var npc = npcs[i]; if (!npc.questActive || !npc.questTarget) continue; // Only for coordinate-based quests if (npc.questType === 'explore' || npc.questType === 'delivery' || npc.questType === 'escort' || npc.questType === 'hunt') { // For hunt, questTarget can be a pirate, otherwise it's {x, y} var tx, ty; if (npc.questType === 'hunt' && npc.questTarget && npc.questTarget.x !== undefined && npc.questTarget.y !== undefined && !npc.questTarget._destroyed) { tx = npc.questTarget.x; ty = npc.questTarget.y; } else if (npc.questTarget.x !== undefined && npc.questTarget.y !== undefined) { tx = npc.questTarget.x; ty = npc.questTarget.y; } if (typeof tx === "number" && typeof ty === "number") { var dist = Math.sqrt(Math.pow(tx - ship.x, 2) + Math.pow(ty - ship.y, 2)); if (dist > 120 && dist < minDist) { // Only show if not already at target minDist = dist; questTarget = { x: tx, y: ty }; } } } } // --- Derelict ship arrow logic --- if (!questTarget && window.derelictShip && window.derelictShip.x !== undefined && window.derelictShip.y !== undefined) { var d = window.derelictShip; var dist = Math.sqrt(Math.pow(d.x - ship.x, 2) + Math.pow(d.y - ship.y, 2)); if (dist > 120 && dist < 3000) { questTarget = { x: d.x, y: d.y }; } } if (questTarget) { // Project questTarget to screen edge var dx = questTarget.x - ship.x; var dy = questTarget.y - ship.y; var angle = Math.atan2(dy, dx); // Screen center in game coordinates var screenW = 2048, screenH = 2732; var margin = 90; // keep arrow inside screen var cx = screenW / 2, cy = screenH / 2; // Find intersection with screen edge var ex = cx + Math.cos(angle) * (Math.min(cx, cy) - margin); var ey = cy + Math.sin(angle) * (Math.min(cx, cy) - margin); // Clamp to screen bounds ex = Math.max(margin, Math.min(screenW - margin, ex)); ey = Math.max(margin, Math.min(screenH - margin, ey)); // Don't show if target is on screen (within 40% of screen from center) if (Math.abs(dx) > screenW * 0.2 || Math.abs(dy) > screenH * 0.2) { showArrow = true; arrowX = ex; arrowY = ey; arrowRot = angle + Math.PI / 2; } } if (window.questArrow) { window.questArrow.visible = showArrow; if (showArrow) { window.questArrow.x = arrowX; window.questArrow.y = arrowY; window.questArrow.rotation = arrowRot; window.questArrow.alpha = 0.85 + Math.sin(LK.ticks * 0.1) * 0.15; window.questArrow.scaleX = 2.5 + Math.sin(LK.ticks * 0.07) * 0.2; window.questArrow.scaleY = 1.2 + Math.cos(LK.ticks * 0.09) * 0.1; window.questArrow.zIndex = 9999; } } // --- Remaining Distance Label for Quest/Event Target --- // Only one label at a time, create if needed if (!window.questDistanceLabel) { var questDistanceLabel = new Text2('', { size: Math.round(unifiedFontSize * 0.9), fill: 0xffffff, align: "center" }); questDistanceLabel.anchor.set(0.5, 0.5); questDistanceLabel.visible = false; window.questDistanceLabel = questDistanceLabel; game.addChild(questDistanceLabel); } var showDistanceLabel = false; var labelX = 0, labelY = 0, labelText = ''; if (questTarget) { // Calculate distance var distToTarget = Math.sqrt(Math.pow(questTarget.x - ship.x, 2) + Math.pow(questTarget.y - ship.y, 2)); // Only show if not already at target and not on screen if (showArrow && distToTarget > 120) { showDistanceLabel = true; // Place label near the arrow, but offset further outwards var labelOffset = 70; labelX = arrowX + Math.cos(arrowRot - Math.PI / 2) * labelOffset; labelY = arrowY + Math.sin(arrowRot - Math.PI / 2) * labelOffset; // Show in km if >1000, else in px if (distToTarget >= 1000) { labelText = Math.round(distToTarget / 10) / 100 + "k px"; } else { labelText = Math.round(distToTarget) + " px"; } // If quest is from an NPC, show a short label for (var i = 0; i < npcs.length; i++) { var npc = npcs[i]; if (npc.questActive && npc.questTarget && (npc.questType === 'explore' || npc.questType === 'delivery' || npc.questType === 'escort') && npc.questTarget.x === questTarget.x && npc.questTarget.y === questTarget.y) { if (npc.questType === 'explore') labelText = "Keşif: " + labelText;else if (npc.questType === 'delivery') labelText = "Teslimat: " + labelText;else if (npc.questType === 'escort') labelText = "Eşlik: " + labelText; break; } if (npc.questActive && npc.questType === 'hunt' && npc.questTarget && npc.questTarget.x === questTarget.x && npc.questTarget.y === questTarget.y) { labelText = "Av: " + labelText; break; } } } } // Derelict ship distance if (!showDistanceLabel && window.derelictShip && window.derelictShip.x !== undefined && window.derelictShip.y !== undefined) { var d = window.derelictShip; var dist = Math.sqrt(Math.pow(d.x - ship.x, 2) + Math.pow(d.y - ship.y, 2)); if (dist > 120 && dist < 3000 && window.questArrow && window.questArrow.visible) { showDistanceLabel = true; var labelOffset = 70; labelX = window.questArrow.x + Math.cos(window.questArrow.rotation - Math.PI / 2) * labelOffset; labelY = window.questArrow.y + Math.sin(window.questArrow.rotation - Math.PI / 2) * labelOffset; labelText = "Gemi: " + (dist >= 1000 ? Math.round(dist / 10) / 100 + "k px" : Math.round(dist) + " px"); } } // Distress event distance if (!showDistanceLabel && window.distressEvent && window.distressEvent.x !== undefined && window.distressEvent.y !== undefined) { var e = window.distressEvent; var dist = Math.sqrt(Math.pow(e.x - ship.x, 2) + Math.pow(e.y - ship.y, 2)); if (dist > 120 && dist < 3000 && window.questArrow && window.questArrow.visible) { showDistanceLabel = true; var labelOffset = 70; labelX = window.questArrow.x + Math.cos(window.questArrow.rotation - Math.PI / 2) * labelOffset; labelY = window.questArrow.y + Math.sin(window.questArrow.rotation - Math.PI / 2) * labelOffset; labelText = "İmdat: " + (dist >= 1000 ? Math.round(dist / 10) / 100 + "k px" : Math.round(dist) + " px"); } } if (window.questDistanceLabel) { window.questDistanceLabel.visible = showDistanceLabel; if (showDistanceLabel) { window.questDistanceLabel.x = labelX; window.questDistanceLabel.y = labelY; window.questDistanceLabel.setText(labelText); window.questDistanceLabel.alpha = 0.9 + Math.sin(LK.ticks * 0.1) * 0.1; window.questDistanceLabel.size = Math.round(unifiedFontSize * 0.9); window.questDistanceLabel.zIndex = 9999; } } };
/****
* 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;
}
for (var i = 0; i < boostParticles.length; i++) {
boostParticles[i].x -= dx / dist * pull;
boostParticles[i].y -= dy / dist * pull;
}
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.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
};
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;
self.update = function () {
self.rotation += 0.05;
};
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.update = function () {
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
};
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;
});
var NPC = Container.expand(function () {
var self = Container.call(this);
var npcGraphics = self.attachAsset('npc', {
anchorX: 0.5,
anchorY: 0.5
});
var questTypes = ['trade', 'delivery', 'explore', 'escort', 'hunt', 'puzzle', 'defend'];
// 10% şansla yeni 'defend' questType ata, yoksa normal random
if (Math.random() < 0.10) {
self.questType = 'defend';
} else {
self.questType = questTypes[Math.floor(Math.random() * (questTypes.length - 1))];
}
self.questActive = true;
self.questProgress = 0;
self.questTarget = null;
self.reward = Math.floor(Math.random() * 80) + 30;
self.dialogueStep = 0;
self.hasTraded = false;
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);
self.rpgMood = Math.random() < 0.5 ? "friendly" : "neutral";
self.rpgInventory = {
metal: Math.floor(Math.random() * 10),
energy: Math.floor(Math.random() * 10),
coins: Math.floor(Math.random() * 30)
};
self.rpgTradeOffer = {
give: Math.random() < 0.5 ? "metal" : "energy",
giveAmount: Math.floor(Math.random() * 5) + 1,
want: Math.random() < 0.5 ? "energy" : "metal",
wantAmount: Math.floor(Math.random() * 5) + 1
};
// --- AI/Quest logic ---
self.update = function () {
// 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);
} else {
self.targetX = null;
self.targetY = null;
}
} else {
self.x += Math.cos(self.moveAngle) * self.moveSpeed;
self.y += Math.sin(self.moveAngle) * self.moveSpeed;
}
// Float animation on top of movement
self.y += Math.sin(LK.ticks * 0.05) * 0.5;
// --- Defend quest: spawn pirates, request help, handle accept/reject, rep and reward ---
if (self.questActive && self.questType === 'defend') {
// 1. Eğer henüz korsan saldırısı başlatılmadıysa, başlat
if (!self._defendStarted) {
// 3-5 korsan spawn et, NPC'yi hedeflesin
var squadCount = 3 + Math.floor(Math.random() * 3);
self._defendPirates = spawnPirateSquad(self.x, self.y, squadCount, self);
self._defendStarted = true;
self._defendHelpRequested = false;
self._defendHelpAccepted = undefined; // undefined: karar verilmedi, true: kabul, false: reddedildi
self._defendHelpPanelShown = false;
self._defendPiratesAlive = squadCount;
self._defendRewardGiven = false;
self._defendPirateDestroyed = 0;
self._defendPirateDestroyedLast = 0;
self._defendPirateDestroyedTicks = 0;
self._defendPirateDestroyedTotal = squadCount;
self._defendPirateDestroyedList = [];
self._defendPirateDestroyedRepGiven = false;
}
// 2. Eğer oyuncu yakınsa ve yardım isteği henüz gösterilmediyse, Accept/Reject panelini göster
var playerDist = Math.sqrt(Math.pow(ship.x - self.x, 2) + Math.pow(ship.y - self.y, 2));
if (!self._defendHelpPanelShown && playerDist < 180 && self._defendHelpAccepted === undefined) {
// Accept/Reject paneli göster
if (!self._questDecisionPanel) {
// Remove any previous quest panel
if (game._questDecisionPanel && game._questDecisionPanel.parent) {
game._questDecisionPanel.parent.removeChild(game._questDecisionPanel);
game._questDecisionPanel = null;
}
if (game._questDecisionAcceptBtn && game._questDecisionAcceptBtn.parent) {
game._questDecisionAcceptBtn.parent.removeChild(game._questDecisionAcceptBtn);
game._questDecisionAcceptBtn = null;
}
if (game._questDecisionRejectBtn && game._questDecisionRejectBtn.parent) {
game._questDecisionRejectBtn.parent.removeChild(game._questDecisionRejectBtn);
game._questDecisionRejectBtn = null;
}
// Panel background
var panel = LK.getAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
x: ship.x,
y: ship.y - 180,
scaleX: 7,
scaleY: 2,
tint: 0x222244
});
// Quest text
var questText = new Text2("Korsanlar saldırıyor! Yardım eder misin?", {
size: 38,
fill: 0xffffff,
align: "center"
});
questText.anchor.set(0.5, 0.5);
questText.x = ship.x;
questText.y = ship.y - 200;
// Accept button
var acceptBtn = new Text2("Yardım Et", {
size: 44,
fill: 0x00ff99,
align: "center"
});
acceptBtn.anchor.set(0.5, 0.5);
acceptBtn.x = ship.x - 100;
acceptBtn.y = ship.y - 120;
// Reject button
var rejectBtn = new Text2("Yardım Etme", {
size: 44,
fill: 0xff4444,
align: "center"
});
rejectBtn.anchor.set(0.5, 0.5);
rejectBtn.x = ship.x + 100;
rejectBtn.y = ship.y - 120;
// Add to game
game.addChild(panel);
game.addChild(questText);
game.addChild(acceptBtn);
game.addChild(rejectBtn);
// Store references for cleanup
self._questDecisionPanel = panel;
self._questDecisionText = questText;
self._questDecisionAcceptBtn = acceptBtn;
self._questDecisionRejectBtn = rejectBtn;
game._questDecisionPanel = panel;
game._questDecisionAcceptBtn = acceptBtn;
game._questDecisionRejectBtn = rejectBtn;
game._questDecisionText = questText;
game._questDecisionNPC = self;
acceptBtn._isQuestAcceptBtn = true;
rejectBtn._isQuestRejectBtn = true;
acceptBtn._questNPC = self;
rejectBtn._questNPC = self;
acceptBtn._questType = self.questType;
rejectBtn._questType = self.questType;
}
self._defendHelpPanelShown = true;
}
// 3. Oyuncu Accept/Reject panelinden karar verirse, _defendHelpAccepted güncellenir (game.down'da)
// 4. Eğer yardım kabul edildiyse, korsanlar oyuncuya ve NPC'ye saldırır, oyuncu korsanları öldürmeli
if (self._defendHelpAccepted === true) {
// Korsanlar hem NPC'ye hem oyuncuya saldırır, oyuncu korsanları öldürmeli
// Korsanlar update fonksiyonunda zaten NPC'yi hedefliyor
// Eğer tüm korsanlar öldüyse, ödül ver ve rep artır
var alive = 0;
for (var i = 0; i < self._defendPirates.length; i++) {
if (!self._defendPirates[i]._destroyed) alive++;
}
if (alive === 0 && !self._defendRewardGiven) {
// Görev tamamlandı, ödül ver
playerCoins += self.reward + 50;
playerGoodRep += 10;
self.questProgress = 1;
self._defendRewardGiven = true;
questsCompleted++;
// Görev tamamlandı mesajı göster
var completeLabel = new Text2("NPC kurtarıldı! Ödül: " + (self.reward + 50) + " coin\n+10 İyi Rep", {
size: 32,
fill: 0x00ff99,
align: "center"
});
completeLabel.anchor.set(0.5, 0.5);
completeLabel.x = self.x;
completeLabel.y = self.y + 110;
game.addChild(completeLabel);
self._npcInteractionLabel = completeLabel;
self._npcInteractionLabelExpire = LK.ticks + 150;
self.pauseUntil = LK.ticks + 120;
self.questActive = false;
self._questCompleteShown = true;
updateUI();
saveProgress();
}
}
// 5. Eğer yardım reddedildiyse, korsanlar sadece NPC'ye saldırır, NPC yok edilirse kötü rep ver
if (self._defendHelpAccepted === false) {
// Korsanlar sadece NPC'yi hedefler
var alive = 0;
for (var i = 0; i < self._defendPirates.length; i++) {
if (!self._defendPirates[i]._destroyed) alive++;
}
// NPC öldüyse ve rep verilmediyse
if (self.health !== undefined && self.health <= 0 && !self._defendPirateDestroyedRepGiven) {
playerBadRep += 10;
self._defendPirateDestroyedRepGiven = true;
// Kötü rep mesajı
var failLabel = new Text2("NPC yok edildi!\n+10 Kötü Rep", {
size: 32,
fill: 0xff4444,
align: "center"
});
failLabel.anchor.set(0.5, 0.5);
failLabel.x = self.x;
failLabel.y = self.y + 110;
game.addChild(failLabel);
self._npcInteractionLabel = failLabel;
self._npcInteractionLabelExpire = LK.ticks + 150;
self.pauseUntil = LK.ticks + 120;
self.questActive = false;
self._questCompleteShown = true;
updateUI();
saveProgress();
}
}
}
// --- RPG/Quest/Adventure logic: update quest progress if active ---
if (self.questActive && self.questType === 'explore' && self.questTarget) {
// If player is close to quest target, complete quest
var dx = ship.x - self.questTarget.x;
var dy = ship.y - self.questTarget.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 80) {
self.questProgress = 1;
}
}
if (self.questActive && self.questType === 'escort' && self.questTarget) {
// Oyuncu NPC'ye yakınsa, NPC hedefe doğru hareket etmeye başlasın, uzaksa beklesin
var playerDist = Math.sqrt(Math.pow(ship.x - self.x, 2) + Math.pow(ship.y - self.y, 2));
if (self.questProgress === 0) {
if (playerDist < 220) {
// Hedefe doğru hareket et
var tdx = self.questTarget.x - self.x;
var tdy = self.questTarget.y - self.y;
var tdist = Math.sqrt(tdx * tdx + tdy * tdy);
if (tdist > 10) {
self.x += tdx / tdist * self.moveSpeed;
self.y += tdy / tdist * self.moveSpeed;
self.moveAngle = Math.atan2(tdy, tdx);
}
self._escortWaiting = false;
} else {
// Oyuncu uzaksa bekle, "Bekliyorum..." mesajı göster
if (!self._escortWaiting) {
if (self._npcText && self._npcText.parent) {
self._npcText.parent.removeChild(self._npcText);
}
self._npcText = new Text2(self.rpgName + ": Seni bekliyorum, lütfen yakınımda kal!", {
size: 32,
fill: 0x00ffcc,
align: "center"
});
self._npcText.anchor.set(0.5, 1);
self._npcText.x = self.x;
self._npcText.y = self.y - 70;
if (game && game.addChild) game.addChild(self._npcText);
self._escortWaiting = true;
}
}
}
// Eğer NPC hedefe ulaştıysa görevi tamamla
var dx = self.x - self.questTarget.x;
var dy = self.y - self.questTarget.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 80) {
self.questProgress = 1;
// Hedefe ulaşınca bekleme mesajını kaldır
if (self._npcText && self._npcText.parent) {
self._npcText.parent.removeChild(self._npcText);
self._npcText = null;
}
self._escortWaiting = false;
}
}
if (self.questActive && self.questType === 'hunt') {
// Avcılık görevi başlatıldıysa, görev süresi ve hedef sayısı belirle
if (!self._huntStarted) {
// Görev kabul edildiğinde başlatılır
self._huntStarted = true;
// Görev süresi (ör: 30 saniye = 1800 tick)
self._huntTimeLimit = 1800 + Math.floor(Math.random() * 600); // 30-40 sn arası
self._huntTimeStart = LK.ticks;
// Kaç korsan öldürülmesi gerektiği
self._huntTargetCount = 2 + Math.floor(Math.random() * 3); // 2-4 arası
self._huntKilled = 0;
self._huntTargetList = [];
// Hedef korsanları seç
var availablePirates = [];
for (var i = 0; i < pirates.length; i++) {
if (!pirates[i]._destroyed) availablePirates.push(pirates[i]);
}
// Eğer yeterli korsan yoksa, yeni korsan spawn et
while (availablePirates.length < self._huntTargetCount) {
spawnPirate();
for (var i = 0; i < pirates.length; i++) {
if (!pirates[i]._destroyed && availablePirates.indexOf(pirates[i]) === -1) {
availablePirates.push(pirates[i]);
}
}
}
// Hedef korsanları seç
for (var i = 0; i < self._huntTargetCount; i++) {
var p = availablePirates[i % availablePirates.length];
// Limit pirate spawn distance to max 2 minutes of travel
var maxTravelTime = 120;
var maxDistance = (ship && ship.speed ? ship.speed : 5) * 60 * maxTravelTime;
var distToNPC = Math.sqrt(Math.pow(p.x - self.x, 2) + Math.pow(p.y - self.y, 2));
if (distToNPC > maxDistance) {
// Move pirate closer to NPC
var angle = Math.atan2(p.y - self.x, p.x - self.x);
p.x = self.x + Math.cos(angle) * (Math.random() * (maxDistance * 0.5) + 400);
p.y = self.y + Math.sin(angle) * (Math.random() * (maxDistance * 0.5) + 400);
}
self._huntTargetList.push(p);
}
self.questTarget = self._huntTargetList[0];
self._huntCurrentTargetIndex = 0;
self._huntComplete = false;
self._huntFailed = false;
}
// Görev süresi kontrolü
var elapsed = LK.ticks - self._huntTimeStart;
if (!self._huntComplete && !self._huntFailed && elapsed > self._huntTimeLimit) {
// Süre doldu, görev başarısız
self._huntFailed = true;
self.questActive = false;
self.questProgress = -1;
// Kısa süreli başarısızlık mesajı
var failLabel = new Text2("Av görevi başarısız! Süre doldu.", {
size: 32,
fill: 0xff4444,
align: "center"
});
failLabel.anchor.set(0.5, 0.5);
failLabel.x = self.x;
failLabel.y = self.y + 110;
game.addChild(failLabel);
self._npcInteractionLabel = failLabel;
self._npcInteractionLabelExpire = LK.ticks + 150;
self.pauseUntil = LK.ticks + 120;
}
// Görev başarısızsa, başka işlem yapma
if (self._huntFailed) return;
// Hedef korsan öldürüldüyse, bir sonrakine geç
if (self._huntTargetList && self._huntCurrentTargetIndex < self._huntTargetList.length) {
var currentTarget = self._huntTargetList[self._huntCurrentTargetIndex];
if (currentTarget && currentTarget._destroyed) {
self._huntKilled++;
self._huntCurrentTargetIndex++;
if (self._huntCurrentTargetIndex < self._huntTargetList.length) {
self.questTarget = self._huntTargetList[self._huntCurrentTargetIndex];
} else {
self.questTarget = null;
}
}
}
// Görev tamamlandı mı?
if (!self._huntComplete && self._huntKilled >= self._huntTargetCount) {
self._huntComplete = true;
self.questProgress = 1;
self.questActive = false;
playerCoins += self.reward + 30;
questsCompleted++;
var completeLabel = new Text2("Av görevi tamamlandı! Ödül: " + (self.reward + 30) + " coin", {
size: 32,
fill: 0x00ff99,
align: "center"
});
completeLabel.anchor.set(0.5, 0.5);
completeLabel.x = self.x;
completeLabel.y = self.y + 110;
game.addChild(completeLabel);
self._npcInteractionLabel = completeLabel;
self._npcInteractionLabelExpire = LK.ticks + 150;
self.pauseUntil = LK.ticks + 120;
updateUI();
saveProgress();
}
// Hedef korsan varsa, ona saldır
if (self.questTarget && !self.questTarget._destroyed) {
var dx = self.questTarget.x - self.x;
var dy = self.questTarget.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
// Saldırı mesafesi %10 arttırıldı (120 -> 132)
if (dist > 132) {
self.x += dx / dist * self.moveSpeed * 1.2;
self.y += dy / dist * self.moveSpeed * 1.2;
self.moveAngle = Math.atan2(dy, dx);
}
// Saldırı: NPC ateş etsin (her 40 tickte bir)
if (!self._lastAttack || LK.ticks - self._lastAttack > 40) {
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
var angle = Math.atan2(self.questTarget.y - self.y, self.questTarget.x - self.x);
bullet.directionX = Math.cos(angle);
bullet.directionY = Math.sin(angle);
bullet.rotation = angle + Math.PI / 2;
bullets.push(bullet);
if (game && game.addChild) game.addChild(bullet);
self._lastAttack = LK.ticks;
}
// Eğer oyuncu da yakınsa, birlikte saldırı efekti
// Saldırı mesafesi %10 arttırıldı (200 -> 220)
var playerDist = Math.sqrt(Math.pow(ship.x - self.questTarget.x, 2) + Math.pow(ship.y - self.questTarget.y, 2));
if (playerDist < 220 && dist < 220) {
self.questTarget.moveAngle = Math.atan2(ship.y - self.questTarget.y, ship.x - self.questTarget.x);
}
}
}
if (self.questActive && self.questType === 'puzzle') {
// Simulate puzzle solved after some time
if (LK.ticks % 600 === 0) {
self.questProgress = 1;
}
}
};
// --- RPG/Adventure/Trade: interaction method for player tap/close ---
self.interact = function () {
// Dialogue/quest/trade logic
if (!self.questActive) return "I'm just exploring the stars!";
// Helper for EXP gain and level up
function gainEXP(amount) {
playerEXP += amount;
var leveledUp = false;
while (playerEXP >= expToNext(playerLevel)) {
playerEXP -= expToNext(playerLevel);
playerLevel++;
leveledUp = true;
}
updateUI();
if (leveledUp) {
LK.effects.flashObject(ship, 0x00ffcc, 800);
return "Level Up! Now Level " + playerLevel + "!";
}
return null;
}
if (self.questType === 'trade') {
if (!self.hasTraded) {
// Offer trade
if (self.rpgTradeOffer.give === 'metal' && playerMetal >= self.rpgTradeOffer.wantAmount) {
playerMetal -= self.rpgTradeOffer.wantAmount;
playerEnergy += self.rpgTradeOffer.giveAmount;
self.hasTraded = true;
var expMsg = gainEXP(10 + Math.floor(Math.random() * 10));
return self.rpgName + ": Thanks for the trade! +" + self.rpgTradeOffer.giveAmount + " energy!" + (expMsg ? "\n" + expMsg : "");
} else if (self.rpgTradeOffer.give === 'energy' && playerEnergy >= self.rpgTradeOffer.wantAmount) {
playerEnergy -= self.rpgTradeOffer.wantAmount;
playerMetal += self.rpgTradeOffer.giveAmount;
self.hasTraded = true;
var expMsg = gainEXP(10 + Math.floor(Math.random() * 10));
return self.rpgName + ": Thanks for the trade! +" + self.rpgTradeOffer.giveAmount + " metal!" + (expMsg ? "\n" + expMsg : "");
} else {
return self.rpgName + ": I can trade " + self.rpgTradeOffer.giveAmount + " " + self.rpgTradeOffer.give + " for " + self.rpgTradeOffer.wantAmount + " " + self.rpgTradeOffer.want + ".";
}
} else {
return self.rpgName + ": Good luck out there!";
}
}
if (self.questType === 'delivery') {
if (self.questProgress === 0) {
// Assign delivery quest: go to a random point
if (!self.questTarget) {
// Limit quest target distance to max 2 minutes of travel (approx 60*ship.speed*2 px)
var maxTravelTime = 120; // seconds
var maxDistance = (ship && ship.speed ? ship.speed : 5) * 60 * maxTravelTime; // px
var angle = Math.random() * Math.PI * 2;
var dist = Math.random() * (maxDistance * 0.5) + 400; // 400px min, up to maxDistance/2
self.questTarget = {
x: self.x + Math.cos(angle) * dist,
y: self.y + Math.sin(angle) * dist
};
return self.rpgName + ": Can you deliver this package to (" + Math.floor(self.questTarget.x) + "," + Math.floor(self.questTarget.y) + ")?";
} else {
var dx = ship.x - self.questTarget.x;
var dy = ship.y - self.questTarget.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 80) {
self.questProgress = 1;
playerCoins += self.reward;
questsCompleted++;
var expMsg = gainEXP(40 + Math.floor(Math.random() * 20));
return self.rpgName + ": Delivery complete! Reward: " + self.reward + " coins!" + (expMsg ? "\n" + expMsg : "");
} else {
return self.rpgName + ": Please deliver to (" + Math.floor(self.questTarget.x) + "," + Math.floor(self.questTarget.y) + ")";
}
}
} else {
return self.rpgName + ": Thank you for your help!";
}
}
if (self.questType === 'explore') {
if (self.questProgress === 0) {
if (!self.questTarget) {
// Limit quest target distance to max 2 minutes of travel
var maxTravelTime = 120;
var maxDistance = (ship && ship.speed ? ship.speed : 5) * 60 * maxTravelTime;
var angle = Math.random() * Math.PI * 2;
var dist = Math.random() * (maxDistance * 0.5) + 400;
self.questTarget = {
x: self.x + Math.cos(angle) * dist,
y: self.y + Math.sin(angle) * dist
};
return self.rpgName + ": Can you explore the area at (" + Math.floor(self.questTarget.x) + "," + Math.floor(self.questTarget.y) + ")?";
} else {
var dx = ship.x - self.questTarget.x;
var dy = ship.y - self.questTarget.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 80) {
self.questProgress = 1;
playerCoins += self.reward;
questsCompleted++;
var expMsg = gainEXP(30 + Math.floor(Math.random() * 15));
return self.rpgName + ": Thanks for exploring! Reward: " + self.reward + " coins!" + (expMsg ? "\n" + expMsg : "");
} else {
return self.rpgName + ": Please explore (" + Math.floor(self.questTarget.x) + "," + Math.floor(self.questTarget.y) + ")";
}
}
} else {
return self.rpgName + ": You are a true explorer!";
}
}
if (self.questType === 'escort') {
if (self.questProgress === 0) {
if (!self.questTarget) {
// Limit quest target distance to max 2 minutes of travel
var maxTravelTime = 120;
var maxDistance = (ship && ship.speed ? ship.speed : 5) * 60 * maxTravelTime;
var angle = Math.random() * Math.PI * 2;
var dist = Math.random() * (maxDistance * 0.5) + 400;
self.questTarget = {
x: self.x + Math.cos(angle) * dist,
y: self.y + Math.sin(angle) * dist
};
return self.rpgName + ": Can you escort me to (" + Math.floor(self.questTarget.x) + "," + Math.floor(self.questTarget.y) + ")?";
} else {
var dx = self.x - self.questTarget.x;
var dy = self.y - self.questTarget.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < 80) {
self.questProgress = 1;
playerCoins += self.reward;
questsCompleted++;
var expMsg = gainEXP(35 + Math.floor(Math.random() * 15));
return self.rpgName + ": Safe arrival! Reward: " + self.reward + " coins!" + (expMsg ? "\n" + expMsg : "");
} else {
// Move toward target if player is close
var pdx = ship.x - self.x;
var pdy = ship.y - self.y;
var pdist = Math.sqrt(pdx * pdx + pdy * pdy);
if (pdist < 200) {
var tdx = self.questTarget.x - self.x;
var tdy = self.questTarget.y - self.y;
var tdist = Math.sqrt(tdx * tdx + tdy * tdy);
if (tdist > 10) {
self.x += tdx / tdist * self.moveSpeed;
self.y += tdy / tdist * self.moveSpeed;
}
}
return self.rpgName + ": Please escort me to (" + Math.floor(self.questTarget.x) + "," + Math.floor(self.questTarget.y) + ")";
}
}
} else {
return self.rpgName + ": Thank you for the escort!";
}
}
if (self.questType === 'hunt') {
if (self.questProgress === 0) {
if (!self.questTarget) {
// Assign a random pirate as target, and give pirates random names if not already set
if (pirates.length > 0) {
// Pirate name pool
var pirateNames = ["Kaptan Kara", "Uzay Korsanı Zed", "Astro Jack", "Korsan Nova", "Kaptan Yıldız", "Korsan Rex", "Korsan Luna", "Korsan Vex", "Korsan Orion", "Korsan Vega"];
// Assign names to pirates if not already named
for (var i = 0; i < pirates.length; i++) {
if (!pirates[i].rpgName || pirates[i].rpgName.indexOf("Korsan-") === 0 || pirates[i].rpgName.indexOf("BaseKorsan-") === 0) {
pirates[i].rpgName = pirateNames[Math.floor(Math.random() * pirateNames.length)];
}
}
self.questTarget = pirates[Math.floor(Math.random() * pirates.length)];
return self.rpgName + ": " + self.questTarget.rpgName + " adlı korsanı yenebilir misin?";
} else {
return self.rpgName + ": Yakında korsan yok, sonra tekrar gel!";
}
} else {
if (self.questTarget._destroyed) {
self.questProgress = 1;
playerCoins += self.reward;
questsCompleted++;
var expMsg = gainEXP(50 + Math.floor(Math.random() * 20));
return self.rpgName + ": Korsan yenildi! Ödül: " + self.reward + " coin!" + (expMsg ? "\n" + expMsg : "");
} else {
return self.rpgName + ": Lütfen " + (self.questTarget.rpgName || "bilinmeyen korsan") + " adlı korsanı yen.";
}
}
} else {
return self.rpgName + ": Gerçek bir kahramansın!";
}
}
if (self.questType === 'puzzle') {
if (self.questProgress === 0) {
return self.rpgName + ": Solve my puzzle! (Wait a bit...)";
} else {
playerCoins += self.reward;
questsCompleted++;
self.questProgress = 2;
var expMsg = gainEXP(25 + Math.floor(Math.random() * 10));
return self.rpgName + ": Puzzle solved! Reward: " + self.reward + " coins!" + (expMsg ? "\n" + expMsg : "");
}
}
return self.rpgName + ": Safe travels!";
};
return self;
});
var Pirate = Container.expand(function () {
var self = Container.call(this);
var pirateGraphics = self.attachAsset('pirate', {
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
// --- 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);
// Level text only, defense text removed
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xffffff, 100);
};
self.update = function () {
// 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);
}
// 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);
}
// 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;
}
} else {
self.x += Math.cos(self.moveAngle) * self.speed * 0.5;
self.y += Math.sin(self.moveAngle) * self.speed * 0.5;
}
};
return self;
});
// Pirate Base Class
var PirateBase = Container.expand(function () {
var self = Container.call(this);
var baseGraphics = self.attachAsset('pirate', {
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;
// 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("Korsan Üssü Yok Edildi!", {
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
});
self.type = Math.random() < 0.5 ? 'metal' : 'energy';
self.amount = Math.floor(Math.random() * 5) + 1;
self.update = function () {
self.rotation += 0.03;
};
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.boostSpeed = 0;
self.boostCooldown = 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) {
self.health = 0;
LK.showGameOver();
}
};
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);
}
// Boost cooldown
if (self.boostCooldown > 0) {
self.boostCooldown--;
}
if (self.boostSpeed > 0) {
self.boostSpeed -= 0.2;
}
};
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);
var tradeGraphics = self.attachAsset('npc', {
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;
});
var UpgradeStation = Container.expand(function () {
var self = Container.call(this);
var stationGraphics = self.attachAsset('pirate', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00ffff,
scaleX: 1.5,
scaleY: 1.5
});
self.type = 'upgrade';
self.upgrades = [{
name: 'health',
cost: 100,
metalCost: 10
}, {
name: 'shield',
cost: 150,
metalCost: 15
}, {
name: 'damage',
cost: 200,
energyCost: 20
}, {
name: 'speed',
cost: 120,
energyCost: 10
}];
self.currentUpgrade = 0;
self.rotationSpeed = 0.01;
self.update = function () {
self.rotation += self.rotationSpeed;
// Pulse effect
var scale = 1.5 + Math.sin(LK.ticks * 0.05) * 0.1;
stationGraphics.scaleX = scale;
stationGraphics.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;
}
for (var i = 0; i < boostParticles.length; i++) {
boostParticles[i].x -= dx2;
boostParticles[i].y -= dy2;
}
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
****/
// Game variables
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 boostParticles = [];
var explosions = [];
var blackHoles = [];
var wormholes = [];
var tradeStations = [];
var pirateBases = []; // NEW: array for pirate bases
var tradeText = new Text2('', {
size: 40,
fill: 0x00ff99
});
// 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.anchor.set(0.5, 0);
tradeText.y = 180;
LK.gui.top.addChild(tradeText);
var camera = {
x: 0,
y: 0
};
// --- Reputation system ---
var playerGoodRep = storage.playerGoodRep || 0;
var playerBadRep = storage.playerBadRep || 0;
// --- Helper for multi-pirate attack quest ---
function spawnPirateSquad(centerX, centerY, count, targetNPC) {
var squad = [];
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;
var playerMetal = storage.metal || 0;
var playerEnergy = storage.energy || 0;
var questsCompleted = storage.questsCompleted || 0;
// 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);
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);
var resourceText = new Text2('Metal: ' + playerMetal + ' Energy: ' + playerEnergy, {
size: unifiedFontSize,
fill: 0xFFFFFF
});
resourceText.anchor.set(0, 0);
resourceText.x = 120;
resourceText.y = 80;
LK.gui.topLeft.addChild(resourceText);
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);
var boostText = new Text2('BOOST READY', {
size: unifiedFontSize,
fill: 0x00FFFF
});
boostText.anchor.set(0.5, 0);
boostText.y = 110;
LK.gui.top.addChild(boostText);
var waveText = new Text2('Wave: 1', {
size: unifiedFontSize,
fill: 0xFFFFFF
});
waveText.anchor.set(1, 0);
waveText.x = -20;
waveText.y = 20;
LK.gui.topRight.addChild(waveText);
// --- RESET BUTTON (bottom right) ---
var resetBtn = new Text2('Reset', {
size: 44,
fill: 0xff4444,
font: "'Heath', Impact, 'Arial Black', Tahoma",
// Heath-like font
align: "center"
});
resetBtn.anchor.set(1, 1); // bottom right
resetBtn.x = -40;
resetBtn.y = -40;
LK.gui.bottomRight.addChild(resetBtn);
// Touch/click handler for reset button
resetBtn._isResetBtn = true;
game._resetBtn = resetBtn;
// Level/EXP system
var playerLevel = storage.playerLevel || 1;
var playerEXP = storage.playerEXP || 0;
var expToNext = function expToNext(lv) {
return 100 + (lv - 1) * 50;
};
var expText = new Text2('Level: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel), {
size: unifiedFontSize,
fill: 0x00ffcc
});
expText.anchor.set(1, 0);
expText.x = -20;
expText.y = 80;
LK.gui.topRight.addChild(expText);
// Move coordinateText to the left of waveText, decrease its size by 30%, and update its position and size in real time
var coordinateText = new Text2('X: 0 Y: 0', {
size: unifiedFontSize,
// 30% smaller than Level text
fill: 0xcccccc
});
coordinateText.anchor.set(1, 0.5); // right center of the text
// Position coordinateText to the left of waveText
coordinateText.x = waveText.x - waveText.width - 30;
coordinateText.y = waveText.y + waveText.height / 2;
LK.gui.topRight.addChild(coordinateText);
// Create joystick
joystickBase = game.addChild(LK.getAsset('joystickBase', {
anchorX: 0.5,
anchorY: 0.5,
x: 300,
y: 2432
}));
joystickBase.alpha = 0.5;
joystickHandle = game.addChild(LK.getAsset('joystickHandle', {
anchorX: 0.5,
anchorY: 0.5,
x: 300,
y: 2432
}));
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
for (var i = 0; i < 50; i++) {
spawnStar();
}
// Helper functions
function spawnStar() {
var star = new Star();
// Spawn from edges of visible area
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
// Top
star.x = ship.x + (Math.random() - 0.5) * 2500;
star.y = ship.y - 1500;
break;
case 1:
// Right
star.x = ship.x + 1500;
star.y = ship.y + (Math.random() - 0.5) * 2500;
break;
case 2:
// Bottom
star.x = ship.x + (Math.random() - 0.5) * 2500;
star.y = ship.y + 1500;
break;
case 3:
// Left
star.x = ship.x - 1500;
star.y = ship.y + (Math.random() - 0.5) * 2500;
break;
}
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;
// 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;
},
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;
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);
}
function spawnUpgradeStation() {
var station = new UpgradeStation();
station.x = ship.x + (Math.random() - 0.5) * 3000;
station.y = ship.y + (Math.random() - 0.5) * 3000;
upgradeStations.push(station);
game.addChild(station);
}
function spawnTradeStation() {
var ts = new TradeStation();
// Spawn from edges of visible area
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
ts.x = ship.x + (Math.random() - 0.5) * 2000;
ts.y = ship.y - 1500;
break;
case 1:
ts.x = ship.x + 1500;
ts.y = ship.y + (Math.random() - 0.5) * 2000;
break;
case 2:
ts.x = ship.x + (Math.random() - 0.5) * 2000;
ts.y = ship.y + 1500;
break;
case 3:
ts.x = ship.x - 1500;
ts.y = ship.y + (Math.random() - 0.5) * 2000;
break;
}
tradeStations.push(ts);
game.addChild(ts);
}
function createExplosion(x, y) {
for (var i = 0; i < 8; i++) {
var particle = LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: y
});
particle.vx = (Math.random() - 0.5) * 10;
particle.vy = (Math.random() - 0.5) * 10;
particle.life = 30;
explosions.push(particle);
game.addChild(particle);
}
}
function spawnBlackHole() {
var bh = new BlackHole();
// Spawn far from ship, random edge
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
bh.x = ship.x + (Math.random() - 0.5) * 2000;
bh.y = ship.y - 1700;
break;
case 1:
bh.x = ship.x + 1700;
bh.y = ship.y + (Math.random() - 0.5) * 2000;
break;
case 2:
bh.x = ship.x + (Math.random() - 0.5) * 2000;
bh.y = ship.y + 1700;
break;
case 3:
bh.x = ship.x - 1700;
bh.y = ship.y + (Math.random() - 0.5) * 2000;
break;
}
blackHoles.push(bh);
game.addChild(bh);
}
function spawnWormhole() {
var wh = new Wormhole();
// Spawn far from ship, random edge
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
wh.x = ship.x + (Math.random() - 0.5) * 2000;
wh.y = ship.y - 1700;
break;
case 1:
wh.x = ship.x + 1700;
wh.y = ship.y + (Math.random() - 0.5) * 2000;
break;
case 2:
wh.x = ship.x + (Math.random() - 0.5) * 2000;
wh.y = ship.y + 1700;
break;
case 3:
wh.x = ship.x - 1700;
wh.y = ship.y + (Math.random() - 0.5) * 2000;
break;
}
wormholes.push(wh);
game.addChild(wh);
}
function createBoostParticle() {
var particle = LK.getAsset('boostParticle', {
anchorX: 0.5,
anchorY: 0.5,
x: ship.x - Math.cos(ship.rotation - Math.PI / 2) * 50,
y: ship.y - Math.sin(ship.rotation - Math.PI / 2) * 50
});
particle.vx = -Math.cos(ship.rotation - Math.PI / 2) * 5 + (Math.random() - 0.5) * 2;
particle.vy = -Math.sin(ship.rotation - Math.PI / 2) * 5 + (Math.random() - 0.5) * 2;
particle.life = 20;
boostParticles.push(particle);
game.addChild(particle);
}
function updateUI() {
coinText.setText('Coins: ' + playerCoins);
resourceText.setText('Metal: ' + playerMetal + ' Energy: ' + playerEnergy);
healthText.setText('Health: ' + ship.health + '/' + ship.maxHealth);
shieldText.setText('Shield: ' + Math.floor(ship.shield) + '/' + ship.maxShield);
waveText.setText('Wave: ' + (waveNumber + 1));
expText.setText('Level: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel));
// Show rep in GUI
if (!window.repText) {
window.repText = new Text2('Rep: +' + playerGoodRep + ' / -' + playerBadRep, {
size: unifiedFontSize,
fill: 0xffffff
});
window.repText.anchor.set(1, 0);
window.repText.x = -20;
window.repText.y = 140;
LK.gui.topRight.addChild(window.repText);
}
window.repText.setText('Rep: +' + playerGoodRep + ' / -' + playerBadRep);
// 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;
}
// Update boost text
if (ship.boostCooldown <= 0) {
boostText.setText('BOOST READY');
boostText.fill = 0x00ffff;
} else {
boostText.setText('BOOST: ' + Math.ceil(ship.boostCooldown / 60) + 's');
boostText.fill = 0x888888;
}
storage.playerLevel = playerLevel;
storage.playerEXP = playerEXP;
}
function saveProgress() {
storage.coins = playerCoins;
storage.metal = playerMetal;
storage.energy = playerEnergy;
storage.questsCompleted = questsCompleted;
storage.playerGoodRep = playerGoodRep;
storage.playerBadRep = playerBadRep;
}
// Event handlers
game.down = function (x, y, obj) {
// --- 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
storage.coins = 0;
storage.metal = 0;
storage.energy = 0;
storage.questsCompleted = 0;
storage.playerLevel = 1;
storage.playerEXP = 0;
storage.playerGoodRep = 0;
storage.playerBadRep = 0;
// Optionally clear any other custom progress keys
// Show a quick feedback
btn.setText("Reset!");
btn.fill = 0x00ff99;
// Reload game (reset all progress)
LK.showGameOver();
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;
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;
var cbx = cbtn.x,
cby = cbtn.y;
if (x > cbx - 90 && x < cbx + 90 && y > cby - 40 && y < cby + 40) {
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;
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 ---
if (ship && ship.parent) {
// Ship is at (ship.x, ship.y), size is 80x100 (see asset)
var sx = ship.x,
sy = ship.y;
var sw = 80,
sh = 100;
// Accept generous tap area
if (x > sx - sw / 2 && x < sx + sw / 2 && y > sy - sh / 2 && y < sy + sh / 2) {
// Only show if not already shown
if (!window.shipUpgradePanel) {
// Panel background
var panelWidth = 700;
var panelHeight = 340;
panelWidth = Math.round(panelWidth * 1.25); // %25 yana büyüt
panelHeight = Math.round(panelHeight * 1.5); // %50 yukarı ve aşağı büyüt
panelHeight = Math.round(panelHeight * 1.5); // %50 tekrar yukarı ve aşağı büyüt (toplamda %50+%50=2x)
var panel = LK.getAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
x: sx,
y: sy - 220,
scaleX: panelWidth / 40,
// asset is 40px wide
scaleY: panelHeight / 40,
// asset is 40px tall
tint: 0x224488
});
// Title
var titleFont = Math.round(62 * 1.1);
var title = new Text2("Yükseltmeler & Can Satın Al", {
size: titleFont,
fill: 0xffffff,
align: "center"
});
title.anchor.set(0.5, 0.5);
title.x = sx;
title.y = sy - 220 - panelHeight / 2 + 100;
// Upgrade buttons, dikeyde aralıklı ve panelin içine sığacak şekilde
var upgBtnY = title.y + 110;
var upgBtnSpacingY = 140;
// Paneldeki yazı ve butonların font boyutunu, Wave font boyutu ile aynı yap
var upgBtnFont = unifiedFontSize;
var upgBtnWidth = 340;
var upgBtnHeight = 90;
var colSpacing = 80;
var colXOffset = upgBtnWidth / 2 + colSpacing / 2;
// Normal upgrades
var upg1 = new Text2("Max Can +20\n(100 coin, 10 metal)", {
size: upgBtnFont,
fill: 0x00ff99,
align: "center"
});
upg1.anchor.set(0.5, 0.5);
upg1.x = sx - colXOffset;
upg1.y = upgBtnY;
upg1._isShipUpgradeBtn = true;
upg1._upgradeType = "maxHealth";
upg1._upgradeCost = {
coins: 100,
metal: 10,
energy: 0
};
var upg1x2 = new Text2("Max Can +20\n(200 coin, 20 metal)", {
size: upgBtnFont,
fill: 0x00cc99,
align: "center"
});
upg1x2.anchor.set(0.5, 0.5);
upg1x2.x = sx + colXOffset;
upg1x2.y = upgBtnY;
upg1x2._isShipUpgradeBtn = true;
upg1x2._upgradeType = "maxHealth";
upg1x2._upgradeCost = {
coins: 200,
metal: 20,
energy: 0
};
upg1x2._isDoubleCost = true;
var upg2 = new Text2("Max Kalkan +10\n(120 coin, 12 metal)", {
size: upgBtnFont,
fill: 0x00ccff,
align: "center"
});
upg2.anchor.set(0.5, 0.5);
upg2.x = sx - colXOffset;
upg2.y = upgBtnY + upgBtnSpacingY;
upg2._isShipUpgradeBtn = true;
upg2._upgradeType = "maxShield";
upg2._upgradeCost = {
coins: 120,
metal: 12,
energy: 0
};
var upg2x2 = new Text2("Max Kalkan +10\n(240 coin, 24 metal)", {
size: upgBtnFont,
fill: 0x00aaff,
align: "center"
});
upg2x2.anchor.set(0.5, 0.5);
upg2x2.x = sx + colXOffset;
upg2x2.y = upgBtnY + upgBtnSpacingY;
upg2x2._isShipUpgradeBtn = true;
upg2x2._upgradeType = "maxShield";
upg2x2._upgradeCost = {
coins: 240,
metal: 24,
energy: 0
};
upg2x2._isDoubleCost = true;
var upg3 = new Text2("Hasar +5\n(150 coin, 15 enerji)", {
size: upgBtnFont,
fill: 0xffcc00,
align: "center"
});
upg3.anchor.set(0.5, 0.5);
upg3.x = sx - colXOffset;
upg3.y = upgBtnY + upgBtnSpacingY * 2;
upg3._isShipUpgradeBtn = true;
upg3._upgradeType = "damage";
upg3._upgradeCost = {
coins: 150,
metal: 0,
energy: 15
};
var upg3x2 = new Text2("Hasar +5\n(300 coin, 30 enerji)", {
size: upgBtnFont,
fill: 0xff9900,
align: "center"
});
upg3x2.anchor.set(0.5, 0.5);
upg3x2.x = sx + colXOffset;
upg3x2.y = upgBtnY + upgBtnSpacingY * 2;
upg3x2._isShipUpgradeBtn = true;
upg3x2._upgradeType = "damage";
upg3x2._upgradeCost = {
coins: 300,
metal: 0,
energy: 30
};
upg3x2._isDoubleCost = true;
// Buy health button (if not full)
var canBuyHealth = ship.health < ship.maxHealth;
var buyHealthBtnFont = unifiedFontSize;
var buyHealthBtn = new Text2("Canı Doldur (" + Math.max(30, Math.round((ship.maxHealth - ship.health) * 1.5)) + " coin, " + Math.max(2, Math.round((ship.maxHealth - ship.health) / 20)) + " metal, " + Math.max(2, Math.round((ship.maxHealth - ship.health) / 25)) + " enerji)", {
size: buyHealthBtnFont,
fill: canBuyHealth ? 0x00ffcc : 0x888888,
align: "center"
});
buyHealthBtn.anchor.set(0.5, 0.5);
buyHealthBtn.x = sx;
buyHealthBtn.y = upgBtnY + upgBtnSpacingY * 3 + 30;
buyHealthBtn._isShipBuyHealthBtn = true;
buyHealthBtn._repairCostCoins = Math.max(30, Math.round((ship.maxHealth - ship.health) * 1.5));
buyHealthBtn._repairCostMetal = Math.max(2, Math.round((ship.maxHealth - ship.health) / 20));
buyHealthBtn._repairCostEnergy = Math.max(2, Math.round((ship.maxHealth - ship.health) / 25));
// Close button
var closeBtnFont = unifiedFontSize;
var closeBtn = new Text2("Kapat", {
size: closeBtnFont,
fill: 0xff4444,
align: "center"
});
closeBtn.anchor.set(0.5, 0.5);
// Move close button above the panel title, with extra margin
closeBtn.x = sx;
closeBtn.y = title.y - 90;
closeBtn._isShipUpgradeCloseBtn = true;
// Add to game
game.addChild(panel);
game.addChild(title);
game.addChild(upg1);
game.addChild(upg1x2);
game.addChild(upg2);
game.addChild(upg2x2);
game.addChild(upg3);
game.addChild(upg3x2);
game.addChild(buyHealthBtn);
game.addChild(closeBtn);
// Store refs for tap logic/cleanup
window.shipUpgradePanel = panel;
window.shipUpgradeTitle = title;
window.shipUpgradeBtn1 = upg1;
window.shipUpgradeBtn1x2 = upg1x2;
window.shipUpgradeBtn2 = upg2;
window.shipUpgradeBtn2x2 = upg2x2;
window.shipUpgradeBtn3 = upg3;
window.shipUpgradeBtn3x2 = upg3x2;
window.shipBuyHealthBtn = buyHealthBtn;
window.shipUpgradeCloseBtn = closeBtn;
}
return;
}
}
if (Math.sqrt(Math.pow(x - joystickBase.x, 2) + Math.pow(y - joystickBase.y, 2)) < 100) {
isDragging = true;
joystickHandle.x = x;
joystickHandle.y = y;
} else if (y < 300 && ship.boostCooldown <= 0) {
ship.boostSpeed = 10;
ship.boostCooldown = 300; // 5 seconds cooldown
if (LK.getSound('boost')) {
LK.getSound('boost').play();
}
} else {
// --- SHIP UPGRADE PANEL BUTTONS ---
if (window.shipUpgradePanel) {
// Upgrade buttons
var btns = [window.shipUpgradeBtn1, window.shipUpgradeBtn1x2, window.shipUpgradeBtn2, window.shipUpgradeBtn2x2, window.shipUpgradeBtn3, window.shipUpgradeBtn3x2];
for (var i = 0; i < btns.length; i++) {
var btn = btns[i];
if (!btn) continue;
var bx = btn.x,
by = btn.y;
if (x > bx - 120 && x < bx + 120 && y > by - 50 && y < by + 50) {
// Try to buy upgrade
var upg = btn._upgradeType;
var cost = btn._upgradeCost;
var isDouble = !!btn._isDoubleCost;
if (upg === "maxHealth" && playerCoins >= cost.coins && playerMetal >= cost.metal) {
ship.maxHealth += 20;
ship.health = ship.maxHealth;
playerCoins -= cost.coins;
playerMetal -= cost.metal;
LK.effects.flashObject(ship, isDouble ? 0x00cc99 : 0x00ff99, 400);
btn.setText("Alındı!");
btn.fill = 0x00ffcc;
updateUI();
saveProgress();
} else if (upg === "maxShield" && playerCoins >= cost.coins && playerMetal >= cost.metal) {
ship.maxShield += 10;
ship.shield = ship.maxShield;
playerCoins -= cost.coins;
playerMetal -= cost.metal;
LK.effects.flashObject(ship, isDouble ? 0x00aaff : 0x00ccff, 400);
btn.setText("Alındı!");
btn.fill = 0x00ffcc;
updateUI();
saveProgress();
} else if (upg === "damage" && playerCoins >= cost.coins && playerEnergy >= cost.energy) {
ship.damage += 5;
playerCoins -= cost.coins;
playerEnergy -= cost.energy;
LK.effects.flashObject(ship, isDouble ? 0xff9900 : 0xffcc00, 400);
btn.setText("Alındı!");
btn.fill = 0x00ffcc;
updateUI();
saveProgress();
} else {
btn.setText("Yetersiz kaynak!");
btn.fill = 0xff4444;
}
return;
}
}
// Buy health button
var bhb = window.shipBuyHealthBtn;
if (bhb) {
var bx = bhb.x,
by = bhb.y;
if (x > bx - 180 && x < bx + 180 && y > by - 50 && y < by + 50) {
if (ship.health < ship.maxHealth) {
var costCoins = bhb._repairCostCoins,
costMetal = bhb._repairCostMetal,
costEnergy = bhb._repairCostEnergy;
if (playerCoins >= costCoins && playerMetal >= costMetal && playerEnergy >= costEnergy) {
playerCoins -= costCoins;
playerMetal -= costMetal;
playerEnergy -= costEnergy;
ship.health = ship.maxHealth;
updateUI();
saveProgress();
bhb.setText("Can Dolduruldu!");
bhb.fill = 0x00ffcc;
} else {
bhb.setText("Yetersiz kaynak!");
bhb.fill = 0xff4444;
}
} else {
bhb.setText("Can zaten tam!");
bhb.fill = 0x888888;
}
return;
}
}
// Close button
var cb = window.shipUpgradeCloseBtn;
if (cb) {
var bx = cb.x,
by = cb.y;
if (x > bx - 120 && x < bx + 120 && y > by - 50 && y < by + 50) {
// Remove all panel elements
if (window.shipUpgradePanel && window.shipUpgradePanel.parent) window.shipUpgradePanel.parent.removeChild(window.shipUpgradePanel);
if (window.shipUpgradeTitle && window.shipUpgradeTitle.parent) window.shipUpgradeTitle.parent.removeChild(window.shipUpgradeTitle);
if (window.shipUpgradeBtn1 && window.shipUpgradeBtn1.parent) window.shipUpgradeBtn1.parent.removeChild(window.shipUpgradeBtn1);
if (window.shipUpgradeBtn1x2 && window.shipUpgradeBtn1x2.parent) window.shipUpgradeBtn1x2.parent.removeChild(window.shipUpgradeBtn1x2);
if (window.shipUpgradeBtn2 && window.shipUpgradeBtn2.parent) window.shipUpgradeBtn2.parent.removeChild(window.shipUpgradeBtn2);
if (window.shipUpgradeBtn2x2 && window.shipUpgradeBtn2x2.parent) window.shipUpgradeBtn2x2.parent.removeChild(window.shipUpgradeBtn2x2);
if (window.shipUpgradeBtn3 && window.shipUpgradeBtn3.parent) window.shipUpgradeBtn3.parent.removeChild(window.shipUpgradeBtn3);
if (window.shipUpgradeBtn3x2 && window.shipUpgradeBtn3x2.parent) window.shipUpgradeBtn3x2.parent.removeChild(window.shipUpgradeBtn3x2);
if (window.shipBuyHealthBtn && window.shipBuyHealthBtn.parent) window.shipBuyHealthBtn.parent.removeChild(window.shipBuyHealthBtn);
if (window.shipUpgradeCloseBtn && window.shipUpgradeCloseBtn.parent) window.shipUpgradeCloseBtn.parent.removeChild(window.shipUpgradeCloseBtn);
window.shipUpgradePanel = null;
window.shipUpgradeTitle = null;
window.shipUpgradeBtn1 = null;
window.shipUpgradeBtn1x2 = null;
window.shipUpgradeBtn2 = null;
window.shipUpgradeBtn2x2 = null;
window.shipUpgradeBtn3 = null;
window.shipUpgradeBtn3x2 = null;
window.shipBuyHealthBtn = null;
window.shipUpgradeCloseBtn = null;
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) {
if (isDragging) {
var dx = x - joystickBase.x;
var dy = y - joystickBase.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 100) {
dx = dx / distance * 100;
dy = dy / distance * 100;
}
joystickHandle.x = joystickBase.x + dx;
joystickHandle.y = joystickBase.y + dy;
}
};
game.up = function (x, y, obj) {
isDragging = false;
joystickHandle.x = joystickBase.x;
joystickHandle.y = joystickBase.y;
};
// Main game loop
game.update = function () {
// Ship movement
var shipDX = 0;
var shipDY = 0;
if (isDragging) {
var dx = joystickHandle.x - joystickBase.x;
var dy = joystickHandle.y - joystickBase.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 10) {
var effectiveSpeed = ship.speed + ship.boostSpeed;
shipDX = dx / 100 * effectiveSpeed;
shipDY = dy / 100 * effectiveSpeed;
ship.rotation = Math.atan2(dy, dx) + Math.PI / 2;
// Create boost particles when boosting
if (ship.boostSpeed > 0 && LK.ticks % 2 === 0) {
createBoostParticle();
}
}
}
// Track total distance traveled
totalDistance += Math.sqrt(shipDX * shipDX + shipDY * shipDY);
// Update camera to follow ship movement
camera.x += shipDX;
camera.y += shipDY;
// Move all world objects opposite to ship movement to create camera effect
for (var i = 0; i < stars.length; i++) {
stars[i].x -= shipDX;
stars[i].y -= shipDY;
}
for (var i = 0; i < npcs.length; i++) {
npcs[i].x -= shipDX;
npcs[i].y -= shipDY;
}
for (var i = 0; i < pirates.length; i++) {
pirates[i].x -= shipDX;
pirates[i].y -= shipDY;
}
for (var i = 0; i < coins.length; i++) {
coins[i].x -= shipDX;
coins[i].y -= shipDY;
}
for (var i = 0; i < resources.length; i++) {
resources[i].x -= shipDX;
resources[i].y -= shipDY;
}
for (var i = 0; i < bullets.length; i++) {
bullets[i].x -= shipDX;
bullets[i].y -= shipDY;
}
for (var i = 0; i < enemyBullets.length; i++) {
enemyBullets[i].x -= shipDX;
enemyBullets[i].y -= shipDY;
}
for (var i = 0; i < asteroids.length; i++) {
asteroids[i].x -= shipDX;
asteroids[i].y -= shipDY;
}
for (var i = 0; i < upgradeStations.length; i++) {
upgradeStations[i].x -= shipDX;
upgradeStations[i].y -= shipDY;
}
for (var i = 0; i < boostParticles.length; i++) {
boostParticles[i].x -= shipDX;
boostParticles[i].y -= shipDY;
}
for (var i = 0; i < explosions.length; i++) {
explosions[i].x -= shipDX;
explosions[i].y -= shipDY;
}
// Auto fire at very close pirates or asteroids
// Saldırı mesafesi %10 arttırıldı (200 -> 220)
var ATTACK_RANGE = 220;
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 dist = Math.sqrt(Math.pow(pirates[i].x - ship.x, 2) + Math.pow(pirates[i].y - ship.y, 2));
if (dist < ATTACK_RANGE && dist < nearestDistance) {
nearestDistance = dist;
nearestTarget = pirates[i];
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;
bullets.push(bullet);
game.addChild(bullet);
ship.lastFire = LK.ticks;
LK.getSound('shoot').play();
}
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
if (bullet.x < -50 || bullet.x > 2098 || bullet.y < -50 || bullet.y > 2782) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// Check collision with pirates
for (var j = pirates.length - 1; j >= 0; j--) {
if (bullet.intersects(pirates[j])) {
pirates[j].takeDamage(bullet.damage);
if (pirates[j].health <= 0) {
enemiesKilled++;
createExplosion(pirates[j].x, pirates[j].y);
// Drop loot
var coin = new Coin();
coin.x = pirates[j].x;
coin.y = pirates[j].y;
coin.value = coin.value * (1 + Math.floor(waveNumber / 3));
coins.push(coin);
game.addChild(coin);
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);
}
// Give 1-3 EXP for each pirate killed
var pirateExp = 1 + 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);
}
// 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;
// Spawn upgrade station every 3 waves
if (waveNumber % 3 === 0) {
spawnUpgradeStation();
}
}
}
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])) {
asteroids[j].takeDamage(bullet.damage);
if (asteroids[j].health <= 0) {
// Drop multiple resources
for (var k = 0; k < asteroids[j].resources; 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.x < -50 || bullet.x > 2098 || bullet.y < -50 || bullet.y > 2782) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
if (bullet.intersects(ship)) {
ship.takeDamage(bullet.damage);
updateUI();
bullet.destroy();
enemyBullets.splice(i, 1);
LK.getSound('hit').play();
}
}
// Update pirates
for (var i = 0; i < pirates.length; i++) {
var pirate = pirates[i];
if (pirate._healthBar && pirate._healthBar.update) pirate._healthBar.update();
// --- Show "KILL" label above quest target pirate for hunt quests ---
var isHuntTarget = false;
for (var n = 0; n < npcs.length; n++) {
var npc = npcs[n];
if (npc.questType === 'hunt' && npc.questActive && npc.questTarget === pirate && !pirate._destroyed) {
isHuntTarget = true;
break;
}
}
if (isHuntTarget) {
if (!pirate._killLabel) {
var killLabel = new Text2("KILL", {
size: 38,
fill: 0xff4444,
align: "center"
});
killLabel.anchor.set(0.5, 0);
killLabel.x = pirate.x;
killLabel.y = pirate.y - (pirate.children[0] ? pirate.children[0].height / 2 + 38 : 90);
pirate._killLabel = killLabel;
game.addChild(killLabel);
} else {
pirate._killLabel.x = pirate.x;
pirate._killLabel.y = pirate.y - (pirate.children[0] ? pirate.children[0].height / 2 + 38 : 90);
pirate._killLabel.visible = true;
pirate._killLabel.alpha = 0.95 + Math.sin(LK.ticks * 0.12) * 0.05;
pirate._killLabel.zIndex = 9999;
}
} else if (pirate._killLabel) {
if (pirate._killLabel.parent) pirate._killLabel.parent.removeChild(pirate._killLabel);
pirate._killLabel = null;
}
// Update pirate AI
var dist = Math.sqrt(Math.pow(ship.x - pirate.x, 2) + Math.pow(ship.y - pirate.y, 2));
// Eğer bir NPC'nin hunt görevi varsa ve bu korsan hedefse, birlikte saldırı efekti
var huntedByNPC = false;
for (var n = 0; n < npcs.length; n++) {
var npc = npcs[n];
if (npc.questType === 'hunt' && npc.questActive && npc.questTarget === pirate && !pirate._destroyed) {
huntedByNPC = true;
// NPC'nin saldırı animasyonu için efekt
if (!pirate._lastHuntFlash || LK.ticks - pirate._lastHuntFlash > 30) {
LK.effects.flashObject(pirate, 0xffcccc, 100);
pirate._lastHuntFlash = LK.ticks;
}
// Eğer oyuncu da yakınsa, korsan hem NPC'ye hem oyuncuya saldırabilir
var distToNPC = Math.sqrt(Math.pow(npc.x - pirate.x, 2) + Math.pow(npc.y - pirate.y, 2));
if (distToNPC < 200 && dist < 200) {
// Rastgele oyuncuya veya NPC'ye saldır
var target = Math.random() < 0.5 ? ship : npc;
var angle = Math.atan2(target.y - pirate.y, target.x - pirate.x);
pirate.x += Math.cos(angle) * pirate.speed;
pirate.y += Math.sin(angle) * pirate.speed;
pirate.rotation = angle + Math.PI / 2;
// Pirate shooting
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);
enemyBullets.push(bullet);
game.addChild(bullet);
pirate.lastFire = LK.ticks;
}
continue;
}
}
}
// Only chase and attack when very close (within 220 pixels)
// Saldırı mesafesi %10 arttırıldı (200 -> 220)
if (!huntedByNPC && dist < 220) {
// Chase ship when very close
var angle = Math.atan2(ship.y - pirate.y, ship.x - pirate.x);
pirate.x += Math.cos(angle) * pirate.speed;
pirate.y += Math.sin(angle) * pirate.speed;
pirate.rotation = angle + Math.PI / 2;
// Pirate shooting when very close
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);
enemyBullets.push(bullet);
game.addChild(bullet);
pirate.lastFire = LK.ticks;
}
}
if (dist > 2000) {
pirate.destroy();
pirates.splice(i, 1);
i--;
}
}
// Update NPCs
var npcActive = false;
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;
}
// Görev tamamlandıysa, NPC'nin etkileşim mesajı ve durumu güncellensin
if (npc.questActive && npc.questProgress === 1 && !npc._questCompleteShown) {
// Görev tamamlandı mesajı göster
var completeLabel = new Text2("Görev tamamlandı!", {
size: 36,
fill: 0x00ff99,
align: "center"
});
completeLabel.anchor.set(0.5, 0.5);
completeLabel.x = npc.x;
completeLabel.y = npc.y + 110;
game.addChild(completeLabel);
npc._npcInteractionLabel = completeLabel;
npc._npcInteractionLabelExpire = LK.ticks + 120;
npc._questCompleteShown = true;
// NPC kısa süre dursun
npc.pauseUntil = LK.ticks + 120;
// NPC'nin questActive'ini pasif yap (tekrar etkileşim olmasın)
npc.questActive = false;
}
// RPG/Adventure: If player is close, show interaction UI
if (dist < 180) {
npcActive = true;
// Remove any previous floating text
if (npc._npcText && npc._npcText.parent) {
npc._npcText.parent.removeChild(npc._npcText);
npc._npcText = null;
}
var npcMsg = "";
if (game._lastNPCTap !== undefined && game._lastNPCTap + 30 > LK.ticks && game._lastNPC !== npc) {
// Player tapped recently, interact
var msg = npc.interact();
npcMsg = msg;
updateUI();
saveProgress();
game._lastNPC = npc;
// Pause NPC for 2 seconds (120 ticks) after interaction
npc.pauseUntil = LK.ticks + 120;
// Also clear tradeText in GUI
tradeText.setText('');
// --- Show floating interaction label under NPC for a short duration ---
if (msg && typeof msg === "string" && msg.length > 0) {
// Remove previous interaction label if any
if (npc._npcInteractionLabel && npc._npcInteractionLabel.parent) {
npc._npcInteractionLabel.parent.removeChild(npc._npcInteractionLabel);
npc._npcInteractionLabel = null;
}
var interactionLabel = new Text2(msg.split('\n')[0], {
size: 30,
fill: 0xffffff,
align: "center"
});
interactionLabel.anchor.set(0.5, 0);
interactionLabel.x = npc.x;
interactionLabel.y = npc.y + 70;
npc._npcInteractionLabel = interactionLabel;
game.addChild(interactionLabel);
// Set a timer to remove after 2.5 seconds (150 ticks)
npc._npcInteractionLabelExpire = LK.ticks + 150;
}
// --- Show Accept/Reject quest buttons above the ship if NPC is a quest-giver and quest is not yet accepted ---
if (npc.questActive && npc.questType !== 'trade' && npc.questProgress === 0 && !npc._questDecisionPanel) {
// Remove any previous quest panel
if (game._questDecisionPanel && game._questDecisionPanel.parent) {
game._questDecisionPanel.parent.removeChild(game._questDecisionPanel);
game._questDecisionPanel = null;
}
if (game._questDecisionAcceptBtn && game._questDecisionAcceptBtn.parent) {
game._questDecisionAcceptBtn.parent.removeChild(game._questDecisionAcceptBtn);
game._questDecisionAcceptBtn = null;
}
if (game._questDecisionRejectBtn && game._questDecisionRejectBtn.parent) {
game._questDecisionRejectBtn.parent.removeChild(game._questDecisionRejectBtn);
game._questDecisionRejectBtn = null;
}
// Panel background
var panel = LK.getAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
x: ship.x,
y: ship.y - 180,
scaleX: 7,
scaleY: 2,
tint: 0x222244
});
// Quest text
var questText = new Text2("Görev: " + (npcMsg ? npcMsg.split('\n')[0] : "Bir görev var!"), {
size: 38,
fill: 0xffffff,
align: "center"
});
questText.anchor.set(0.5, 0.5);
questText.x = ship.x;
questText.y = ship.y - 200;
// Accept button
var acceptBtn = new Text2("Kabul Et", {
size: 44,
fill: 0x00ff99,
align: "center"
});
acceptBtn.anchor.set(0.5, 0.5);
acceptBtn.x = ship.x - 100;
acceptBtn.y = ship.y - 120;
// Reject button
var rejectBtn = new Text2("Reddet", {
size: 44,
fill: 0xff4444,
align: "center"
});
rejectBtn.anchor.set(0.5, 0.5);
rejectBtn.x = ship.x + 100;
rejectBtn.y = ship.y - 120;
// Add to game
game.addChild(panel);
game.addChild(questText);
game.addChild(acceptBtn);
game.addChild(rejectBtn);
// Store references for cleanup
npc._questDecisionPanel = panel;
npc._questDecisionText = questText;
npc._questDecisionAcceptBtn = acceptBtn;
npc._questDecisionRejectBtn = rejectBtn;
game._questDecisionPanel = panel;
game._questDecisionAcceptBtn = acceptBtn;
game._questDecisionRejectBtn = rejectBtn;
game._questDecisionText = questText;
game._questDecisionNPC = npc;
// Accept/Reject tap logic
acceptBtn._isQuestAcceptBtn = true;
rejectBtn._isQuestRejectBtn = true;
acceptBtn._questNPC = npc;
rejectBtn._questNPC = npc;
acceptBtn._questType = npc.questType;
rejectBtn._questType = npc.questType;
}
// --- NPC Trade Panel Logic ---
if (npc.questType === 'trade' && npc.questActive && !npc.hasTraded) {
// Show a trade panel above the NPC with offer and tap-to-trade
var offer = npc.rpgTradeOffer;
var canTrade = false;
var tradeMsg = "";
if (offer.give === 'metal' && playerMetal >= offer.wantAmount) {
canTrade = true;
tradeMsg = "Trade: Give " + offer.wantAmount + " " + offer.want + " for " + offer.giveAmount + " " + offer.give + " (Tap to confirm)";
} else if (offer.give === 'energy' && playerEnergy >= offer.wantAmount) {
canTrade = true;
tradeMsg = "Trade: Give " + offer.wantAmount + " " + offer.want + " for " + offer.giveAmount + " " + offer.give + " (Tap to confirm)";
} else {
tradeMsg = "Trade: Need " + offer.wantAmount + " " + offer.want + " to trade for " + offer.giveAmount + " " + offer.give;
}
// Remove previous trade panel if any
if (npc._npcTradePanel && npc._npcTradePanel.parent) {
npc._npcTradePanel.parent.removeChild(npc._npcTradePanel);
npc._npcTradePanel = null;
}
var tradePanel = new Text2(tradeMsg, {
size: 36,
fill: canTrade ? 0x00ff99 : 0xff8888,
align: "center"
});
tradePanel.anchor.set(0.5, 0);
tradePanel.x = npc.x;
tradePanel.y = npc.y + 80;
npc._npcTradePanel = tradePanel;
game.addChild(tradePanel);
// If tapped again within window, perform trade
if (canTrade && game._lastNPCTap !== undefined && game._lastNPCTap + 15 > LK.ticks && game._lastNPCTrade !== npc) {
// Actually perform trade
if (offer.give === 'metal') {
playerMetal -= offer.wantAmount;
playerEnergy += offer.giveAmount;
} else {
playerEnergy -= offer.wantAmount;
playerMetal += offer.giveAmount;
}
npc.hasTraded = true;
var expMsg = "EXP +" + (10 + Math.floor(Math.random() * 10));
playerEXP += 10 + Math.floor(Math.random() * 10);
updateUI();
saveProgress();
// Show confirmation
tradePanel.setText("Trade complete!\n" + expMsg);
tradePanel.fill = 0x00ffcc;
game._lastNPCTrade = npc;
}
} else {
// Remove trade panel if not trading
if (npc._npcTradePanel && npc._npcTradePanel.parent) {
npc._npcTradePanel.parent.removeChild(npc._npcTradePanel);
npc._npcTradePanel = null;
}
}
} else if (!npc.questActive || npc.questProgress > 0) {
npcMsg = npc.rpgName + ": " + (npc.questActive ? "Thank you!" : "Safe travels!");
// Remove trade panel if not trading
if (npc._npcTradePanel && npc._npcTradePanel.parent) {
npc._npcTradePanel.parent.removeChild(npc._npcTradePanel);
npc._npcTradePanel = null;
}
} else {
// Show quest/trade offer
if (npc.questType === 'trade') {
npcMsg = npc.rpgName + ": Trade? Tap to interact.";
} else if (npc.questType === 'delivery') {
npcMsg = npc.rpgName + ": Delivery quest! Tap to interact.";
} else if (npc.questType === 'explore') {
npcMsg = npc.rpgName + ": Exploration quest! Tap to interact.";
} else if (npc.questType === 'escort') {
npcMsg = npc.rpgName + ": Escort quest! Tap to interact.";
} else if (npc.questType === 'hunt') {
npcMsg = npc.rpgName + ": Hunt quest! Tap to interact.";
} else if (npc.questType === 'puzzle') {
npcMsg = npc.rpgName + ": Puzzle quest! Tap to interact.";
}
// Remove trade panel if not trading
if (npc._npcTradePanel && npc._npcTradePanel.parent) {
npc._npcTradePanel.parent.removeChild(npc._npcTradePanel);
npc._npcTradePanel = null;
}
}
// Show floating text above NPC
if (npcMsg) {
var npcText = new Text2(npcMsg, {
size: 38,
fill: 0x00ffcc,
align: "center"
});
npcText.anchor.set(0.5, 1);
npcText.x = npc.x;
npcText.y = npc.y - 70;
// Remove previous if any
if (npc._npcText && npc._npcText.parent) {
npc._npcText.parent.removeChild(npc._npcText);
}
npc._npcText = npcText;
game.addChild(npcText);
}
// Remove tradeText from GUI
tradeText.setText('');
} else {
// Remove floating text if player is not close
if (npc._npcText && npc._npcText.parent) {
npc._npcText.parent.removeChild(npc._npcText);
npc._npcText = null;
}
}
// Remove quest Accept/Reject panel if player moves away from NPC
if (npc._questDecisionPanel && dist >= 220) {
if (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;
}
// Remove floating interaction label if expired
if (npc._npcInteractionLabel && npc._npcInteractionLabelExpire !== undefined && LK.ticks > npc._npcInteractionLabelExpire) {
if (npc._npcInteractionLabel.parent) {
npc._npcInteractionLabel.parent.removeChild(npc._npcInteractionLabel);
}
npc._npcInteractionLabel = null;
npc._npcInteractionLabelExpire = undefined;
}
}
if (!npcActive) {
game._lastNPC = 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);
}
}
// --- Space Debris Field Event ---
// Every 18000 ticks, 10% chance, only one at a time
if (!window.debrisField && LK.ticks % 18000 === 0 && Math.random() < 0.1) {
var cx = ship.x + (Math.random() - 0.5) * 3000;
var cy = ship.y + (Math.random() - 0.5) * 3000;
var debris = [];
for (var i = 0; i < 15; i++) {
var a = new Asteroid();
var angle = Math.random() * Math.PI * 2;
var radius = 200 + Math.random() * 200;
a.x = cx + Math.cos(angle) * radius;
a.y = cy + Math.sin(angle) * radius;
a.health = 10 + Math.floor(Math.random() * 20);
a.resources = 1 + Math.floor(Math.random() * 3);
asteroids.push(a);
game.addChild(a);
debris.push(a);
}
var debrisLabel = new Text2("Enkaz Alanı", {
size: 36,
fill: 0xcccccc,
align: "center"
});
debrisLabel.anchor.set(0.5, 0);
debrisLabel.x = cx;
debrisLabel.y = cy + 250;
game.addChild(debrisLabel);
window.debrisField = {
debris: debris,
label: debrisLabel,
x: cx,
y: cy
};
}
// Remove debris field if all asteroids gone or too far
if (window.debrisField) {
var allGone = true;
for (var i = 0; i < window.debrisField.debris.length; i++) {
if (asteroids.indexOf(window.debrisField.debris[i]) !== -1) {
allGone = false;
break;
}
}
var dist = Math.sqrt(Math.pow(window.debrisField.x - ship.x, 2) + Math.pow(window.debrisField.y - ship.y, 2));
if (allGone || dist > 4000) {
if (window.debrisField.label && window.debrisField.label.parent) window.debrisField.label.parent.removeChild(window.debrisField.label);
window.debrisField = null;
} else {
// Keep label above field
if (window.debrisField.label) {
window.debrisField.label.x = window.debrisField.x;
window.debrisField.label.y = window.debrisField.y + 250;
}
}
}
// Update upgrade stations
for (var i = upgradeStations.length - 1; i >= 0; i--) {
var station = upgradeStations[i];
var dist = Math.sqrt(Math.pow(station.x - ship.x, 2) + Math.pow(station.y - ship.y, 2));
if (dist < 100 && playerCoins >= 100) {
// Apply random upgrade
var upgrade = station.upgrades[Math.floor(Math.random() * station.upgrades.length)];
if (upgrade.name === 'health' && playerMetal >= (upgrade.metalCost || 0)) {
ship.maxHealth += 20;
ship.health = ship.maxHealth;
playerCoins -= upgrade.cost;
playerMetal -= upgrade.metalCost || 0;
} else if (upgrade.name === 'shield' && playerMetal >= (upgrade.metalCost || 0)) {
ship.maxShield += 10;
ship.shield = ship.maxShield;
playerCoins -= upgrade.cost;
playerMetal -= upgrade.metalCost || 0;
} else if (upgrade.name === 'damage' && playerEnergy >= (upgrade.energyCost || 0)) {
ship.damage += 5;
playerCoins -= upgrade.cost;
playerEnergy -= upgrade.energyCost || 0;
} else if (upgrade.name === 'speed' && playerEnergy >= (upgrade.energyCost || 0)) {
ship.speed += 1;
playerCoins -= upgrade.cost;
playerEnergy -= upgrade.energyCost || 0;
}
if (LK.getSound('upgrade')) {
LK.getSound('upgrade').play();
}
updateUI();
saveProgress();
station.destroy();
upgradeStations.splice(i, 1);
}
if (dist > 3000) {
station.destroy();
upgradeStations.splice(i, 1);
}
}
// Update boost particles
for (var i = boostParticles.length - 1; i >= 0; i--) {
var particle = boostParticles[i];
particle.x += particle.vx;
particle.y += particle.vy;
particle.life--;
particle.alpha = particle.life / 20;
if (particle.life <= 0) {
particle.destroy();
boostParticles.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--) {
if (coins[i].intersects(ship)) {
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--) {
if (resources[i].intersects(ship)) {
if (resources[i].type === 'metal') {
playerMetal += resources[i].amount;
} else {
playerEnergy += resources[i].amount;
}
resources[i].destroy();
resources.splice(i, 1);
LK.getSound('collect').play();
updateUI();
saveProgress();
}
}
// 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);
}
}
// Update pirate bases
for (var i = pirateBases.length - 1; i >= 0; i--) {
var base = pirateBases[i];
if (base.update) base.update();
var dist = Math.sqrt(Math.pow(base.x - ship.x, 2) + Math.pow(base.y - ship.y, 2));
// Remove base if too far or destroyed
if (dist > 3500 || base._destroyed) {
if (base._healthBar && base._healthBar.parent) base._healthBar.parent.removeChild(base._healthBar);
base.destroy();
pirateBases.splice(i, 1);
}
}
// Update black holes
for (var i = blackHoles.length - 1; i >= 0; i--) {
var bh = blackHoles[i];
if (bh.update) bh.update();
var dist = Math.sqrt(Math.pow(bh.x - ship.x, 2) + Math.pow(bh.y - ship.y, 2));
if (dist > 3000) {
bh.destroy();
blackHoles.splice(i, 1);
}
}
// Update wormholes
for (var i = wormholes.length - 1; i >= 0; i--) {
var wh = wormholes[i];
if (wh.update) wh.update();
var dist = Math.sqrt(Math.pow(wh.x - ship.x, 2) + Math.pow(wh.y - ship.y, 2));
if (dist > 3000) {
wh.destroy();
wormholes.splice(i, 1);
}
}
// Update trade stations (RPG/ticaret)
var tradeActive = false;
for (var i = tradeStations.length - 1; i >= 0; i--) {
var ts = tradeStations[i];
if (ts.update) ts.update();
var dist = Math.sqrt(Math.pow(ts.x - ship.x, 2) + Math.pow(ts.y - ship.y, 2));
if (dist < 180) {
tradeActive = true;
// Show trade offer
if (ts.specialOffer && ts.specialUpgrade) {
tradeText.setText('Special: ' + ts.specialUpgrade.label + ' for ' + ts.specialUpgrade.cost + ' coins\n(Tap to buy)');
// Buy special upgrade on tap
if (game._lastTradeStation !== ts && game._lastTradeStationTap !== undefined && game._lastTradeStationTap + 30 > LK.ticks) {
if (playerCoins >= ts.specialUpgrade.cost) {
playerCoins -= ts.specialUpgrade.cost;
if (ts.specialUpgrade.name === 'maxHealth') {
ship.maxHealth += 30;
ship.health = ship.maxHealth;
} else if (ts.specialUpgrade.name === 'maxShield') {
ship.maxShield += 20;
ship.shield = ship.maxShield;
} else if (ts.specialUpgrade.name === 'damage') {
ship.damage += 5;
} else if (ts.specialUpgrade.name === 'speed') {
ship.speed += 1;
}
LK.effects.flashObject(ship, 0x00ff99, 400);
updateUI();
saveProgress();
ts.destroy();
tradeStations.splice(i, 1);
tradeText.setText('Upgrade purchased!');
game._lastTradeStation = null;
break;
} else {
tradeText.setText('Not enough coins!');
}
}
} else if (ts.offerType === 'buy') {
tradeText.setText('Trade: Sell ' + ts.amount + ' ' + ts.resourceType + ' for ' + ts.price + ' coins\n(Tap to sell)');
// Sell resource on tap
if (game._lastTradeStation !== ts && game._lastTradeStationTap !== undefined && game._lastTradeStationTap + 30 > LK.ticks) {
if (ts.resourceType === 'metal' && playerMetal >= ts.amount) {
playerMetal -= ts.amount;
playerCoins += ts.price;
LK.effects.flashObject(ship, 0x00ff99, 200);
updateUI();
saveProgress();
ts.destroy();
tradeStations.splice(i, 1);
tradeText.setText('Sold!');
game._lastTradeStation = null;
break;
} else if (ts.resourceType === 'energy' && playerEnergy >= ts.amount) {
playerEnergy -= ts.amount;
playerCoins += ts.price;
LK.effects.flashObject(ship, 0x00ff99, 200);
updateUI();
saveProgress();
ts.destroy();
tradeStations.splice(i, 1);
tradeText.setText('Sold!');
game._lastTradeStation = null;
break;
} else {
tradeText.setText('Not enough ' + ts.resourceType + '!');
}
}
} else if (ts.offerType === 'sell') {
tradeText.setText('Trade: Buy ' + ts.amount + ' ' + ts.resourceType + ' for ' + ts.price + ' coins\n(Tap to buy)');
// Buy resource on tap
if (game._lastTradeStation !== ts && game._lastTradeStationTap !== undefined && game._lastTradeStationTap + 30 > LK.ticks) {
if (playerCoins >= ts.price) {
playerCoins -= ts.price;
if (ts.resourceType === 'metal') {
playerMetal += ts.amount;
} else {
playerEnergy += ts.amount;
}
LK.effects.flashObject(ship, 0x00ff99, 200);
updateUI();
saveProgress();
ts.destroy();
tradeStations.splice(i, 1);
tradeText.setText('Purchased!');
game._lastTradeStation = null;
break;
} else {
tradeText.setText('Not enough coins!');
}
}
}
game._lastTradeStation = ts;
}
if (dist > 2500) {
ts.destroy();
tradeStations.splice(i, 1);
}
}
if (!tradeActive) {
tradeText.setText('');
game._lastTradeStation = null;
}
// Spawn entities (reduced by 70%)
if (LK.ticks % 100 === 0) {
if (Math.random() < 0.5) {
// 50% chance
spawnStar();
}
}
// --- Anomaly Events: Space Storm, Nebula, Time Warp ---
// Only one anomaly at a time, random chance every 9000 ticks
if (!window.activeAnomaly && LK.ticks % 9000 === 0 && Math.random() < 0.33) {
var anomalyType = Math.random();
if (anomalyType < 0.34) {
// Space Storm: disables boost and slows ship for 20 seconds
window.activeAnomaly = "storm";
window.anomalyExpire = LK.ticks + 1200;
ship.speed = Math.max(2, ship.speed - 2);
ship.boostCooldown = 9999;
var anomalyFontSize = Math.round(unifiedFontSize * 1.1);
var stormLabel = new Text2("UZAY FIRTINASI! Hız ve boost devre dışı!", {
size: anomalyFontSize,
fill: 0x00ffff,
align: "center"
});
stormLabel.anchor.set(0.5, 0.5);
stormLabel.x = ship.x;
stormLabel.y = ship.y - 200;
game.addChild(stormLabel);
window.anomalyLabel = stormLabel;
} else if (anomalyType < 0.67) {
// Nebula: disables auto-fire and reduces vision (star alpha)
window.activeAnomaly = "nebula";
window.anomalyExpire = LK.ticks + 1200;
window._oldStarAlpha = [];
for (var i = 0; i < stars.length; i++) {
window._oldStarAlpha[i] = stars[i].children[0].alpha;
stars[i].children[0].alpha = 0.1;
}
window._oldAutoFire = ship.autoFireDisabled;
ship.autoFireDisabled = true;
var nebulaLabel = new Text2("NEBULA! Otomatik ateş ve görüş kısıtlı!", {
size: anomalyFontSize,
fill: 0xcc99ff,
align: "center"
});
nebulaLabel.anchor.set(0.5, 0.5);
nebulaLabel.x = ship.x;
nebulaLabel.y = ship.y - 200;
game.addChild(nebulaLabel);
window.anomalyLabel = nebulaLabel;
} else {
// Time Warp: all pirates/asteroids move double speed, player gets double EXP
window.activeAnomaly = "timewarp";
window.anomalyExpire = LK.ticks + 1200;
for (var i = 0; i < pirates.length; i++) pirates[i].speed *= 2;
for (var i = 0; i < asteroids.length; i++) asteroids[i].vx *= 2, asteroids[i].vy *= 2;
window._expMultiplier = 2;
var warpLabel = new Text2("ZAMAN YAMULMASI! Düşmanlar hızlı, EXP x2!", {
size: anomalyFontSize,
fill: 0xffcc00,
align: "center"
});
warpLabel.anchor.set(0.5, 0.5);
warpLabel.x = ship.x;
warpLabel.y = ship.y - 200;
game.addChild(warpLabel);
window.anomalyLabel = warpLabel;
}
}
// Anomaly expire logic
if (window.activeAnomaly && LK.ticks > window.anomalyExpire) {
if (window.activeAnomaly === "storm") {
ship.speed = Math.min(ship.speed + 2, 10);
ship.boostCooldown = 0;
} else if (window.activeAnomaly === "nebula") {
for (var i = 0; i < stars.length; i++) {
if (window._oldStarAlpha && window._oldStarAlpha[i] !== undefined) stars[i].children[0].alpha = window._oldStarAlpha[i];
}
ship.autoFireDisabled = window._oldAutoFire || false;
} else if (window.activeAnomaly === "timewarp") {
for (var i = 0; i < pirates.length; i++) pirates[i].speed /= 2;
for (var i = 0; i < asteroids.length; i++) asteroids[i].vx /= 2, asteroids[i].vy /= 2;
window._expMultiplier = 1;
}
if (window.anomalyLabel && window.anomalyLabel.parent) window.anomalyLabel.parent.removeChild(window.anomalyLabel);
window.activeAnomaly = null;
window.anomalyExpire = null;
window.anomalyLabel = null;
}
// Spawn black holes occasionally
if (LK.ticks % 3600 === 0 && Math.random() < 0.6) {
spawnBlackHole();
}
// Spawn wormholes occasionally
if (LK.ticks % 4200 === 0 && Math.random() < 0.6) {
spawnWormhole();
}
// Spawn trade stations occasionally (RPG/ticaret)
if (LK.ticks % 4800 === 0 && Math.random() < 0.6) {
spawnTradeStation();
}
// Spawn pirate bases occasionally
if (LK.ticks % 6000 === 0 && Math.random() < 0.5 && pirateBases.length < 3) {
spawnPirateBase();
}
if (LK.ticks % 900 === 0) {
var spawnRoll = Math.random();
if (spawnRoll < 0.15) {
// 15% chance (pirates reduced by 85%)
spawnPirate();
} else if (spawnRoll < 0.31) {
// 16% chance (NPCs increased by 10%)
spawnNPC();
} else if (spawnRoll < 0.33) {
// 2% chance
// Spawn multiple entities occasionally
spawnPirate();
spawnNPC();
}
}
// Spawn asteroids (reduced by 70%)
if (LK.ticks % 1500 === 0) {
if (Math.random() < 0.5) {
// 50% chance
spawnAsteroid();
}
}
// --- Derelict Ship Event ---
// Every 12000 ticks, 20% chance, only one at a time
if (!window.derelictShip && LK.ticks % 12000 === 0 && Math.random() < 0.2) {
// Limit derelict ship spawn distance to max 2 minutes of travel
var maxTravelTime = 120;
var maxDistance = (ship && ship.speed ? ship.speed : 5) * 60 * maxTravelTime;
var angle = Math.random() * Math.PI * 2;
var dist = Math.random() * (maxDistance * 0.5) + 400;
var dx = Math.cos(angle) * dist;
var dy = Math.sin(angle) * dist;
var derelict = LK.getAsset('ship', {
anchorX: 0.5,
anchorY: 0.5,
x: ship.x + dx,
y: ship.y + dy,
tint: 0x888888,
scaleX: 1.2,
scaleY: 1.2,
alpha: 0.7
});
derelict._isDerelict = true;
derelict._loot = {
coins: 50 + Math.floor(Math.random() * 100),
metal: 5 + Math.floor(Math.random() * 10),
energy: 5 + Math.floor(Math.random() * 10)
};
derelict._label = new Text2("Terkedilmiş Gemi", {
size: 32,
fill: 0xcccccc,
align: "center"
});
derelict._label.anchor.set(0.5, 0);
derelict._label.x = derelict.x;
derelict._label.y = derelict.y + 70;
game.addChild(derelict);
game.addChild(derelict._label);
window.derelictShip = derelict;
}
// Derelict ship interaction
if (window.derelictShip) {
var d = window.derelictShip;
var dist = Math.sqrt(Math.pow(d.x - ship.x, 2) + Math.pow(d.y - ship.y, 2));
if (dist < 120) {
// Collect loot
playerCoins += d._loot.coins;
playerMetal += d._loot.metal;
playerEnergy += d._loot.energy;
var lootLabel = new Text2("Gemi yağmalandı!\n+" + d._loot.coins + " coin, +" + d._loot.metal + " metal, +" + d._loot.energy + " enerji", {
size: 36,
fill: 0x00ffcc,
align: "center"
});
lootLabel.anchor.set(0.5, 0.5);
lootLabel.x = d.x;
lootLabel.y = d.y - 80;
game.addChild(lootLabel);
if (d.parent) d.parent.removeChild(d);
if (d._label && d._label.parent) d._label.parent.removeChild(d._label);
window.derelictShip = null;
updateUI();
saveProgress();
// Remove loot label after 2 seconds
(function (lbl) {
var expire = LK.ticks + 120;
var removeLootLabel = function removeLootLabel() {
if (LK.ticks > expire && lbl.parent) {
lbl.parent.removeChild(lbl);
game.update = origUpdate;
}
};
var origUpdate = game.update;
game.update = function () {
removeLootLabel();
if (origUpdate) origUpdate.apply(game, arguments);
};
})(lootLabel);
} else if (dist > 3000) {
if (d.parent) d.parent.removeChild(d);
if (d._label && d._label.parent) d._label.parent.removeChild(d._label);
window.derelictShip = null;
} else {
// Keep label above derelict
if (d._label) {
d._label.x = d.x;
d._label.y = d.y + 70;
}
}
}
// Wave-based pirate spawning
if (LK.ticks % (180 - Math.min(waveNumber * 10, 100)) === 0) {
var piratesInWave = 1 + Math.floor(waveNumber / 2);
for (var j = 0; j < piratesInWave; j++) {
spawnPirate();
}
}
// --- Distress Signal Event ---
// Every 15000 ticks, 15% chance, only one at a time
if (!window.distressEvent && LK.ticks % 15000 === 0 && Math.random() < 0.15) {
// Limit distress event spawn distance to max 2 minutes of travel
var maxTravelTime = 120;
var maxDistance = (ship && ship.speed ? ship.speed : 5) * 60 * maxTravelTime;
var angle = Math.random() * Math.PI * 2;
var dist = Math.random() * (maxDistance * 0.5) + 400;
var sx = ship.x + Math.cos(angle) * dist;
var sy = ship.y + Math.sin(angle) * dist;
var signal = LK.getAsset('star', {
anchorX: 0.5,
anchorY: 0.5,
x: sx,
y: sy,
tint: 0xff00ff,
scaleX: 4,
scaleY: 4,
alpha: 0.8
});
var label = new Text2("İmdat Sinyali", {
size: 36,
fill: 0xff00ff,
align: "center"
});
label.anchor.set(0.5, 0);
label.x = sx;
label.y = sy + 70;
game.addChild(signal);
game.addChild(label);
window.distressEvent = {
signal: signal,
label: label,
x: sx,
y: sy,
state: "waiting"
};
}
// Distress event logic
if (window.distressEvent) {
var e = window.distressEvent;
var dist = Math.sqrt(Math.pow(e.x - ship.x, 2) + Math.pow(e.y - ship.y, 2));
if (e.state === "waiting" && dist < 120) {
// Show choice: rescue or ignore (ambush possibility)
if (!e.panel) {
var panel = LK.getAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
x: e.x,
y: e.y - 180,
scaleX: 7,
scaleY: 2,
tint: 0x222244
});
var text = new Text2("İmdat sinyali! Yardım et veya uzaklaş?", {
size: 38,
fill: 0xffffff,
align: "center"
});
text.anchor.set(0.5, 0.5);
text.x = e.x;
text.y = e.y - 200;
var helpBtn = new Text2("Yardım Et", {
size: 44,
fill: 0x00ff99,
align: "center"
});
helpBtn.anchor.set(0.5, 0.5);
helpBtn.x = e.x - 100;
helpBtn.y = e.y - 120;
var ignoreBtn = new Text2("Uzaklaş", {
size: 44,
fill: 0xff4444,
align: "center"
});
ignoreBtn.anchor.set(0.5, 0.5);
ignoreBtn.x = e.x + 100;
ignoreBtn.y = e.y - 120;
game.addChild(panel);
game.addChild(text);
game.addChild(helpBtn);
game.addChild(ignoreBtn);
e.panel = panel;
e.text = text;
e.helpBtn = helpBtn;
e.ignoreBtn = ignoreBtn;
helpBtn._isDistressHelp = true;
ignoreBtn._isDistressIgnore = true;
helpBtn._distressEvent = e;
ignoreBtn._distressEvent = e;
}
// Tap logic for help/ignore
if (game._lastDistressTap !== e && game._lastDistressTapTick !== undefined && game._lastDistressTapTick + 30 > LK.ticks) {
var tapX = game._lastDistressTapX,
tapY = game._lastDistressTapY;
if (tapX > e.helpBtn.x - 90 && tapX < e.helpBtn.x + 90 && tapY > e.helpBtn.y - 40 && tapY < e.helpBtn.y + 40) {
// Help: 70% chance for reward, 30% ambush
if (Math.random() < 0.7) {
var reward = 100 + Math.floor(Math.random() * 100);
playerCoins += reward;
var msg = new Text2("Kurtarılan gemi! Ödül: " + reward + " coin", {
size: 36,
fill: 0x00ffcc,
align: "center"
});
msg.anchor.set(0.5, 0.5);
msg.x = e.x;
msg.y = e.y - 80;
game.addChild(msg);
updateUI();
saveProgress();
} else {
// Ambush: spawn 3 pirates
for (var i = 0; i < 3; i++) {
var pirate = new Pirate();
pirate.x = e.x + (Math.random() - 0.5) * 100;
pirate.y = e.y + (Math.random() - 0.5) * 100;
pirates.push(pirate);
game.addChild(pirate);
}
var msg = new Text2("Pusu! Korsanlar saldırıyor!", {
size: 36,
fill: 0xff4444,
align: "center"
});
msg.anchor.set(0.5, 0.5);
msg.x = e.x;
msg.y = e.y - 80;
game.addChild(msg);
}
// Clean up
if (e.signal && e.signal.parent) e.signal.parent.removeChild(e.signal);
if (e.label && e.label.parent) e.label.parent.removeChild(e.label);
if (e.panel && e.panel.parent) e.panel.parent.removeChild(e.panel);
if (e.text && e.text.parent) e.text.parent.removeChild(e.text);
if (e.helpBtn && e.helpBtn.parent) e.helpBtn.parent.removeChild(e.helpBtn);
if (e.ignoreBtn && e.ignoreBtn.parent) e.ignoreBtn.parent.removeChild(e.ignoreBtn);
window.distressEvent = null;
} else if (tapX > e.ignoreBtn.x - 90 && tapX < e.ignoreBtn.x + 90 && tapY > e.ignoreBtn.y - 40 && tapY < e.ignoreBtn.y + 40) {
// Ignore: nothing happens, clean up
if (e.signal && e.signal.parent) e.signal.parent.removeChild(e.signal);
if (e.label && e.label.parent) e.label.parent.removeChild(e.label);
if (e.panel && e.panel.parent) e.panel.parent.removeChild(e.panel);
if (e.text && e.text.parent) e.text.parent.removeChild(e.text);
if (e.helpBtn && e.helpBtn.parent) e.helpBtn.parent.removeChild(e.helpBtn);
if (e.ignoreBtn && e.ignoreBtn.parent) e.ignoreBtn.parent.removeChild(e.ignoreBtn);
window.distressEvent = null;
}
game._lastDistressTap = e;
}
} else if (dist > 3000) {
if (e.signal && e.signal.parent) e.signal.parent.removeChild(e.signal);
if (e.label && e.label.parent) e.label.parent.removeChild(e.label);
if (e.panel && e.panel.parent) e.panel.parent.removeChild(e.panel);
if (e.text && e.text.parent) e.text.parent.removeChild(e.text);
if (e.helpBtn && e.helpBtn.parent) e.helpBtn.parent.removeChild(e.helpBtn);
if (e.ignoreBtn && e.ignoreBtn.parent) e.ignoreBtn.parent.removeChild(e.ignoreBtn);
window.distressEvent = null;
} else {
// Keep label above signal
if (e.label) {
e.label.x = e.x;
e.label.y = e.y + 70;
}
}
}
// Boss wave every 5 waves
if (waveNumber > 0 && waveNumber % 5 === 0 && pirates.length === 0) {
var bossPirate = new Pirate();
bossPirate.x = ship.x + 1000;
bossPirate.y = ship.y;
// Level range: wave 1 => 1-3, wave 2 => 3-5, wave 3 => 5-7, etc.
var minLevel = 1 + waveNumber * 2;
var maxLevel = minLevel + 2;
bossPirate.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1));
bossPirate.health = 200 + (bossPirate.level - 1) * 20;
bossPirate.damage = 10 + Math.floor(bossPirate.level * 1.5);
bossPirate.defense = 2 + Math.floor(bossPirate.level * 1.2);
bossPirate.loot = 200 + bossPirate.level * 50;
bossPirate.speed = 3;
var bossGraphics = bossPirate.children[0];
bossGraphics.tint = 0xff00ff;
bossGraphics.scaleX = 2;
bossGraphics.scaleY = 2;
pirates.push(bossPirate);
game.addChild(bossPirate);
}
// 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 if health < 100% and not dead, and not already shown
if (!window.repairPanel && ship.health < ship.maxHealth && ship.health > 0 && window._showRepairPanel) {
var repairCostCoins = Math.max(30, Math.round((ship.maxHealth - ship.health) * 1.5));
var repairCostMetal = Math.max(2, Math.round((ship.maxHealth - ship.health) / 20));
var repairCostEnergy = Math.max(2, Math.round((ship.maxHealth - ship.health) / 25));
// Panel background
var panel = LK.getAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
x: ship.x,
y: ship.y - 220,
scaleX: 7,
scaleY: 2,
tint: 0x224444
});
// Repair text
var repairText = new Text2("Gemi hasarlı! Tamir: " + repairCostCoins + " coin, " + repairCostMetal + " metal, " + repairCostEnergy + " enerji", {
size: 38,
fill: 0xffffff,
align: "center"
});
repairText.anchor.set(0.5, 0.5);
repairText.x = ship.x;
repairText.y = ship.y - 240;
// Repair button
var repairBtn = new Text2("Tamir Et", {
size: 44,
fill: 0x00ff99,
align: "center"
});
repairBtn.anchor.set(0.5, 0.5);
repairBtn.x = ship.x - 100;
repairBtn.y = ship.y - 160;
// Cancel button
var cancelBtn = new Text2("Kapat", {
size: 44,
fill: 0xff4444,
align: "center"
});
cancelBtn.anchor.set(0.5, 0.5);
cancelBtn.x = ship.x + 100;
cancelBtn.y = ship.y - 160;
// Add to game
game.addChild(panel);
game.addChild(repairText);
game.addChild(repairBtn);
game.addChild(cancelBtn);
// Store references for tap logic and cleanup
window.repairPanel = panel;
window.repairText = repairText;
window.repairBtn = repairBtn;
window.cancelRepairBtn = cancelBtn;
repairBtn._isRepairBtn = true;
cancelBtn._isCancelRepairBtn = true;
repairBtn._repairCostCoins = repairCostCoins;
repairBtn._repairCostMetal = repairCostMetal;
repairBtn._repairCostEnergy = repairCostEnergy;
window._showRepairPanel = false; // Reset flag
} 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, auto-show repair panel (legacy behavior)
if (!window.repairPanel && ship.health < ship.maxHealth * 0.5 && ship.health > 0) {
window._showRepairPanel = true;
}
// Update coordinateText with current ship coordinates (rounded) in real time to the left of waveText
if (typeof coordinateText !== "undefined" && ship && typeof waveText !== "undefined") {
// Show the actual current coordinates of the ship
coordinateText.setText('X: ' + Math.round(ship.x) + ' Y: ' + Math.round(ship.y));
// Always keep coordinateText to the left of waveText, and keep size 30% smaller than expText
coordinateText.size = Math.round(expText.size * 0.7);
coordinateText.x = waveText.x - waveText.width - 30;
coordinateText.y = waveText.y + waveText.height / 2;
}
// --- QUEST TARGET ARROW INDICATOR ---
// Only one arrow at a time, create if needed
if (!window.questArrow) {
// Use a yellow triangle as arrow
var arrow = LK.getAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 2.5,
scaleY: 1.2,
tint: 0xffff00,
alpha: 0.85
});
arrow.visible = false;
window.questArrow = arrow;
game.addChild(arrow);
}
var showArrow = false;
var arrowX = 0,
arrowY = 0,
arrowRot = 0;
var questTarget = null;
var minDist = Infinity;
// Find the nearest active coordinate-based quest target
for (var i = 0; i < npcs.length; i++) {
var npc = npcs[i];
if (!npc.questActive || !npc.questTarget) continue;
// Only for coordinate-based quests
if (npc.questType === 'explore' || npc.questType === 'delivery' || npc.questType === 'escort' || npc.questType === 'hunt') {
// For hunt, questTarget can be a pirate, otherwise it's {x, y}
var tx, ty;
if (npc.questType === 'hunt' && npc.questTarget && npc.questTarget.x !== undefined && npc.questTarget.y !== undefined && !npc.questTarget._destroyed) {
tx = npc.questTarget.x;
ty = npc.questTarget.y;
} else if (npc.questTarget.x !== undefined && npc.questTarget.y !== undefined) {
tx = npc.questTarget.x;
ty = npc.questTarget.y;
}
if (typeof tx === "number" && typeof ty === "number") {
var dist = Math.sqrt(Math.pow(tx - ship.x, 2) + Math.pow(ty - ship.y, 2));
if (dist > 120 && dist < minDist) {
// Only show if not already at target
minDist = dist;
questTarget = {
x: tx,
y: ty
};
}
}
}
}
// --- Derelict ship arrow logic ---
if (!questTarget && window.derelictShip && window.derelictShip.x !== undefined && window.derelictShip.y !== undefined) {
var d = window.derelictShip;
var dist = Math.sqrt(Math.pow(d.x - ship.x, 2) + Math.pow(d.y - ship.y, 2));
if (dist > 120 && dist < 3000) {
questTarget = {
x: d.x,
y: d.y
};
}
}
if (questTarget) {
// Project questTarget to screen edge
var dx = questTarget.x - ship.x;
var dy = questTarget.y - ship.y;
var angle = Math.atan2(dy, dx);
// Screen center in game coordinates
var screenW = 2048,
screenH = 2732;
var margin = 90; // keep arrow inside screen
var cx = screenW / 2,
cy = screenH / 2;
// Find intersection with screen edge
var ex = cx + Math.cos(angle) * (Math.min(cx, cy) - margin);
var ey = cy + Math.sin(angle) * (Math.min(cx, cy) - margin);
// Clamp to screen bounds
ex = Math.max(margin, Math.min(screenW - margin, ex));
ey = Math.max(margin, Math.min(screenH - margin, ey));
// Don't show if target is on screen (within 40% of screen from center)
if (Math.abs(dx) > screenW * 0.2 || Math.abs(dy) > screenH * 0.2) {
showArrow = true;
arrowX = ex;
arrowY = ey;
arrowRot = angle + Math.PI / 2;
}
}
if (window.questArrow) {
window.questArrow.visible = showArrow;
if (showArrow) {
window.questArrow.x = arrowX;
window.questArrow.y = arrowY;
window.questArrow.rotation = arrowRot;
window.questArrow.alpha = 0.85 + Math.sin(LK.ticks * 0.1) * 0.15;
window.questArrow.scaleX = 2.5 + Math.sin(LK.ticks * 0.07) * 0.2;
window.questArrow.scaleY = 1.2 + Math.cos(LK.ticks * 0.09) * 0.1;
window.questArrow.zIndex = 9999;
}
}
// --- Remaining Distance Label for Quest/Event Target ---
// Only one label at a time, create if needed
if (!window.questDistanceLabel) {
var questDistanceLabel = new Text2('', {
size: Math.round(unifiedFontSize * 0.9),
fill: 0xffffff,
align: "center"
});
questDistanceLabel.anchor.set(0.5, 0.5);
questDistanceLabel.visible = false;
window.questDistanceLabel = questDistanceLabel;
game.addChild(questDistanceLabel);
}
var showDistanceLabel = false;
var labelX = 0,
labelY = 0,
labelText = '';
if (questTarget) {
// Calculate distance
var distToTarget = Math.sqrt(Math.pow(questTarget.x - ship.x, 2) + Math.pow(questTarget.y - ship.y, 2));
// Only show if not already at target and not on screen
if (showArrow && distToTarget > 120) {
showDistanceLabel = true;
// Place label near the arrow, but offset further outwards
var labelOffset = 70;
labelX = arrowX + Math.cos(arrowRot - Math.PI / 2) * labelOffset;
labelY = arrowY + Math.sin(arrowRot - Math.PI / 2) * labelOffset;
// Show in km if >1000, else in px
if (distToTarget >= 1000) {
labelText = Math.round(distToTarget / 10) / 100 + "k px";
} else {
labelText = Math.round(distToTarget) + " px";
}
// If quest is from an NPC, show a short label
for (var i = 0; i < npcs.length; i++) {
var npc = npcs[i];
if (npc.questActive && npc.questTarget && (npc.questType === 'explore' || npc.questType === 'delivery' || npc.questType === 'escort') && npc.questTarget.x === questTarget.x && npc.questTarget.y === questTarget.y) {
if (npc.questType === 'explore') labelText = "Keşif: " + labelText;else if (npc.questType === 'delivery') labelText = "Teslimat: " + labelText;else if (npc.questType === 'escort') labelText = "Eşlik: " + labelText;
break;
}
if (npc.questActive && npc.questType === 'hunt' && npc.questTarget && npc.questTarget.x === questTarget.x && npc.questTarget.y === questTarget.y) {
labelText = "Av: " + labelText;
break;
}
}
}
}
// Derelict ship distance
if (!showDistanceLabel && window.derelictShip && window.derelictShip.x !== undefined && window.derelictShip.y !== undefined) {
var d = window.derelictShip;
var dist = Math.sqrt(Math.pow(d.x - ship.x, 2) + Math.pow(d.y - ship.y, 2));
if (dist > 120 && dist < 3000 && window.questArrow && window.questArrow.visible) {
showDistanceLabel = true;
var labelOffset = 70;
labelX = window.questArrow.x + Math.cos(window.questArrow.rotation - Math.PI / 2) * labelOffset;
labelY = window.questArrow.y + Math.sin(window.questArrow.rotation - Math.PI / 2) * labelOffset;
labelText = "Gemi: " + (dist >= 1000 ? Math.round(dist / 10) / 100 + "k px" : Math.round(dist) + " px");
}
}
// Distress event distance
if (!showDistanceLabel && window.distressEvent && window.distressEvent.x !== undefined && window.distressEvent.y !== undefined) {
var e = window.distressEvent;
var dist = Math.sqrt(Math.pow(e.x - ship.x, 2) + Math.pow(e.y - ship.y, 2));
if (dist > 120 && dist < 3000 && window.questArrow && window.questArrow.visible) {
showDistanceLabel = true;
var labelOffset = 70;
labelX = window.questArrow.x + Math.cos(window.questArrow.rotation - Math.PI / 2) * labelOffset;
labelY = window.questArrow.y + Math.sin(window.questArrow.rotation - Math.PI / 2) * labelOffset;
labelText = "İmdat: " + (dist >= 1000 ? Math.round(dist / 10) / 100 + "k px" : Math.round(dist) + " px");
}
}
if (window.questDistanceLabel) {
window.questDistanceLabel.visible = showDistanceLabel;
if (showDistanceLabel) {
window.questDistanceLabel.x = labelX;
window.questDistanceLabel.y = labelY;
window.questDistanceLabel.setText(labelText);
window.questDistanceLabel.alpha = 0.9 + Math.sin(LK.ticks * 0.1) * 0.1;
window.questDistanceLabel.size = Math.round(unifiedFontSize * 0.9);
window.questDistanceLabel.zIndex = 9999;
}
}
};
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