/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Asteroid = Container.expand(function () { var self = Container.call(this); var asteroidGraphics = self.attachAsset('resource', { anchorX: 0.5, anchorY: 0.5, tint: 0x8B4513, scaleX: 2, scaleY: 2 }); self.health = 50; self.resources = Math.floor(Math.random() * 10) + 5; self.vx = (Math.random() - 0.5) * 0.5; self.vy = (Math.random() - 0.5) * 0.5; self.rotationSpeed = (Math.random() - 0.5) * 0.02; self.takeDamage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xffffff, 100); }; self.update = function () { self.x += self.vx; self.y += self.vy; self.rotation += self.rotationSpeed; }; return self; }); // Black Hole Hazard Class var BlackHole = Container.expand(function () { var self = Container.call(this); var bhGraphics = self.attachAsset('star', { anchorX: 0.5, anchorY: 0.5, scaleX: 8, scaleY: 8, tint: 0x222222 }); self.radius = 200; self.pullStrength = 0.7 + Math.random() * 0.5; self.damageRadius = 120; self.update = function () { // Animate black hole bhGraphics.rotation += 0.03; // Pull ship if in range var dx = ship.x - self.x; var dy = ship.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < self.radius) { var pull = (self.radius - dist) / self.radius * self.pullStrength; ship.x -= dx / dist * pull; ship.y -= dy / dist * pull; // Camera follows ship, so move all objects accordingly for (var i = 0; i < stars.length; i++) { stars[i].x -= dx / dist * pull; stars[i].y -= dy / dist * pull; } for (var i = 0; i < npcs.length; i++) { npcs[i].x -= dx / dist * pull; npcs[i].y -= dy / dist * pull; } for (var i = 0; i < pirates.length; i++) { pirates[i].x -= dx / dist * pull; pirates[i].y -= dy / dist * pull; } for (var i = 0; i < coins.length; i++) { coins[i].x -= dx / dist * pull; coins[i].y -= dy / dist * pull; } for (var i = 0; i < resources.length; i++) { resources[i].x -= dx / dist * pull; resources[i].y -= dy / dist * pull; } for (var i = 0; i < bullets.length; i++) { bullets[i].x -= dx / dist * pull; bullets[i].y -= dy / dist * pull; } for (var i = 0; i < enemyBullets.length; i++) { enemyBullets[i].x -= dx / dist * pull; enemyBullets[i].y -= dy / dist * pull; } for (var i = 0; i < asteroids.length; i++) { asteroids[i].x -= dx / dist * pull; asteroids[i].y -= dy / dist * pull; } for (var i = 0; i < upgradeStations.length; i++) { upgradeStations[i].x -= dx / dist * pull; upgradeStations[i].y -= dy / dist * pull; } // boostParticles removed (no boost system) for (var i = 0; i < explosions.length; i++) { explosions[i].x -= dx / dist * pull; explosions[i].y -= dy / dist * pull; } } // Damage ship if too close if (dist < self.damageRadius) { if (!self.lastDamaged || LK.ticks - self.lastDamaged > 30) { ship.takeDamage(10); self.lastDamaged = LK.ticks; LK.effects.flashObject(ship, 0x000000, 200); } } }; return self; }); var Bullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 15; self.damage = 10; self.directionX = 0; self.directionY = -1; self._outOfRangeTick = null; self._fadeStartTick = null; self._expireTick = null; self._range = typeof ATTACK_RANGE !== "undefined" ? ATTACK_RANGE : 220; self._startX = null; self._startY = null; self.update = function () { if (self._startX === null || self._startY === null) { self._startX = self.x; self._startY = self.y; } self.x += self.directionX * self.speed; self.y += self.directionY * self.speed; // Out of range detection: bullet range = attack range var dx = self.x - self._startX; var dy = self.y - self._startY; var dist = Math.sqrt(dx * dx + dy * dy); var outOfRange = dist > self._range; if (outOfRange && self._outOfRangeTick === null) { self._outOfRangeTick = LK.ticks; self._fadeStartTick = self._outOfRangeTick; self._expireTick = self._fadeStartTick + 60; // 1 second fade } // Fade out logic if out of range if (self._fadeStartTick !== null && self._expireTick !== null) { var now = LK.ticks; var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick)); self.alpha = 1 - fadeProgress; if (now >= self._expireTick) { self._shouldRemove = true; } } else { self.alpha = 1; } }; return self; }); var Coin = Container.expand(function () { var self = Container.call(this); var coinGraphics = self.attachAsset('coin', { anchorX: 0.5, anchorY: 0.5 }); self.value = Math.floor(Math.random() * 10) + 5; // --- Timed fade-out system --- self._spawnTick = LK.ticks; self._fadeStartTick = self._spawnTick + 180; // 3 seconds visible self._expireTick = self._fadeStartTick + 120; // 2 seconds fade (total 5s) self.update = function () { self.rotation += 0.05; // Timed fade-out logic if (typeof self._expireTick === "number" && typeof self._fadeStartTick === "number") { var now = LK.ticks; if (now >= self._fadeStartTick) { // Start fading out var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick)); self.alpha = 1 - fadeProgress; if (now >= self._expireTick) { // Mark for removal (actual removal in game.update) self._shouldRemove = true; } } else { self.alpha = 1; } } }; return self; }); // CoinBox: Toplanınca 50-100 coin veren, fade-out ile kaybolan obje var CoinBox = Container.expand(function () { var self = Container.call(this); var boxGraphics = self.attachAsset('coinbox', { anchorX: 0.5, anchorY: 0.5, scaleX: 0.9, scaleY: 0.9 }); self.value = 50 + Math.floor(Math.random() * 51); // 50-100 coin arası // Timed fade-out system self._spawnTick = LK.ticks; self._fadeStartTick = self._spawnTick + 240; // 4 seconds visible self._expireTick = self._fadeStartTick + 120; // 2 seconds fade (total 6s) self.update = function () { boxGraphics.rotation += 0.07; // Timed fade-out logic if (typeof self._expireTick === "number" && typeof self._fadeStartTick === "number") { var now = LK.ticks; if (now >= self._fadeStartTick) { var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick)); self.alpha = 1 - fadeProgress; if (now >= self._expireTick) { self._shouldRemove = true; } } else { self.alpha = 1; } } }; return self; }); // DerelictDebris: Enkaz, üstüne gelince 0-50 arası rastgele hammadde verir ve fade-out ile kaybolur var DerelictDebris = Container.expand(function () { var self = Container.call(this); // Use randomly selected pirate asset for debris appearance var pirateImageIndex = Math.floor(Math.random() * 20) + 1; var pirateAssetName = 'pirate' + pirateImageIndex; var debrisGraphics = self.attachAsset(pirateAssetName, { anchorX: 0.5, anchorY: 0.5, tint: 0x00ff00, // yeşil enkaz scaleX: 1.5, scaleY: 1.5 }); self.type = 'derelict'; self.rotationSpeed = 0.01; self._collected = false; self._resourceGiven = false; self._fadeStartTick = null; self._expireTick = null; self._resourceAmount = Math.floor(Math.random() * 51); // 0-50 arası self.update = function () { self.rotation += self.rotationSpeed; // Fade-out logic if (self._fadeStartTick !== null && self._expireTick !== null) { var now = LK.ticks; var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick)); self.alpha = 1 - fadeProgress; if (now >= self._expireTick) { self._shouldRemove = true; } } else { self.alpha = 1; } }; return self; }); // --- DRON CLASS --- // Dron: ship'i takip eder, %80 yakınındaki maden/coinleri toplar ve ship'e getirir var Dron = Container.expand(function () { var self = Container.call(this); // Randomly select one of 2 NPC images for dron appearance var npcImageIndex = Math.floor(Math.random() * 2) + 1; var npcAssetName = 'npc' + npcImageIndex; var dronGraphics = self.attachAsset(npcAssetName, { anchorX: 0.5, anchorY: 0.5, tint: 0x1a2dcb, scaleX: 1.2, scaleY: 1.2 }); self._followOffset = { x: -100 + Math.random() * 40, y: 80 + Math.random() * 40 }; self._targetItem = null; self._collecting = false; self._collectSpeed = 10; self.update = function () { // --- Hafif float/wobble animasyonu: Dron sabit durmasın, havada uçuyormuş gibi hareket etsin --- if (typeof self._floatBaseY === "undefined") { self._floatBaseY = self.y; } if (typeof self._floatBaseX === "undefined") { self._floatBaseX = self.x; } if (typeof self._floatPhase === "undefined") { self._floatPhase = Math.random() * Math.PI * 2; } if (typeof self._floatPhaseX === "undefined") { self._floatPhaseX = Math.random() * Math.PI * 2; } // Ship'i takip et (eğer item toplamıyorsa) if (!self._collecting && typeof ship !== "undefined" && ship) { var targetX = ship.x + self._followOffset.x; var targetY = ship.y + self._followOffset.y; var dx = targetX - self.x; var dy = targetY - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { self.x += dx / dist * 7; self.y += dy / dist * 7; } // Float baseyi güncelle ki float animasyonu ship ile birlikte hareket etsin self._floatBaseX = self.x; self._floatBaseY = self.y; } // Eğer item topluyorsa, ona doğru git if (self._collecting && self._targetItem) { var dx = self._targetItem.x - self.x; var dy = self._targetItem.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { self.x += dx / dist * self._collectSpeed; self.y += dy / dist * self._collectSpeed; } else { // Toplama mesafesine ulaştı // Coin veya resource ise ship'e getir self._targetItem._dronCollected = true; // Ship'e doğru item'ı taşı self._targetItem._beingCarried = true; self._targetItem._carrier = self; self._collecting = false; self._targetItem = null; } // Float baseyi güncelle ki float animasyonu item toplarken de düzgün çalışsın self._floatBaseX = self.x; self._floatBaseY = self.y; } // Eğer item taşıyorsa, item'ı ship'e doğru taşı for (var i = 0; i < coins.length; i++) { var c = coins[i]; if (c._beingCarried && c._carrier === self) { var dx = ship.x - c.x; var dy = ship.y - c.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { c.x += dx / dist * self._collectSpeed; c.y += dy / dist * self._collectSpeed; } else { // Ship'e ulaştı, coin topla playerCoins += c.value; c.destroy(); coins.splice(i, 1); LK.getSound('collect').play(); updateUI && updateUI(); saveProgress && saveProgress(); } } } for (var i = 0; i < resources.length; i++) { var r = resources[i]; if (r._beingCarried && r._carrier === self) { var dx = ship.x - r.x; var dy = ship.y - r.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { r.x += dx / dist * self._collectSpeed; r.y += dy / dist * self._collectSpeed; } else { // Ship'e ulaştı, resource topla var resourceTypes = ['metal', 'energy', 'crystal', 'gas', 'ice', 'uranium', 'silicon', 'carbon', 'plasma', 'antimatter']; var idx = resourceTypes.indexOf(r.type); if (idx >= 0) { playerResources[idx] += r.amount; checkResourceCollectAchievements && checkResourceCollectAchievements(); } r.destroy(); resources.splice(i, 1); LK.getSound('collect').play(); updateUI && updateUI(); saveProgress && saveProgress(); } } } // Eğer item toplamıyorsa, yakındaki item'ı ara if (!self._collecting && !self._targetItem) { // %25 ship'e yakın, ekranda olan coin/resource bul var found = false; var maxDist = Math.max(2048, 2732) * 0.25; for (var i = 0; i < coins.length; i++) { var c = coins[i]; if (!c._dronCollected && Math.sqrt(Math.pow(c.x - ship.x, 2) + Math.pow(c.y - ship.y, 2)) < maxDist) { self._targetItem = c; self._collecting = true; found = true; break; } } if (!found) { for (var i = 0; i < resources.length; i++) { var r = resources[i]; if (!r._dronCollected && Math.sqrt(Math.pow(r.x - ship.x, 2) + Math.pow(r.y - ship.y, 2)) < maxDist) { self._targetItem = r; self._collecting = true; break; } } } } // --- Hafif float/wobble animasyonu uygula --- // Dron'un gerçek pozisyonunu floatBaseX/floatBaseY olarak tut, ek olarak x/y'ye küçük bir sinusoidal offset uygula // Amplitüd: 8px (y), 4px (x), Periyot: 2-3 saniye arası var floatY = Math.sin(LK.ticks * 0.05 + self._floatPhase) * 8; var floatX = Math.cos(LK.ticks * 0.033 + self._floatPhaseX) * 4; self.x = self._floatBaseX + floatX; self.y = self._floatBaseY + floatY; }; return self; }); var EnemyBullet = Container.expand(function () { var self = Container.call(this); var bulletGraphics = self.attachAsset('enemyBullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 8; self.damage = 5; self.directionX = 0; self.directionY = 0; self._outOfRangeTick = null; self._fadeStartTick = null; self._expireTick = null; self._range = typeof ATTACK_RANGE !== "undefined" ? ATTACK_RANGE : 220; self._startX = null; self._startY = null; self.update = function () { if (self._startX === null || self._startY === null) { self._startX = self.x; self._startY = self.y; } self.x += self.directionX * self.speed; self.y += self.directionY * self.speed; // Out of range detection: bullet range = attack range var dx = self.x - self._startX; var dy = self.y - self._startY; var dist = Math.sqrt(dx * dx + dy * dy); var outOfRange = dist > self._range; if (outOfRange && self._outOfRangeTick === null) { self._outOfRangeTick = LK.ticks; self._fadeStartTick = self._outOfRangeTick; self._expireTick = self._fadeStartTick + 60; // 1 second fade } // Fade out logic if out of range if (self._fadeStartTick !== null && self._expireTick !== null) { var now = LK.ticks; var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick)); self.alpha = 1 - fadeProgress; if (now >= self._expireTick) { self._shouldRemove = true; } } else { self.alpha = 1; } }; return self; }); // --- FRIEND CLASS --- // Dost birlik: ship'i takip eder, saldırı sırasında %50 hasar ile saldırır var Friend = Container.expand(function () { var self = Container.call(this); // Randomly select one of 2 NPC images for friend appearance var npcImageIndex = Math.floor(Math.random() * 2) + 1; var npcAssetName = 'npc' + npcImageIndex; var friendGraphics = self.attachAsset(npcAssetName, { anchorX: 0.5, anchorY: 0.5, tint: 0x83de44, scaleX: 1.1, scaleY: 1.1 }); // --- Dynamic follow offset: will be updated every frame for movement/spacing --- self._followOffset = { x: 80 - Math.random() * 40, y: 80 + Math.random() * 40 }; self._attackTarget = null; self._attacking = false; self._attackStartTick = 0; self._lastAttackTick = 0; self._attackDelay = 60; // 1 saniye (60fps) self._attackInterval = 10; // her 10 tickte bir ateş et (ateş aralığı hızlı kalsın, istenirse 5 yapılabilir) // --- For movement/spacing --- self._moveAngle = Math.random() * Math.PI * 2; self._moveTimer = Math.floor(Math.random() * 60); self._friendIdx = -1; // will be set in update self._desiredDist = 90; // base distance from ship self._avoidDist = 70; // min distance to other friends self.update = function () { // Find my index in friends array for spacing if (typeof friends !== "undefined" && Array.isArray(friends)) { for (var fi = 0; fi < friends.length; fi++) { if (friends[fi] === self) { self._friendIdx = fi; break; } } } // --- Dynamic orbit/spacing around ship --- if (typeof ship !== "undefined" && ship) { // Orbit angle: spread friends evenly in a circle, animate with time for movement var n = 0, myIdx = 0; for (var fi = 0; fi < friends.length; fi++) { if (friends[fi]) { n++; } if (friends[fi] === self) { myIdx = fi; } } if (n === 0) { n = 1; } var baseAngle = Math.PI * 2 * (myIdx / n); var timeWobble = Math.sin((LK.ticks + myIdx * 30) / 60) * 0.5 + Math.cos((LK.ticks + myIdx * 60) / 80) * 0.3; var angle = baseAngle + timeWobble; var radius = self._desiredDist + 20 * Math.sin((LK.ticks + myIdx * 40) / 50); // Target position around ship var targetX = ship.x + Math.cos(angle) * radius; var targetY = ship.y + Math.sin(angle) * radius; // --- Avoid overlapping with other friends --- if (typeof friends !== "undefined" && Array.isArray(friends)) { for (var fi = 0; fi < friends.length; fi++) { var f2 = friends[fi]; if (f2 && f2 !== self) { var dx = self.x - f2.x; var dy = self.y - f2.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < self._avoidDist && dist > 0) { // Push away from other friend var push = (self._avoidDist - dist) * 0.15; targetX += dx / dist * push; targetY += dy / dist * push; } } } } // --- Smoothly move toward target position --- var dx = targetX - self.x; var dy = targetY - self.y; var dist = Math.sqrt(dx * dx + dy * dy); var moveSpeed = 5 + Math.sin((LK.ticks + myIdx * 20) / 40) * 1.5; if (dist > 2) { self.x += dx / dist * Math.min(moveSpeed, dist); self.y += dy / dist * Math.min(moveSpeed, dist); } } // --- Friend ship rotation: rotate to match player ship direction --- if (typeof ship !== "undefined" && ship && typeof ship.rotation === "number") { // Smoothly rotate friend ship to match player ship rotation var targetRotation = ship.rotation; var rotDiff = targetRotation - self.rotation; // Normalize angle difference to [-π, π] while (rotDiff > Math.PI) { rotDiff -= Math.PI * 2; } while (rotDiff < -Math.PI) { rotDiff += Math.PI * 2; } // Apply smooth rotation with lerp factor var rotLerp = 0.1; // Adjust for rotation smoothness self.rotation += rotDiff * rotLerp; } // --- Saldırı kontrolü --- if (self._attacking && self._attackTarget && typeof self._attackTarget.x === "number" && typeof self._attackTarget.y === "number") { // 2 saniye gecikmeden sonra saldır if (LK.ticks - self._attackStartTick >= self._attackDelay) { // Hedef hala hayatta mı? if (self._attackTarget.health > 0) { // Her attackInterval tickte bir ateş et if (LK.ticks - self._lastAttackTick > self._attackInterval) { var bullet = new Bullet(); bullet.x = self.x; bullet.y = self.y; var angle = Math.atan2(self._attackTarget.y - self.y, self._attackTarget.x - self.x); bullet.directionX = Math.cos(angle); bullet.directionY = Math.sin(angle); bullet.rotation = angle + Math.PI / 2; bullet.damage = Math.round((ship.damage || 10) * 0.5); bullets.push(bullet); game.addChild(bullet); self._lastAttackTick = LK.ticks; // Calculate distance from ship to friend bullet spawn point and adjust volume var dx = bullet.x - ship.x; var dy = bullet.y - ship.y; var distanceFromShip = Math.sqrt(dx * dx + dy * dy); // Calculate volume based on distance (closer = louder, farther = quieter) // Max distance for sound falloff is 500 pixels var maxSoundDistance = 500; var volumeMultiplier = Math.max(0.1, Math.min(1.0, 1.0 - distanceFromShip / maxSoundDistance)); // Get the sound and play with adjusted volume var shootSound = LK.getSound('shoot'); var originalVolume = shootSound.volume || 0.3; // fallback to default volume shootSound.volume = originalVolume * volumeMultiplier; shootSound.play(); // Restore original volume for next play shootSound.volume = originalVolume; } } else { // Hedef öldü, saldırıyı bırak self._attacking = false; self._attackTarget = null; } } } }; // Saldırı başlat self.startAttack = function (target) { self._attackTarget = target; self._attacking = true; self._attackStartTick = LK.ticks; self._lastAttackTick = LK.ticks; }; // Saldırı bırak self.stopAttack = function () { self._attacking = false; self._attackTarget = null; }; return self; }); // GrupKorsan (Group Pirate) class: 2-4 pirates moving and attacking together, with reward label under each var GrupKorsan = Container.expand(function () { var self = Container.call(this); // Use randomly selected pirate asset for each group member var pirateImageIndex = Math.floor(Math.random() * 20) + 1; var pirateAssetName = 'pirate' + pirateImageIndex; var pirateGraphics = self.attachAsset(pirateAssetName, { anchorX: 0.5, anchorY: 0.5 }); // Group member index and group size (set externally) self.groupIndex = 0; self.groupSize = 2; // Level and stats (set externally) self.level = 1; self.health = 30; self.speed = 2; self.fireRate = 40; self.lastFire = 0; self.damage = 10; self.defense = 5; self.loot = 200; self._isGrupKorsan = true; // --- Enemy Text --- self._enemyText = new Text2("Enemy", { size: 18, fill: 0xff4444, align: "center" }); self._enemyText.anchor.set(0.5, 0); self._enemyText.x = 0; self._enemyText.y = -50; // Above the pirate sprite self.addChild(self._enemyText); // --- Level Text --- self._levelText = new Text2("Lv." + self.level, { size: 22, fill: 0xcccccc, align: "center" }); self._levelText.anchor.set(0.5, 0); self._levelText.x = 0; self._levelText.y = pirateGraphics.height / 2 + 4; self.addChild(self._levelText); // Reward label and coin value text removed for GrupKorsan // --- Health bar --- self._healthBar = new HealthBar(); self._healthBar.set({ maxValue: function maxValue() { return self.health > 0 ? self.health : 0; }, getValueFn: function getValueFn() { return self.health > 0 ? self.health : 0; }, getMaxFn: function getMaxFn() { return 30 + (self.level - 1) * 10; }, width: 60, height: 10 }); self._healthBar.y = -60; self.addChild(self._healthBar); // --- Group movement: all members follow the same target/angle, set externally --- self._groupTarget = null; self._groupAngle = 0; self._groupMoveSpeed = 2; // --- Damage handler --- self.takeDamage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xffffff, 100); }; // --- Update --- self.update = function () { // Update enemy text position in case of movement if (self._enemyText) { self._enemyText.x = 0; self._enemyText.y = -50; } // Update level/reward text positions if (self._levelText) { self._levelText.x = 0; self._levelText.y = pirateGraphics.height / 2 + 4; self._levelText.setText("Lv." + self.level); } // Reward label and coin value update removed for GrupKorsan // Health bar update if (self._healthBar && self._healthBar.update) { self._healthBar.update(); } // Group movement: follow group target/angle if set if (self._groupTarget && typeof self._groupTarget.x === "number" && typeof self._groupTarget.y === "number") { var dx = self._groupTarget.x - self.x; var dy = self._groupTarget.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { self.x += dx / dist * self._groupMoveSpeed; self.y += dy / dist * self._groupMoveSpeed; self._groupAngle = Math.atan2(dy, dx); self.rotation = self._groupAngle + Math.PI / 2; } } else if (typeof self._groupAngle === "number") { // Move in group direction if no target self.x += Math.cos(self._groupAngle) * self._groupMoveSpeed; self.y += Math.sin(self._groupAngle) * self._groupMoveSpeed; self.rotation = self._groupAngle + Math.PI / 2; } }; return self; }); // HealthBar class for displaying health above entities var HealthBar = Container.expand(function () { var self = Container.call(this); // width, height, color, maxValue, getValueFn self._width = 60; self._height = 10; self._color = 0x00ff00; self._maxValue = 100; self._getValueFn = null; self._getMaxFn = null; self._bg = self.attachAsset('resource', { anchorX: 0.5, anchorY: 0.5, scaleX: self._width / 40, scaleY: self._height / 40, tint: 0x222222 }); self._bar = self.attachAsset('resource', { anchorX: 0.5, anchorY: 0.5, scaleX: (self._width - 4) / 40, scaleY: (self._height - 4) / 40, tint: self._color }); self._bar.x = 0; self._bar.y = 0; self.set = function (opts) { self._maxValue = opts.maxValue || 100; self._getValueFn = opts.getValueFn; self._getMaxFn = opts.getMaxFn; self._color = opts.color || 0x00ff00; self._bar.tint = self._color; if (opts.width) { self._width = opts.width; self._bg.scaleX = self._width / 40; self._bar.scaleX = (self._width - 4) / 40; } if (opts.height) { self._height = opts.height; self._bg.scaleY = self._height / 40; self._bar.scaleY = (self._height - 4) / 40; } }; self.update = function () { var value = self._getValueFn ? self._getValueFn() : self._maxValue; var maxValue = self._getMaxFn ? self._getMaxFn() : self._maxValue; var ratio = Math.max(0, Math.min(1, value / maxValue)); // Color: green > yellow > red if (ratio > 0.6) { self._bar.tint = 0x00ff00; } else if (ratio > 0.3) { self._bar.tint = 0xffff00; } else { self._bar.tint = 0xff0000; } self._bar.scaleX = (self._width - 4) * ratio / 40; }; return self; }); // --- MECHANIC CLASS --- // Mechanic: Automatically repairs ship when health is low var Mechanic = Container.expand(function () { var self = Container.call(this); // Randomly select one of 2 NPC images for mechanic appearance var npcImageIndex = Math.floor(Math.random() * 2) + 1; var npcAssetName = 'npc' + npcImageIndex; var mechanicGraphics = self.attachAsset(npcAssetName, { anchorX: 0.5, anchorY: 0.5, tint: 0xff6600, // Orange tint for mechanic scaleX: 1.1, scaleY: 1.1 }); self._followOffset = { x: -120 + Math.random() * 40, y: -80 + Math.random() * 40 }; self._repairDelay = 180; // 3 seconds between repairs self._lastRepairTick = 0; self.update = function () { // Float animation like Dron if (typeof self._floatBaseY === "undefined") { self._floatBaseY = self.y; } if (typeof self._floatBaseX === "undefined") { self._floatBaseX = self.x; } if (typeof self._floatPhase === "undefined") { self._floatPhase = Math.random() * Math.PI * 2; } if (typeof self._floatPhaseX === "undefined") { self._floatPhaseX = Math.random() * Math.PI * 2; } // Follow ship if (typeof ship !== "undefined" && ship) { var targetX = ship.x + self._followOffset.x; var targetY = ship.y + self._followOffset.y; var dx = targetX - self.x; var dy = targetY - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { self.x += dx / dist * 6; self.y += dy / dist * 6; } self._floatBaseX = self.x; self._floatBaseY = self.y; } // Auto repair ship when health is below 50% if (ship && ship.health < ship.maxHealth * 0.5 && LK.ticks - self._lastRepairTick > self._repairDelay) { var repairAmount = Math.round(ship.maxHealth * 0.1); // Repair 10% of max health ship.health = Math.min(ship.health + repairAmount, ship.maxHealth); self._lastRepairTick = LK.ticks; // Visual effect LK.effects.flashObject(ship, 0x00ff99, 300); // Floating label var label = new Text2("Mechanic Repair: +" + repairAmount + " HP", { size: 32, fill: 0x00ff99, align: "center" }); label.anchor.set(0.5, 0.5); label.x = ship.x; label.y = ship.y - 100; if (typeof game !== "undefined" && game.addChild) { game.addChild(label); } LK.setTimeout(function () { if (label.parent) { label.parent.removeChild(label); } }, 1200); updateUI && updateUI(); } // Float animation var floatY = Math.sin(LK.ticks * 0.04 + self._floatPhase) * 6; var floatX = Math.cos(LK.ticks * 0.03 + self._floatPhaseX) * 3; self.x = self._floatBaseX + floatX; self.y = self._floatBaseY + floatY; }; return self; }); // Helper: open Crew panel var NPC = Container.expand(function () { var self = Container.call(this); // Randomly select one of 2 NPC images var npcImageIndex = Math.floor(Math.random() * 2) + 1; var npcAssetName = 'npc' + npcImageIndex; var npcGraphics = self.attachAsset(npcAssetName, { anchorX: 0.5, anchorY: 0.5 }); self.isAI = true; // AI-driven self.baseY = 0; self.moveSpeed = 1 + Math.random() * 2; self.moveAngle = Math.random() * Math.PI * 2; self.wanderTimer = 0; self.lastAIAction = 0; self.targetX = null; self.targetY = null; self.rpgName = "NPC-" + Math.floor(Math.random() * 10000); // --- NPC Level System --- // Level range: wave 1 => 1-3, wave 2 => 3-5, wave 3 => 5-7, etc. var minLevel = 1 + (typeof waveNumber !== "undefined" ? waveNumber : 0) * 2; var maxLevel = minLevel + 2; self.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1)); self.health = 100 + (self.level - 1) * 10; // --- Level Text --- self._levelText = new Text2("Lv." + self.level, { size: 22, fill: 0xcccccc, align: "center" }); self._levelText.anchor.set(0.5, 0); self._levelText.x = 0; self._levelText.y = 40; // Under the NPC sprite self.addChild(self._levelText); // Only floating AI logic, no quests/events/trade // Add dummy interact method to prevent TypeError self.interact = function () { // No interaction for basic NPCs, return empty string return ""; }; self.update = function () { // Update level text position in case of movement if (self._levelText) { self._levelText.x = 0; self._levelText.y = npcGraphics.height / 2 + 4; self._levelText.setText("Lv." + self.level); } // Pause logic: If pauseUntil is set and not expired, skip movement if (self.pauseUntil && LK.ticks < self.pauseUntil) { // Still paused, only float animation self.y += Math.sin(LK.ticks * 0.05) * 0.5; return; } // AI: Sometimes move toward a random point, sometimes wander self.wanderTimer++; if (self.wanderTimer > 120 + Math.random() * 120) { if (Math.random() < 0.5) { // Pick a random point to move toward (simulate AI goal seeking) self.targetX = self.x + (Math.random() - 0.5) * 800; self.targetY = self.y + (Math.random() - 0.5) * 800; } else { self.targetX = null; self.targetY = null; } self.moveAngle = Math.random() * Math.PI * 2; self.wanderTimer = 0; } // Move toward target if set, else wander if (self.targetX !== null && self.targetY !== null) { var dx = self.targetX - self.x; var dy = self.targetY - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { self.x += dx / dist * self.moveSpeed; self.y += dy / dist * self.moveSpeed; self.moveAngle = Math.atan2(dy, dx); self._targetRotation = self.moveAngle + Math.PI / 2; } else { self.targetX = null; self.targetY = null; } } else { // Wander randomly with smooth gliding motion (no sharp turns) if (typeof self._wanderVX === "undefined") { self._wanderVX = Math.cos(self.moveAngle) * self.moveSpeed; } if (typeof self._wanderVY === "undefined") { self._wanderVY = Math.sin(self.moveAngle) * self.moveSpeed; } // Target velocity for this wander direction var targetVX = Math.cos(self.moveAngle) * self.moveSpeed; var targetVY = Math.sin(self.moveAngle) * self.moveSpeed; // Smoothly interpolate velocity for gliding effect self._wanderVX += (targetVX - self._wanderVX) * 0.03; self._wanderVY += (targetVY - self._wanderVY) * 0.03; self.x += self._wanderVX; self.y += self._wanderVY; self._targetRotation = Math.atan2(self._wanderVY, self._wanderVX) + Math.PI / 2; } // Smoothly rotate toward target rotation if (typeof self._targetRotation === "number") { var diff = self._targetRotation - self.rotation; while (diff > Math.PI) { diff -= Math.PI * 2; } while (diff < -Math.PI) { diff += Math.PI * 2; } var lerp = 0.05; self.rotation += diff * lerp; } // Float animation on top of movement self.y += Math.sin(LK.ticks * 0.05) * 0.5; }; return self; }); var Pirate = Container.expand(function () { var self = Container.call(this); // Randomly select one of 20 pirate images var pirateImageIndex = Math.floor(Math.random() * 20) + 1; var pirateAssetName = 'pirate' + pirateImageIndex; var pirateGraphics = self.attachAsset(pirateAssetName, { anchorX: 0.5, anchorY: 0.5 }); // --- Pirate Level & Scaling --- self.level = typeof waveNumber !== "undefined" ? 1 + waveNumber : 1; self.health = 30 + (self.level - 1) * 10; self.speed = 2 + Math.floor((self.level - 1) / 10) * 0.2; // Slight speed up every 10 waves self.fireRate = 60 - Math.min((self.level - 1) * 2, 30); // Faster fire at higher levels self.lastFire = 0; self.loot = Math.floor(Math.random() * 30) + 10 + (self.level - 1) * 2; self.moveAngle = Math.random() * Math.PI * 2; self.patrolTimer = 0; self.aggroRange = 600; // Korsan saldırı ve savunma gücü seviyeye göre orantılı artar self.damage = 5 + Math.floor((self.level - 1) * 1.5); // Saldırı gücü daha hızlı artar self.defense = 2 + Math.floor((self.level - 1) * 1.2); // Savunma gücü de seviyeye göre artar // --- Enemy Text --- self._enemyText = new Text2("Enemy", { size: 18, fill: 0xff4444, align: "center" }); self._enemyText.anchor.set(0.5, 0); self._enemyText.x = 0; self._enemyText.y = -50; // Above the pirate sprite self.addChild(self._enemyText); // --- Level Text --- self._levelText = new Text2("Lv." + self.level, { size: 22, fill: 0xcccccc, align: "center" }); self._levelText.anchor.set(0.5, 0); self._levelText.x = 0; self._levelText.y = 40; // Under the pirate sprite self.addChild(self._levelText); // Katil korsan için "Ödül:" yazısı ekle self._rewardText = null; if (self.rpgName === "Katil" || self._isKatil) { self._rewardText = new Text2("Reward:", { size: 22, fill: 0xffcc00, align: "center" }); self._rewardText.anchor.set(0.5, 0); self._rewardText.x = 0; self._rewardText.y = self._levelText.y + 28; // Seviye yazısının hemen altında, aynı büyüklükte self.addChild(self._rewardText); } // Level text only, defense text removed self.takeDamage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xffffff, 100); }; self.update = function () { // Update enemy text position in case of movement if (self._enemyText) { self._enemyText.x = 0; self._enemyText.y = -50; } // Update level text position in case of movement if (self._levelText) { self._levelText.x = 0; self._levelText.y = pirateGraphics.height / 2 + 4; self._levelText.setText("Lv." + self.level); } // Katil korsan için ödül yazısı pozisyonunu güncel tut if (self.rpgName === "Katil" || self._isKatil) { if (!self._rewardText) { self._rewardText = new Text2("Ödül:", { size: 22, fill: 0xffcc00, align: "center" }); self._rewardText.anchor.set(0.5, 0); self.addChild(self._rewardText); } self._rewardText.x = 0; self._rewardText.y = self._levelText.y + 28; self._rewardText.visible = true; // Katil korsan için coin miktarı yazısı ekle/güncelle var rewardCoinValue = 0; // Katil korsan için coin değeri, öldüğünde verilecek coin miktarı ile aynı olmalı // Oyun kodunda coin.value = coin.value * (1 + Math.floor(waveNumber / 3)) * 10; // Burada coin.value rastgele 5-14 arası, ama göstermek için ortalama değer (ör: 10) kullanılabilir // Alternatif: self.level ve waveNumber'a göre hesapla var baseCoin = 10; // ortalama coin var waveNum = typeof waveNumber !== "undefined" ? waveNumber : 0; rewardCoinValue = baseCoin * (1 + Math.floor(waveNum / 3)) * 10; if (!self._rewardCoinText) { self._rewardCoinText = new Text2(rewardCoinValue + " coin", { size: 22, fill: 0xffcc00, align: "center" }); self._rewardCoinText.anchor.set(0.5, 0); self.addChild(self._rewardCoinText); } self._rewardCoinText.x = 0; self._rewardCoinText.y = self._rewardText.y + 28; self._rewardCoinText.setText(rewardCoinValue + " coin"); self._rewardCoinText.visible = true; } else if (self._rewardText) { self._rewardText.visible = false; if (self._rewardCoinText) { self._rewardCoinText.visible = false; } } // Defense text removed // Movement AI self.patrolTimer++; if (self.patrolTimer > 180) { self.moveAngle = Math.random() * Math.PI * 2; self.patrolTimer = 0; } // Patrol movement if (self._squadTargetNPC && self._squadTargetNPC.questType === 'defend' && self._squadTargetNPC.questActive) { // Korsan, NPC'yi hedefle var target = self._squadTargetNPC; // Eğer defend quest'te yardım kabul edildiyse, oyuncuya da saldırabilir if (target._defendHelpAccepted === true && Math.random() < 0.5) { // %50 ihtimalle oyuncuya saldır target = Math.random() < 0.5 ? self._squadTargetNPC : ship; } var dx = target.x - self.x; var dy = target.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 10) { self.x += dx / dist * self.speed; self.y += dy / dist * self.speed; self.moveAngle = Math.atan2(dy, dx); self._targetRotation = self.moveAngle + Math.PI / 2; } else { // Hedefe çok yaklaştıysa sadece dön var angle = Math.atan2(target.y - self.y, target.x - self.x); self._targetRotation = angle + Math.PI / 2; } // Ateş et if (LK.ticks - self.lastFire > self.fireRate) { var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y; var angle = Math.atan2(target.y - self.y, target.x - self.x); bullet.directionX = Math.cos(angle); bullet.directionY = Math.sin(angle); bullet.rotation = angle + Math.PI / 2; enemyBullets.push(bullet); if (game && game.addChild) { game.addChild(bullet); } self.lastFire = LK.ticks; // Keep pirate rotated to attack direction after firing self._targetRotation = angle + Math.PI / 2; } } else { // Wander randomly with smooth gliding motion (no sharp turns) if (typeof self._wanderVX === "undefined") { self._wanderVX = Math.cos(self.moveAngle) * self.speed * 0.5; } if (typeof self._wanderVY === "undefined") { self._wanderVY = Math.sin(self.moveAngle) * self.speed * 0.5; } // Target velocity for this wander direction var targetVX = Math.cos(self.moveAngle) * self.speed * 0.5; var targetVY = Math.sin(self.moveAngle) * self.speed * 0.5; // Smoothly interpolate velocity for gliding effect self._wanderVX += (targetVX - self._wanderVX) * 0.03; self._wanderVY += (targetVY - self._wanderVY) * 0.03; self.x += self._wanderVX; self.y += self._wanderVY; self._targetRotation = Math.atan2(self._wanderVY, self._wanderVX) + Math.PI / 2; } // Smoothly rotate toward target rotation if (typeof self._targetRotation === "number") { var diff = self._targetRotation - self.rotation; while (diff > Math.PI) { diff -= Math.PI * 2; } while (diff < -Math.PI) { diff += Math.PI * 2; } var lerp = 0.05; self.rotation += diff * lerp; } }; return self; }); // Pirate Base Class var PirateBase = Container.expand(function () { var self = Container.call(this); // Use randomly selected pirate asset for base appearance var pirateImageIndex = Math.floor(Math.random() * 20) + 1; var pirateAssetName = 'pirate' + pirateImageIndex; var baseGraphics = self.attachAsset(pirateAssetName, { anchorX: 0.5, anchorY: 0.5, tint: 0x990000, scaleX: 3, scaleY: 3 }); self.health = 300; self.maxHealth = 300; self.attackPower = 10; self.defensePower = 10; self.spawnRadius = 350; self.pirates = []; self.lastPirateSpawn = 0; self.maxPirates = 10; self._destroyed = false; // Health bar self._healthBar = new HealthBar(); self._healthBar.set({ maxValue: function maxValue() { return self.maxHealth; }, getValueFn: function getValueFn() { return self.health; }, getMaxFn: function getMaxFn() { return self.maxHealth; }, width: 120, height: 16, color: 0xff0000 }); self._healthBar.y = -120; self.addChild(self._healthBar); self.takeDamage = function (amount) { self.health -= amount; LK.effects.flashObject(self, 0xff0000, 120); if (self.health <= 0 && !self._destroyed) { self.health = 0; self._destroyed = true; // --- BAŞARIM: Korsan üssü yok etme --- if (typeof checkPirateBaseAchievements === "function") { checkPirateBaseAchievements(); } // Remove all pirates around base for (var i = 0; i < self.pirates.length; i++) { if (self.pirates[i] && !self.pirates[i]._destroyed) { self.pirates[i].destroy(); } } // Explosion effect createExplosion(self.x, self.y); // Floating label var label = new Text2("Pirate Base Destroyed!", { size: 48, fill: 0xffcc00, align: "center" }); label.anchor.set(0.5, 0.5); label.x = self.x; label.y = self.y - 180; if (game && game.addChild) { game.addChild(label); } self._destroyed = true; self.destroy(); } }; self.update = function () { // Health bar update if (self._healthBar && self._healthBar.update) { self._healthBar.update(); } // Remove dead pirates from list for (var i = self.pirates.length - 1; i >= 0; i--) { if (!self.pirates[i] || self.pirates[i]._destroyed) { self.pirates.splice(i, 1); } } // Spawn pirates if less than max if (!self._destroyed && self.pirates.length < self.maxPirates && LK.ticks - self.lastPirateSpawn > 90) { var pirate = new Pirate(); var angle = Math.random() * Math.PI * 2; pirate.x = self.x + Math.cos(angle) * (self.spawnRadius + Math.random() * 60); pirate.y = self.y + Math.sin(angle) * (self.spawnRadius + Math.random() * 60); pirate.health = 30 + self.attackPower * 2; pirate.damage = 5 + self.attackPower; pirate.rpgName = "BaseKorsan-" + Math.floor(Math.random() * 10000); pirate._baseRef = self; // Add health bar to pirate pirate._healthBar = new HealthBar(); pirate._healthBar.set({ maxValue: function maxValue() { return pirate.health > 0 ? pirate.health : 0; }, getValueFn: function getValueFn() { return pirate.health > 0 ? pirate.health : 0; }, getMaxFn: function getMaxFn() { return 30 + self.attackPower * 2; }, width: 60, height: 10 }); pirate._healthBar.y = -60; pirate.addChild(pirate._healthBar); pirates.push(pirate); self.pirates.push(pirate); if (game && game.addChild) { game.addChild(pirate); } self.lastPirateSpawn = LK.ticks; } // Base defense: shoot at player if close var distToShip = Math.sqrt(Math.pow(ship.x - self.x, 2) + Math.pow(ship.y - self.y, 2)); if (!self._destroyed && distToShip < self.spawnRadius + 80 && LK.ticks % 30 === 0) { var bullet = new EnemyBullet(); bullet.x = self.x; bullet.y = self.y; var angle = Math.atan2(ship.y - self.y, ship.x - self.x); bullet.directionX = Math.cos(angle); bullet.directionY = Math.sin(angle); bullet.rotation = angle + Math.PI / 2; bullet.damage = 10 + self.attackPower; enemyBullets.push(bullet); if (game && game.addChild) { game.addChild(bullet); } } }; return self; }); var Resource = Container.expand(function () { var self = Container.call(this); var resourceGraphics = self.attachAsset('resource', { anchorX: 0.5, anchorY: 0.5 }); // 10 farklı hammadde tipi var resourceTypes = ['metal', 'energy', 'crystal', 'gas', 'ice', 'uranium', 'silicon', 'carbon', 'plasma', 'antimatter']; // --- Değerli hammaddelerin daha az çıkmasını sağlamak için ağırlıklı seçim --- // Market kaldırıldı, sabit fiyatlar kullanılıyor var prices = [100, 80, 120, 90, 70, 200, 110, 60, 150, 300]; // Her hammaddenin ağırlığı: 1 / (fiyat^0.7) ile ters orantılı (daha değerli = daha az) // 0.7 üssüyle daha yumuşak bir dağılım, istenirse 1.0 yapılabilir var weights = []; var totalWeight = 0; for (var i = 0; i < prices.length; i++) { var w = 1 / Math.pow(prices[i], 0.7); weights.push(w); totalWeight += w; } // Rastgele ağırlıklı seçim var r = Math.random() * totalWeight; var acc = 0, idx = 0; for (var i = 0; i < weights.length; i++) { acc += weights[i]; if (r <= acc) { idx = i; break; } } self.type = resourceTypes[idx]; // Miktar: Değerli hammaddelerde miktar da az olsun // maxAmount = 1 + 4 * (ucuzluk ağırlığı) var maxAmount = Math.max(1, Math.round(1 + 4 * (weights[idx] / totalWeight * prices.length))); self.amount = Math.floor(Math.random() * maxAmount) + 1; // --- Timed fade-out system --- self._spawnTick = LK.ticks; self._fadeStartTick = self._spawnTick + 180; // 3 seconds visible self._expireTick = self._fadeStartTick + 120; // 2 seconds fade (total 5s) self.update = function () { self.rotation += 0.03; // Timed fade-out logic if (typeof self._expireTick === "number" && typeof self._fadeStartTick === "number") { var now = LK.ticks; if (now >= self._fadeStartTick) { // Start fading out var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick)); self.alpha = 1 - fadeProgress; if (now >= self._expireTick) { // Mark for removal (actual removal in game.update) self._shouldRemove = true; } } else { self.alpha = 1; } } }; return self; }); var Ship = Container.expand(function () { var self = Container.call(this); var shipGraphics = self.attachAsset('ship', { anchorX: 0.5, anchorY: 0.5 }); self.health = 100; self.maxHealth = 100; self.shield = 50; self.maxShield = 50; self.shieldRegenDelay = 0; self.speed = 5; self.fireRate = 10; self.damage = 10; self.lastFire = 0; self.takeDamage = function (amount) { // Shield absorbs damage first if (self.shield > 0) { var shieldDamage = Math.min(amount, self.shield); self.shield -= shieldDamage; amount -= shieldDamage; LK.effects.flashObject(self, 0x0088ff, 100); self.shieldRegenDelay = 180; // 3 seconds delay before shield regen } if (amount > 0) { self.health -= amount; LK.effects.flashObject(self, 0xff0000, 200); } if (self.health <= 0) { // --- Clear all world entities on respawn --- var _destroyAll = function _destroyAll(arr) { if (!arr) { return; } for (var i = arr.length - 1; i >= 0; i--) { if (arr[i] && typeof arr[i].destroy === "function") { arr[i].destroy(); } arr.splice(i, 1); } }; // Remove all pirates, npcs, asteroids, coins, resources, enemyBullets, bullets, stars, explosions, upgradeStations, pirateBases self.health = 0; // --- Respawn logic instead of Game Over --- // 1. Lose 50% coins and 50% of each resource var lostCoins = Math.floor(playerCoins * 0.5); playerCoins = Math.floor(playerCoins * 0.5); var lostResources = []; for (var i = 0; i < playerResources.length; i++) { var lost = Math.floor(playerResources[i] * 0.5); lostResources.push(lost); playerResources[i] = Math.floor(playerResources[i] * 0.5); } // 2. Respawn ship at a random far location, restore health/shield var respawnRadius = 1200 + Math.random() * 1800; // 1200-3000 px away from last position var respawnAngle = Math.random() * Math.PI * 2; self.x = self.x + Math.cos(respawnAngle) * respawnRadius; self.y = self.y + Math.sin(respawnAngle) * respawnRadius; self.health = self.maxHealth; self.shield = self.maxShield; self.shieldRegenDelay = 180; _destroyAll(pirates); _destroyAll(npcs); _destroyAll(asteroids); _destroyAll(coins); _destroyAll(resources); _destroyAll(enemyBullets); _destroyAll(bullets); _destroyAll(stars); _destroyAll(explosions); _destroyAll(upgradeStations); _destroyAll(pirateBases); // Also clear any katil korsan respawn timer if (typeof window._katilPirateRespawnTimer !== "undefined" && window._katilPirateRespawnTimer) { LK.clearTimeout(window._katilPirateRespawnTimer); window._katilPirateRespawnTimer = null; } window._katilPirateSpawned = false; // Katil korsan öldü ve dünya sıfırlandıktan sonra tekrar spawn et if (typeof window._katilPirateRespawnTimer !== "undefined" && window._katilPirateRespawnTimer) { LK.clearTimeout(window._katilPirateRespawnTimer); window._katilPirateRespawnTimer = null; } window._katilPirateRespawnTimer = LK.setTimeout(function () { if (!window._katilPirateSpawned) { var pirate = new Pirate(); // Katil korsan statlarını tekrar ayarla var minLevel = 1 + waveNumber * 2; pirate.level = minLevel + 10; pirate.health = (30 + (pirate.level - 1) * 10) * 10; pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10; pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10; pirate.rpgName = "Katil"; pirate._isKatil = true; pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3)); pirate._attackRange = Math.floor(600 * 0.9); // Katil korsan için %10 daha kısa menzil window._katilPirateSpawned = true; // Diğer korsanlar gibi kenar spawn algoritmasıyla doğsun var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y - 1400; break; case 1: pirate.x = ship.x + 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; case 2: pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y + 1400; break; case 3: pirate.x = ship.x - 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; } // Add health bar to katil pirate pirate._healthBar = new HealthBar(); pirate._healthBar.set({ maxValue: function maxValue() { return pirate.health > 0 ? pirate.health : 0; }, getValueFn: function getValueFn() { return pirate.health > 0 ? pirate.health : 0; }, getMaxFn: function getMaxFn() { return 30 + waveNumber * 10; }, width: 60, height: 10 }); pirate._healthBar.y = -60; pirate.addChild(pirate._healthBar); pirates.push(pirate); if (typeof game !== "undefined" && game.addChild) { game.addChild(pirate); } } window._katilPirateRespawnTimer = null; }, 6000); // Optionally reset wave progress enemiesKilled = 0; // Respawn initial world elements for (var i = 0; i < 135; i++) { spawnStar(true); } for (var i = 0; i < 6; i++) { spawnNPC(); } for (var i = 0; i < 6; i++) { spawnPirate(); } for (var i = 0; i < 2; i++) { spawnAsteroid(); } // Track respawn tick for input blocking window._lastRespawnTick = LK.ticks; // 3. Show floating label for penalty var lostText = "-%50 coin & hammadde kaybı! Yeniden doğdun."; var label = new Text2(lostText, { size: 48, fill: 0xff4444, align: "center" }); label.anchor.set(0.5, 0.5); label.x = 1024; label.y = 1366 - 200; if (typeof game !== "undefined" && game.addChild) { game.addChild(label); } LK.setTimeout(function () { if (label.parent) { label.parent.removeChild(label); } }, 1800); // 4. Update UI and save progress if (typeof updateUI === "function") { updateUI(); } if (typeof saveProgress === "function") { saveProgress(); } } }; self.update = function () { // Shield regeneration if (self.shieldRegenDelay > 0) { self.shieldRegenDelay--; } else if (self.shield < self.maxShield) { self.shield = Math.min(self.shield + 0.1, self.maxShield); } // --- SHIP ROTATION: Keep ship rotated to last movement or attack direction, but turn smoothly --- // If joystick is being dragged, rotate to movement direction if (typeof isDragging !== "undefined" && isDragging && typeof joystickHandle !== "undefined" && typeof joystickBase !== "undefined") { var dx = joystickHandle.x - joystickBase.x; var dy = joystickHandle.y - joystickBase.y; var distance = Math.sqrt(dx * dx + dy * dy); if (distance > 10) { self._lastMoveAngle = Math.atan2(dy, dx); self._targetRotation = self._lastMoveAngle + Math.PI / 2; } } else if (typeof game !== "undefined" && typeof game._lastShipAttackAngle !== "undefined") { // If not moving, but last attack angle exists, keep ship rotated to last attack direction self._targetRotation = game._lastShipAttackAngle + Math.PI / 2; } // Smoothly rotate toward target rotation if (typeof self._targetRotation === "number") { // Calculate shortest angle difference var diff = self._targetRotation - self.rotation; while (diff > Math.PI) { diff -= Math.PI * 2; } while (diff < -Math.PI) { diff += Math.PI * 2; } // Lerp factor: lower = softer (0.05 = very soft, 0.2 = sharp) var lerp = 0.05; self.rotation += diff * lerp; } }; return self; }); var Star = Container.expand(function () { var self = Container.call(this); var starGraphics = self.attachAsset('star', { anchorX: 0.5, anchorY: 0.5 }); self.speed = Math.random() * 2 + 0.5; starGraphics.alpha = Math.random() * 0.8 + 0.2; self.vx = (Math.random() - 0.5) * 0.5; self.vy = (Math.random() - 0.5) * 0.5; self.update = function () { // Slow floating movement self.x += self.vx; self.y += self.vy; }; return self; }); // Trade Station Class var TradeStation = Container.expand(function () { var self = Container.call(this); // Randomly select one of 2 NPC images for trade station appearance var npcImageIndex = Math.floor(Math.random() * 2) + 1; var npcAssetName = 'npc' + npcImageIndex; var tradeGraphics = self.attachAsset(npcAssetName, { anchorX: 0.5, anchorY: 0.5, tint: 0x00ff99, scaleX: 2, scaleY: 2 }); self.type = 'trade'; self.rotationSpeed = 0.01; self.offerType = Math.random() < 0.5 ? 'buy' : 'sell'; // buy: station buys from player, sell: station sells to player self.resourceType = Math.random() < 0.5 ? 'metal' : 'energy'; self.amount = Math.floor(Math.random() * 10) + 5; self.price = Math.floor(Math.random() * 30) + 10; self.specialOffer = Math.random() < 0.2; // 20% chance for special upgrade offer self.specialUpgrade = null; if (self.specialOffer) { var upgrades = [{ name: 'maxHealth', label: 'Max Health +30', cost: 200 }, { name: 'maxShield', label: 'Max Shield +20', cost: 180 }, { name: 'damage', label: 'Damage +5', cost: 150 }, { name: 'speed', label: 'Speed +1', cost: 120 }]; self.specialUpgrade = upgrades[Math.floor(Math.random() * upgrades.length)]; } self.update = function () { self.rotation += self.rotationSpeed; // Pulse effect var scale = 2 + Math.sin(LK.ticks * 0.05) * 0.1; tradeGraphics.scaleX = scale; tradeGraphics.scaleY = scale; }; return self; }); // Wormhole Teleporter Class var Wormhole = Container.expand(function () { var self = Container.call(this); var whGraphics = self.attachAsset('star', { anchorX: 0.5, anchorY: 0.5, scaleX: 5, scaleY: 5, tint: 0x00ffff }); self.radius = 120; self.cooldown = 0; self.update = function () { whGraphics.rotation += 0.07; // Teleport ship if close and not on cooldown var dx = ship.x - self.x; var dy = ship.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < self.radius && self.cooldown <= 0) { // Teleport ship to a random far location var tx = ship.x + (Math.random() - 0.5) * 4000; var ty = ship.y + (Math.random() - 0.5) * 4000; var dx2 = tx - ship.x; var dy2 = ty - ship.y; ship.x = tx; ship.y = ty; // Move all objects to keep camera effect for (var i = 0; i < stars.length; i++) { stars[i].x -= dx2; stars[i].y -= dy2; } for (var i = 0; i < npcs.length; i++) { npcs[i].x -= dx2; npcs[i].y -= dy2; } for (var i = 0; i < pirates.length; i++) { pirates[i].x -= dx2; pirates[i].y -= dy2; } for (var i = 0; i < coins.length; i++) { coins[i].x -= dx2; coins[i].y -= dy2; } for (var i = 0; i < resources.length; i++) { resources[i].x -= dx2; resources[i].y -= dy2; } for (var i = 0; i < bullets.length; i++) { bullets[i].x -= dx2; bullets[i].y -= dy2; } for (var i = 0; i < enemyBullets.length; i++) { enemyBullets[i].x -= dx2; enemyBullets[i].y -= dy2; } for (var i = 0; i < asteroids.length; i++) { asteroids[i].x -= dx2; asteroids[i].y -= dy2; } for (var i = 0; i < upgradeStations.length; i++) { upgradeStations[i].x -= dx2; upgradeStations[i].y -= dy2; } // boostParticles removed (no boost system) for (var i = 0; i < explosions.length; i++) { explosions[i].x -= dx2; explosions[i].y -= dy2; } for (var i = 0; i < blackHoles.length; i++) { blackHoles[i].x -= dx2; blackHoles[i].y -= dy2; } for (var i = 0; i < wormholes.length; i++) { if (wormholes[i] !== self) { wormholes[i].x -= dx2; wormholes[i].y -= dy2; } } self.cooldown = 180; LK.effects.flashScreen(0x00ffff, 500); } if (self.cooldown > 0) { self.cooldown--; } }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x000033 }); /**** * Game Code ****/ // Her friend, en yakın pirate'ı (veya asteroid) bulup saldıracak, ship gibi // --- FRIENDS: Dost birlikler NPC gibi düşmana karşı savaşa girsin --- if (typeof friends !== "undefined" && Array.isArray(friends)) { for (var i = 0; i < friends.length; i++) { var friend = friends[i]; if (!friend) { continue; } // En yakın pirate'ı bul var nearestTarget = null; var nearestDistance = Infinity; for (var j = 0; j < pirates.length; j++) { var pirate = pirates[j]; if (!pirate) { continue; } var dist = Math.sqrt(Math.pow(pirate.x - friend.x, 2) + Math.pow(pirate.y - friend.y, 2)); if (dist < nearestDistance) { nearestDistance = dist; nearestTarget = pirate; } } // Asteroidleri de hedef olarak eklemek isterseniz, aşağıdaki kodu açabilirsiniz: // for (var j = 0; j < asteroids.length; j++) { // var asteroid = asteroids[j]; // var dist = Math.sqrt(Math.pow(asteroid.x - friend.x, 2) + Math.pow(asteroid.y - friend.y, 2)); // if (dist < nearestDistance) { // nearestDistance = dist; // nearestTarget = asteroid; // } // } // Saldırı menzili ship ile aynı olsun (ATTACK_RANGE) var friendRange = typeof ATTACK_RANGE !== "undefined" ? ATTACK_RANGE : 220; if (nearestTarget && nearestDistance < friendRange) { // Eğer friend zaten bu hedefe saldırmıyorsa, saldırıyı başlat if (!friend._attacking || friend._attackTarget !== nearestTarget) { if (typeof friend.startAttack === "function") { friend.startAttack(nearestTarget); } } } else { // Hedef yoksa saldırıyı bırak if (typeof friend.stopAttack === "function") { friend.stopAttack(); } } } } var ship; var joystickBase; var joystickHandle; var isDragging = false; var bullets = []; var enemyBullets = []; var npcs = []; var pirates = []; var coins = []; var resources = []; var stars = []; var asteroids = []; var upgradeStations = []; var explosions = []; var blackHoles = []; var wormholes = []; var tradeStations = []; var pirateBases = []; var tradeText = null; // No trade text needed // Helper: spawn a pirate base at a random edge, with pirates around it function spawnPirateBase() { var base = new PirateBase(); // Spawn from edges of visible area var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: // Top base.x = ship.x + (Math.random() - 0.5) * 2000; base.y = ship.y - 1600; break; case 1: // Right base.x = ship.x + 1600; base.y = ship.y + (Math.random() - 0.5) * 2000; break; case 2: // Bottom base.x = ship.x + (Math.random() - 0.5) * 2000; base.y = ship.y + 1600; break; case 3: // Left base.x = ship.x - 1600; base.y = ship.y + (Math.random() - 0.5) * 2000; break; } ; pirateBases.push(base); game.addChild(base); // Spawn initial pirates around base for (var i = 0; i < 10; i++) { var pirate = new Pirate(); var angle = Math.PI * 2 * (i / 10); pirate.x = base.x + Math.cos(angle) * (base.spawnRadius + Math.random() * 60); pirate.y = base.y + Math.sin(angle) * (base.spawnRadius + Math.random() * 60); // Level range: wave 1 => 1-3, wave 2 => 3-5, wave 3 => 5-7, etc. var minLevel = 1 + waveNumber * 2; var maxLevel = minLevel + 2; pirate.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1)); pirate.health = 30 + (pirate.level - 1) * 10 + base.attackPower * 2; pirate.damage = 5 + Math.floor((pirate.level - 1) * 1.5) + base.attackPower; pirate.defense = 2 + Math.floor((pirate.level - 1) * 1.2) + base.defensePower; pirate.rpgName = "BaseKorsan-" + Math.floor(Math.random() * 10000); pirate._baseRef = base; // Add health bar to pirate pirate._healthBar = new HealthBar(); pirate._healthBar.set({ maxValue: function maxValue() { return pirate.health > 0 ? pirate.health : 0; }, getValueFn: function getValueFn() { return pirate.health > 0 ? pirate.health : 0; }, getMaxFn: function getMaxFn() { return 30 + base.attackPower * 2; }, width: 60, height: 10 }); pirate._healthBar.y = -60; pirate.addChild(pirate._healthBar); pirates.push(pirate); base.pirates.push(pirate); game.addChild(pirate); } } // tradeText UI removed (no tradeText in this version) var camera = { x: 0, y: 0 }; // --- Helper for multi-pirate attack quest --- // If groupCount >= 2, spawn GrupKorsan squad with reward label under each function spawnPirateSquad(centerX, centerY, count, targetNPC) { var squad = []; if (count >= 2) { // GrupKorsan: all move together, same target/angle var minLevel = 1 + waveNumber * 2 + 5; var maxLevel = minLevel + 2; var groupAngle = Math.random() * Math.PI * 2; var groupTarget = targetNPC || { x: centerX + Math.cos(groupAngle) * 400, y: centerY + Math.sin(groupAngle) * 400 }; for (var i = 0; i < count; i++) { var gpirate = new GrupKorsan(); gpirate.groupIndex = i; gpirate.groupSize = count; gpirate.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1)); // Katil Korsan'ın statları: level = minLevel+10, health = (30 + (level-1)*10)*10, damage = (5 + ...)*10, defense = (2 + ...)*10 // GrupKorsan, Katil Korsan'ın statlarının %20 daha düşüğü olacak şekilde ayarlanır var katilHealth = (30 + (gpirate.level - 1) * 10) * 10; var katilDamage = (5 + Math.floor((gpirate.level - 1) * 1.5)) * 10; var katilDefense = (2 + Math.floor((gpirate.level - 1) * 1.2)) * 10; gpirate.health = Math.round(katilHealth * 0.8); gpirate.damage = Math.round(katilDamage * 0.8); gpirate.defense = Math.round(katilDefense * 0.8); gpirate.loot = 200 + (gpirate.level - 1) * 50; // --- GrupKorsan arası mesafe %15 oranında açıldı (200px yerine 200*1.15=230px) --- // Her bir GrupKorsan'ın pozisyonu, merkezden eşit aralıklı ve %15 daha uzak olacak şekilde ayarlanır var grupDistance = 200 * 1.15; var angleOffset = Math.PI * 2 * i / count; gpirate.x = centerX + Math.cos(groupAngle + angleOffset) * grupDistance + (Math.random() - 0.5) * 60; gpirate.y = centerY + Math.sin(groupAngle + angleOffset) * grupDistance + (Math.random() - 0.5) * 60; gpirate._groupTarget = groupTarget; gpirate._groupAngle = groupAngle; gpirate._groupMoveSpeed = 2 + Math.random() * 0.5; gpirate.rpgName = "GrupKorsan-" + (i + 1) + "/" + count; gpirate._isGrupKorsan = true; pirates.push(gpirate); game.addChild(gpirate); squad.push(gpirate); } } else { // Fallback: single pirate for (var i = 0; i < count; i++) { var pirate = new Pirate(); var angle = Math.PI * 2 * (i / count); pirate.x = centerX + Math.cos(angle) * 200 + (Math.random() - 0.5) * 60; pirate.y = centerY + Math.sin(angle) * 200 + (Math.random() - 0.5) * 60; // Level range: wave 1 => 1-3, wave 2 => 3-5, wave 3 => 5-7, etc. var minLevel = 1 + waveNumber * 2; var maxLevel = minLevel + 2; pirate.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1)); pirate.health = 30 + (pirate.level - 1) * 10; pirate.damage = 5 + Math.floor((pirate.level - 1) * 1.5); pirate.defense = 2 + Math.floor((pirate.level - 1) * 1.2); pirate.rpgName = "Korsan-" + Math.floor(Math.random() * 10000); pirate._squadTargetNPC = targetNPC; pirates.push(pirate); game.addChild(pirate); squad.push(pirate); } } return squad; } // Trade station tap detection (RPG/ticaret) for (var i = 0; i < tradeStations.length; i++) { var ts = tradeStations[i]; var dist = Math.sqrt(Math.pow(ts.x - ship.x, 2) + Math.pow(ts.y - ship.y, 2)); if (dist < 180) { game._lastTradeStationTap = LK.ticks; break; } } var waveNumber = 0; var enemiesKilled = 0; var totalDistance = 0; // Player stats var playerCoins = storage.coins || 0; // 10 hammadde için oyuncu envanteri var playerResources = [storage.metal || 0, // metal storage.energy || 0, // energy storage.crystal || 0, // crystal storage.gas || 0, // gas storage.ice || 0, // ice storage.uranium || 0, // uranium storage.silicon || 0, // silicon storage.carbon || 0, // carbon storage.plasma || 0, // plasma storage.antimatter || 0 // antimatter ]; // Aliases for compatibility with old code var playerMetal = playerResources[0]; var playerEnergy = playerResources[1]; var questsCompleted = storage.questsCompleted || 0; // --- Player Level/EXP --- var playerLevel = typeof storage.playerLevel !== "undefined" ? storage.playerLevel : 1; var playerEXP = typeof storage.playerEXP !== "undefined" ? storage.playerEXP : 0; function expToNext(level) { // Simple EXP curve: next = 10 + 5*(level-1) return 10 + 5 * (level - 1); } // UI Elements // Calculate the unified font size (30% smaller than expText, then %10 büyüt) var unifiedFontSize = Math.round(38 * 0.7 * 1.1); // Add coin icon var coinIcon = LK.getAsset('coin', { anchorX: 0.5, anchorY: 0.5, x: 100, y: 35, scaleX: 1.2, scaleY: 1.2 }); LK.gui.topLeft.addChild(coinIcon); var coinText = new Text2('Coins: ' + playerCoins, { size: unifiedFontSize, fill: 0xFFFF00 }); coinText.anchor.set(0, 0); coinText.x = 120; coinText.y = 20; LK.gui.topLeft.addChild(coinText); // --- NPC ile savaş butonu --- var npcFightBtnFont = Math.round(unifiedFontSize * 1.1); window.npcFightBtn = new Text2("Fight NPCs", { size: npcFightBtnFont, fill: 0xff4444, align: "center" }); window.npcFightBtn.anchor.set(0.5, 0.5); // Place at bottom center, above the bottom edge window.npcFightBtn.x = 2048 / 2; window.npcFightBtn.y = 2732 - 300; // moved 2 lines (about 120px) higher LK.gui.bottom.addChild(window.npcFightBtn); window.npcFightMode = false; // --- UZAY İSTASYONU TESLİMAT BUTTON (above Reset, 2 rows up) --- if (!window.stationDeliveryBtn) { var deliveryBtnFont = Math.round(unifiedFontSize * 1.1); // window.stationDeliveryBtn = new Text2("", { ... }); // BUTTON IS HIDDEN, DO NOT CREATE // window.stationDeliveryBtn.anchor.set(1, 1); // window.stationDeliveryBtn.x = -40; // window.stationDeliveryBtn.y = -40 - 2 * 90; // Add image above the button, 10% higher (yukarıya %10) if (!window.stationDeliveryBtnImage) { // Use the special image for the transparent button area var imgAsset = LK.getAsset('stationDeliveryBtnAreaImg', { anchorX: 1, anchorY: 1, scaleX: 0.7, scaleY: 0.7 }); // Place image at the same x, but y is 10% higher (10% of image height above the button) // Keep the station image fixed (do not move it up) imgAsset.x = -40; imgAsset.y = -40 - 2 * 90 - imgAsset.height * 1.1; window.stationDeliveryBtnImage = imgAsset; LK.gui.bottomRight.addChild(window.stationDeliveryBtnImage); // Add a transparent button area exactly the size and position of the image for İstasyon Teslimat var stationDeliveryBtnArea = new Text2("", { size: 1, fill: 0xffffff, align: "center" }); stationDeliveryBtnArea.anchor.set(1, 1); // Place the button area exactly behind the image, matching its position and size stationDeliveryBtnArea.x = imgAsset.x; // Move the button area 1 row (imgAsset.height) above the image stationDeliveryBtnArea.y = imgAsset.y - imgAsset.height; stationDeliveryBtnArea.width = imgAsset.width; stationDeliveryBtnArea.height = imgAsset.height; stationDeliveryBtnArea.alpha = 0; // fully transparent, but still receives taps window.stationDeliveryBtnArea = stationDeliveryBtnArea; // Add the button area BEFORE the image so it is behind LK.gui.bottomRight.addChild(stationDeliveryBtnArea); LK.gui.bottomRight.addChild(window.stationDeliveryBtnImage); } // DO NOT ADD THE BUTTON TO THE GUI (HIDDEN) // Add 'İstasyona Hammadde Gönder' text 2 rows below the button if (!window.stationSendResourceText) { var sendTextFont = Math.max(8, Math.round(unifiedFontSize * 1.0) - 2); window.stationSendResourceText = new Text2("", { size: sendTextFont, fill: 0xffffff, align: "center" }); window.stationSendResourceText.anchor.set(1, 1); // 2 rows below the stationDeliveryBtn (row = 90px) // Use the same x/y as the image, but offset as if the button was there window.stationSendResourceText.x = -40; window.stationSendResourceText.y = -40 - 2 * 90 + 1 * 90; LK.gui.bottomRight.addChild(window.stationSendResourceText); } } // --- RESET BUTTON (bottom right) --- if (!game._resetBtn) { game._resetBtn = new Text2("Reset", { size: Math.round(unifiedFontSize * 1.1), fill: 0xff4444, align: "center" }); game._resetBtn.anchor.set(1, 1); // Place at bottom right, with margin from edge game._resetBtn.x = -40; game._resetBtn.y = -40; LK.gui.bottomRight.addChild(game._resetBtn); } // --- UZAY İSTASYONU TESLİMAT PANEL SYSTEM --- // State for delivery system if (!window.stationDeliveryState) { // 10 hammadde için rastgele 50-200 arası istek, bonus %2-5 arası window.stationDeliveryState = { requests: [], delivered: [], completed: false, bonus: 0 }; for (var i = 0; i < 10; i++) { var req = 50 + Math.floor(Math.random() * 151); // 50-200 window.stationDeliveryState.requests.push(req); window.stationDeliveryState.delivered.push(0); } window.stationDeliveryState.bonus = 2 + Math.floor(Math.random() * 4); // 2-5 window.stationDeliveryState.completed = false; window.stationDeliveryState.rewardClaimed = false; } // Helper to open/close panel window.openStationDeliveryPanel = function () { if (window.stationDeliveryPanel) { return; } // Increase font size by 2 for all panel texts/buttons var panelFontSize = unifiedFontSize + 4; var panelBtnFontSize = panelFontSize; var labelFontSize = panelFontSize; var closeBtnFontSize = panelFontSize; var claimBtnFontSize = panelFontSize; var bonusFontSize = panelFontSize; var infoFontSize = panelFontSize; var titleFontSize = panelFontSize; // Calculate new panel size to fit enlarged text/buttons var rowH = 80 + 8; // add 8px for extra font size var panelW = 900 + 120; // add width for larger text/buttons var panelH = 1100 + 2 * rowH; // add height for larger text/buttons var px = 2048 / 2, py = 2732 / 2; // Panel BG window.stationDeliveryPanelBG = LK.getAsset('resource', { anchorX: 0.5, anchorY: 0.5, x: px, y: py, scaleX: panelW / 40, scaleY: panelH / 40, tint: 0x223355, alpha: 0.97 }); // Title window.stationDeliveryPanelTitle = new Text2("Space Station Delivery", { size: titleFontSize, fill: 0x00ffcc, align: "center" }); window.stationDeliveryPanelTitle.anchor.set(0.5, 0); window.stationDeliveryPanelTitle.x = px; window.stationDeliveryPanelTitle.y = py - panelH / 2 + 40; // Info text window.stationDeliveryPanelInfoText = new Text2("Complete the station's special resource requests!\nDeliver all to sell at +" + window.stationDeliveryState.bonus + "% bonus price.", { size: infoFontSize, fill: 0xffffff, align: "center" }); window.stationDeliveryPanelInfoText.anchor.set(0.5, 0); window.stationDeliveryPanelInfoText.x = px; window.stationDeliveryPanelInfoText.y = window.stationDeliveryPanelTitle.y + 60; // Close button window.stationDeliveryPanelCloseBtn = new Text2("Close", { size: Math.round((unifiedFontSize + 4) * 1.3), // 2 font larger than Coins, then 30% larger fill: 0xff4444, align: "center" }); window.stationDeliveryPanelCloseBtn.anchor.set(0.5, 0.5); // Move close button above the panel title, with extra margin window.stationDeliveryPanelCloseBtn.x = px; window.stationDeliveryPanelCloseBtn.y = window.stationDeliveryPanelTitle.y - 110; // further above title // Resource request labels and delivery buttons window.stationDeliveryLabels = []; window.stationDeliveryDeliverBtns = []; window.stationDeliveryStockLabels = []; // Move all resource rows down by 3% of the panel height var startY = window.stationDeliveryPanelInfoText.y + 80 + Math.round(panelH * 0.03); for (var i = 0; i < window.resourceLabelOrder.length; i++) { var resObj = window.resourceLabelOrder[i]; var req = window.stationDeliveryState.requests[resObj.origIdx]; var delivered = window.stationDeliveryState.delivered[resObj.origIdx]; var label = new Text2(resObj.name + ": " + delivered + " / " + req, { size: labelFontSize, fill: 0xffffff, align: "left" }); label.anchor.set(0, 0.5); label.x = px - panelW / 2 + 60; label.y = startY + i * rowH; window.stationDeliveryLabels.push(label); // Oyuncu stok durumu etiketi var stockAmount = typeof playerResources[resObj.origIdx] !== "undefined" ? playerResources[resObj.origIdx] : 0; var stockLabel = new Text2("Stock: " + stockAmount, { size: Math.max(18, Math.round(labelFontSize * 0.85)), fill: 0xcccccc, align: "left" }); stockLabel.anchor.set(0, 0.5); stockLabel.x = label.x + 340; stockLabel.y = label.y; window.stationDeliveryStockLabels.push(stockLabel); // Deliver button var teslimBtn = new Text2("Deliver", { size: panelBtnFontSize, fill: 0x00ff99, align: "center" }); teslimBtn.anchor.set(0.5, 0.5); teslimBtn.x = px + 220; teslimBtn.y = label.y; teslimBtn._stationResIdx = resObj.origIdx; window.stationDeliveryDeliverBtns.push(teslimBtn); } // Bonus info window.stationDeliveryPanelBonusText = new Text2("Complete all requests to sell at +" + window.stationDeliveryState.bonus + "% bonus price.", { size: bonusFontSize, fill: 0xffff00, align: "center" }); window.stationDeliveryPanelBonusText.anchor.set(0.5, 0); window.stationDeliveryPanelBonusText.x = px; window.stationDeliveryPanelBonusText.y = startY + rowH * 10 + 30; // Complete delivery (claim reward) button window.stationDeliveryPanelClaimBtn = new Text2("Deliver All & Claim Reward", { size: claimBtnFontSize, fill: 0xffcc00, align: "center" }); window.stationDeliveryPanelClaimBtn.anchor.set(0.5, 0.5); window.stationDeliveryPanelClaimBtn.x = px; window.stationDeliveryPanelClaimBtn.y = window.stationDeliveryPanelBonusText.y + 90; // Add to game game.addChild(window.stationDeliveryPanelBG); game.addChild(window.stationDeliveryPanelTitle); game.addChild(window.stationDeliveryPanelInfoText); game.addChild(window.stationDeliveryPanelCloseBtn); for (var i = 0; i < window.stationDeliveryLabels.length; i++) { game.addChild(window.stationDeliveryLabels[i]); } for (var i = 0; i < window.stationDeliveryStockLabels.length; i++) { game.addChild(window.stationDeliveryStockLabels[i]); } for (var i = 0; i < window.stationDeliveryDeliverBtns.length; i++) { game.addChild(window.stationDeliveryDeliverBtns[i]); } game.addChild(window.stationDeliveryPanelBonusText); game.addChild(window.stationDeliveryPanelClaimBtn); window.stationDeliveryPanel = true; }; window.closeStationDeliveryPanel = function () { if (!window.stationDeliveryPanel) { return; } if (window.stationDeliveryPanelBG && window.stationDeliveryPanelBG.parent) { window.stationDeliveryPanelBG.parent.removeChild(window.stationDeliveryPanelBG); } if (window.stationDeliveryPanelTitle && window.stationDeliveryPanelTitle.parent) { window.stationDeliveryPanelTitle.parent.removeChild(window.stationDeliveryPanelTitle); } if (window.stationDeliveryPanelInfoText && window.stationDeliveryPanelInfoText.parent) { window.stationDeliveryPanelInfoText.parent.removeChild(window.stationDeliveryPanelInfoText); } if (window.stationDeliveryPanelCloseBtn && window.stationDeliveryPanelCloseBtn.parent) { window.stationDeliveryPanelCloseBtn.parent.removeChild(window.stationDeliveryPanelCloseBtn); } if (window.stationDeliveryLabels) { for (var i = 0; i < window.stationDeliveryLabels.length; i++) { if (window.stationDeliveryLabels[i] && window.stationDeliveryLabels[i].parent) { window.stationDeliveryLabels[i].parent.removeChild(window.stationDeliveryLabels[i]); } } } if (window.stationDeliveryDeliverBtns) { for (var i = 0; i < window.stationDeliveryDeliverBtns.length; i++) { if (window.stationDeliveryDeliverBtns[i] && window.stationDeliveryDeliverBtns[i].parent) { window.stationDeliveryDeliverBtns[i].parent.removeChild(window.stationDeliveryDeliverBtns[i]); } } } if (window.stationDeliveryPanelBonusText && window.stationDeliveryPanelBonusText.parent) { window.stationDeliveryPanelBonusText.parent.removeChild(window.stationDeliveryPanelBonusText); } if (window.stationDeliveryPanelClaimBtn && window.stationDeliveryPanelClaimBtn.parent) { window.stationDeliveryPanelClaimBtn.parent.removeChild(window.stationDeliveryPanelClaimBtn); } window.stationDeliveryPanel = null; window.stationDeliveryPanelBG = null; window.stationDeliveryPanelTitle = null; window.stationDeliveryPanelInfoText = null; window.stationDeliveryPanelCloseBtn = null; window.stationDeliveryLabels = null; window.stationDeliveryDeliverBtns = null; window.stationDeliveryPanelBonusText = null; window.stationDeliveryPanelClaimBtn = null; // Hide station stock labels when panel is closed if (window.stationDeliveryStockLabels) { for (var i = 0; i < window.stationDeliveryStockLabels.length; i++) { if (window.stationDeliveryStockLabels[i] && window.stationDeliveryStockLabels[i].parent) { window.stationDeliveryStockLabels[i].parent.removeChild(window.stationDeliveryStockLabels[i]); } } window.stationDeliveryStockLabels = null; } }; // --- GAME TIME LABEL --- // Track game start time in ticks var gameStartTick = LK.ticks; var gameTimeText = new Text2('Game Time: 0:00', { size: unifiedFontSize, fill: 0xcccccc }); gameTimeText.anchor.set(0, 0); gameTimeText.x = coinText.x + coinText.width + 40; gameTimeText.y = coinText.y; LK.gui.topLeft.addChild(gameTimeText); // Add an image below the Game Time label if (!window.gameTimeImage) { // Use an existing image asset, e.g. 'teslimEtBtnBg' as a placeholder // Move the Global Panel image and button to the row above the station image var imgScale = 0.7; var imgAsset = LK.getAsset('teslimEtBtnBg', { anchorX: 1, anchorY: 1, scaleX: imgScale, scaleY: imgScale }); // Place the image at the same x as the stationDeliveryBtnImage, but 1 row above it // If stationDeliveryBtnImage exists, align to it var refImg = window.stationDeliveryBtnImage; if (refImg) { imgAsset.x = refImg.x; imgAsset.y = refImg.y - refImg.height; // 1 row above station image } else { // fallback: 10% right from left edge, and 10px below the Game Time label imgAsset.x = Math.round(2048 * 0.10); imgAsset.y = gameTimeText.y + gameTimeText.height + 10; } window.gameTimeImage = imgAsset; LK.gui.bottomRight.addChild(window.gameTimeImage); // --- Add a new independent image file at the position 2 rows above Hammadde Takası (Resource Trade) image --- // Remove the previously created image if it exists if (window.resourceTradePanelImage && window.resourceTradePanelImage.parent) { window.resourceTradePanelImage.parent.removeChild(window.resourceTradePanelImage); window.resourceTradePanelImage = null; } // Create a new independent image at the same position (now 1 row above Hammadde Takası image) if (!window.resourceTradePanelIndependentImage) { var independentImgScale = 0.7; var independentImgAsset = LK.getAsset('resourceTradePanelIndependentCus', { anchorX: 1, anchorY: 1, scaleX: independentImgScale, scaleY: independentImgScale }); // Place 1 row above the Hammadde Takası (Resource Trade) image independentImgAsset.x = imgAsset.x; independentImgAsset.y = imgAsset.y - imgAsset.height * 1; window.resourceTradePanelIndependentImage = independentImgAsset; LK.gui.bottomRight.addChild(independentImgAsset); // Move the Hammadde Stoğu panel button (tap area) 2 rows above the image, image stays fixed if (!window.resourceTradePanelIndependentBtn) { var btn = new Text2("", { size: 1, fill: 0xffffff, align: "center" }); btn.anchor.set(1, 1); btn.x = independentImgAsset.x; btn.y = independentImgAsset.y - independentImgAsset.height * 2; // 2 rows above the image btn.width = independentImgAsset.width; btn.height = independentImgAsset.height; btn.alpha = 0; // fully transparent, but still receives taps window.resourceTradePanelIndependentBtn = btn; LK.gui.bottomRight.addChild(btn); } } // Add a transparent button area exactly the size and position of the image for Global Market var marketBtn = new Text2("Global Market", { size: 1, fill: 0xffffff, align: "center" }); marketBtn.anchor.set(1, 1); marketBtn.x = imgAsset.x; // Move the button area 2 rows (imgAsset.height * 2) above the image, image stays fixed marketBtn.y = imgAsset.y - imgAsset.height * 2; // Grow the button area 10% downward and shrink it 5% from the top marketBtn.width = imgAsset.width; marketBtn.height = imgAsset.height * 1.10 * 0.95; // first grow 10%, then shrink 5% from top marketBtn.alpha = 0; // fully transparent, but still receives taps window.globalMarketBtn = marketBtn; LK.gui.bottomRight.addChild(marketBtn); } // --- RESOURCE TRADE PANEL STATE --- if (!window.resourceTradePanelState) { // 10 hammadde için değerler (1-10 arası, rastgele başlat) window.resourceTradeValues = []; for (var i = 0; i < 10; i++) { window.resourceTradeValues.push(1 + Math.floor(Math.random() * 10)); } window.resourceTradePanelState = { open: false, lastValueUpdateTick: LK.ticks }; } // Timer: her 5 saniyede bir (300 tick) değerleri değiştir if (!window._resourceTradeValueTimer) { window._resourceTradeValueTimer = LK.setInterval(function () { if (!window.resourceTradeValues) { return; } for (var i = 0; i < window.resourceTradeValues.length; i++) { // Değeri -3/+3 arası değiştir, 1-10 aralığında tut var delta = Math.floor(Math.random() * 7) - 3; window.resourceTradeValues[i] = Math.max(1, Math.min(10, window.resourceTradeValues[i] + delta)); } // Panel açıksa UI güncelle if (window.resourceTradePanelState && window.resourceTradePanelState.open && typeof window.updateResourceTradePanel === "function") { window.updateResourceTradePanel(); } }, 5000); } // Resource UI: create 10 vertically stacked resource labels, left-aligned // Sort resourceTypes by name length descending, keep original index for mapping to playerResources var resourceTypes = ['Metal', 'Energy', 'Crystal', 'Gas', 'Ice', 'Uranium', 'Silicon', 'Carbon', 'Plasma', 'Antimatter']; var resourceTypeObjs = []; for (var i = 0; i < resourceTypes.length; i++) { resourceTypeObjs.push({ name: resourceTypes[i], origIdx: i }); } resourceTypeObjs.sort(function (a, b) { // Sort by name length descending, then alphabetically for ties if (b.name.length !== a.name.length) { return b.name.length - a.name.length; } return a.name.localeCompare(b.name); }); window.resourceLabels = []; window.resourceLabelOrder = resourceTypeObjs; // Save for updateUI var resourceIconMargin = 8; var resourceLabelStartY = Math.round(2732 * 0.05); // 5% down from the top for (var i = 0; i < resourceTypeObjs.length; i++) { var label = new Text2(resourceTypeObjs[i].name + ': 0', { size: unifiedFontSize, fill: 0xFFFFFF }); label.anchor.set(0, 0); label.x = 20; // flush to left edge, but leave margin for menu icon label.y = resourceLabelStartY + i * (unifiedFontSize + resourceIconMargin); // Do NOT add to GUI, so resource stock text is hidden on main screen // LK.gui.topLeft.addChild(label); window.resourceLabels.push(label); } // --- RESOURCE TRADE PANEL LOGIC --- window.openResourceTradePanel = function () { if (window.resourceTradePanelState.open) { return; } window.resourceTradePanelState.open = true; // Increase font size by 2 for all panel texts/buttons var panelFontSize = unifiedFontSize + 4; var panelBtnFontSize = panelFontSize; var labelFontSize = panelFontSize; var closeBtnFontSize = panelFontSize; var infoFontSize = panelFontSize; var titleFontSize = panelFontSize; // Panel boyutları ve konum (resize for larger text/buttons) var rowH = 80 + 8; var panelW = 1200 + 120; var panelH = 1200 + 2 * rowH; var px = 2048 / 2, py = 2732 / 2; // Panel BG window.resourceTradePanelBG = LK.getAsset('resource', { anchorX: 0.5, anchorY: 0.5, x: px, y: py, scaleX: panelW / 40, scaleY: panelH / 40, tint: 0x223355, alpha: 0.97 }); // Başlık window.resourceTradePanelTitle = new Text2("Resource Trading", { size: titleFontSize, fill: 0x00ffcc, align: "center" }); window.resourceTradePanelTitle.anchor.set(0.5, 0); window.resourceTradePanelTitle.x = px; window.resourceTradePanelTitle.y = py - panelH / 2 + 40; // Info text window.resourceTradePanelInfoText = new Text2("You can trade your resources.\nEach resource value changes between 5-30 and updates every 5 seconds.", { size: infoFontSize, fill: 0xffffff, align: "center" }); window.resourceTradePanelInfoText.anchor.set(0.5, 0); window.resourceTradePanelInfoText.x = px; window.resourceTradePanelInfoText.y = window.resourceTradePanelTitle.y + 60; // Kapat butonu window.resourceTradePanelCloseBtn = new Text2("Close", { size: Math.round((unifiedFontSize + 4) * 1.3), // 2 font larger than Coins, then 30% larger fill: 0xff4444, align: "center" }); window.resourceTradePanelCloseBtn.anchor.set(0.5, 0.5); window.resourceTradePanelCloseBtn.x = px; window.resourceTradePanelCloseBtn.y = window.resourceTradePanelTitle.y - 110; // further above title // Hammadde satırları ve butonları window.resourceTradeLabels = []; window.resourceTradeSellBtns = []; window.resourceTradeSellAllBtns = []; window.resourceTradeBuyBtns = []; var startY = window.resourceTradePanelInfoText.y + 80 + rowH * 2; // move all rows down by 2 rows (1 row further) for (var i = 0; i < window.resourceLabelOrder.length; i++) { var resObj = window.resourceLabelOrder[i]; var idx = resObj.origIdx; var label = new Text2(resObj.name + ": " + (playerResources[idx] || 0) + " | Value: " + (window.resourceTradeValues ? window.resourceTradeValues[idx] : 0), { size: labelFontSize, fill: 0xffffff, align: "left" }); label.anchor.set(0, 0.5); label.x = px - panelW / 2 + 60; label.y = startY + i * rowH; window.resourceTradeLabels.push(label); // Sell button var sellBtn = new Text2("Sell", { size: panelBtnFontSize, fill: 0x00ff99, align: "center" }); sellBtn.anchor.set(0.5, 0.5); sellBtn.x = px + 220; sellBtn.y = label.y; sellBtn._resourceIdx = idx; window.resourceTradeSellBtns.push(sellBtn); // Sell All button var sellAllBtn = new Text2("Sell All", { size: panelBtnFontSize, fill: 0x00cc99, align: "center" }); sellAllBtn.anchor.set(0.5, 0.5); sellAllBtn.x = px + 400; sellAllBtn.y = label.y; sellAllBtn._resourceIdx = idx; window.resourceTradeSellAllBtns.push(sellAllBtn); // Buy button var buyBtn = new Text2("Buy", { size: panelBtnFontSize, fill: 0x0099ff, align: "center" }); buyBtn.anchor.set(0.5, 0.5); buyBtn.x = px + 580; buyBtn.y = label.y; buyBtn._resourceIdx = idx; window.resourceTradeBuyBtns.push(buyBtn); } // Add to game game.addChild(window.resourceTradePanelBG); game.addChild(window.resourceTradePanelTitle); game.addChild(window.resourceTradePanelInfoText); game.addChild(window.resourceTradePanelCloseBtn); for (var i = 0; i < window.resourceTradeLabels.length; i++) { game.addChild(window.resourceTradeLabels[i]); } for (var i = 0; i < window.resourceTradeSellBtns.length; i++) { game.addChild(window.resourceTradeSellBtns[i]); } for (var i = 0; i < window.resourceTradeSellAllBtns.length; i++) { game.addChild(window.resourceTradeSellAllBtns[i]); } for (var i = 0; i < window.resourceTradeBuyBtns.length; i++) { game.addChild(window.resourceTradeBuyBtns[i]); } // Panel güncelleme fonksiyonu window.updateResourceTradePanel = function () { for (var i = 0; i < window.resourceLabelOrder.length; i++) { var resObj = window.resourceLabelOrder[i]; var idx = resObj.origIdx; if (window.resourceTradeLabels[i]) { window.resourceTradeLabels[i].setText(resObj.name + ": " + (playerResources[idx] || 0) + " | Değer: " + (window.resourceTradeValues ? window.resourceTradeValues[idx] : 0)); } } }; window.updateResourceTradePanel(); }; window.closeResourceTradePanel = function () { if (!window.resourceTradePanelState.open) { return; } window.resourceTradePanelState.open = false; if (window.resourceTradePanelBG && window.resourceTradePanelBG.parent) { window.resourceTradePanelBG.parent.removeChild(window.resourceTradePanelBG); } if (window.resourceTradePanelTitle && window.resourceTradePanelTitle.parent) { window.resourceTradePanelTitle.parent.removeChild(window.resourceTradePanelTitle); } if (window.resourceTradePanelInfoText && window.resourceTradePanelInfoText.parent) { window.resourceTradePanelInfoText.parent.removeChild(window.resourceTradePanelInfoText); } if (window.resourceTradePanelCloseBtn && window.resourceTradePanelCloseBtn.parent) { window.resourceTradePanelCloseBtn.parent.removeChild(window.resourceTradePanelCloseBtn); } if (window.resourceTradeLabels) { for (var i = 0; i < window.resourceTradeLabels.length; i++) { if (window.resourceTradeLabels[i] && window.resourceTradeLabels[i].parent) { window.resourceTradeLabels[i].parent.removeChild(window.resourceTradeLabels[i]); } } } if (window.resourceTradeSellBtns) { for (var i = 0; i < window.resourceTradeSellBtns.length; i++) { if (window.resourceTradeSellBtns[i] && window.resourceTradeSellBtns[i].parent) { window.resourceTradeSellBtns[i].parent.removeChild(window.resourceTradeSellBtns[i]); } } } if (window.resourceTradeSellAllBtns) { for (var i = 0; i < window.resourceTradeSellAllBtns.length; i++) { if (window.resourceTradeSellAllBtns[i] && window.resourceTradeSellAllBtns[i].parent) { window.resourceTradeSellAllBtns[i].parent.removeChild(window.resourceTradeSellAllBtns[i]); } } } if (window.resourceTradeBuyBtns) { for (var i = 0; i < window.resourceTradeBuyBtns.length; i++) { if (window.resourceTradeBuyBtns[i] && window.resourceTradeBuyBtns[i].parent) { window.resourceTradeBuyBtns[i].parent.removeChild(window.resourceTradeBuyBtns[i]); } } } window.resourceTradePanelBG = null; window.resourceTradePanelTitle = null; window.resourceTradePanelInfoText = null; window.resourceTradePanelCloseBtn = null; window.resourceTradeLabels = null; window.resourceTradeSellBtns = null; window.resourceTradeSellAllBtns = null; window.resourceTradeBuyBtns = null; }; var healthText = new Text2('Health: 100/100', { size: unifiedFontSize, fill: 0x00FF00 }); healthText.anchor.set(0.5, 0); healthText._isHealthBar = true; // Mark for tap detection LK.gui.top.addChild(healthText); var shieldText = new Text2('Shield: 50/50', { size: unifiedFontSize, fill: 0x0088FF }); shieldText.anchor.set(0.5, 0); shieldText.y = 60; LK.gui.top.addChild(shieldText); // --- Add waveText and expText UI for wave/level display --- // --- REPAIR PANEL WARNING SUPPRESSION --- // When repair panel is closed, set a suppression timer for 30 seconds (1800 ticks) if (window.repairPanel && !window._repairPanelSuppressUntil) { window._repairPanelSuppressUntil = LK.ticks + 1800; } // (removed duplicate handler, see main game.down below) var waveText = new Text2('Wave: 1', { size: unifiedFontSize, fill: 0xffffff }); waveText.anchor.set(0.5, 0); waveText.x = 1024; waveText.y = 60; // Move down to make space for market button LK.gui.top.addChild(waveText); var expText = new Text2('Level: 1 EXP: 0/10', { size: unifiedFontSize, fill: 0xffffff }); expText.anchor.set(0.5, 0); expText.x = 1024; expText.y = 70; LK.gui.top.addChild(expText); // --- Add image to right edge, 3 rows below waveText --- if (!window.levelPanelBelowImg) { // Use the 'levelPanelBelowImg' asset as the image var imgAsset = LK.getAsset('levelPanelBelowImg', { anchorX: 1, // right edge anchorY: 0, // top scaleX: 0.7, scaleY: 0.7 }); // Place at right edge (x=0 in bottomRight, so x=-margin from right) // LK.gui.topRight is anchored at (1,0), so x=0 is right edge, y=0 is top // waveText.y is 60, so 3 rows below is 60 + 3*unifiedFontSize + 3*10 (10px margin per row) var rowH = unifiedFontSize + 10; imgAsset.x = -40; // 40px margin from right edge imgAsset.y = waveText.y + 3 * rowH; window.levelPanelBelowImg = imgAsset; LK.gui.topRight.addChild(imgAsset); // --- Achievements Panel Tap Area (transparent, over Achievements image) --- if (!window.achievementsPanelBtn) { var achBtn = new Text2("", { size: 1, fill: 0xffffff, align: "center" }); achBtn.anchor.set(1, 0); achBtn.x = imgAsset.x; achBtn.y = imgAsset.y; achBtn.width = imgAsset.width; achBtn.height = imgAsset.height; achBtn.alpha = 0; // fully transparent, but receives taps window.achievementsPanelBtn = achBtn; LK.gui.topRight.addChild(achBtn); } // --- Add new image just below Achievements image (right edge, keep all others fixed) --- if (!window.newImageAboveStorage) { var newImgAsset = LK.getAsset('newImageAboveStorage', { anchorX: 1, anchorY: 0, scaleX: 0.7, scaleY: 0.7 }); // Place just below Achievements image (levelPanelBelowImg) if (window.levelPanelBelowImg) { newImgAsset.x = window.levelPanelBelowImg.x; newImgAsset.y = window.levelPanelBelowImg.y + window.levelPanelBelowImg.height + 10; } else if (typeof imgAsset !== "undefined") { // fallback: just below the previous image newImgAsset.x = imgAsset.x; newImgAsset.y = imgAsset.y + imgAsset.height + 10; } else { // fallback: right edge, 2 rows from top newImgAsset.x = -40; newImgAsset.y = 10 + newImgAsset.height * 2 + 20; } window.newImageAboveStorage = newImgAsset; LK.gui.topRight.addChild(newImgAsset); } // --- Add upg image to right edge, 3 rows below waveText, just below the previous image --- if (!window.upgPanelBelowImg) { var upgImgAsset = LK.getAsset('upg', { anchorX: 1, anchorY: 0, scaleX: 0.7, scaleY: 0.7 }); upgImgAsset.x = -40; upgImgAsset.y = imgAsset.y + imgAsset.height + 10 + upgImgAsset.height + 10; // move 1 row (image height + 10px) further down window.upgPanelBelowImg = upgImgAsset; LK.gui.topRight.addChild(upgImgAsset); // Add a transparent tap area over the upg image for opening the Upgrades panel if (!window.upgPanelBtn) { var upgBtn = new Text2("", { size: 1, fill: 0xffffff, align: "center" }); upgBtn.anchor.set(1, 0); upgBtn.x = upgImgAsset.x; // Move the button 1 row (image height + 10px) further down, image stays fixed upgBtn.y = upgImgAsset.y + upgImgAsset.height + 10; // Make the button area 1.5x wider and 1.5x taller than the image, centered on the image's position upgBtn.width = upgImgAsset.width * 1.5; upgBtn.height = upgImgAsset.height * 1.5; // Shift the button left and up so it stays centered on the image upgBtn.x = upgImgAsset.x - (upgBtn.width - upgImgAsset.width) / 2; upgBtn.y = upgImgAsset.y + upgImgAsset.height + 10 - (upgBtn.height - upgImgAsset.height) / 2; upgBtn.alpha = 0; // fully transparent, but receives taps window.upgPanelBtn = upgBtn; LK.gui.topRight.addChild(upgBtn); } } } // --- Achievements Panel Logic --- // 50 achievements, with difficulty and rewards if (!window.achievementsList) { window.achievementsList = [ // id, name, desc, reward, difficulty { id: 1, name: "First Blood", desc: "Destroy your first pirate.", reward: 50, difficulty: "Easy" }, { id: 2, name: "Miner", desc: "Collect resources from an asteroid.", reward: 30, difficulty: "Easy" }, { id: 3, name: "Trader", desc: "Trade a resource.", reward: 40, difficulty: "Easy" }, { id: 5, name: "Killer Pirate", desc: "Destroy the killer pirate.", reward: 500, difficulty: "Hard" }, { id: 6, name: "Rich", desc: "Accumulate 1000 coins.", reward: 100, difficulty: "Medium" }, { id: 7, name: "Mechanic", desc: "Repair your ship.", reward: 30, difficulty: "Easy" }, { id: 8, name: "Level Up", desc: "Reach level 5.", reward: 80, difficulty: "Medium" }, { id: 9, name: "Shield Master", desc: "Fully charge your shield.", reward: 40, difficulty: "Easy" }, { id: 11, name: "Pirate Hunter", desc: "Destroy 10 pirates.", reward: 120, difficulty: "Medium" }, { id: 12, name: "Asteroid Destroyer", desc: "Destroy 5 asteroids.", reward: 80, difficulty: "Medium" }, { id: 13, name: "Trade Master", desc: "Complete 10 trades total.", reward: 150, difficulty: "Hard" }, { id: 14, name: "Explorer", desc: "Travel a total of 10,000 distance units.", reward: 100, difficulty: "Medium" }, { id: 15, name: "Pirate King", desc: "Destroy 50 pirates.", reward: 300, difficulty: "Hard" }, { id: 16, name: "Resource King", desc: "Collect 100 units of all resources.", reward: 200, difficulty: "Hard" }, { id: 17, name: "Upgrader", desc: "Purchase an upgrade.", reward: 60, difficulty: "Easy" }, { id: 18, name: "Repair Specialist", desc: "Repair 5 times.", reward: 100, difficulty: "Medium" }, { id: 19, name: "Wave Passer", desc: "Reach wave 5.", reward: 80, difficulty: "Medium" }, { id: 20, name: "Wave Master", desc: "Reach wave 10.", reward: 200, difficulty: "Hard" }, { id: 21, name: "Shield King", desc: "Upgrade shield to 200.", reward: 120, difficulty: "Hard" }, { id: 22, name: "Beast", desc: "Upgrade health to 300.", reward: 120, difficulty: "Hard" }, { id: 23, name: "Fast", desc: "Upgrade speed to 10.", reward: 100, difficulty: "Hard" }, { id: 24, name: "Bullet Storm", desc: "Reduce fire rate to 5.", reward: 100, difficulty: "Hard" }, { id: 25, name: "Energy Master", desc: "Collect a total of 500 energy.", reward: 100, difficulty: "Medium" }, { id: 26, name: "Metal Worker", desc: "Collect a total of 500 metal.", reward: 100, difficulty: "Medium" }, { id: 27, name: "Ice Man", desc: "Collect a total of 100 ice.", reward: 80, difficulty: "Medium" }, { id: 28, name: "Plasma Hunter", desc: "Collect a total of 50 plasma.", reward: 80, difficulty: "Medium" }, { id: 29, name: "Antimatter Collector", desc: "Collect a total of 10 antimatter.", reward: 120, difficulty: "Hard" }, { id: 33, name: "Space Station", desc: "Make a delivery to the station.", reward: 100, difficulty: "Medium" }, { id: 34, name: "Big Delivery", desc: "Complete all station requests.", reward: 300, difficulty: "Hard" }, { id: 35, name: "Pirate Base", desc: "Destroy a pirate base.", reward: 200, difficulty: "Hard" }, { id: 36, name: "Pirate Base Hunter", desc: "Destroy 3 pirate bases.", reward: 400, difficulty: "Very Hard" }, { id: 38, name: "Resource Tycoon", desc: "Collect 500 units of all resources.", reward: 400, difficulty: "Very Hard" }, { id: 39, name: "Trade Tycoon", desc: "Complete a total of 100 trades.", reward: 400, difficulty: "Very Hard" }, { id: 40, name: "Level Master", desc: "Reach level 20.", reward: 400, difficulty: "Very Hard" }, { id: 41, name: "Wave Legend", desc: "Reach wave 20.", reward: 500, difficulty: "Very Hard" }, { id: 42, name: "Space Conqueror", desc: "Travel a total of 100,000 distance units.", reward: 500, difficulty: "Very Hard" }, { id: 43, name: "Pirate Slayer", desc: "Destroy 100 pirates.", reward: 600, difficulty: "Very Hard" }, { id: 44, name: "Asteroid King", desc: "Destroy 50 asteroids.", reward: 400, difficulty: "Very Hard" }, { id: 45, name: "Upgrade King", desc: "Max out all upgrades.", reward: 600, difficulty: "Very Hard" }, { id: 46, name: "Repair King", desc: "Repair 20 times.", reward: 300, difficulty: "Very Hard" }, // NEW: Active gameplay feature achievements { id: 51, name: "Asteroid Hunter", desc: "Destroy 20 asteroids.", reward: 150, difficulty: "Medium" }, { id: 52, name: "Pirate Raider", desc: "Destroy 10 pirates in one wave.", reward: 200, difficulty: "Hard" }, { id: 53, name: "Fast Collector", desc: "Collect 10 resources in one minute.", reward: 120, difficulty: "Medium" }, { id: 54, name: "Rich Miner", desc: "Have 1000 coins and 100 resources at the same time.", reward: 250, difficulty: "Hard" }, { id: 55, name: "Upgrade Series", desc: "Buy 3 upgrades in a row.", reward: 180, difficulty: "Medium" }, { id: 56, name: "Achievement Hunter", desc: "Complete all achievements.", reward: 1000, difficulty: "Legendary" }, // --- NEW 10 ACHIEVEMENTS (latest features) --- { id: 57, name: "GroupPirate Hunter", desc: "Completely destroy a GroupPirate squad.", reward: 250, difficulty: "Hard" }, { id: 58, name: "CoinBox Hunter", desc: "Collect a CoinBox.", reward: 80, difficulty: "Easy" }, { id: 59, name: "CoinBox Tycoon", desc: "Collect a total of 10 CoinBoxes.", reward: 300, difficulty: "Hard" }, { id: 60, name: "Wreck Hunter", desc: "Collect a Derelict Wreck.", reward: 60, difficulty: "Easy" }, { id: 61, name: "Wreck Tycoon", desc: "Collect a total of 10 Derelict Wrecks.", reward: 250, difficulty: "Medium" }, { id: 62, name: "Double Pirate", desc: "Fight 2 pirates at the same time.", reward: 120, difficulty: "Medium" }, { id: 63, name: "Triple GroupPirate", desc: "Fight 3 GroupPirates at the same time.", reward: 200, difficulty: "Hard" }, { id: 64, name: "Asteroid Rain", desc: "Destroy 5 asteroids in one wave.", reward: 180, difficulty: "Hard" }, { id: 65, name: "Resource Burst", desc: "Collect more than 20 resources at once.", reward: 220, difficulty: "Hard" }, { id: 66, name: "Pirate Swarm", desc: "Fight more than 5 pirates in one wave.", reward: 250, difficulty: "Very Hard" }]; // Save progress if (!storage.achievements) { storage.achievements = []; } window.achievementsProgress = storage.achievements; } // --- BAŞARIM MEKANİĞİ EKLENDİ --- // Yardımcı fonksiyon: başarıma sahip mi? function hasAchievement(id) { return window.achievementsProgress && window.achievementsProgress.indexOf(id) !== -1; } // Yardımcı fonksiyon: başarıma ekle ve ödül ver function grantAchievement(id) { if (!window.achievementsProgress) { window.achievementsProgress = []; } if (window.achievementsProgress.indexOf(id) !== -1) { return; } // Zaten kazanıldı window.achievementsProgress.push(id); storage.achievements = window.achievementsProgress; // Ödül ver var ach = null; for (var i = 0; i < window.achievementsList.length; i++) { if (window.achievementsList[i].id === id) { ach = window.achievementsList[i]; break; } } if (ach && typeof playerCoins !== "undefined") { playerCoins += ach.reward; updateUI && updateUI(); saveProgress && saveProgress(); // Show a short notification var label = new Text2("Achievement Earned!\n" + ach.name + "\n+" + ach.reward + " coin", { size: 44, fill: 0x00ffcc, align: "center" }); label.anchor.set(0.5, 0.5); label.x = 1024; label.y = 400; if (typeof game !== "undefined" && game.addChild) { game.addChild(label); } LK.setTimeout(function () { if (label.parent) { label.parent.removeChild(label); } }, 1800); } // Tüm başarımlar kazanıldıysa 50. başarıma bak if (window.achievementsProgress.length === 50 && !hasAchievement(50)) { grantAchievement(50); } } // --- BAŞARIMLARIN TAKİBİ İÇİN SAYACLAR --- // Oyun içi sayaçlar if (!window._achPirateKills) { window._achPirateKills = 0; } if (!window._achAsteroidKills) { window._achAsteroidKills = 0; } if (!window._achTradeCount) { window._achTradeCount = 0; } if (!window._achRepairCount) { window._achRepairCount = 0; } if (!window._achQuestComplete) { window._achQuestComplete = 0; } if (!window._achNPCKills) { window._achNPCKills = 0; } if (!window._achStationDeliver) { window._achStationDeliver = 0; } if (!window._achStationAllDeliver) { window._achStationAllDeliver = 0; } if (!window._achPirateBaseKills) { window._achPirateBaseKills = 0; } if (!window._achPirateBaseKills3) { window._achPirateBaseKills3 = 0; } if (!window._achUpgradeCount) { window._achUpgradeCount = 0; } if (!window._achKatilEscape) { window._achKatilEscape = false; } if (!window._achKatilKill) { window._achKatilKill = false; } if (!window._achWormhole) { window._achWormhole = false; } if (!window._achPirateTrade) { window._achPirateTrade = false; } if (!window._achProtectNPC) { window._achProtectNPC = false; } if (!window._ach3PirateFight) { window._ach3PirateFight = false; } // --- BAŞARIM KONTROLLERİ --- // 1. Korsan öldürme (İlk Kan, Korsan Avcısı, Korsan Kralı, Korsan Katili) function checkPirateKillAchievements() { window._achPirateKills++; if (window._achPirateKills === 1) { grantAchievement(1); } // İlk Kan if (window._achPirateKills === 10) { grantAchievement(11); } // Korsan Avcısı if (window._achPirateKills === 50) { grantAchievement(15); } // Korsan Kralı if (window._achPirateKills === 100) { grantAchievement(43); } // Korsan Katili } // 2. Katil korsan öldürme function checkKatilKillAchievement() { if (!window._achKatilKill) { window._achKatilKill = true; grantAchievement(5); // Katil Korsan } } // 3. Asteroit patlatma function checkAsteroidKillAchievements() { window._achAsteroidKills++; if (window._achAsteroidKills === 5) { grantAchievement(12); } // Asteroit Patlatıcı if (window._achAsteroidKills === 50) { grantAchievement(44); } // Asteroit Kralı } // 4. Hammadde toplama (Madenci, Hammadde Kralı, Hammadde Zengini, Enerji Ustası, Metalci, Buz Adam, Plazma Avcısı, Antimaddeci) function checkResourceCollectAchievements() { // Madenci grantAchievement(2); // Enerji Ustası, Metalci, Buz Adam, Plazma Avcısı, Antimaddeci, Hammadde Kralı, Hammadde Zengini var res = playerResources; if (res[1] >= 500) { grantAchievement(25); } // Enerji Ustası if (res[0] >= 500) { grantAchievement(26); } // Metalci if (res[4] >= 100) { grantAchievement(27); } // Buz Adam if (res[8] >= 50) { grantAchievement(28); } // Plazma Avcısı if (res[9] >= 10) { grantAchievement(29); } // Antimaddeci var all100 = true, all500 = true; for (var i = 0; i < res.length; i++) { if (res[i] < 100) { all100 = false; } if (res[i] < 500) { all500 = false; } } if (all100) { grantAchievement(16); } // Hammadde Kralı if (all500) { grantAchievement(38); } // Hammadde Zengini } // 5. Takas (Tüccar, Ticaret Ustası, Takas Zengini) function checkTradeAchievements() { window._achTradeCount++; if (window._achTradeCount === 1) { grantAchievement(3); } // Tüccar if (window._achTradeCount === 10) { grantAchievement(13); } // Ticaret Ustası if (window._achTradeCount === 100) { grantAchievement(39); } // Takas Zengini } // 6. Seviye atlama (Seviye Atla, Seviye Ustası) function checkLevelAchievements() { if (playerLevel >= 5) { grantAchievement(8); } // Seviye Atla if (playerLevel >= 20) { grantAchievement(40); } // Seviye Ustası } // 7. Coin biriktirme (Zengin) function checkCoinAchievements() { if (playerCoins >= 1000) { grantAchievement(6); } // Zengin } // 8. Tamir (Mekanik, Tamirci, Tamir Kralı) function checkRepairAchievements() { window._achRepairCount++; if (window._achRepairCount === 1) { grantAchievement(7); } // Mekanik if (window._achRepairCount === 5) { grantAchievement(18); } // Tamirci if (window._achRepairCount === 20) { grantAchievement(46); } // Tamir Kralı } // 9. Kalkanı doldurma (Kalkan Ustası, Kalkan Kralı) function checkShieldAchievements() { if (ship.shield >= ship.maxShield && ship.shield >= 50) { grantAchievement(9); } // Kalkan Ustası if (ship.maxShield >= 200) { grantAchievement(21); } // Kalkan Kralı } // 10. Canı yükseltme (Canavar) function checkHealthAchievements() { if (ship.maxHealth >= 300) { grantAchievement(22); } // Canavar } // 11. Hızı yükseltme (Hızlı) function checkSpeedAchievements() { if (ship.speed >= 10) { grantAchievement(23); } // Hızlı } // 12. Atış hızını düşürme (Ateş Yağmuru) function checkFireRateAchievements() { if (ship.fireRate <= 5) { grantAchievement(24); } // Ateş Yağmuru } // 13. Görev tamamlama (Görev Adamı) function checkQuestAchievements() { window._achQuestComplete++; if (window._achQuestComplete === 1) { grantAchievement(10); } // Görev Adamı } // 14. Dalga geçme (Dalga Geç, Dalga Ustası, Dalga Efsanesi) function checkWaveAchievements() { if (waveNumber + 1 >= 5) { grantAchievement(19); } // Dalga Geç if (waveNumber + 1 >= 10) { grantAchievement(20); } // Dalga Ustası if (waveNumber + 1 >= 20) { grantAchievement(41); } // Dalga Efsanesi } // 15. Yol katetme (Gezgin, Uzay Fatihi) function checkDistanceAchievements() { if (totalDistance >= 10000) { grantAchievement(14); } // Gezgin if (totalDistance >= 100000) { grantAchievement(42); } // Uzay Fatihi } // 16. Upgrade satın alma (Upgradeci, Upgrade Kralı) function checkUpgradeAchievements() { window._achUpgradeCount++; if (window._achUpgradeCount === 1) { grantAchievement(17); } // Upgradeci // Tüm upgrade'ler maksimuma çıkarıldıysa if (ship.maxHealth >= 300 && ship.maxShield >= 200 && ship.damage >= 50 && ship.speed >= 10 && ship.fireRate <= 5) { grantAchievement(45); // Upgrade Kralı } } // 17. Korsan üssü yok etme (Korsan Üssü, Korsan Üssü Avcısı) function checkPirateBaseAchievements() { window._achPirateBaseKills++; if (window._achPirateBaseKills === 1) { grantAchievement(35); } // Korsan Üssü if (window._achPirateBaseKills === 3) { grantAchievement(36); } // Korsan Üssü Avcısı } // 18. NPC öldürme (NPC Avcısı) function checkNPCKillAchievements() { window._achNPCKills++; if (window._achNPCKills === 10) { grantAchievement(48); } // NPC Avcısı } // 19. İstasyon teslimatı (Uzay İstasyonu, Büyük Teslimat) function checkStationDeliveryAchievements(allDone) { window._achStationDeliver++; grantAchievement(33); // Uzay İstasyonu if (allDone) { window._achStationAllDeliver++; grantAchievement(34); // Büyük Teslimat } } // 20. Solucan deliğinden geçme (Kaşif) function checkWormholeAchievement() { if (!window._achWormhole) { window._achWormhole = true; grantAchievement(4); // Kaşif } } // 21. Katil korsandan kaçma (Katil Kaçışı) function checkKatilEscapeAchievement() { if (!window._achKatilEscape) { window._achKatilEscape = true; grantAchievement(37); // Katil Kaçışı } } // 22. Bir korsandan kaçma (Korsan Kaçakçısı) function checkPirateEscapeAchievement() { grantAchievement(30); // Korsan Kaçakçısı } // 23. Bir NPC'yi koruma (Dost Canlısı) function checkProtectNPCAchievement() { if (!window._achProtectNPC) { window._achProtectNPC = true; grantAchievement(31); // Dost Canlısı } } // 24. Aynı anda 3 korsanla savaş (Korsan Saldırısı) function check3PirateFightAchievement() { if (!window._ach3PirateFight) { window._ach3PirateFight = true; grantAchievement(32); // Korsan Saldırısı } } // 25. Bir korsanla ticaret yapma (Korsan Dostu) function checkPirateTradeAchievement() { if (!window._achPirateTrade) { window._achPirateTrade = true; grantAchievement(47); // Korsan Dostu } } // 26. 30 dakika hayatta kalma (Uzayda Hayatta Kal) function checkSurvive30MinAchievement(elapsedSeconds) { if (elapsedSeconds >= 1800) { grantAchievement(49); } } // --- Achievements Panel Open/Close --- window.openAchievementsPanel = function (page) { if (window.achievementsPanelBG) { return; } var pageSize = 10; var total = window.achievementsList.length; var totalPages = Math.ceil(total / pageSize); var currentPage = typeof page === "number" ? Math.max(1, Math.min(page, totalPages)) : 1; window._achievementsPanelPage = currentPage; var panelW = 1100, panelH = 1200; var px = 2048 / 2, py = 2732 / 2; // BG var bg = LK.getAsset('resource', { anchorX: 0.5, anchorY: 0.5, x: px, y: py, scaleX: panelW / 40, scaleY: panelH / 40, tint: 0x222244, alpha: 0.97 }); // Title var title = new Text2("Achievements", { size: Math.round(unifiedFontSize * 1.3) + 2, fill: 0x00ffcc, align: "center" }); title.anchor.set(0.5, 0); title.x = px; title.y = py - panelH / 2 + 40; // Page info var pageInfo = new Text2("Page " + currentPage + "/" + totalPages, { size: Math.round(unifiedFontSize * 0.9) + 2, fill: 0xffffff, align: "center" }); pageInfo.anchor.set(0.5, 0); pageInfo.x = px; pageInfo.y = title.y + 60; // Achievements rows var startY = pageInfo.y + 60; var rowH = 90; var achRows = []; for (var i = 0; i < pageSize; i++) { var idx = (currentPage - 1) * pageSize + i; if (idx >= total) { break; } var ach = window.achievementsList[idx]; var unlocked = window.achievementsProgress.indexOf(ach.id) !== -1; var text = (unlocked ? "✅ " : "⬜ ") + ach.name + " - " + ach.desc + " [" + ach.difficulty + "]\nÖdül: " + ach.reward + " coin"; var color = unlocked ? 0x00ff99 : 0xffffff; var row = new Text2(text, { size: Math.round(unifiedFontSize * 0.9) + 2, fill: color, align: "left" }); row.anchor.set(0, 0.5); row.x = px - panelW / 2 + 40; row.y = startY + i * rowH; achRows.push(row); } // Page number buttons (1-2-3...) var btnW = 45, btnH = 35; var pageBtns = []; var pageBtnY = py + panelH / 2 - btnH / 2 - 20; var pageBtnStartX = px - (totalPages - 1) * (btnW + 10) / 2; for (var p = 1; p <= totalPages; p++) { var isCurrent = p === currentPage; var btn = new Text2("" + p, { size: Math.round(unifiedFontSize * 0.6) + 2, fill: isCurrent ? 0x00ffcc : 0xffffff, align: "center" }); btn.anchor.set(0.5, 0.5); btn.x = pageBtnStartX + (p - 1) * (btnW + 10); btn.y = pageBtnY; btn.width = btnW; btn.height = btnH; btn._achPageBtn = p; if (isCurrent) { btn.fill = 0x00ffcc; btn.setText("[" + p + "]"); } pageBtns.push(btn); } // Close button var closeBtn = new Text2("Close", { size: Math.round((unifiedFontSize + 4) * 1.3), fill: 0xff4444, align: "center" }); closeBtn.anchor.set(0.5, 0.5); closeBtn.x = px; closeBtn.y = title.y - 80; closeBtn._achCloseBtn = true; // Add to game game.addChild(bg); game.addChild(title); game.addChild(pageInfo); for (var i = 0; i < achRows.length; i++) { game.addChild(achRows[i]); } for (var i = 0; i < pageBtns.length; i++) { game.addChild(pageBtns[i]); } game.addChild(closeBtn); // Store refs for tap logic/cleanup window.achievementsPanelBG = bg; window.achievementsPanelTitle = title; window.achievementsPanelPageInfo = pageInfo; window.achievementsPanelRows = achRows; window.achievementsPanelPageBtns = pageBtns; window.achievementsPanelCloseBtn = closeBtn; }; window.closeAchievementsPanel = function () { if (!window.achievementsPanelBG) { return; } if (window.achievementsPanelBG.parent) { window.achievementsPanelBG.parent.removeChild(window.achievementsPanelBG); } if (window.achievementsPanelTitle && window.achievementsPanelTitle.parent) { window.achievementsPanelTitle.parent.removeChild(window.achievementsPanelTitle); } if (window.achievementsPanelPageInfo && window.achievementsPanelPageInfo.parent) { window.achievementsPanelPageInfo.parent.removeChild(window.achievementsPanelPageInfo); } if (window.achievementsPanelRows) { for (var i = 0; i < window.achievementsPanelRows.length; i++) { if (window.achievementsPanelRows[i] && window.achievementsPanelRows[i].parent) { window.achievementsPanelRows[i].parent.removeChild(window.achievementsPanelRows[i]); } } } if (window.achievementsPanelPageBtns) { for (var i = 0; i < window.achievementsPanelPageBtns.length; i++) { if (window.achievementsPanelPageBtns[i] && window.achievementsPanelPageBtns[i].parent) { window.achievementsPanelPageBtns[i].parent.removeChild(window.achievementsPanelPageBtns[i]); } } } if (window.achievementsPanelCloseBtn && window.achievementsPanelCloseBtn.parent) { window.achievementsPanelCloseBtn.parent.removeChild(window.achievementsPanelCloseBtn); } window.achievementsPanelBG = null; window.achievementsPanelTitle = null; window.achievementsPanelPageInfo = null; window.achievementsPanelRows = null; window.achievementsPanelPageBtns = null; window.achievementsPanelCloseBtn = null; window._achievementsPanelPage = null; }; // --- CREW PANEL SYSTEM --- // Global arrays for friends, dron, and mechanic var friends = []; var dron = null; var mechanic = null; // Helper: open Crew panel window.openCrewPanel = function () { if (window.crewPanelBG) { return; } var px = 2048 / 2, py = 2732 / 2; var panelW = 900, panelH = 900; var rowH = 110; var fontSize = Math.round(unifiedFontSize * 1.1) + 2; // BG var bg = LK.getAsset('resource', { anchorX: 0.5, anchorY: 0.5, x: px, y: py, scaleX: panelW / 40, scaleY: panelH / 40, tint: 0x1a2dcb, alpha: 0.97 }); // Title var title = new Text2("Crew (Friends & Dron)", { size: fontSize + 8, fill: 0x00ffcc, align: "center" }); title.anchor.set(0.5, 0); title.x = px; title.y = py - panelH / 2 + 40; // Info var info = new Text2("You can buy up to 3 Friends. Each Friend attacks with 50% of your attack power.\nDron collects nearby resources/coins.\nMechanic automatically repairs when health drops below 50%.", { size: fontSize - 2, fill: 0xffffff, align: "center" }); info.anchor.set(0.5, 0); info.x = px; info.y = title.y + 60; // Friend satın al butonları ve etiketleri window.crewFriendBtns = []; window.crewFriendLabels = []; for (var i = 0; i < 3; i++) { var label = new Text2("Friend #" + (i + 1) + (friends[i] ? " (Purchased)" : " (Empty)"), { size: fontSize, fill: friends[i] ? 0x00ff99 : 0xffffff, align: "left" }); label.anchor.set(0, 0.5); label.x = px - panelW / 2 + 60; label.y = info.y + 100 + rowH + i * rowH; window.crewFriendLabels.push(label); var btn = new Text2(friends[i] ? "Purchased" : "Buy (500 coin)", { size: fontSize, fill: friends[i] ? 0x888888 : 0x00ff99, align: "center" }); btn.anchor.set(0.5, 0.5); btn.x = px + 220; btn.y = label.y; btn._friendIdx = i; window.crewFriendBtns.push(btn); } // Dron satın al etiketi ve butonu var dronLabel = new Text2("Dron: " + (dron ? "Purchased" : "None"), { size: fontSize, fill: dron ? 0x00ff99 : 0xffffff, align: "left" }); dronLabel.anchor.set(0, 0.5); dronLabel.x = px - panelW / 2 + 60; dronLabel.y = info.y + 100 + rowH + 3 * rowH; var dronBtn = new Text2(dron ? "Purchased" : "Buy (1200 coin)", { size: fontSize, fill: dron ? 0x888888 : 0x00ff99, align: "center" }); dronBtn.anchor.set(0.5, 0.5); dronBtn.x = px + 220; dronBtn.y = dronLabel.y; // Mechanic satın al etiketi ve butonu var mechanicLabel = new Text2("Mechanic: " + (window.mechanic ? "Purchased" : "None"), { size: fontSize, fill: window.mechanic ? 0x00ff99 : 0xffffff, align: "left" }); mechanicLabel.anchor.set(0, 0.5); mechanicLabel.x = px - panelW / 2 + 60; mechanicLabel.y = info.y + 100 + rowH + 4 * rowH; var mechanicBtn = new Text2(window.mechanic ? "Purchased" : "Buy (800 coin)", { size: fontSize, fill: window.mechanic ? 0x888888 : 0x00ff99, align: "center" }); mechanicBtn.anchor.set(0.5, 0.5); mechanicBtn.x = px + 220; mechanicBtn.y = mechanicLabel.y; // Close button var closeBtn = new Text2("Close", { size: fontSize + 6, fill: 0xff4444, align: "center" }); closeBtn.anchor.set(0.5, 0.5); closeBtn.x = px; closeBtn.y = title.y - 80; // Add to game game.addChild(bg); game.addChild(title); game.addChild(info); for (var i = 0; i < window.crewFriendLabels.length; i++) { game.addChild(window.crewFriendLabels[i]); } for (var i = 0; i < window.crewFriendBtns.length; i++) { game.addChild(window.crewFriendBtns[i]); } game.addChild(dronLabel); game.addChild(dronBtn); game.addChild(mechanicLabel); game.addChild(mechanicBtn); game.addChild(closeBtn); // Store refs for tap logic/cleanup window.crewPanelBG = bg; window.crewPanelTitle = title; window.crewPanelInfo = info; window.crewPanelDronLabel = dronLabel; window.crewPanelDronBtn = dronBtn; window.crewPanelMechanicLabel = mechanicLabel; window.crewPanelMechanicBtn = mechanicBtn; window.crewPanelCloseBtn = closeBtn; }; window.closeCrewPanel = function () { if (!window.crewPanelBG) { return; } if (window.crewPanelBG.parent) { window.crewPanelBG.parent.removeChild(window.crewPanelBG); } if (window.crewPanelTitle && window.crewPanelTitle.parent) { window.crewPanelTitle.parent.removeChild(window.crewPanelTitle); } if (window.crewPanelInfo && window.crewPanelInfo.parent) { window.crewPanelInfo.parent.removeChild(window.crewPanelInfo); } if (window.crewFriendLabels) { for (var i = 0; i < window.crewFriendLabels.length; i++) { if (window.crewFriendLabels[i] && window.crewFriendLabels[i].parent) { window.crewFriendLabels[i].parent.removeChild(window.crewFriendLabels[i]); } } } if (window.crewFriendBtns) { for (var i = 0; i < window.crewFriendBtns.length; i++) { if (window.crewFriendBtns[i] && window.crewFriendBtns[i].parent) { window.crewFriendBtns[i].parent.removeChild(window.crewFriendBtns[i]); } } } if (window.crewPanelDronLabel && window.crewPanelDronLabel.parent) { window.crewPanelDronLabel.parent.removeChild(window.crewPanelDronLabel); } if (window.crewPanelDronBtn && window.crewPanelDronBtn.parent) { window.crewPanelDronBtn.parent.removeChild(window.crewPanelDronBtn); } if (window.crewPanelMechanicLabel && window.crewPanelMechanicLabel.parent) { window.crewPanelMechanicLabel.parent.removeChild(window.crewPanelMechanicLabel); } if (window.crewPanelMechanicBtn && window.crewPanelMechanicBtn.parent) { window.crewPanelMechanicBtn.parent.removeChild(window.crewPanelMechanicBtn); } if (window.crewPanelCloseBtn && window.crewPanelCloseBtn.parent) { window.crewPanelCloseBtn.parent.removeChild(window.crewPanelCloseBtn); } window.crewPanelBG = null; window.crewPanelTitle = null; window.crewPanelInfo = null; window.crewFriendLabels = null; window.crewFriendBtns = null; window.crewPanelDronLabel = null; window.crewPanelDronBtn = null; window.crewPanelMechanicLabel = null; window.crewPanelMechanicBtn = null; window.crewPanelCloseBtn = null; }; // --- Upgrades Panel Logic --- if (!window.openUpgradesPanel) { window.openUpgradesPanel = function () { if (window.upgradesPanel) { return; } // Panel size and position var panelW = 900, panelH = 1100; // Center the panel on the screen var px = 2048 / 2; var py = 2732 / 2; // Panel BG var bg = LK.getAsset('resource', { anchorX: 0.5, anchorY: 0.5, x: px, y: py, scaleX: panelW / 40, scaleY: panelH / 40, tint: 0x222244, alpha: 0.97 }); // Title var title = new Text2("Upgrades", { size: Math.round(unifiedFontSize * 1.3), fill: 0x00ffcc, align: "center" }); title.anchor.set(0.5, 0); title.x = px; title.y = py - panelH / 2 + 40; // Upgrade options (example: maxHealth, maxShield, damage, speed, fireRate) var upgFont = unifiedFontSize + 2; var upgSpacing = 120; var upgStartY = title.y + 120; var upgBtns = []; var upgDefs = [{ label: "Attack Range +5%\n(300 coin)", type: "range", fill: 0x99ff99, cost: { coins: 300, metal: 0, energy: 0 } }, { label: "Heal 5%\n(50 coin)", type: "heal5", fill: 0xff6699, cost: { coins: 50, metal: 0, energy: 0 } }, { label: "Max Health +20\n(600 coin, 60 metal)", type: "maxHealth", fill: 0x00ff99, cost: { coins: 600, metal: 60, energy: 0 } }, { label: "Max Shield +10\n(720 coin, 72 metal)", type: "maxShield", fill: 0x00ccff, cost: { coins: 720, metal: 72, energy: 0 } }, { label: "Damage +5\n(900 coin, 90 energy)", type: "damage", fill: 0xffcc00, cost: { coins: 900, metal: 0, energy: 90 } }, { label: "Speed +10%\n(1000 coin)", type: "speed", fill: 0x00ffff, cost: { coins: 1000, metal: 0, energy: 0 } }, { label: "Fire Rate +10%\n(1200 coin)", type: "fireRate", fill: 0xff99ff, cost: { coins: 1200, metal: 0, energy: 0 } }]; for (var i = 0; i < upgDefs.length; i++) { var upg = upgDefs[i]; var btn = new Text2(upg.label, { size: upgFont, fill: upg.fill, align: "center" }); btn.anchor.set(0.5, 0.5); btn.x = px; btn.y = upgStartY + i * upgSpacing; btn._isUpgradesPanelBtn = true; btn._upgradeType = upg.type; btn._upgradeCost = upg.cost; upgBtns.push(btn); } // Close button var closeBtn = new Text2("Close", { size: Math.round((unifiedFontSize + 4) * 1.3), fill: 0xff4444, align: "center" }); closeBtn.anchor.set(0.5, 0.5); closeBtn.x = px; closeBtn.y = title.y - 80; closeBtn._isUpgradesPanelCloseBtn = true; // Add to game game.addChild(bg); game.addChild(title); for (var i = 0; i < upgBtns.length; i++) { game.addChild(upgBtns[i]); } game.addChild(closeBtn); // Store refs for tap logic/cleanup window.upgradesPanel = bg; window.upgradesPanelTitle = title; window.upgradesPanelBtns = upgBtns; window.upgradesPanelCloseBtn = closeBtn; }; window.closeUpgradesPanel = function () { if (!window.upgradesPanel) { return; } if (window.upgradesPanel && window.upgradesPanel.parent) { window.upgradesPanel.parent.removeChild(window.upgradesPanel); } if (window.upgradesPanelTitle && window.upgradesPanelTitle.parent) { window.upgradesPanelTitle.parent.removeChild(window.upgradesPanelTitle); } if (window.upgradesPanelBtns) { for (var i = 0; i < window.upgradesPanelBtns.length; i++) { if (window.upgradesPanelBtns[i] && window.upgradesPanelBtns[i].parent) { window.upgradesPanelBtns[i].parent.removeChild(window.upgradesPanelBtns[i]); } } } if (window.upgradesPanelCloseBtn && window.upgradesPanelCloseBtn.parent) { window.upgradesPanelCloseBtn.parent.removeChild(window.upgradesPanelCloseBtn); } window.upgradesPanel = null; window.upgradesPanelTitle = null; window.upgradesPanelBtns = null; window.upgradesPanelCloseBtn = null; }; } // No wave, minimap, exp, or coordinate UI in this version // Create joystick joystickBase = game.addChild(LK.getAsset('joystickBase', { anchorX: 0.5, anchorY: 0.5, x: 300, y: 2432, scaleX: 1.3, scaleY: 1.3 })); joystickBase.alpha = 0.5; joystickHandle = game.addChild(LK.getAsset('joystickHandle', { anchorX: 0.5, anchorY: 0.5, x: 300, y: 2432, scaleX: 1.3, scaleY: 1.3 })); joystickHandle.alpha = 0.7; // Create ship ship = game.addChild(new Ship()); ship.x = 1024; ship.y = 1366; // Add health bar to ship ship._healthBar = new HealthBar(); ship._healthBar.set({ maxValue: function maxValue() { return ship.maxHealth; }, getValueFn: function getValueFn() { return ship.health; }, getMaxFn: function getMaxFn() { return ship.maxHealth; }, width: 80, height: 12 }); ship._healthBar.y = -70; ship.addChild(ship._healthBar); // Start background music LK.playMusic('bgmusic'); // Initialize star field (increased by 50%) and make them colorful for (var i = 0; i < 135; i++) { // 90 * 1.5 = 135 spawnStar(true); // pass true to enable colorful stars } // Spawn more map elements at start (20% more) for (var i = 0; i < 6; i++) { // was 5, now 6 spawnNPC(); } for (var i = 0; i < 6; i++) { // was 5, now 6 spawnPirate(); } // Ensure at least one Katil korsan exists at game start if (typeof window._katilPirateSpawned === "undefined" || !window._katilPirateSpawned) { var pirate = new Pirate(); var minLevel = 1 + waveNumber * 2; pirate.level = minLevel + 10; pirate.health = (30 + (pirate.level - 1) * 10) * 10; pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10; pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10; pirate.rpgName = "Katil"; pirate._isKatil = true; pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3)); pirate._attackRange = Math.floor(600 * 0.9); // Katil korsan için %10 daha kısa menzil window._katilPirateSpawned = true; // Spawn from edge like other pirates var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y - 1400; break; case 1: pirate.x = ship.x + 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; case 2: pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y + 1400; break; case 3: pirate.x = ship.x - 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; } // Add health bar to katil pirate pirate._healthBar = new HealthBar(); pirate._healthBar.set({ maxValue: function maxValue() { return pirate.health > 0 ? pirate.health : 0; }, getValueFn: function getValueFn() { return pirate.health > 0 ? pirate.health : 0; }, getMaxFn: function getMaxFn() { return 30 + waveNumber * 10; }, width: 60, height: 10 }); pirate._healthBar.y = -60; pirate.addChild(pirate._healthBar); pirates.push(pirate); game.addChild(pirate); } for (var i = 0; i < 2; i++) { // was 4, now 2 (50% reduction) spawnAsteroid(); } // Helper functions function spawnStar(colorful) { var star = new Star(); // If colorful is true, assign a random color if (colorful) { // Pick a random color from a vibrant palette var palette = [0xffe066, // yellow 0xff66cc, // pink 0x66ffcc, // aqua 0x66ccff, // blue 0xcc66ff, // purple 0xff6666, // red 0x66ff66, // green 0xffffff, // white 0xffcc66, // orange 0x66ffff // cyan ]; var color = palette[Math.floor(Math.random() * palette.length)]; if (star.children && star.children.length > 0 && star.children[0]) { star.children[0].tint = color; } } // Spawn at a random position within a large area around the ship var radius = 1800 + Math.random() * 1200; // 1800-3000 px from ship var angle = Math.random() * Math.PI * 2; star.x = ship.x + Math.cos(angle) * radius; star.y = ship.y + Math.sin(angle) * radius; stars.push(star); game.addChild(star); } function spawnNPC() { var npc = new NPC(); // Spawn from edges of visible area var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: // Top npc.x = ship.x + (Math.random() - 0.5) * 2000; npc.y = ship.y - 1500; break; case 1: // Right npc.x = ship.x + 1500; npc.y = ship.y + (Math.random() - 0.5) * 2000; break; case 2: // Bottom npc.x = ship.x + (Math.random() - 0.5) * 2000; npc.y = ship.y + 1500; break; case 3: // Left npc.x = ship.x - 1500; npc.y = ship.y + (Math.random() - 0.5) * 2000; break; } npc.baseY = npc.y; // --- Set NPC level and health to match pirate scaling --- var minLevel = 1 + waveNumber * 2; var maxLevel = minLevel + 2; npc.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1)); npc.health = 100 + (npc.level - 1) * 10; // Add health bar to NPC (for defend/hunt/escort/delivery/explore) npc._healthBar = new HealthBar(); npc._healthBar.set({ maxValue: function maxValue() { return npc.health !== undefined ? npc.health : 100; }, getValueFn: function getValueFn() { return npc.health !== undefined ? npc.health : 100; }, getMaxFn: function getMaxFn() { return 100 + (npc.level - 1) * 10; }, width: 60, height: 10 }); npc._healthBar.y = -60; npc.addChild(npc._healthBar); npcs.push(npc); game.addChild(npc); } function spawnPirate() { var pirate = new Pirate(); // Spawn from edges of visible area var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: // Top pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y - 1400; break; case 1: // Right pirate.x = ship.x + 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; case 2: // Bottom pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y + 1400; break; case 3: // Left pirate.x = ship.x - 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; } // Set pirate level and scale stats based on wave // Level range: wave 1 => 1-3, wave 2 => 3-5, wave 3 => 5-7, etc. var minLevel = 1 + waveNumber * 2; var maxLevel = minLevel + 2; // Katil korsan spawn etme şansı: Eğer henüz yoksa ve %20 şansla, bu korsanı Katil yap if (typeof window._katilPirateSpawned === "undefined") { window._katilPirateSpawned = false; } if (!window._katilPirateSpawned && Math.random() < 0.2) { // Katil korsan! pirate.level = minLevel + 10; pirate.health = (30 + (pirate.level - 1) * 10) * 10; pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10; pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10; pirate.rpgName = "Katil"; pirate._isKatil = true; window._katilPirateSpawned = true; // Katil korsan saldırı oranını 3 kat arttır pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3)); pirate._attackRange = Math.floor(600 * 0.9); // Katil korsan için %10 daha kısa menzil // Add Enemy text to Katil Korsan if (!pirate._enemyText) { pirate._enemyText = new Text2("Enemy", { size: 18, fill: 0xff4444, align: "center" }); pirate._enemyText.anchor.set(0.5, 0); pirate._enemyText.x = 0; pirate._enemyText.y = -50; pirate.addChild(pirate._enemyText); } // Katil korsan için özel bir renk veya efekt istenirse burada eklenebilir } else { pirate.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1)); pirate.health = 30 + (pirate.level - 1) * 10; pirate.damage = 5 + Math.floor((pirate.level - 1) * 1.5); pirate.defense = 2 + Math.floor((pirate.level - 1) * 1.2); } // Add health bar to pirate pirate._healthBar = new HealthBar(); pirate._healthBar.set({ maxValue: function maxValue() { return pirate.health > 0 ? pirate.health : 0; }, getValueFn: function getValueFn() { return pirate.health > 0 ? pirate.health : 0; }, getMaxFn: function getMaxFn() { return 30 + waveNumber * 10; }, width: 60, height: 10 }); pirate._healthBar.y = -60; pirate.addChild(pirate._healthBar); pirates.push(pirate); game.addChild(pirate); } function spawnAsteroid() { var asteroid = new Asteroid(); var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: asteroid.x = ship.x + (Math.random() - 0.5) * 2200; asteroid.y = ship.y - 1600; break; case 1: asteroid.x = ship.x + 1600; asteroid.y = ship.y + (Math.random() - 0.5) * 2200; break; case 2: asteroid.x = ship.x + (Math.random() - 0.5) * 2200; asteroid.y = ship.y + 1600; break; case 3: asteroid.x = ship.x - 1600; asteroid.y = ship.y + (Math.random() - 0.5) * 2200; break; } // Add health bar to asteroid asteroid._healthBar = new HealthBar(); asteroid._healthBar.set({ maxValue: function maxValue() { return asteroid.health; }, getValueFn: function getValueFn() { return asteroid.health; }, getMaxFn: function getMaxFn() { return 50; }, width: 50, height: 8 }); asteroid._healthBar.y = -50; asteroid.addChild(asteroid._healthBar); asteroids.push(asteroid); game.addChild(asteroid); } // Spawn an upgrade station at a random edge and add it to the world function spawnUpgradeStation() { // Artık UpgradeStation yerine DerelictDebris (enkaz) spawn ediyoruz var debris = new DerelictDebris(); // Spawn from edges of visible area var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: debris.x = ship.x + (Math.random() - 0.5) * 1800; debris.y = ship.y - 1400; break; case 1: debris.x = ship.x + 1400; debris.y = ship.y + (Math.random() - 0.5) * 1800; break; case 2: debris.x = ship.x + (Math.random() - 0.5) * 1800; debris.y = ship.y + 1400; break; case 3: debris.x = ship.x - 1400; debris.y = ship.y + (Math.random() - 0.5) * 1800; break; } upgradeStations.push(debris); game.addChild(debris); } // No trade stations in this version function createExplosion(x, y) { // %95 küçült: 8 * 0.05 = 0.4, en az 1 partikül bırakıyoruz for (var i = 0; i < 1; i++) { var particle = LK.getAsset('explosion', { anchorX: 0.5, anchorY: 0.5, x: x, y: y, scaleX: 0.22, // %95 küçült (1 - 0.95 = 0.05, yani 0.05x, ama çok küçük olur, 0.22 ile 8 partikülün toplam alanı korunur) scaleY: 0.22 }); particle.vx = (Math.random() - 0.5) * 2; // Hızı da azalt, daha küçük efekt için particle.vy = (Math.random() - 0.5) * 2; particle.life = 12; // Daha kısa ömür explosions.push(particle); game.addChild(particle); } } // No black holes or wormholes in this version function updateUI() { coinText.setText('Coins: ' + playerCoins); // Keep playerMetal/playerEnergy in sync with playerResources playerMetal = playerResources[0]; playerEnergy = playerResources[1]; // Use sorted resource label order for display if (window.resourceLabels && window.resourceLabelOrder && window.resourceLabels.length === window.resourceLabelOrder.length) { for (var i = 0; i < window.resourceLabelOrder.length; i++) { var resObj = window.resourceLabelOrder[i]; window.resourceLabels[i].setText(resObj.name + ': ' + (playerResources[resObj.origIdx] || 0)); } } // (No market price display) var healthPercent = Math.round(ship.health / ship.maxHealth * 100); healthText.setText('Health: %' + healthPercent); var shieldPercent = Math.round(ship.shield / ship.maxShield * 100); shieldText.setText('Shield: %' + shieldPercent); // Restore wave and exp UI if (typeof waveText !== "undefined") { waveText.setText('Wave: ' + (waveNumber + 1)); } if (typeof expText !== "undefined") { expText.setText('Level: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel)); } waveText.setText('Wave: ' + (waveNumber + 1)); expText.setText('Level: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel)); // Show player level/exp in GUI (top right, above repText) if (!window.levelText) { window.levelText = new Text2('Seviye: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel), { size: unifiedFontSize, fill: 0xffffff }); window.levelText.anchor.set(1, 0); window.levelText.x = -20; window.levelText.y = 20; // moved up by one row (40px) LK.gui.topRight.addChild(window.levelText); } window.levelText.setText('Seviye: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel)); // Update player level/exp in top right if (window.levelText) { window.levelText.setText('Seviye: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel)); } // Update health text color if (ship.health > 60) { healthText.fill = 0x00ff00; } else if (ship.health > 30) { healthText.fill = 0xffff00; } else { healthText.fill = 0xff0000; } // Update shield text color if (ship.shield > 30) { shieldText.fill = 0x0088ff; } else if (ship.shield > 10) { shieldText.fill = 0xffff00; } else { shieldText.fill = 0xff0000; } storage.playerLevel = playerLevel; storage.playerEXP = playerEXP; } function saveProgress() { storage.coins = playerCoins; var resourceKeys = ['metal', 'energy', 'crystal', 'gas', 'ice', 'uranium', 'silicon', 'carbon', 'plasma', 'antimatter']; for (var i = 0; i < resourceKeys.length; i++) { storage[resourceKeys[i]] = playerResources[i] || 0; } // Keep playerMetal/playerEnergy in sync with playerResources playerMetal = playerResources[0]; playerEnergy = playerResources[1]; storage.questsCompleted = questsCompleted; } // Event handlers game.down = function (x, y, obj) { // --- BLOCK ALL TAPS IF ANY PANEL IS OPEN (fixes tap blocking bug) --- // --- ALSO BLOCK ALL INPUT IF SHIP IS DEAD --- if (window.stationDeliveryPanel && window.stationDeliveryPanelBG && window.stationDeliveryPanelBG.parent || window.resourceTradePanelState && window.resourceTradePanelState.open || window._resourceStockPanel && window._resourceStockPanel.bg && window._resourceStockPanel.bg.parent || window.shipUpgradePanel && window.shipUpgradePanel.parent || window.repairPanel && window.repairPanel.parent || ship && ship.health <= 0) { // If a panel is open or ship is dead, only allow taps on that panel's close/confirm buttons (handled below), block all other taps // (Panel-specific tap logic is handled further down, so just return here to block all other UI/joystick/game taps) // Exception: let panel close/confirm buttons work (handled in their own blocks) // If ship is dead, block all input until respawn label is gone if (ship && ship.health <= 0) { return; } // (Do not return here, let the rest of the handler run for panel close/confirm buttons) } else { // --- PAUSE BUTTON (top left) tap detection: always allow pause tap, never block it --- if (x < 100 && y < 100) { // This is the reserved area for the platform pause/menu button. // Let the LK engine handle the pause/menu tap, do not block it. // Do not return here, let the engine handle it. } // --- GLOBAL MARKET BUTTON HANDLER (exact image area) --- if (window.globalMarketBtn && window.globalMarketBtn.parent) { var btn = window.globalMarketBtn; var bx = btn.x, by = btn.y; var bw = btn.width || 320, bh = btn.height || 80; // LK.gui.bottomRight is anchored at (1,1), so x,y negative from bottom right // The area is anchored at (1,1), so its (x,y) is the bottom right corner of the area // So the area covers (bx-bw, by-bh) to (bx, by) if (x > 2048 + bx - bw && x < 2048 + bx && y > 2732 + by - bh && y < 2732 + by) { window.openResourceTradePanel(); return; } } // --- CREW PANEL BUTTON HANDLER (just below Achievements image) --- if (window.newImageAboveStorage) { // Place a transparent tap area over the newImageAboveStorage image if not already present if (!window.crewPanelBtn) { var btn = new Text2("", { size: 1, fill: 0xffffff, align: "center" }); btn.anchor.set(1, 0); btn.x = window.newImageAboveStorage.x; // Move the Crew panel button to exactly the same y as the image btn.y = window.newImageAboveStorage.y; // Grow the button downward by the image's height (so it covers 2x the image height, starting at image.y) btn.width = window.newImageAboveStorage.width; btn.height = window.newImageAboveStorage.height * 2; btn.alpha = 0; window.crewPanelBtn = btn; LK.gui.topRight.addChild(btn); } var btn = window.crewPanelBtn; var bx = btn.x, by = btn.y; var bw = btn.width, bh = btn.height; // LK.gui.topRight is anchored at (1,0), so x negative from right, y positive from top if (x > 2048 + bx - bw && x < 2048 + bx && y > by && y < by + bh) { window.openCrewPanel(); return; } } // --- ACHIEVEMENTS PANEL BUTTON HANDLER (Achievements image tap) --- if (window.achievementsPanelBtn && window.achievementsPanelBtn.parent) { var btn = window.achievementsPanelBtn; var bx = btn.x, by = btn.y; var bw = btn.width, bh = btn.height; // LK.gui.topRight is anchored at (1,0), so x negative from right, y positive from top // So the area covers (2048+bx-bw, by) to (2048+bx, by+bh) if (x > 2048 + bx - bw && x < 2048 + bx && y > by && y < by + bh) { window.openAchievementsPanel(1); return; } } // --- CREW PANEL HANDLER --- if (window.crewPanelBG) { // Close btn if (window.crewPanelCloseBtn) { var bx = window.crewPanelCloseBtn.x, by = window.crewPanelCloseBtn.y; var bw = window.crewPanelCloseBtn.width || 300, bh = window.crewPanelCloseBtn.height || 80; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { window.closeCrewPanel(); return; } } // Friend satın al btns if (window.crewFriendBtns) { for (var i = 0; i < window.crewFriendBtns.length; i++) { var btn = window.crewFriendBtns[i]; var bx = btn.x, by = btn.y; var bw = btn.width || 220, bh = btn.height || 60; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { var idx = btn._friendIdx; if (!friends[idx] && playerCoins >= 500) { playerCoins -= 500; // Create Friend instance (see class below) var friend = new Friend(); friend.x = ship.x + 80 + 60 * idx; friend.y = ship.y + 80; friends[idx] = friend; game.addChild(friend); btn.setText("Purchased"); btn.fill = 0x888888; if (window.crewFriendLabels && window.crewFriendLabels[idx]) { window.crewFriendLabels[idx].setText("Friend #" + (idx + 1) + " (Purchased)"); window.crewFriendLabels[idx].fill = 0x00ff99; } updateUI && updateUI(); saveProgress && saveProgress(); } else if (friends[idx]) { btn.setText("Already Purchased"); btn.fill = 0x888888; } else if (playerCoins < 500) { btn.setText("Insufficient coins!"); btn.fill = 0xff4444; } // Reset label after short delay (function (b, orig, origFill) { LK.setTimeout(function () { b.setText(friends[idx] ? "Purchased" : "Buy (500 coin)"); b.fill = friends[idx] ? 0x888888 : 0x00ff99; }, 900); })(btn, "Buy (500 coin)", 0x00ff99); return; } } } // Dron satın al btn if (window.crewPanelDronBtn) { var btn = window.crewPanelDronBtn; var bx = btn.x, by = btn.y; var bw = btn.width || 220, bh = btn.height || 60; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { if (!dron && playerCoins >= 1200) { playerCoins -= 1200; dron = new Dron(); dron.x = ship.x - 100; dron.y = ship.y + 80; game.addChild(dron); btn.setText("Purchased"); btn.fill = 0x888888; if (window.crewPanelDronLabel) { window.crewPanelDronLabel.setText("Dron: Purchased"); window.crewPanelDronLabel.fill = 0x00ff99; } updateUI && updateUI(); saveProgress && saveProgress(); } else if (dron) { btn.setText("Already Purchased"); btn.fill = 0x888888; } else if (playerCoins < 1200) { btn.setText("Insufficient coins!"); btn.fill = 0xff4444; } (function (b, orig, origFill) { LK.setTimeout(function () { b.setText(dron ? "Purchased" : "Buy (1200 coin)"); b.fill = dron ? 0x888888 : 0x00ff99; }, 900); })(btn, "Buy (1200 coin)", 0x00ff99); return; } } // Mechanic satın al btn if (window.crewPanelMechanicBtn) { var btn = window.crewPanelMechanicBtn; var bx = btn.x, by = btn.y; var bw = btn.width || 220, bh = btn.height || 60; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { if (!window.mechanic && playerCoins >= 800) { playerCoins -= 800; window.mechanic = new Mechanic(); window.mechanic.x = ship.x - 120; window.mechanic.y = ship.y - 80; game.addChild(window.mechanic); btn.setText("Purchased"); btn.fill = 0x888888; if (window.crewPanelMechanicLabel) { window.crewPanelMechanicLabel.setText("Mechanic: Purchased"); window.crewPanelMechanicLabel.fill = 0x00ff99; } updateUI && updateUI(); saveProgress && saveProgress(); } else if (window.mechanic) { btn.setText("Already Purchased"); btn.fill = 0x888888; } else if (playerCoins < 800) { btn.setText("Insufficient coins!"); btn.fill = 0xff4444; } (function (b, orig, origFill) { LK.setTimeout(function () { b.setText(window.mechanic ? "Purchased" : "Buy (800 coin)"); b.fill = window.mechanic ? 0x888888 : 0x00ff99; }, 900); })(btn, "Buy (800 coin)", 0x00ff99); return; } } // Block all other taps when panel is open return; } // --- UPGRADE PANEL BUTTON HANDLER (upg image tap) --- if (window.upgPanelBtn && window.upgPanelBtn.parent) { var btn = window.upgPanelBtn; var bx = btn.x, by = btn.y; var bw = btn.width, bh = btn.height; // LK.gui.topRight is anchored at (1,0), so x negative from right, y positive from top // So the area covers (2048+bx-bw, by) to (2048+bx, by+bh) if (x > 2048 + bx - bw && x < 2048 + bx && y > by && y < by + bh) { window.openUpgradesPanel(); return; } } } // --- RESOURCE TRADE PANEL INDEPENDENT BUTTON HANDLER --- // If the independent button exists, check tap if (window.resourceTradePanelIndependentBtn && window.resourceTradePanelIndependentBtn.parent) { var btn = window.resourceTradePanelIndependentBtn; var bx = btn.x, by = btn.y; var bw = btn.width, bh = btn.height; // LK.gui.bottomRight is anchored at (1,1), so x,y negative from bottom right // The area is anchored at (1,1), so its (x,y) is the bottom right corner of the area // So the area covers (bx-bw, by-bh) to (bx, by) if (x > 2048 + bx - bw && x < 2048 + bx && y > 2732 + by - bh && y < 2732 + by) { // Open 'Hammadde Stoğu' panel if (typeof window.openResourceStockPanel === "function") { window.openResourceStockPanel(); } else { // Fallback: show a simple panel with player's resource stock if (window._resourceStockPanel) { return; } var panelW = 900, panelH = 900; var px = 2048 / 2, py = 2732 / 2; // Increase font size by 2 for all panel texts/buttons var panelFontSize = unifiedFontSize + 4; var labelFontSize = panelFontSize; var closeBtnFontSize = panelFontSize; var titleFontSize = panelFontSize; // Resize panel background to fit enlarged text/buttons var rowH = 70 + 8; var panelW = 900 + 120; var panelH = 900 + 2 * rowH; var bg = LK.getAsset('resource', { anchorX: 0.5, anchorY: 0.5, x: px, y: py, scaleX: panelW / 40, scaleY: panelH / 40, tint: 0x223355, alpha: 0.97 }); var title = new Text2("Hammadde Stoğu", { size: titleFontSize, fill: 0x00ffcc, align: "center" }); title.anchor.set(0.5, 0); title.x = px; title.y = py - panelH / 2 + 40; // List resources var labels = []; var startY = title.y + 80; for (var i = 0; i < window.resourceLabelOrder.length; i++) { var resObj = window.resourceLabelOrder[i]; var idx = resObj.origIdx; var label = new Text2(resObj.name + ": " + (playerResources[idx] || 0), { size: labelFontSize, fill: 0xffffff, align: "left" }); label.anchor.set(0, 0.5); label.x = px - panelW / 2 + 60; label.y = startY + i * rowH; labels.push(label); } // Close button var closeBtn = new Text2("Kapat", { size: Math.round((unifiedFontSize + 4) * 1.3), // 2 font larger than Coins, then 30% larger fill: 0xff4444, align: "center" }); closeBtn.anchor.set(0.5, 0.5); closeBtn.x = px; closeBtn.y = title.y - 110; // above the title // Add to game game.addChild(bg); game.addChild(title); for (var i = 0; i < labels.length; i++) { game.addChild(labels[i]); } game.addChild(closeBtn); // Store refs for tap logic/cleanup window._resourceStockPanel = { bg: bg, title: title, labels: labels, closeBtn: closeBtn }; // Add close logic closeBtn._isResourceStockCloseBtn = true; } return; } } // --- RESOURCE TRADE PANEL HANDLER --- if (window._resourceStockPanel && window._resourceStockPanel.closeBtn) { var btn = window._resourceStockPanel.closeBtn; var bx = btn.x, by = btn.y; var bw = btn.width || 300, bh = btn.height || 80; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { // Remove all panel elements if (window._resourceStockPanel.bg && window._resourceStockPanel.bg.parent) { window._resourceStockPanel.bg.parent.removeChild(window._resourceStockPanel.bg); } if (window._resourceStockPanel.title && window._resourceStockPanel.title.parent) { window._resourceStockPanel.title.parent.removeChild(window._resourceStockPanel.title); } if (window._resourceStockPanel.labels) { for (var i = 0; i < window._resourceStockPanel.labels.length; i++) { if (window._resourceStockPanel.labels[i] && window._resourceStockPanel.labels[i].parent) { window._resourceStockPanel.labels[i].parent.removeChild(window._resourceStockPanel.labels[i]); } } } if (window._resourceStockPanel.closeBtn && window._resourceStockPanel.closeBtn.parent) { window._resourceStockPanel.closeBtn.parent.removeChild(window._resourceStockPanel.closeBtn); } window._resourceStockPanel = null; return; } } if (window.resourceTradePanelState && window.resourceTradePanelState.open) { // Close btn if (window.resourceTradePanelCloseBtn) { var bx = window.resourceTradePanelCloseBtn.x, by = window.resourceTradePanelCloseBtn.y; var bw = window.resourceTradePanelCloseBtn.width || 300, bh = window.resourceTradePanelCloseBtn.height || 80; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { window.closeResourceTradePanel(); return; } } // Sat btns if (window.resourceTradeSellBtns) { for (var i = 0; i < window.resourceTradeSellBtns.length; i++) { var btn = window.resourceTradeSellBtns[i]; var bx = btn.x, by = btn.y; var bw = btn.width || 120, bh = btn.height || 60; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { var idx = btn._resourceIdx; var amount = playerResources[idx] || 0; var value = window.resourceTradeValues ? window.resourceTradeValues[idx] : 0; if (amount > 0 && value > 0) { playerCoins += value * amount; playerResources[idx] = 0; // --- BAŞARIM: Takas yapınca --- checkTradeAchievements(); btn.setText("Satıldı!"); btn.fill = 0x00ffcc; if (window.updateResourceTradePanel) { window.updateResourceTradePanel(); } updateUI(); saveProgress(); } else { btn.setText("Yok!"); btn.fill = 0xff4444; } // Reset label after short delay (function (b, orig, origFill) { LK.setTimeout(function () { b.setText("Sat"); b.fill = 0x00ff99; }, 700); })(btn, "Sat", 0x00ff99); return; } } } // Tümünü Sat btns if (window.resourceTradeSellAllBtns) { for (var i = 0; i < window.resourceTradeSellAllBtns.length; i++) { var btn = window.resourceTradeSellAllBtns[i]; var bx = btn.x, by = btn.y; var bw = btn.width || 180, bh = btn.height || 60; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { var idx = btn._resourceIdx; var amount = playerResources[idx] || 0; var value = window.resourceTradeValues ? window.resourceTradeValues[idx] : 0; if (amount > 0 && value > 0) { playerCoins += value * amount; playerResources[idx] = 0; btn.setText("Hepsi Satıldı!"); btn.fill = 0x00ffcc; if (window.updateResourceTradePanel) { window.updateResourceTradePanel(); } updateUI(); saveProgress(); } else { btn.setText("Yok!"); btn.fill = 0xff4444; } (function (b, orig, origFill) { LK.setTimeout(function () { b.setText("Tümünü Sat"); b.fill = 0x00cc99; }, 700); })(btn, "Tümünü Sat", 0x00cc99); return; } } } // Al btns if (window.resourceTradeBuyBtns) { for (var i = 0; i < window.resourceTradeBuyBtns.length; i++) { var btn = window.resourceTradeBuyBtns[i]; var bx = btn.x, by = btn.y; var bw = btn.width || 120, bh = btn.height || 60; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { var idx = btn._resourceIdx; var value = window.resourceTradeValues ? window.resourceTradeValues[idx] : 0; // Satın alma miktarı: 1 (veya istenirse artırılabilir) var buyAmount = 1; var totalCost = value * buyAmount; if (playerCoins >= totalCost && value > 0) { playerCoins -= totalCost; playerResources[idx] = (playerResources[idx] || 0) + buyAmount; btn.setText("Alındı!"); btn.fill = 0x00ffcc; if (window.updateResourceTradePanel) { window.updateResourceTradePanel(); } updateUI(); saveProgress(); } else { btn.setText("Yetersiz coin!"); btn.fill = 0xff4444; } (function (b, orig, origFill) { LK.setTimeout(function () { b.setText("Al"); b.fill = 0x0099ff; }, 700); })(btn, "Al", 0x0099ff); return; } } } // Block all other taps when panel is open return; } // --- UZAY İSTASYONU TESLİMAT BUTTON HANDLER --- // Handle tap on stationDeliveryBtnArea (transparent area, exactly image size) if (window.stationDeliveryBtnArea && window.stationDeliveryBtnArea.parent) { var btn = window.stationDeliveryBtnArea; var bx = btn.x, by = btn.y; var bw = btn.width, bh = btn.height; // LK.gui.bottomRight is anchored at (1,1), so x,y negative from bottom right // The area is anchored at (1,1), so its (x,y) is the bottom right corner of the area // So the area covers (bx-bw, by-bh) to (bx, by) if (x > 2048 + bx - bw && x < 2048 + bx && y > 2732 + by - bh && y < 2732 + by) { window.openStationDeliveryPanel(); return; } } // LK.gui.bottomRight is anchored at (1,1), so x,y negative from bottom right if (window.stationDeliveryBtn && window.stationDeliveryBtn.parent) { var btn = window.stationDeliveryBtn; var bx = btn.x, by = btn.y; // Button is about 180x80 px, so check if x is within 200px of right edge and y within 300-480px of bottom if (x > 2048 - 200 && y > 2732 - 300 && y < 2732 - 100) { window.openStationDeliveryPanel(); return; } } // --- UZAY İSTASYONU TESLİMAT PANEL HANDLER --- if (window.stationDeliveryPanel) { // Close btn if (window.stationDeliveryPanelCloseBtn) { var bx = window.stationDeliveryPanelCloseBtn.x, by = window.stationDeliveryPanelCloseBtn.y; var bw = window.stationDeliveryPanelCloseBtn.width || 300, bh = window.stationDeliveryPanelCloseBtn.height || 80; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { window.closeStationDeliveryPanel(); return; } } // Teslim Et btns if (window.stationDeliveryDeliverBtns) { for (var i = 0; i < window.stationDeliveryDeliverBtns.length; i++) { var btn = window.stationDeliveryDeliverBtns[i]; var bx = btn.x, by = btn.y; var bw = btn.width || 120, bh = btn.height || 60; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { var idx = btn._stationResIdx; var req = window.stationDeliveryState.requests[idx]; var delivered = window.stationDeliveryState.delivered[idx]; var canDeliver = Math.min(playerResources[idx], req - delivered); if (canDeliver > 0) { playerResources[idx] -= canDeliver; window.stationDeliveryState.delivered[idx] += canDeliver; btn.setText("Teslim Edildi!"); btn.fill = 0x00ffcc; // Update label if (window.stationDeliveryLabels && window.stationDeliveryLabels[i]) { window.stationDeliveryLabels[i].setText(window.resourceLabelOrder[i].name + ": " + window.stationDeliveryState.delivered[idx] + " / " + req); } // Update stock label if (window.stationDeliveryStockLabels && window.stationDeliveryStockLabels[i]) { var stockAmount = typeof playerResources[idx] !== "undefined" ? playerResources[idx] : 0; window.stationDeliveryStockLabels[i].setText("Stok: " + stockAmount); } updateUI(); saveProgress(); } else { btn.setText("Yok!"); btn.fill = 0xff4444; } // Reset label after short delay (function (b, orig, origFill) { LK.setTimeout(function () { b.setText(orig); b.fill = origFill; }, 700); })(btn, "Teslim Et", 0x00ff99); return; } } } // Teslimatı Tamamla (ödül al) butonu if (window.stationDeliveryPanelClaimBtn) { var btn = window.stationDeliveryPanelClaimBtn; var bx = btn.x, by = btn.y; var bw = btn.width || 400, bh = btn.height || 80; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { // Check if all delivered var allDone = true; for (var i = 0; i < 10; i++) { if (window.stationDeliveryState.delivered[i] < window.stationDeliveryState.requests[i]) { allDone = false; break; } } if (allDone && !window.stationDeliveryState.rewardClaimed) { // Calculate total value (normal market price * delivered) * (1+bonus%) var totalValue = 0; for (var i = 0; i < 10; i++) { var price = 1; // Market removed, use static price totalValue += price * window.stationDeliveryState.requests[i]; } var bonus = window.stationDeliveryState.bonus; var reward = Math.round(totalValue * (1 + bonus / 100)); playerCoins += reward; window.stationDeliveryState.rewardClaimed = true; btn.setText("Ödül Alındı! +" + reward + " coin"); btn.fill = 0x00ffcc; // Show floating label var label = new Text2("İstasyon Teslimatı Tamamlandı!\n+" + reward + " coin", { size: 48, fill: 0xffcc00, align: "center" }); label.anchor.set(0.5, 0.5); label.x = 1024; label.y = 600; game.addChild(label); LK.setTimeout(function () { if (label.parent) { label.parent.removeChild(label); } }, 1800); updateUI(); saveProgress(); } else if (window.stationDeliveryState.rewardClaimed) { btn.setText("Zaten Alındı!"); btn.fill = 0x888888; } else { btn.setText("Eksik Teslimat!"); btn.fill = 0xff4444; } // Reset label after short delay (function (b, orig, origFill) { LK.setTimeout(function () { b.setText("Tümünü Teslim Et & Ödülü Al"); b.fill = 0xffcc00; }, 1200); })(btn, "Tümünü Teslim Et & Ödülü Al", 0xffcc00); return; } } // Block all other taps when panel is open return; } // --- RESET BUTTON HANDLER --- if (game._resetBtn && game._resetBtn.parent) { // Convert GUI coordinates for bottomRight // LK.gui.bottomRight is anchored at (1,1) so x,y are negative from bottom right var btn = game._resetBtn; var bx = btn.x, by = btn.y; // Button is about 180x80 px, but let's use a safe area // Since anchor is (1,1), bottom right, and x/y are negative from bottom right // So, for a tap, check if x is within 200px of right edge and y within 100px of bottom // LK.gui.bottomRight: x=0,y=0 is bottom right, so x,y negative if (x > 2048 - 200 && y > 2732 - 100) { // Clear all persistent storage, including achievements and all custom progress storage.coins = 200; // Başlangıçta 200 coin ile başla storage.metal = 0; storage.energy = 0; storage.crystal = 0; storage.gas = 0; storage.ice = 0; storage.uranium = 0; storage.silicon = 0; storage.carbon = 0; storage.plasma = 0; storage.antimatter = 0; storage.questsCompleted = 0; storage.playerLevel = 1; storage.playerEXP = 0; // Clear all achievements storage.achievements = []; // Clear all custom progress keys storage.rangeUpgradeLevel = 0; storage.fireRateUpgradeLevel = 0; // Remove any other custom keys that may have been set // (defensive: remove all keys except built-in ones) for (var k in storage) { if (storage.hasOwnProperty(k) && ["coins", "metal", "energy", "crystal", "gas", "ice", "uranium", "silicon", "carbon", "plasma", "antimatter", "questsCompleted", "playerLevel", "playerEXP", "achievements", "rangeUpgradeLevel", "fireRateUpgradeLevel"].indexOf(k) === -1) { try { storage[k] = 0; } catch (e) {} } } // Show a quick feedback btn.setText("Reset!"); btn.fill = 0x00ff99; // Reload game (reset all progress) LK.showGameOver(); return; } } // --- NPC ile savaş butonu handler --- if (window.npcFightBtn) { var bx = window.npcFightBtn.x, by = window.npcFightBtn.y; var bw = window.npcFightBtn.width || 400, bh = window.npcFightBtn.height || 100; // Accept generous tap area if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { window.npcFightMode = !window.npcFightMode; if (window.npcFightMode) { window.npcFightBtn.setText("NPC Savaş: AÇIK"); window.npcFightBtn.fill = 0xff0000; } else { window.npcFightBtn.setText("NPC ile Savaş"); window.npcFightBtn.fill = 0xff4444; } return; } } if (window.repairPanel && window.repairBtn && window.repairBtn.parent) { // Check tap on repair button var btn = window.repairBtn; var bx = btn.x, by = btn.y; if (x > bx - 90 && x < bx + 90 && y > by - 40 && y < by + 40) { // Try to repair var costCoins = btn._repairCostCoins, costMetal = btn._repairCostMetal, costEnergy = btn._repairCostEnergy; if (playerCoins >= costCoins && playerMetal >= costMetal && playerEnergy >= costEnergy) { playerCoins -= costCoins; playerMetal -= costMetal; playerEnergy -= costEnergy; ship.health = ship.maxHealth; // --- BAŞARIM: Tamir --- checkRepairAchievements(); updateUI(); saveProgress(); // Show feedback btn.setText("Tamir Edildi!"); btn.fill = 0x00ffcc; // Remove panel after short delay var expire = LK.ticks + 40; var origUpdate = game.update; game.update = function () { if (LK.ticks > expire) { if (window.repairPanel && window.repairPanel.parent) { window.repairPanel.parent.removeChild(window.repairPanel); } if (window.repairText && window.repairText.parent) { window.repairText.parent.removeChild(window.repairText); } if (window.repairBtn && window.repairBtn.parent) { window.repairBtn.parent.removeChild(window.repairBtn); } if (window.cancelRepairBtn && window.cancelRepairBtn.parent) { window.cancelRepairBtn.parent.removeChild(window.cancelRepairBtn); } window.repairPanel = null; window.repairText = null; window.repairBtn = null; window.cancelRepairBtn = null; game.update = origUpdate; } if (origUpdate) { origUpdate.apply(game, arguments); } }; } else { // Not enough resources btn.setText("Yetersiz kaynak!"); btn.fill = 0xff4444; } return; } // Check tap on cancel button var cbtn = window.cancelRepairBtn; if (cbtn) { var cbx = cbtn.x, cby = cbtn.y; if (x > cbx - 90 && x < cbx + 90 && y > cby - 40 && y < cby + 40) { // Remove all repair panel elements, allow closing even if not repaired if (window.repairPanel && window.repairPanel.parent) { window.repairPanel.parent.removeChild(window.repairPanel); } if (window.repairText && window.repairText.parent) { window.repairText.parent.removeChild(window.repairText); } if (window.repairBtn && window.repairBtn.parent) { window.repairBtn.parent.removeChild(window.repairBtn); } if (window.cancelRepairBtn && window.cancelRepairBtn.parent) { window.cancelRepairBtn.parent.removeChild(window.cancelRepairBtn); } window.repairPanel = null; window.repairText = null; window.repairBtn = null; window.cancelRepairBtn = null; window._showRepairPanel = false; return; } } } // --- Health bar tap to show repair panel --- if (healthText && healthText.parent && healthText._isHealthBar && ship.health < ship.maxHealth && ship.health > 0) { // Get healthText bounds in screen coordinates // healthText is anchored at (0.5, 0), at top center // Let's estimate its bounds var hx = healthText.x, hy = healthText.y; var hw = healthText.width || 300; var hh = healthText.height || 60; // Accept generous tap area if (x > hx - hw / 2 && x < hx + hw / 2 && y > hy && y < hy + hh) { window._showRepairPanel = true; return; } } // --- SHIP TAP: Show upgrade/can-buy-health panel --- // Oyuncu gemisi resmi üzerindeki buton GİZLENDİ. Artık bu panel gemi resmine tıklanarak açılamaz. // (Bu alanı bilerek boş bırakıyoruz, panel açılmayacak.) // --- Only allow joystick drag if ship is alive --- if (ship && ship.health > 0 && Math.sqrt(Math.pow(x - joystickBase.x, 2) + Math.pow(y - joystickBase.y, 2)) < 130) { isDragging = true; joystickHandle.x = x; joystickHandle.y = y; return; // Prevent other interactions when dragging joystick } // --- If any panel is open, block all other game/ship input (except panel close/confirm buttons) --- if (window.stationDeliveryPanel && window.stationDeliveryPanelBG && window.stationDeliveryPanelBG.parent || window.resourceTradePanelState && window.resourceTradePanelState.open || window._resourceStockPanel && window._resourceStockPanel.bg && window._resourceStockPanel.bg.parent || window.shipUpgradePanel && window.shipUpgradePanel.parent || window.repairPanel && window.repairPanel.parent) { // Only allow panel close/confirm buttons (handled below), block all other game/ship input // (Do not return here, let the rest of the handler run for panel close/confirm buttons) } else { // --- SHIP UPGRADE PANEL BUTTONS --- if (window.shipUpgradePanel) { // (existing ship upgrade panel logic unchanged) // ... (no change here) } // --- ACHIEVEMENTS PANEL BUTTONS --- if (window.achievementsPanelBG) { // Close button var cb = window.achievementsPanelCloseBtn; if (cb) { var bx = cb.x, by = cb.y; var bw = cb.width || 300, bh = cb.height || 80; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { window.closeAchievementsPanel(); return; } } // Page number buttons var pageBtns = window.achievementsPanelPageBtns; if (pageBtns) { for (var i = 0; i < pageBtns.length; i++) { var btn = pageBtns[i]; var bx = btn.x, by = btn.y; var bw = btn.width || 90, bh = btn.height || 70; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { if (window._achievementsPanelPage !== btn._achPageBtn) { window.closeAchievementsPanel(); window.openAchievementsPanel(btn._achPageBtn); } return; } } } // Block all other taps when panel is open return; } // --- UPGRADE PANEL BUTTONS --- if (window.upgradesPanel) { // Upgrade buttons var btns = window.upgradesPanelBtns || []; for (var i = 0; i < btns.length; i++) { var btn = btns[i]; if (!btn) { continue; } var bx = btn.x, by = btn.y; var bw = btn.width || 400, bh = btn.height || 80; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { // Try to buy upgrade var upg = btn._upgradeType; var cost = btn._upgradeCost; if (upg === "range" && playerCoins >= cost.coins) { // Increase attack range by 5% if (typeof window._rangeUpgradeLevel === "undefined") { window._rangeUpgradeLevel = 0; } window._rangeUpgradeLevel++; // Store in storage for persistence storage.rangeUpgradeLevel = window._rangeUpgradeLevel; playerCoins -= cost.coins; LK.effects.flashObject(ship, 0x99ff99, 400); btn.setText("Alındı!"); btn.fill = 0x00ffcc; // --- BAŞARIM: Upgrade satın alma --- checkUpgradeAchievements(); updateUI(); saveProgress(); } else if (upg === "heal5" && playerCoins >= cost.coins) { // Heal 5% of max health, but not above max var healAmount = Math.round(ship.maxHealth * 0.05); ship.health = Math.min(ship.health + healAmount, ship.maxHealth); playerCoins -= cost.coins; LK.effects.flashObject(ship, 0xff6699, 400); btn.setText("Yenilendi!"); btn.fill = 0x00ffcc; // --- BAŞARIM: Tamir --- checkRepairAchievements(); updateUI(); saveProgress(); } else if (upg === "maxHealth" && playerCoins >= cost.coins && playerResources[0] >= cost.metal) { ship.maxHealth += 20; ship.health = ship.maxHealth; playerCoins -= cost.coins; playerResources[0] -= cost.metal; LK.effects.flashObject(ship, 0x00ff99, 400); btn.setText("Alındı!"); btn.fill = 0x00ffcc; // --- BAŞARIM: Upgrade satın alma --- checkUpgradeAchievements(); updateUI(); saveProgress(); } else if (upg === "maxShield" && playerCoins >= cost.coins && playerResources[0] >= cost.metal) { ship.maxShield += 10; ship.shield = ship.maxShield; playerCoins -= cost.coins; playerResources[0] -= cost.metal; LK.effects.flashObject(ship, 0x00ccff, 400); btn.setText("Alındı!"); btn.fill = 0x00ffcc; // --- BAŞARIM: Upgrade satın alma --- checkUpgradeAchievements(); updateUI(); saveProgress(); } else if (upg === "damage" && playerCoins >= cost.coins && playerResources[1] >= cost.energy) { ship.damage += 5; playerCoins -= cost.coins; playerResources[1] -= cost.energy; LK.effects.flashObject(ship, 0xffcc00, 400); btn.setText("Alındı!"); btn.fill = 0x00ffcc; // --- BAŞARIM: Upgrade satın alma --- checkUpgradeAchievements(); updateUI(); saveProgress(); } else if (upg === "speed" && playerCoins >= cost.coins) { ship.speed = Math.round(ship.speed * 1.1 * 100) / 100; playerCoins -= cost.coins; LK.effects.flashObject(ship, 0x00ffff, 400); btn.setText("Alındı!"); btn.fill = 0x00ffcc; // --- BAŞARIM: Upgrade satın alma --- checkUpgradeAchievements(); updateUI(); saveProgress(); } else if (upg === "fireRate") { if (typeof window._fireRateUpgradeLevel === "undefined") { window._fireRateUpgradeLevel = 0; } if (window._fireRateUpgradeLevel >= 5) { btn.setText("Maks. seviye!"); btn.fill = 0xff4444; LK.setTimeout(function () { btn.setText("Atış Hızı +10%\n(1200 coin)"); btn.fill = 0xff99ff; }, 900); } else if (playerCoins >= cost.coins) { ship.fireRate = Math.max(2, Math.round(ship.fireRate * 0.9)); playerCoins -= cost.coins; window._fireRateUpgradeLevel++; storage.fireRateUpgradeLevel = window._fireRateUpgradeLevel; LK.effects.flashObject(ship, 0xff99ff, 400); btn.setText("Alındı!"); btn.fill = 0x00ffcc; updateUI(); saveProgress(); } else { btn.setText("Yetersiz kaynak!"); btn.fill = 0xff4444; LK.setTimeout(function () { btn.setText("Atış Hızı +10%\n(1200 coin)"); btn.fill = 0xff99ff; }, 900); } } else { var origText = ""; var origFill = btn.fill; if (upg === "maxHealth") { origText = "Max Can +20\n(600 coin, 60 metal)"; origFill = 0x00ff99; } else if (upg === "maxShield") { origText = "Max Kalkan +10\n(720 coin, 72 metal)"; origFill = 0x00ccff; } else if (upg === "damage") { origText = "Hasar +5\n(900 coin, 90 enerji)"; origFill = 0xffcc00; } else if (upg === "speed") { origText = "Hız +10%\n(1000 coin)"; origFill = 0x00ffff; } else if (upg === "fireRate") { origText = "Atış Hızı +10%\n(1200 coin)"; origFill = 0xff99ff; } else { origText = btn.text; origFill = btn.fill; } btn.setText("Yetersiz kaynak!"); btn.fill = 0xff4444; // Reset label after short delay LK.setTimeout(function () { btn.setText(origText); btn.fill = origFill; }, 900); } return; } } // Close button var cb = window.upgradesPanelCloseBtn; if (cb) { var bx = cb.x, by = cb.y; var bw = cb.width || 300, bh = cb.height || 80; if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) { window.closeUpgradesPanel(); return; } } } // --- Handle Accept/Reject quest button taps --- if (game._questDecisionAcceptBtn && game._questDecisionAcceptBtn.parent) { var btn = game._questDecisionAcceptBtn; var bx = btn.x, by = btn.y; // Button is about 180x80 px if (x > bx - 90 && x < bx + 90 && y > by - 40 && y < by + 40) { // Accept quest var npc = btn._questNPC; if (npc) { // Defend quest özel: yardım kabul edildi if (npc.questType === 'defend') { npc._defendHelpAccepted = true; // Show floating label for accepted var acceptLabel = new Text2("Yardım kabul edildi!", { size: 36, fill: 0x00ff99, align: "center" }); acceptLabel.anchor.set(0.5, 0.5); acceptLabel.x = npc.x; acceptLabel.y = npc.y + 110; game.addChild(acceptLabel); npc._npcInteractionLabel = acceptLabel; npc._npcInteractionLabelExpire = LK.ticks + 120; npc.pauseUntil = LK.ticks + 120; npc._npcText && npc._npcText.parent && npc._npcText.parent.removeChild(npc._npcText); npc._npcText = new Text2(npc.rpgName + ": Teşekkürler! Beni korsanlardan koru!", { size: 32, fill: 0x00ffcc, align: "center" }); npc._npcText.anchor.set(0.5, 1); npc._npcText.x = npc.x; npc._npcText.y = npc.y - 70; game.addChild(npc._npcText); } else { // Mark quest as accepted (progress 1 means started) npc.questProgress = 1; // Show NPC response var acceptLabel = new Text2("Görev kabul edildi!", { size: 36, fill: 0x00ff99, align: "center" }); acceptLabel.anchor.set(0.5, 0.5); acceptLabel.x = npc.x; acceptLabel.y = npc.y + 110; game.addChild(acceptLabel); npc._npcInteractionLabel = acceptLabel; npc._npcInteractionLabelExpire = LK.ticks + 120; npc.pauseUntil = LK.ticks + 120; npc._npcText && npc._npcText.parent && npc._npcText.parent.removeChild(npc._npcText); npc._npcText = new Text2(npc.rpgName + ": Harika! Görev başladı.", { size: 32, fill: 0x00ffcc, align: "center" }); npc._npcText.anchor.set(0.5, 1); npc._npcText.x = npc.x; npc._npcText.y = npc.y - 70; game.addChild(npc._npcText); } // Remove quest panel if (npc._questDecisionPanel && npc._questDecisionPanel.parent) { npc._questDecisionPanel.parent.removeChild(npc._questDecisionPanel); } if (npc._questDecisionText && npc._questDecisionText.parent) { npc._questDecisionText.parent.removeChild(npc._questDecisionText); } if (npc._questDecisionAcceptBtn && npc._questDecisionAcceptBtn.parent) { npc._questDecisionAcceptBtn.parent.removeChild(npc._questDecisionAcceptBtn); } if (npc._questDecisionRejectBtn && npc._questDecisionRejectBtn.parent) { npc._questDecisionRejectBtn.parent.removeChild(npc._questDecisionRejectBtn); } npc._questDecisionPanel = null; npc._questDecisionText = null; npc._questDecisionAcceptBtn = null; npc._questDecisionRejectBtn = null; game._questDecisionPanel = null; game._questDecisionAcceptBtn = null; game._questDecisionRejectBtn = null; game._questDecisionText = null; game._questDecisionNPC = null; } return; } } if (game._questDecisionRejectBtn && game._questDecisionRejectBtn.parent) { var btn = game._questDecisionRejectBtn; var bx = btn.x, by = btn.y; if (x > bx - 90 && x < bx + 90 && y > by - 40 && y < by + 40) { // Reject quest var npc = btn._questNPC; if (npc) { if (npc.questType === 'defend') { npc._defendHelpAccepted = false; // Show floating label for rejected var rejectLabel = new Text2("Yardım edilmedi.", { size: 36, fill: 0xff4444, align: "center" }); rejectLabel.anchor.set(0.5, 0.5); rejectLabel.x = npc.x; rejectLabel.y = npc.y + 110; game.addChild(rejectLabel); npc._npcInteractionLabel = rejectLabel; npc._npcInteractionLabelExpire = LK.ticks + 120; npc.pauseUntil = LK.ticks + 120; npc._npcText && npc._npcText.parent && npc._npcText.parent.removeChild(npc._npcText); npc._npcText = new Text2(npc.rpgName + ": Anlaşıldı, kendi başıma savaşacağım!", { size: 32, fill: 0x00ffcc, align: "center" }); npc._npcText.anchor.set(0.5, 1); npc._npcText.x = npc.x; npc._npcText.y = npc.y - 70; game.addChild(npc._npcText); } else { // Mark quest as inactive npc.questActive = false; // Show floating label for rejected var rejectLabel = new Text2("Görev reddedildi.", { size: 36, fill: 0xff4444, align: "center" }); rejectLabel.anchor.set(0.5, 0.5); rejectLabel.x = npc.x; rejectLabel.y = npc.y + 110; game.addChild(rejectLabel); npc._npcInteractionLabel = rejectLabel; npc._npcInteractionLabelExpire = LK.ticks + 120; npc.pauseUntil = LK.ticks + 120; npc._npcText && npc._npcText.parent && npc._npcText.parent.removeChild(npc._npcText); npc._npcText = new Text2(npc.rpgName + ": Belki başka zaman!", { size: 32, fill: 0x00ffcc, align: "center" }); npc._npcText.anchor.set(0.5, 1); npc._npcText.x = npc.x; npc._npcText.y = npc.y - 70; game.addChild(npc._npcText); } // Remove quest panel if (npc._questDecisionPanel && npc._questDecisionPanel.parent) { npc._questDecisionPanel.parent.removeChild(npc._questDecisionPanel); } if (npc._questDecisionText && npc._questDecisionText.parent) { npc._questDecisionText.parent.removeChild(npc._questDecisionText); } if (npc._questDecisionAcceptBtn && npc._questDecisionAcceptBtn.parent) { npc._questDecisionAcceptBtn.parent.removeChild(npc._questDecisionAcceptBtn); } if (npc._questDecisionRejectBtn && npc._questDecisionRejectBtn.parent) { npc._questDecisionRejectBtn.parent.removeChild(npc._questDecisionRejectBtn); } npc._questDecisionPanel = null; npc._questDecisionText = null; npc._questDecisionAcceptBtn = null; npc._questDecisionRejectBtn = null; game._questDecisionPanel = null; game._questDecisionAcceptBtn = null; game._questDecisionRejectBtn = null; game._questDecisionText = null; game._questDecisionNPC = null; } return; } } // Check for NPC tap (RPG/adventure/trade) for (var i = 0; i < npcs.length; i++) { var npc = npcs[i]; var dist = Math.sqrt(Math.pow(npc.x - ship.x, 2) + Math.pow(npc.y - ship.y, 2)); if (dist < 180) { game._lastNPCTap = LK.ticks; break; } } // --- Distress event tap detection --- if (window.distressEvent && window.distressEvent.helpBtn && window.distressEvent.ignoreBtn) { game._lastDistressTapTick = LK.ticks; game._lastDistressTapX = x; game._lastDistressTapY = y; } } }; game.move = function (x, y, obj) { // Only allow joystick drag if ship is alive if (isDragging && ship && ship.health > 0) { var dx = x - joystickBase.x; var dy = y - joystickBase.y; var distance = Math.sqrt(dx * dx + dy * dy); // Clamp handle to joystick base radius (130px for 30% larger size) if (distance > 130) { dx = dx / distance * 130; dy = dy / distance * 130; } joystickHandle.x = joystickBase.x + dx; joystickHandle.y = joystickBase.y + dy; } }; game.up = function (x, y, obj) { // Only allow joystick release if ship is alive isDragging = false; if (ship && ship.health > 0) { // Snap handle back to base joystickHandle.x = joystickBase.x; joystickHandle.y = joystickBase.y; } }; // Main game loop game.update = function () { // --- PERIODIC WORLD CLEANUP TO PREVENT LAG --- // Every 600 ticks (~10 seconds), remove excess or orphaned entities if (typeof window._lastWorldCleanupTick === "undefined") { window._lastWorldCleanupTick = LK.ticks; } if (LK.ticks - window._lastWorldCleanupTick > 600) { // Defensive: Remove destroyed, undefined, or offscreen objects from all arrays var cleanupArray = function cleanupArray(arr, maxCount, offscreenDist) { if (!arr) { return; } for (var i = arr.length - 1; i >= 0; i--) { var obj = arr[i]; // Remove if destroyed, undefined, or missing x/y if (!obj || typeof obj.x !== "number" || typeof obj.y !== "number" || obj._destroyed) { if (obj && typeof obj.destroy === "function") { obj.destroy(); } arr.splice(i, 1); continue; } // Remove if far offscreen (relative to ship) if (typeof ship !== "undefined" && ship && typeof ship.x === "number" && typeof ship.y === "number" && typeof offscreenDist === "number") { var dx = obj.x - ship.x; var dy = obj.y - ship.y; if (Math.sqrt(dx * dx + dy * dy) > offscreenDist) { if (typeof obj.destroy === "function") { obj.destroy(); } arr.splice(i, 1); continue; } } } // Clamp array to maxCount (remove oldest if too many) if (typeof maxCount === "number" && arr.length > maxCount) { for (var j = arr.length - 1; j >= maxCount; j--) { if (arr[j] && typeof arr[j].destroy === "function") { arr[j].destroy(); } arr.splice(j, 1); } } }; // Clean up all major arrays // Remove any nulls from arrays (defensive) var compactArray = function compactArray(arr) { if (!arr) { return; } for (var i = arr.length - 1; i >= 0; i--) { if (!arr[i]) { arr.splice(i, 1); } } }; cleanupArray(bullets, 100, 3000); cleanupArray(enemyBullets, 100, 3000); cleanupArray(pirates, 30, 4000); cleanupArray(npcs, 20, 4000); cleanupArray(asteroids, 10, 4000); cleanupArray(coins, 30, 4000); cleanupArray(resources, 30, 4000); cleanupArray(stars, 200, 6000); cleanupArray(upgradeStations, 5, 6000); cleanupArray(explosions, 30, 4000); cleanupArray(pirateBases, 5, 6000); compactArray(bullets); compactArray(enemyBullets); compactArray(pirates); compactArray(npcs); compactArray(asteroids); compactArray(coins); compactArray(resources); compactArray(stars); compactArray(upgradeStations); compactArray(explosions); compactArray(pirateBases); window._lastWorldCleanupTick = LK.ticks; } // --- SHIP MOVEMENT SYSTEM --- // Defensive: always reset deltas var shipDX = 0; var shipDY = 0; // Block all ship movement and input if ship is dead or just respawned if (ship && ship.health <= 0) { // Do not move ship, do not process joystick, do not update camera, do not fire, etc. // Optionally, you could add a "dead" animation or effect here. // (Handled by respawn logic) return; } else if (typeof window._lastRespawnTick !== "undefined" && LK.ticks - window._lastRespawnTick < 60) { // Block all input/movement for 1 second after respawn return; } else { // Block ship movement if ANY panel is open (but always allow joystick to move) if (!(window.stationDeliveryPanel && window.stationDeliveryPanelBG && window.stationDeliveryPanelBG.parent) && !(window.resourceTradePanelState && window.resourceTradePanelState.open) && !(window._resourceStockPanel && window._resourceStockPanel.bg && window._resourceStockPanel.bg.parent) && !(window.shipUpgradePanel && window.shipUpgradePanel.parent) && !(window.repairPanel && window.repairPanel.parent)) { // Only move if dragging joystick if (isDragging) { var dx = joystickHandle.x - joystickBase.x; var dy = joystickHandle.y - joystickBase.y; var distance = Math.sqrt(dx * dx + dy * dy); // Only move if joystick is moved enough if (distance > 10) { // Normalize direction, scale by ship speed and joystick displacement var norm = Math.min(distance, 130) / 130; var effectiveSpeed = ship.speed * norm; shipDX = dx / distance * effectiveSpeed; shipDY = dy / distance * effectiveSpeed; // Rotate ship to face movement direction ship.rotation = Math.atan2(dy, dx) + Math.PI / 2; } } } // Actually move the ship's logical position, but keep it visually centered ship.x += shipDX; ship.y += shipDY; } // Track total distance traveled totalDistance += Math.sqrt(shipDX * shipDX + shipDY * shipDY); // --- Keep ship always visually centered --- // Calculate the offset needed to keep the ship at the center of the screen var centerX = 2048 / 2; var centerY = 2732 / 2; var offsetX = centerX - ship.x; var offsetY = centerY - ship.y; // Visually place the ship at the center ship.x = centerX; ship.y = centerY; // Move all world objects by the offset so the ship appears to move through the world for (var i = 0; i < stars.length; i++) { stars[i].x += offsetX; stars[i].y += offsetY; } for (var i = 0; i < npcs.length; i++) { npcs[i].x += offsetX; npcs[i].y += offsetY; } for (var i = 0; i < pirates.length; i++) { pirates[i].x += offsetX; pirates[i].y += offsetY; } for (var i = 0; i < coins.length; i++) { coins[i].x += offsetX; coins[i].y += offsetY; } for (var i = 0; i < resources.length; i++) { resources[i].x += offsetX; resources[i].y += offsetY; } for (var i = 0; i < bullets.length; i++) { bullets[i].x += offsetX; bullets[i].y += offsetY; } for (var i = 0; i < enemyBullets.length; i++) { enemyBullets[i].x += offsetX; enemyBullets[i].y += offsetY; } for (var i = 0; i < asteroids.length; i++) { asteroids[i].x += offsetX; asteroids[i].y += offsetY; } for (var i = 0; i < upgradeStations.length; i++) { upgradeStations[i].x += offsetX; upgradeStations[i].y += offsetY; } // --- DerelictDebris (enkaz) toplama ve fade-out sistemi --- for (var i = upgradeStations.length - 1; i >= 0; i--) { var debris = upgradeStations[i]; if (debris && debris.type === 'derelict' && !debris._collected && debris.intersects(ship)) { debris._collected = true; // 10 hammadde için rastgele bir tip seç var resourceTypes = ['metal', 'energy', 'crystal', 'gas', 'ice', 'uranium', 'silicon', 'carbon', 'plasma', 'antimatter']; var idx = Math.floor(Math.random() * resourceTypes.length); if (debris._resourceAmount > 0) { playerResources[idx] += debris._resourceAmount; // Başarım kontrolü checkResourceCollectAchievements && checkResourceCollectAchievements(); // Kısa bir label göster var label = new Text2("+" + debris._resourceAmount + " " + resourceTypes[idx], { size: 36, fill: 0x00ff00, align: "center" }); label.anchor.set(0.5, 0.5); label.x = ship.x; label.y = ship.y - 120; game.addChild(label); LK.setTimeout(function () { if (label.parent) { label.parent.removeChild(label); } }, 900); updateUI && updateUI(); saveProgress && saveProgress(); } // Fade-out başlat debris._fadeStartTick = LK.ticks; debris._expireTick = debris._fadeStartTick + 60; // 1 saniyede kaybolsun } // Fade-out tamamlandıysa sil if (debris && debris._shouldRemove) { debris.destroy(); upgradeStations.splice(i, 1); } } // boostParticles removed (no boost system) for (var i = 0; i < explosions.length; i++) { explosions[i].x += offsetX; explosions[i].y += offsetY; } // Auto fire at very close pirates or asteroids // Saldırı mesafesi %10 arttırıldı (200 -> 220) // +%5 menzil yükseltmeleri uygula var baseRange = 350; var rangeBonus = (typeof window._rangeUpgradeLevel !== "undefined" ? window._rangeUpgradeLevel : storage.rangeUpgradeLevel || 0) * 0.05; var ATTACK_RANGE = Math.round(baseRange * (1 + rangeBonus)); // %10 daha fazla menzil Katil Korsan ve GrupKorsan'a karşı var ATTACK_RANGE_PIRATE_SPECIAL = Math.round(ATTACK_RANGE * 1.1); if (LK.ticks - ship.lastFire > ship.fireRate && (pirates.length > 0 || asteroids.length > 0)) { // Find nearest pirate or asteroid within 220 pixels var nearestTarget = null; var nearestDistance = Infinity; var isPirate = false; // Check pirates for (var i = 0; i < pirates.length; i++) { var pirate = pirates[i]; var dist = Math.sqrt(Math.pow(pirate.x - ship.x, 2) + Math.pow(pirate.y - ship.y, 2)); var isSpecialPirate = pirate._isKatil === true || pirate._isGrupKorsan === true; var rangeToUse = isSpecialPirate ? ATTACK_RANGE_PIRATE_SPECIAL : ATTACK_RANGE; if (dist < rangeToUse && dist < nearestDistance) { nearestDistance = dist; nearestTarget = pirate; isPirate = true; } } // Check asteroids for (var i = 0; i < asteroids.length; i++) { var dist = Math.sqrt(Math.pow(asteroids[i].x - ship.x, 2) + Math.pow(asteroids[i].y - ship.y, 2)); if (dist < ATTACK_RANGE && dist < nearestDistance) { nearestDistance = dist; nearestTarget = asteroids[i]; isPirate = false; } } if (nearestTarget) { var bullet = new Bullet(); bullet.x = ship.x; bullet.y = ship.y; var angle = Math.atan2(nearestTarget.y - ship.y, nearestTarget.x - ship.x); bullet.directionX = Math.cos(angle); bullet.directionY = Math.sin(angle); bullet.rotation = angle + Math.PI / 2; // Store last attack angle for ship rotation game._lastShipAttackAngle = angle; if (typeof ship !== "undefined") { ship._lastMoveAngle = angle; } bullets.push(bullet); game.addChild(bullet); ship.lastFire = LK.ticks; // Calculate distance from ship to bullet spawn point and adjust volume var dx = bullet.x - ship.x; var dy = bullet.y - ship.y; var distanceFromShip = Math.sqrt(dx * dx + dy * dy); // Calculate volume based on distance (closer = louder, farther = quieter) // Max distance for sound falloff is 500 pixels var maxSoundDistance = 500; var volumeMultiplier = Math.max(0.1, Math.min(1.0, 1.0 - distanceFromShip / maxSoundDistance)); // Get the sound and play with adjusted volume var shootSound = LK.getSound('shoot'); var originalVolume = shootSound.volume || 0.3; // fallback to default volume shootSound.volume = originalVolume * volumeMultiplier; shootSound.play(); // Restore original volume for next play shootSound.volume = originalVolume; // --- FRIENDS: Saldırı başlat --- // Her friend, 1 saniye sonra saldırmaya başlar ve oyuncu saldırıyı bırakana kadar devam eder for (var i = 0; i < friends.length; i++) { if (friends[i] && typeof friends[i].startAttack === "function") { // Eğer zaten bu hedefe saldırıyorsa, tekrar başlatma if (!friends[i]._attacking || friends[i]._attackTarget !== nearestTarget) { friends[i]._attackDelay = 60; // 1 saniye gecikme (her saldırı başlatıldığında sıfırla) friends[i].startAttack(nearestTarget); } } } } else { // --- FRIENDS: Saldırı bırak --- for (var i = 0; i < friends.length; i++) { if (friends[i] && typeof friends[i].stopAttack === "function") { friends[i].stopAttack(); } } } } // Update bullets for (var i = bullets.length - 1; i >= 0; i--) { var bullet = bullets[i]; if (!bullet || typeof bullet.x === "undefined" || typeof bullet.y === "undefined") { // Defensive: remove undefined or destroyed bullet if (bullet && typeof bullet.destroy === "function") { bullet.destroy(); } bullets.splice(i, 1); continue; } // Remove if marked for removal by fade-out if (bullet._shouldRemove) { bullet.destroy(); bullets.splice(i, 1); continue; } // (No longer destroy instantly when out of range, fade-out will handle it) // Check collision with pirates for (var j = pirates.length - 1; j >= 0; j--) { if (bullet.intersects(pirates[j])) { // Small explosion effect at collision createExplosion(bullet.x, bullet.y); pirates[j].takeDamage(bullet.damage); var killedByPlayer = !bullet._npcBullet; var killedByNPC = !!bullet._npcBullet; if (pirates[j].health <= 0) { enemiesKilled++; createExplosion(pirates[j].x, pirates[j].y); // --- BAŞARIM: Korsan öldürme --- if (killedByPlayer) { checkPirateKillAchievements(); // Katil korsan öldürme if (pirates[j]._isKatil) { checkKatilKillAchievement(); } } // Drop loot var coin = new Coin(); coin.x = pirates[j].x; coin.y = pirates[j].y; // Katil korsan ise 10 kat coin ver if (pirates[j]._isKatil) { coin.value = coin.value * (1 + Math.floor(waveNumber / 3)) * 10; // Katil korsan öldü, tekrar spawn edilebilir window._katilPirateSpawned = false; // Katil korsan öldükten veya yok olduğunda tekrar spawn için timer başlat if (typeof window._katilPirateRespawnTimer !== "undefined" && window._katilPirateRespawnTimer) { LK.clearTimeout(window._katilPirateRespawnTimer); } window._katilPirateRespawnTimer = LK.setTimeout(function () { if (!window._katilPirateSpawned) { // Katil korsanı tekrar spawn et var pirate = new Pirate(); // Katil korsan statlarını tekrar ayarla var minLevel = 1 + waveNumber * 2; pirate.level = minLevel + 10; pirate.health = (30 + (pirate.level - 1) * 10) * 10; pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10; pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10; pirate.rpgName = "Katil"; pirate._isKatil = true; pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3)); pirate._attackRange = Math.floor(600 * 0.9); // Katil korsan için %10 daha kısa menzil window._katilPirateSpawned = true; // Diğer korsanlar gibi kenar spawn algoritmasıyla doğsun var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: // Top pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y - 1400; break; case 1: // Right pirate.x = ship.x + 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; case 2: // Bottom pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y + 1400; break; case 3: // Left pirate.x = ship.x - 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; } // Add health bar to katil pirate pirate._healthBar = new HealthBar(); pirate._healthBar.set({ maxValue: function maxValue() { return pirate.health > 0 ? pirate.health : 0; }, getValueFn: function getValueFn() { return pirate.health > 0 ? pirate.health : 0; }, getMaxFn: function getMaxFn() { return 30 + waveNumber * 10; }, width: 60, height: 10 }); pirate._healthBar.y = -60; pirate.addChild(pirate._healthBar); pirates.push(pirate); game.addChild(pirate); } window._katilPirateRespawnTimer = null; }, 6000); // 6 saniye sonra tekrar spawn // Katil korsan yok olduğunda da tekrar spawn edilmesini sağla for (var i = pirates.length - 1; i >= 0; i--) { var p = pirates[i]; if (p && p._isKatil && (p._destroyed || typeof p.x === "undefined" || typeof p.y === "undefined")) { // Katil korsan yok olduysa, tekrar spawn için timer başlat window._katilPirateSpawned = false; if (typeof window._katilPirateRespawnTimer !== "undefined" && window._katilPirateRespawnTimer) { LK.clearTimeout(window._katilPirateRespawnTimer); } window._katilPirateRespawnTimer = LK.setTimeout(function () { if (!window._katilPirateSpawned) { var pirate = new Pirate(); var minLevel = 1 + waveNumber * 2; pirate.level = minLevel + 10; pirate.health = (30 + (pirate.level - 1) * 10) * 10; pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10; pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10; pirate.rpgName = "Katil"; pirate._isKatil = true; pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3)); pirate._attackRange = Math.floor(600 * 0.9); // Katil korsan için %10 daha kısa menzil window._katilPirateSpawned = true; var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y - 1400; break; case 1: pirate.x = ship.x + 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; case 2: pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y + 1400; break; case 3: pirate.x = ship.x - 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; } pirate._healthBar = new HealthBar(); pirate._healthBar.set({ maxValue: function maxValue() { return pirate.health > 0 ? pirate.health : 0; }, getValueFn: function getValueFn() { return pirate.health > 0 ? pirate.health : 0; }, getMaxFn: function getMaxFn() { return 30 + waveNumber * 10; }, width: 60, height: 10 }); pirate._healthBar.y = -60; pirate.addChild(pirate._healthBar); pirates.push(pirate); game.addChild(pirate); } window._katilPirateRespawnTimer = null; }, 6000); break; // Sadece bir kere tetiklenmeli } } // Katil etiketiyle özel bir label göster var katilLabel = new Text2("KATİL KORSAN ÖLDÜRÜLDÜ!\n+10x coin", { size: 40, fill: 0xff0000, align: "center" }); katilLabel.anchor.set(0.5, 0.5); katilLabel.x = pirates[j].x; katilLabel.y = pirates[j].y - 120; game.addChild(katilLabel); LK.setTimeout(function () { if (katilLabel.parent) { katilLabel.parent.removeChild(katilLabel); } }, 1500); } else { coin.value = coin.value * (1 + Math.floor(waveNumber / 3)); } coins.push(coin); game.addChild(coin); // GrupKorsan öldüyse %25 ihtimalle CoinBox düşür if (pirates[j]._isGrupKorsan && Math.random() < 0.25) { var coinBox = new CoinBox(); coinBox.x = pirates[j].x + (Math.random() - 0.5) * 40; coinBox.y = pirates[j].y + (Math.random() - 0.5) * 40; coins.push(coinBox); // CoinBox da coins array'ine eklenir, toplama ve fade-out için game.addChild(coinBox); // Kısa bir label göster var boxLabel = new Text2("COINBOX!", { size: 32, fill: 0xffd700, align: "center" }); boxLabel.anchor.set(0.5, 0.5); boxLabel.x = coinBox.x; boxLabel.y = coinBox.y - 60; game.addChild(boxLabel); LK.setTimeout(function () { if (boxLabel.parent) { boxLabel.parent.removeChild(boxLabel); } }, 900); } if (Math.random() < 0.3) { var resource = new Resource(); resource.x = pirates[j].x + (Math.random() - 0.5) * 50; resource.y = pirates[j].y + (Math.random() - 0.5) * 50; resources.push(resource); game.addChild(resource); } // Remove KILL label after 2 seconds if present if (pirates[j]._killLabel) { // If not already expiring, set expire tick if (!pirates[j]._killLabel._expireTick) { pirates[j]._killLabel._expireTick = LK.ticks + 120; } } // Remove KILL label if expired if (pirates[j]._killLabel && pirates[j]._killLabel._expireTick && LK.ticks > pirates[j]._killLabel._expireTick) { if (pirates[j]._killLabel.parent) { pirates[j]._killLabel.parent.removeChild(pirates[j]._killLabel); } pirates[j]._killLabel = null; } if (killedByPlayer) { // Oyuncu öldürdü: sadece exp kazan var pirateExp = 3 + Math.floor(Math.random() * 3); playerEXP += pirateExp; // Level up if enough EXP while (playerEXP >= expToNext(playerLevel)) { playerEXP -= expToNext(playerLevel); playerLevel++; LK.effects.flashObject(ship, 0x00ffcc, 800); checkLevelAchievements(); } } else if (killedByNPC) { // NPC öldürdü: yoluna devam etsin, rep/exp yok // Kısa bir etiket göster (isteğe bağlı) var npcLabel = new Text2("NPC KILL", { size: 28, fill: 0xccccff, align: "center" }); npcLabel.anchor.set(0.5, 0.5); npcLabel.x = pirates[j].x; npcLabel.y = pirates[j].y - 80; game.addChild(npcLabel); // Fade out and remove after 700ms LK.setTimeout(function () { if (npcLabel.parent) { npcLabel.parent.removeChild(npcLabel); } }, 700); } // Update UI after EXP gain updateUI(); pirates[j].destroy(); pirates.splice(j, 1); // Check for wave completion if (enemiesKilled >= 10 + waveNumber * 5) { waveNumber++; enemiesKilled = 0; // --- BAŞARIM: Dalga geçme --- checkWaveAchievements(); // Spawn upgrade station every 3 waves if (waveNumber % 3 === 0) { spawnUpgradeStation(); } } } bullet.destroy(); bullets.splice(i, 1); LK.getSound('hit').play(); break; } } // --- PLAYER CAN ATTACK NPCs IF FIGHT MODE IS ON --- if (window.npcFightMode) { for (var j = npcs.length - 1; j >= 0; j--) { var npc = npcs[j]; if (bullet.intersects(npc)) { // Small explosion effect at collision createExplosion(bullet.x, bullet.y); if (typeof npc.health === "undefined") { npc.health = 100; } npc.health -= bullet.damage; LK.effects.flashObject(npc, 0xff0000, 120); // Floating label for NPC hit var npcHitLabel = new Text2("NPC HIT", { size: 24, fill: 0xff8888, align: "center" }); npcHitLabel.anchor.set(0.5, 0.5); npcHitLabel.x = npc.x; npcHitLabel.y = npc.y - 60; game.addChild(npcHitLabel); LK.setTimeout(function (label) { if (label.parent) { label.parent.removeChild(label); } }.bind(null, npcHitLabel), 600); // Remove NPC if dead if (npc.health <= 0) { // Floating label for NPC kill var npcKillLabel = new Text2("NPC KILL", { size: 28, fill: 0xccccff, align: "center" }); npcKillLabel.anchor.set(0.5, 0.5); npcKillLabel.x = npc.x; npcKillLabel.y = npc.y - 80; game.addChild(npcKillLabel); LK.setTimeout(function (label) { if (label.parent) { label.parent.removeChild(label); } }.bind(null, npcKillLabel), 700); // Oyuncu NPC öldürdü: coin kaybı uygula var coinLoss = 10 + Math.floor(Math.random() * 41); // 10-50 arası coin kaybı playerCoins = Math.max(0, playerCoins - coinLoss); // Kısa bir etiket göster var coinLossLabel = new Text2("-" + coinLoss + " coin", { size: 28, fill: 0xff4444, align: "center" }); coinLossLabel.anchor.set(0.5, 0.5); coinLossLabel.x = npc.x; coinLossLabel.y = npc.y - 120; game.addChild(coinLossLabel); LK.setTimeout(function (label) { if (label.parent) { label.parent.removeChild(label); } }, 900, coinLossLabel); updateUI(); // Remove hostile label if present if (npc._hostileLabel && npc._hostileLabel.parent) { npc._hostileLabel.parent.removeChild(npc._hostileLabel); npc._hostileLabel = null; } // Instantly remove Düşman! label if it exists (defensive, in case above missed) if (npc._hostileLabel && npc._hostileLabel.parent) { npc._hostileLabel.parent.removeChild(npc._hostileLabel); npc._hostileLabel = null; } npc.destroy(); npcs.splice(j, 1); } bullet.destroy(); bullets.splice(i, 1); LK.getSound('hit').play(); break; } } } // Check collision with asteroids for (var j = asteroids.length - 1; j >= 0; j--) { if (bullet.intersects(asteroids[j])) { // Small explosion effect at collision createExplosion(bullet.x, bullet.y); asteroids[j].takeDamage(bullet.damage); if (asteroids[j].health <= 0) { // --- BAŞARIM: Asteroit patlatma --- checkAsteroidKillAchievements(); // Drop multiple resources (reduced by 80%) var dropCount = Math.max(1, Math.floor(asteroids[j].resources * 0.2)); for (var k = 0; k < dropCount; k++) { var resource = new Resource(); resource.x = asteroids[j].x + (Math.random() - 0.5) * 100; resource.y = asteroids[j].y + (Math.random() - 0.5) * 100; resource.amount = Math.floor(Math.random() * 3) + 1; resources.push(resource); game.addChild(resource); } createExplosion(asteroids[j].x, asteroids[j].y); asteroids[j].destroy(); asteroids.splice(j, 1); } bullet.destroy(); bullets.splice(i, 1); LK.getSound('hit').play(); break; } } } // Update enemy bullets for (var i = enemyBullets.length - 1; i >= 0; i--) { var bullet = enemyBullets[i]; if (!bullet || typeof bullet.x === "undefined" || typeof bullet.y === "undefined") { // Defensive: remove undefined or destroyed bullet if (bullet && typeof bullet.destroy === "function") { bullet.destroy(); } enemyBullets.splice(i, 1); continue; } // Remove if marked for removal by fade-out if (bullet._shouldRemove) { bullet.destroy(); enemyBullets.splice(i, 1); continue; } // (No longer destroy instantly when out of range, fade-out will handle it) if (bullet.intersects(ship)) { // Small explosion effect at collision createExplosion(bullet.x, bullet.y); ship.takeDamage(bullet.damage); updateUI(); bullet.destroy(); enemyBullets.splice(i, 1); LK.getSound('hit').play(); continue; } // --- NEW: Pirate bullets can hit NPCs --- for (var n = npcs.length - 1; n >= 0; n--) { var npc = npcs[n]; if (bullet.intersects(npc)) { // Small explosion effect at collision createExplosion(bullet.x, bullet.y); // Damage NPC if (typeof npc.health === "undefined") { npc.health = 100; } npc.health -= bullet.damage; LK.effects.flashObject(npc, 0xff0000, 120); // Floating label for NPC hit (optional) var npcHitLabel = new Text2("NPC HIT", { size: 24, fill: 0xff8888, align: "center" }); npcHitLabel.anchor.set(0.5, 0.5); npcHitLabel.x = npc.x; npcHitLabel.y = npc.y - 60; game.addChild(npcHitLabel); LK.setTimeout(function (label) { if (label.parent) { label.parent.removeChild(label); } }.bind(null, npcHitLabel), 600); // Remove NPC if dead if (npc.health <= 0) { // Floating label for NPC kill var npcKillLabel = new Text2("NPC KILL", { size: 28, fill: 0xccccff, align: "center" }); npcKillLabel.anchor.set(0.5, 0.5); npcKillLabel.x = npc.x; npcKillLabel.y = npc.y - 80; game.addChild(npcKillLabel); LK.setTimeout(function (label) { if (label.parent) { label.parent.removeChild(label); } }.bind(null, npcKillLabel), 700); // Remove hostile label if present if (npc._hostileLabel && npc._hostileLabel.parent) { npc._hostileLabel.parent.removeChild(npc._hostileLabel); npc._hostileLabel = null; } // Remove NPC from world npc.destroy(); npcs.splice(n, 1); } bullet.destroy(); enemyBullets.splice(i, 1); LK.getSound('hit').play(); break; } } } // Update pirates for (var i = 0; i < pirates.length; i++) { var pirate = pirates[i]; if (pirate._healthBar && pirate._healthBar.update) { pirate._healthBar.update(); } // --- Pirate AI: Always attack the nearest target (NPC or player) --- var nearestTarget = null; var minDist = Infinity; // Find nearest NPC for (var n = 0; n < npcs.length; n++) { var npc = npcs[n]; var distToNPC = Math.sqrt(Math.pow(npc.x - pirate.x, 2) + Math.pow(npc.y - pirate.y, 2)); if (distToNPC < minDist) { minDist = distToNPC; nearestTarget = npc; } } // Compare with player distance var distToPlayer = Math.sqrt(Math.pow(ship.x - pirate.x, 2) + Math.pow(ship.y - pirate.y, 2)); if (distToPlayer < minDist) { minDist = distToPlayer; nearestTarget = ship; } // If there is a target, move and attack var pirateAttackRange = typeof pirate._attackRange === "number" ? pirate._attackRange : 600; if (nearestTarget && minDist < pirateAttackRange) { // --- 10% mesafe kuralı: hedefe %10 mesafe bırak, iç içe girme --- var targetRadius = 40; // Varsayılan hedef yarıçapı (NPC/ship/pirate boyutu) if (nearestTarget._healthBar && nearestTarget._healthBar._width) { targetRadius = Math.max(targetRadius, nearestTarget._healthBar._width / 2); } var selfRadius = 35; // Korsan yarıçapı if (pirate._healthBar && pirate._healthBar._width) { selfRadius = Math.max(selfRadius, pirate._healthBar._width / 2); } var minBuffer = (minDist + selfRadius + targetRadius) * 0.15; // %15 buffer var minAllowedDist = selfRadius + targetRadius + minBuffer; var angle = Math.atan2(nearestTarget.y - pirate.y, nearestTarget.x - pirate.x); // Sadece minAllowedDist'ten uzaktaysa yaklaş if (minDist > minAllowedDist) { var moveDist = Math.min(pirate.speed, minDist - minAllowedDist); pirate.x += Math.cos(angle) * moveDist; pirate.y += Math.sin(angle) * moveDist; pirate.rotation = angle + Math.PI / 2; } else { // Hedefe çok yaklaştıysa sadece dön, hareket etme pirate.rotation = angle + Math.PI / 2; } // Shoot at target if (LK.ticks - pirate.lastFire > pirate.fireRate) { var bullet = new EnemyBullet(); bullet.x = pirate.x; bullet.y = pirate.y; bullet.directionX = Math.cos(angle); bullet.directionY = Math.sin(angle); bullet.rotation = angle + Math.PI / 2; bullet._pirateBullet = true; enemyBullets.push(bullet); game.addChild(bullet); pirate.lastFire = LK.ticks; // Calculate distance from ship to pirate bullet spawn point and adjust volume var dx = bullet.x - ship.x; var dy = bullet.y - ship.y; var distanceFromShip = Math.sqrt(dx * dx + dy * dy); // Calculate volume based on distance (closer = louder, farther = quieter) // Max distance for sound falloff is 500 pixels var maxSoundDistance = 500; var volumeMultiplier = Math.max(0.1, Math.min(1.0, 1.0 - distanceFromShip / maxSoundDistance)); // Get the sound and play with adjusted volume var shootSound = LK.getSound('shoot'); var originalVolume = shootSound.volume || 0.3; // fallback to default volume shootSound.volume = originalVolume * volumeMultiplier; shootSound.play(); // Restore original volume for next play shootSound.volume = originalVolume; } } else { // Wander randomly pirate.x += Math.cos(pirate.moveAngle) * pirate.speed * 0.5; pirate.y += Math.sin(pirate.moveAngle) * pirate.speed * 0.5; } // Remove pirates that are too far away var dist = Math.sqrt(Math.pow(ship.x - pirate.x, 2) + Math.pow(ship.y - pirate.y, 2)); if (dist > 2000) { // Katil korsan ise tekrar spawn için timer başlat if (pirate._isKatil) { window._katilPirateSpawned = false; if (typeof window._katilPirateRespawnTimer !== "undefined" && window._katilPirateRespawnTimer) { LK.clearTimeout(window._katilPirateRespawnTimer); } window._katilPirateRespawnTimer = LK.setTimeout(function () { if (!window._katilPirateSpawned) { var pirate = new Pirate(); var minLevel = 1 + waveNumber * 2; pirate.level = minLevel + 10; pirate.health = (30 + (pirate.level - 1) * 10) * 10; pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10; pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10; pirate.rpgName = "Katil"; pirate._isKatil = true; pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3)); pirate._attackRange = Math.floor(600 * 0.9); window._katilPirateSpawned = true; var edge = Math.floor(Math.random() * 4); switch (edge) { case 0: pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y - 1400; break; case 1: pirate.x = ship.x + 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; case 2: pirate.x = ship.x + (Math.random() - 0.5) * 1800; pirate.y = ship.y + 1400; break; case 3: pirate.x = ship.x - 1400; pirate.y = ship.y + (Math.random() - 0.5) * 1800; break; } pirate._healthBar = new HealthBar(); pirate._healthBar.set({ maxValue: function maxValue() { return pirate.health > 0 ? pirate.health : 0; }, getValueFn: function getValueFn() { return pirate.health > 0 ? pirate.health : 0; }, getMaxFn: function getMaxFn() { return 30 + waveNumber * 10; }, width: 60, height: 10 }); pirate._healthBar.y = -60; pirate.addChild(pirate._healthBar); pirates.push(pirate); game.addChild(pirate); } window._katilPirateRespawnTimer = null; }, 6000); } pirate.destroy(); pirates.splice(i, 1); i--; } } // Update NPCs for (var i = npcs.length - 1; i >= 0; i--) { var npc = npcs[i]; if (npc._healthBar && npc._healthBar.update) { npc._healthBar.update(); } // Remove NPCs that are too far away var dist = Math.sqrt(Math.pow(npc.x - ship.x, 2) + Math.pow(npc.y - ship.y, 2)); if (dist > 2500) { npc.destroy(); npcs.splice(i, 1); continue; } // Remove any floating text if player is not close if (npc._npcText && npc._npcText.parent) { npc._npcText.parent.removeChild(npc._npcText); npc._npcText = null; } // --- NPC ATTITUDE: Rep sistemi kaldırıldı, sadece NPC Saldırı butonuna göre saldırı --- // Eğer NPC Saldırı aktifse, her durumda NPC'ler oyuncuya saldırır var npcHostile = false; if (window.npcSaldiriBtn && window.npcSaldiriBtn._active) { npcHostile = true; } else { npcHostile = false; } // --- NPC AI: Always attack the nearest target (pirate or player) --- // If NPC Saldırı is PASİF, NPCs never attack the player var nearestTarget = null; var minDist = Infinity; // Find nearest pirate for (var j = 0; j < pirates.length; j++) { var p = pirates[j]; if (p._destroyed) { continue; } var d = Math.sqrt(Math.pow(p.x - npc.x, 2) + Math.pow(p.y - npc.y, 2)); if (d < minDist) { minDist = d; nearestTarget = p; } } // Compare with player distance only if NPC Saldırı is AKTİF var npcSaldiriActive = window.npcSaldiriBtn && window.npcSaldiriBtn._active; if (npcSaldiriActive) { var distToPlayer = Math.sqrt(Math.pow(ship.x - npc.x, 2) + Math.pow(ship.y - npc.y, 2)); if (distToPlayer < minDist) { minDist = distToPlayer; nearestTarget = ship; } } // If there is a target, move and attack if (nearestTarget && minDist < 600) { // --- 10% mesafe kuralı: hedefe %10 mesafe bırak, iç içe girme --- var targetRadius = 40; if (nearestTarget._healthBar && nearestTarget._healthBar._width) { targetRadius = Math.max(targetRadius, nearestTarget._healthBar._width / 2); } var selfRadius = 30; if (npc._healthBar && npc._healthBar._width) { selfRadius = Math.max(selfRadius, npc._healthBar._width / 2); } var minBuffer = (minDist + selfRadius + targetRadius) * 0.15; var minAllowedDist = selfRadius + targetRadius + minBuffer; var dx = nearestTarget.x - npc.x; var dy = nearestTarget.y - npc.y; var d = Math.sqrt(dx * dx + dy * dy); if (d > minAllowedDist) { var moveDist = Math.min(npc.moveSpeed * 1.2, d - minAllowedDist); npc.x += dx / d * moveDist; npc.y += dy / d * moveDist; } // Shoot at target (use NPC bullet fire rate) if (typeof npc._aiAttackTimer === "undefined") { npc._aiAttackTimer = 0; } npc._aiAttackTimer++; // Use the same fire rate as pirates (EnemyBullet speed) var npcFireRate = 8; if (typeof npc.fireRate === "number") { npcFireRate = npc.fireRate; } else { npcFireRate = 40; } if (d < 300 && npc._aiAttackTimer > npcFireRate) { var bullet; var angle = Math.atan2(nearestTarget.y - npc.y, nearestTarget.x - npc.x); if (nearestTarget === ship) { bullet = new EnemyBullet(); bullet._npcBullet = true; } else { bullet = new Bullet(); bullet._npcBullet = true; } bullet.x = npc.x; bullet.y = npc.y; bullet.directionX = Math.cos(angle); bullet.directionY = Math.sin(angle); bullet.rotation = angle + Math.PI / 2; if (nearestTarget === ship) { enemyBullets.push(bullet); } else { bullets.push(bullet); } game.addChild(bullet); npc._aiAttackTimer = 0; // Calculate distance from ship to NPC bullet spawn point and adjust volume var dx = bullet.x - ship.x; var dy = bullet.y - ship.y; var distanceFromShip = Math.sqrt(dx * dx + dy * dy); // Calculate volume based on distance (closer = louder, farther = quieter) // Max distance for sound falloff is 500 pixels var maxSoundDistance = 500; var volumeMultiplier = Math.max(0.1, Math.min(1.0, 1.0 - distanceFromShip / maxSoundDistance)); // Get the sound and play with adjusted volume var shootSound = LK.getSound('shoot'); var originalVolume = shootSound.volume || 0.3; // fallback to default volume shootSound.volume = originalVolume * volumeMultiplier; shootSound.play(); // Restore original volume for next play shootSound.volume = originalVolume; } // Show angry label above NPC if attacking player if (nearestTarget === ship) { if (!npc._hostileLabel || !npc._hostileLabel.parent) { npc._hostileLabel = new Text2("Düşman!", { size: 32, fill: 0xff4444, align: "center" }); npc._hostileLabel.anchor.set(0.5, 0.5); npc._hostileLabel.x = npc.x; npc._hostileLabel.y = npc.y - 80; npc._hostileLabel.alpha = 1; game.addChild(npc._hostileLabel); } if (npc._hostileLabel) { npc._hostileLabel.x = npc.x; npc._hostileLabel.y = npc.y - 80; npc._hostileLabel.alpha = 1; npc._hostileLabel._expireTick = LK.ticks + 60; // 1 second } if (npc._hostileLabel && npc._hostileLabel._expireTick && LK.ticks > npc._hostileLabel._expireTick) { if (npc._hostileLabel.parent) { npc._hostileLabel.parent.removeChild(npc._hostileLabel); } npc._hostileLabel = null; } else if (npc._hostileLabel && npc._hostileLabel._expireTick) { var fadeStart = npc._hostileLabel._expireTick - 20; if (LK.ticks > fadeStart) { npc._hostileLabel.alpha = Math.max(0, (npc._hostileLabel._expireTick - LK.ticks) / 20); } else { npc._hostileLabel.alpha = 1; } } } else { // Remove hostile label if present if (npc._hostileLabel && npc._hostileLabel.parent) { npc._hostileLabel.parent.removeChild(npc._hostileLabel); npc._hostileLabel = null; } } } else { // Remove hostile label if present if (npc._hostileLabel && npc._hostileLabel.parent) { npc._hostileLabel.parent.removeChild(npc._hostileLabel); npc._hostileLabel = null; } } } // Update asteroids for (var i = asteroids.length - 1; i >= 0; i--) { var asteroid = asteroids[i]; if (asteroid._healthBar && asteroid._healthBar.update) { asteroid._healthBar.update(); } var dist = Math.sqrt(Math.pow(asteroid.x - ship.x, 2) + Math.pow(asteroid.y - ship.y, 2)); if (dist > 2500) { asteroid.destroy(); asteroids.splice(i, 1); } } // Update explosion particles for (var i = explosions.length - 1; i >= 0; i--) { var particle = explosions[i]; particle.x += particle.vx; particle.y += particle.vy; particle.vx *= 0.9; particle.vy *= 0.9; particle.life--; particle.alpha = particle.life / 30; particle.scaleX = particle.scaleY = 1 + (30 - particle.life) / 10; if (particle.life <= 0) { particle.destroy(); explosions.splice(i, 1); } } // Collect items for (var i = coins.length - 1; i >= 0; i--) { // Remove if marked for removal by fade-out if (coins[i]._shouldRemove) { coins[i].destroy(); coins.splice(i, 1); continue; } if (coins[i].intersects(ship)) { // CoinBox ise özel toplama efekti ve 50-100 coin ver if (typeof coins[i].value === "number" && coins[i].value >= 50 && coins[i].value <= 100 && coins[i].constructor && coins[i].constructor.name === "CoinBox") { playerCoins += coins[i].value; // Kısa bir label göster var boxLabel = new Text2("+" + coins[i].value + " coin!", { size: 36, fill: 0xffd700, align: "center" }); boxLabel.anchor.set(0.5, 0.5); boxLabel.x = ship.x; boxLabel.y = ship.y - 120; game.addChild(boxLabel); LK.setTimeout(function () { if (boxLabel.parent) { boxLabel.parent.removeChild(boxLabel); } }, 900); coins[i].destroy(); coins.splice(i, 1); LK.getSound('collect').play(); updateUI(); saveProgress(); continue; } // Normal coin ise playerCoins += coins[i].value; coins[i].destroy(); coins.splice(i, 1); LK.getSound('collect').play(); updateUI(); saveProgress(); } } for (var i = resources.length - 1; i >= 0; i--) { // Remove if marked for removal by fade-out if (resources[i]._shouldRemove) { resources[i].destroy(); resources.splice(i, 1); continue; } if (resources[i].intersects(ship)) { // 10 tip için index bul var resourceTypes = ['metal', 'energy', 'crystal', 'gas', 'ice', 'uranium', 'silicon', 'carbon', 'plasma', 'antimatter']; var idx = resourceTypes.indexOf(resources[i].type); if (idx >= 0) { playerResources[idx] += resources[i].amount; // --- BAŞARIM: Hammadde toplama --- checkResourceCollectAchievements(); } resources[i].destroy(); resources.splice(i, 1); LK.getSound('collect').play(); updateUI(); saveProgress(); } } // --- BAŞARIM: Coin biriktirme --- checkCoinAchievements(); // --- BAŞARIM: Seviye atlama --- checkLevelAchievements(); // --- BAŞARIM: Kalkan doldurma --- checkShieldAchievements(); // --- BAŞARIM: Can yükseltme --- checkHealthAchievements(); // --- BAŞARIM: Hız yükseltme --- checkSpeedAchievements(); // --- BAŞARIM: Atış hızı yükseltme --- checkFireRateAchievements(); // --- BAŞARIM: Yol katetme --- checkDistanceAchievements(); // --- BAŞARIM: 30 dakika hayatta kalma --- var elapsedTicks = LK.ticks - gameStartTick; var elapsedSeconds = Math.floor(elapsedTicks / 60); checkSurvive30MinAchievement(elapsedSeconds); // Update stars for (var i = stars.length - 1; i >= 0; i--) { var dist = Math.sqrt(Math.pow(stars[i].x - ship.x, 2) + Math.pow(stars[i].y - ship.y, 2)); if (dist > 2000) { stars[i].destroy(); stars.splice(i, 1); } } // Spawn entities (star spawn frequency is now random interval) if (typeof window._nextStarSpawnTick === "undefined") { window._nextStarSpawnTick = LK.ticks + Math.floor(20 + Math.random() * 60); // 20-80 ticks } if (LK.ticks >= window._nextStarSpawnTick) { spawnStar(true); window._nextStarSpawnTick = LK.ticks + Math.floor(20 + Math.random() * 60); // 20-80 ticks } // Spawn pirates and NPCs at intervals if (pirates.length < 6 && Math.random() < 0.3) { spawnPirate(); } if (npcs.length < 4 && Math.random() < 0.2) { spawnNPC(); } // --- ASTEROID SYSTEM RESTORED --- // Spawn asteroids if less than 3 in the world (50% reduction from 6) // Also halve the spawn rate if (asteroids.length < 3 && Math.random() < 0.0125) { spawnAsteroid(); } // --- GRUP KORSAN SQUAD SYSTEM (at least 1 squad always present, Katil Korsan mantığı ile) --- if (typeof window._grupKorsanSpawned === "undefined") { window._grupKorsanSpawned = false; } if (typeof window._grupKorsanRespawnTimer === "undefined") { window._grupKorsanRespawnTimer = null; } // Count living GrupKorsan pirates var grupKorsanCount = 0; for (var i = 0; i < pirates.length; i++) { if (pirates[i] && pirates[i]._isGrupKorsan) { grupKorsanCount++; } } // If none exist, spawn a new squad (2-4 members) after 6 seconds, like Katil Korsan if (grupKorsanCount === 0 && !window._grupKorsanSpawned && !window._grupKorsanRespawnTimer) { window._grupKorsanRespawnTimer = LK.setTimeout(function () { if (!window._grupKorsanSpawned) { // Pick squad size 2-4 var squadSize = 2 + Math.floor(Math.random() * 3); // 2,3,4 // Spawn from edge like Katil Korsan var edge = Math.floor(Math.random() * 4); var centerX = ship.x, centerY = ship.y; switch (edge) { case 0: centerX = ship.x + (Math.random() - 0.5) * 1800; centerY = ship.y - 1400; break; case 1: centerX = ship.x + 1400; centerY = ship.y + (Math.random() - 0.5) * 1800; break; case 2: centerX = ship.x + (Math.random() - 0.5) * 1800; centerY = ship.y + 1400; break; case 3: centerX = ship.x - 1400; centerY = ship.y + (Math.random() - 0.5) * 1800; break; } spawnPirateSquad(centerX, centerY, squadSize); window._grupKorsanSpawned = true; } window._grupKorsanRespawnTimer = null; }, 6000); } // If all GrupKorsan pirates are dead or removed, allow respawn again if (grupKorsanCount === 0 && window._grupKorsanSpawned) { window._grupKorsanSpawned = false; // If timer is running, clear it to avoid double spawn if (window._grupKorsanRespawnTimer) { LK.clearTimeout(window._grupKorsanRespawnTimer); window._grupKorsanRespawnTimer = null; } } // Update score LK.setScore(playerCoins + questsCompleted * 100 + enemiesKilled * 10 + Math.floor(totalDistance / 100)); if (ship._healthBar && ship._healthBar.update) { ship._healthBar.update(); } updateUI(); // --- AUTO-REPAIR PANEL WHEN HEALTH IS LOW OR HEALTH BAR TAPPED --- // Show repair panel ONLY if explicitly requested (not automatically when health decreases) if (!window.repairPanel && ship.health < ship.maxHealth && ship.health > 0 && window._showRepairPanel && (!window._repairPanelSuppressUntil || LK.ticks > window._repairPanelSuppressUntil)) { // Do nothing: do not auto-open repair panel when health decreases // The repair panel will only open if window._showRepairPanel is set by explicit user action (e.g. health bar tap) } else if (window.repairPanel && ship.health >= ship.maxHealth * 0.7) { // Remove repair panel if health is restored if (window.repairPanel.parent) { window.repairPanel.parent.removeChild(window.repairPanel); } if (window.repairText && window.repairText.parent) { window.repairText.parent.removeChild(window.repairText); } if (window.repairBtn && window.repairBtn.parent) { window.repairBtn.parent.removeChild(window.repairBtn); } if (window.cancelRepairBtn && window.cancelRepairBtn.parent) { window.cancelRepairBtn.parent.removeChild(window.cancelRepairBtn); } window.repairPanel = null; window.repairText = null; window.repairBtn = null; window.cancelRepairBtn = null; window._showRepairPanel = false; } // If health < 50% and not dead, do NOT auto-show repair panel (disabled as per new requirement) // (window._showRepairPanel is only set by explicit user action, e.g. health bar tap) // --- GAME TIME UPDATE --- // Calculate elapsed time in seconds var elapsedTicks = LK.ticks - gameStartTick; var elapsedSeconds = Math.floor(elapsedTicks / 60); var minutes = Math.floor(elapsedSeconds / 60); var seconds = elapsedSeconds % 60; if (typeof gameTimeText !== "undefined") { // Format as mm:ss var timeStr = "Game Time: " + minutes + ":" + (seconds < 10 ? "0" : "") + seconds; gameTimeText.setText(timeStr); // Always keep gameTimeText to the right of coinText gameTimeText.x = coinText.x + coinText.width + 40; gameTimeText.y = coinText.y; } // --- Synchronize LK.ticks with real game time --- // This ensures all time-based systems use real elapsed time, not just frame count LK.ticks = gameStartTick + Math.floor((Date.now() - (window._gameStartRealTime || (window._gameStartRealTime = Date.now()))) / (1000 / 60)); };
/****
* Plugins
****/
var tween = LK.import("@upit/tween.v1");
var storage = LK.import("@upit/storage.v1");
/****
* Classes
****/
var Asteroid = Container.expand(function () {
var self = Container.call(this);
var asteroidGraphics = self.attachAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x8B4513,
scaleX: 2,
scaleY: 2
});
self.health = 50;
self.resources = Math.floor(Math.random() * 10) + 5;
self.vx = (Math.random() - 0.5) * 0.5;
self.vy = (Math.random() - 0.5) * 0.5;
self.rotationSpeed = (Math.random() - 0.5) * 0.02;
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xffffff, 100);
};
self.update = function () {
self.x += self.vx;
self.y += self.vy;
self.rotation += self.rotationSpeed;
};
return self;
});
// Black Hole Hazard Class
var BlackHole = Container.expand(function () {
var self = Container.call(this);
var bhGraphics = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 8,
scaleY: 8,
tint: 0x222222
});
self.radius = 200;
self.pullStrength = 0.7 + Math.random() * 0.5;
self.damageRadius = 120;
self.update = function () {
// Animate black hole
bhGraphics.rotation += 0.03;
// Pull ship if in range
var dx = ship.x - self.x;
var dy = ship.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < self.radius) {
var pull = (self.radius - dist) / self.radius * self.pullStrength;
ship.x -= dx / dist * pull;
ship.y -= dy / dist * pull;
// Camera follows ship, so move all objects accordingly
for (var i = 0; i < stars.length; i++) {
stars[i].x -= dx / dist * pull;
stars[i].y -= dy / dist * pull;
}
for (var i = 0; i < npcs.length; i++) {
npcs[i].x -= dx / dist * pull;
npcs[i].y -= dy / dist * pull;
}
for (var i = 0; i < pirates.length; i++) {
pirates[i].x -= dx / dist * pull;
pirates[i].y -= dy / dist * pull;
}
for (var i = 0; i < coins.length; i++) {
coins[i].x -= dx / dist * pull;
coins[i].y -= dy / dist * pull;
}
for (var i = 0; i < resources.length; i++) {
resources[i].x -= dx / dist * pull;
resources[i].y -= dy / dist * pull;
}
for (var i = 0; i < bullets.length; i++) {
bullets[i].x -= dx / dist * pull;
bullets[i].y -= dy / dist * pull;
}
for (var i = 0; i < enemyBullets.length; i++) {
enemyBullets[i].x -= dx / dist * pull;
enemyBullets[i].y -= dy / dist * pull;
}
for (var i = 0; i < asteroids.length; i++) {
asteroids[i].x -= dx / dist * pull;
asteroids[i].y -= dy / dist * pull;
}
for (var i = 0; i < upgradeStations.length; i++) {
upgradeStations[i].x -= dx / dist * pull;
upgradeStations[i].y -= dy / dist * pull;
}
// boostParticles removed (no boost system)
for (var i = 0; i < explosions.length; i++) {
explosions[i].x -= dx / dist * pull;
explosions[i].y -= dy / dist * pull;
}
}
// Damage ship if too close
if (dist < self.damageRadius) {
if (!self.lastDamaged || LK.ticks - self.lastDamaged > 30) {
ship.takeDamage(10);
self.lastDamaged = LK.ticks;
LK.effects.flashObject(ship, 0x000000, 200);
}
}
};
return self;
});
var Bullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('bullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 15;
self.damage = 10;
self.directionX = 0;
self.directionY = -1;
self._outOfRangeTick = null;
self._fadeStartTick = null;
self._expireTick = null;
self._range = typeof ATTACK_RANGE !== "undefined" ? ATTACK_RANGE : 220;
self._startX = null;
self._startY = null;
self.update = function () {
if (self._startX === null || self._startY === null) {
self._startX = self.x;
self._startY = self.y;
}
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
// Out of range detection: bullet range = attack range
var dx = self.x - self._startX;
var dy = self.y - self._startY;
var dist = Math.sqrt(dx * dx + dy * dy);
var outOfRange = dist > self._range;
if (outOfRange && self._outOfRangeTick === null) {
self._outOfRangeTick = LK.ticks;
self._fadeStartTick = self._outOfRangeTick;
self._expireTick = self._fadeStartTick + 60; // 1 second fade
}
// Fade out logic if out of range
if (self._fadeStartTick !== null && self._expireTick !== null) {
var now = LK.ticks;
var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick));
self.alpha = 1 - fadeProgress;
if (now >= self._expireTick) {
self._shouldRemove = true;
}
} else {
self.alpha = 1;
}
};
return self;
});
var Coin = Container.expand(function () {
var self = Container.call(this);
var coinGraphics = self.attachAsset('coin', {
anchorX: 0.5,
anchorY: 0.5
});
self.value = Math.floor(Math.random() * 10) + 5;
// --- Timed fade-out system ---
self._spawnTick = LK.ticks;
self._fadeStartTick = self._spawnTick + 180; // 3 seconds visible
self._expireTick = self._fadeStartTick + 120; // 2 seconds fade (total 5s)
self.update = function () {
self.rotation += 0.05;
// Timed fade-out logic
if (typeof self._expireTick === "number" && typeof self._fadeStartTick === "number") {
var now = LK.ticks;
if (now >= self._fadeStartTick) {
// Start fading out
var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick));
self.alpha = 1 - fadeProgress;
if (now >= self._expireTick) {
// Mark for removal (actual removal in game.update)
self._shouldRemove = true;
}
} else {
self.alpha = 1;
}
}
};
return self;
});
// CoinBox: Toplanınca 50-100 coin veren, fade-out ile kaybolan obje
var CoinBox = Container.expand(function () {
var self = Container.call(this);
var boxGraphics = self.attachAsset('coinbox', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 0.9,
scaleY: 0.9
});
self.value = 50 + Math.floor(Math.random() * 51); // 50-100 coin arası
// Timed fade-out system
self._spawnTick = LK.ticks;
self._fadeStartTick = self._spawnTick + 240; // 4 seconds visible
self._expireTick = self._fadeStartTick + 120; // 2 seconds fade (total 6s)
self.update = function () {
boxGraphics.rotation += 0.07;
// Timed fade-out logic
if (typeof self._expireTick === "number" && typeof self._fadeStartTick === "number") {
var now = LK.ticks;
if (now >= self._fadeStartTick) {
var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick));
self.alpha = 1 - fadeProgress;
if (now >= self._expireTick) {
self._shouldRemove = true;
}
} else {
self.alpha = 1;
}
}
};
return self;
});
// DerelictDebris: Enkaz, üstüne gelince 0-50 arası rastgele hammadde verir ve fade-out ile kaybolur
var DerelictDebris = Container.expand(function () {
var self = Container.call(this);
// Use randomly selected pirate asset for debris appearance
var pirateImageIndex = Math.floor(Math.random() * 20) + 1;
var pirateAssetName = 'pirate' + pirateImageIndex;
var debrisGraphics = self.attachAsset(pirateAssetName, {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00ff00,
// yeşil enkaz
scaleX: 1.5,
scaleY: 1.5
});
self.type = 'derelict';
self.rotationSpeed = 0.01;
self._collected = false;
self._resourceGiven = false;
self._fadeStartTick = null;
self._expireTick = null;
self._resourceAmount = Math.floor(Math.random() * 51); // 0-50 arası
self.update = function () {
self.rotation += self.rotationSpeed;
// Fade-out logic
if (self._fadeStartTick !== null && self._expireTick !== null) {
var now = LK.ticks;
var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick));
self.alpha = 1 - fadeProgress;
if (now >= self._expireTick) {
self._shouldRemove = true;
}
} else {
self.alpha = 1;
}
};
return self;
});
// --- DRON CLASS ---
// Dron: ship'i takip eder, %80 yakınındaki maden/coinleri toplar ve ship'e getirir
var Dron = Container.expand(function () {
var self = Container.call(this);
// Randomly select one of 2 NPC images for dron appearance
var npcImageIndex = Math.floor(Math.random() * 2) + 1;
var npcAssetName = 'npc' + npcImageIndex;
var dronGraphics = self.attachAsset(npcAssetName, {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x1a2dcb,
scaleX: 1.2,
scaleY: 1.2
});
self._followOffset = {
x: -100 + Math.random() * 40,
y: 80 + Math.random() * 40
};
self._targetItem = null;
self._collecting = false;
self._collectSpeed = 10;
self.update = function () {
// --- Hafif float/wobble animasyonu: Dron sabit durmasın, havada uçuyormuş gibi hareket etsin ---
if (typeof self._floatBaseY === "undefined") {
self._floatBaseY = self.y;
}
if (typeof self._floatBaseX === "undefined") {
self._floatBaseX = self.x;
}
if (typeof self._floatPhase === "undefined") {
self._floatPhase = Math.random() * Math.PI * 2;
}
if (typeof self._floatPhaseX === "undefined") {
self._floatPhaseX = Math.random() * Math.PI * 2;
}
// Ship'i takip et (eğer item toplamıyorsa)
if (!self._collecting && typeof ship !== "undefined" && ship) {
var targetX = ship.x + self._followOffset.x;
var targetY = ship.y + self._followOffset.y;
var dx = targetX - self.x;
var dy = targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 10) {
self.x += dx / dist * 7;
self.y += dy / dist * 7;
}
// Float baseyi güncelle ki float animasyonu ship ile birlikte hareket etsin
self._floatBaseX = self.x;
self._floatBaseY = self.y;
}
// Eğer item topluyorsa, ona doğru git
if (self._collecting && self._targetItem) {
var dx = self._targetItem.x - self.x;
var dy = self._targetItem.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 10) {
self.x += dx / dist * self._collectSpeed;
self.y += dy / dist * self._collectSpeed;
} else {
// Toplama mesafesine ulaştı
// Coin veya resource ise ship'e getir
self._targetItem._dronCollected = true;
// Ship'e doğru item'ı taşı
self._targetItem._beingCarried = true;
self._targetItem._carrier = self;
self._collecting = false;
self._targetItem = null;
}
// Float baseyi güncelle ki float animasyonu item toplarken de düzgün çalışsın
self._floatBaseX = self.x;
self._floatBaseY = self.y;
}
// Eğer item taşıyorsa, item'ı ship'e doğru taşı
for (var i = 0; i < coins.length; i++) {
var c = coins[i];
if (c._beingCarried && c._carrier === self) {
var dx = ship.x - c.x;
var dy = ship.y - c.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 10) {
c.x += dx / dist * self._collectSpeed;
c.y += dy / dist * self._collectSpeed;
} else {
// Ship'e ulaştı, coin topla
playerCoins += c.value;
c.destroy();
coins.splice(i, 1);
LK.getSound('collect').play();
updateUI && updateUI();
saveProgress && saveProgress();
}
}
}
for (var i = 0; i < resources.length; i++) {
var r = resources[i];
if (r._beingCarried && r._carrier === self) {
var dx = ship.x - r.x;
var dy = ship.y - r.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 10) {
r.x += dx / dist * self._collectSpeed;
r.y += dy / dist * self._collectSpeed;
} else {
// Ship'e ulaştı, resource topla
var resourceTypes = ['metal', 'energy', 'crystal', 'gas', 'ice', 'uranium', 'silicon', 'carbon', 'plasma', 'antimatter'];
var idx = resourceTypes.indexOf(r.type);
if (idx >= 0) {
playerResources[idx] += r.amount;
checkResourceCollectAchievements && checkResourceCollectAchievements();
}
r.destroy();
resources.splice(i, 1);
LK.getSound('collect').play();
updateUI && updateUI();
saveProgress && saveProgress();
}
}
}
// Eğer item toplamıyorsa, yakındaki item'ı ara
if (!self._collecting && !self._targetItem) {
// %25 ship'e yakın, ekranda olan coin/resource bul
var found = false;
var maxDist = Math.max(2048, 2732) * 0.25;
for (var i = 0; i < coins.length; i++) {
var c = coins[i];
if (!c._dronCollected && Math.sqrt(Math.pow(c.x - ship.x, 2) + Math.pow(c.y - ship.y, 2)) < maxDist) {
self._targetItem = c;
self._collecting = true;
found = true;
break;
}
}
if (!found) {
for (var i = 0; i < resources.length; i++) {
var r = resources[i];
if (!r._dronCollected && Math.sqrt(Math.pow(r.x - ship.x, 2) + Math.pow(r.y - ship.y, 2)) < maxDist) {
self._targetItem = r;
self._collecting = true;
break;
}
}
}
}
// --- Hafif float/wobble animasyonu uygula ---
// Dron'un gerçek pozisyonunu floatBaseX/floatBaseY olarak tut, ek olarak x/y'ye küçük bir sinusoidal offset uygula
// Amplitüd: 8px (y), 4px (x), Periyot: 2-3 saniye arası
var floatY = Math.sin(LK.ticks * 0.05 + self._floatPhase) * 8;
var floatX = Math.cos(LK.ticks * 0.033 + self._floatPhaseX) * 4;
self.x = self._floatBaseX + floatX;
self.y = self._floatBaseY + floatY;
};
return self;
});
var EnemyBullet = Container.expand(function () {
var self = Container.call(this);
var bulletGraphics = self.attachAsset('enemyBullet', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = 8;
self.damage = 5;
self.directionX = 0;
self.directionY = 0;
self._outOfRangeTick = null;
self._fadeStartTick = null;
self._expireTick = null;
self._range = typeof ATTACK_RANGE !== "undefined" ? ATTACK_RANGE : 220;
self._startX = null;
self._startY = null;
self.update = function () {
if (self._startX === null || self._startY === null) {
self._startX = self.x;
self._startY = self.y;
}
self.x += self.directionX * self.speed;
self.y += self.directionY * self.speed;
// Out of range detection: bullet range = attack range
var dx = self.x - self._startX;
var dy = self.y - self._startY;
var dist = Math.sqrt(dx * dx + dy * dy);
var outOfRange = dist > self._range;
if (outOfRange && self._outOfRangeTick === null) {
self._outOfRangeTick = LK.ticks;
self._fadeStartTick = self._outOfRangeTick;
self._expireTick = self._fadeStartTick + 60; // 1 second fade
}
// Fade out logic if out of range
if (self._fadeStartTick !== null && self._expireTick !== null) {
var now = LK.ticks;
var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick));
self.alpha = 1 - fadeProgress;
if (now >= self._expireTick) {
self._shouldRemove = true;
}
} else {
self.alpha = 1;
}
};
return self;
});
// --- FRIEND CLASS ---
// Dost birlik: ship'i takip eder, saldırı sırasında %50 hasar ile saldırır
var Friend = Container.expand(function () {
var self = Container.call(this);
// Randomly select one of 2 NPC images for friend appearance
var npcImageIndex = Math.floor(Math.random() * 2) + 1;
var npcAssetName = 'npc' + npcImageIndex;
var friendGraphics = self.attachAsset(npcAssetName, {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x83de44,
scaleX: 1.1,
scaleY: 1.1
});
// --- Dynamic follow offset: will be updated every frame for movement/spacing ---
self._followOffset = {
x: 80 - Math.random() * 40,
y: 80 + Math.random() * 40
};
self._attackTarget = null;
self._attacking = false;
self._attackStartTick = 0;
self._lastAttackTick = 0;
self._attackDelay = 60; // 1 saniye (60fps)
self._attackInterval = 10; // her 10 tickte bir ateş et (ateş aralığı hızlı kalsın, istenirse 5 yapılabilir)
// --- For movement/spacing ---
self._moveAngle = Math.random() * Math.PI * 2;
self._moveTimer = Math.floor(Math.random() * 60);
self._friendIdx = -1; // will be set in update
self._desiredDist = 90; // base distance from ship
self._avoidDist = 70; // min distance to other friends
self.update = function () {
// Find my index in friends array for spacing
if (typeof friends !== "undefined" && Array.isArray(friends)) {
for (var fi = 0; fi < friends.length; fi++) {
if (friends[fi] === self) {
self._friendIdx = fi;
break;
}
}
}
// --- Dynamic orbit/spacing around ship ---
if (typeof ship !== "undefined" && ship) {
// Orbit angle: spread friends evenly in a circle, animate with time for movement
var n = 0,
myIdx = 0;
for (var fi = 0; fi < friends.length; fi++) {
if (friends[fi]) {
n++;
}
if (friends[fi] === self) {
myIdx = fi;
}
}
if (n === 0) {
n = 1;
}
var baseAngle = Math.PI * 2 * (myIdx / n);
var timeWobble = Math.sin((LK.ticks + myIdx * 30) / 60) * 0.5 + Math.cos((LK.ticks + myIdx * 60) / 80) * 0.3;
var angle = baseAngle + timeWobble;
var radius = self._desiredDist + 20 * Math.sin((LK.ticks + myIdx * 40) / 50);
// Target position around ship
var targetX = ship.x + Math.cos(angle) * radius;
var targetY = ship.y + Math.sin(angle) * radius;
// --- Avoid overlapping with other friends ---
if (typeof friends !== "undefined" && Array.isArray(friends)) {
for (var fi = 0; fi < friends.length; fi++) {
var f2 = friends[fi];
if (f2 && f2 !== self) {
var dx = self.x - f2.x;
var dy = self.y - f2.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < self._avoidDist && dist > 0) {
// Push away from other friend
var push = (self._avoidDist - dist) * 0.15;
targetX += dx / dist * push;
targetY += dy / dist * push;
}
}
}
}
// --- Smoothly move toward target position ---
var dx = targetX - self.x;
var dy = targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
var moveSpeed = 5 + Math.sin((LK.ticks + myIdx * 20) / 40) * 1.5;
if (dist > 2) {
self.x += dx / dist * Math.min(moveSpeed, dist);
self.y += dy / dist * Math.min(moveSpeed, dist);
}
}
// --- Friend ship rotation: rotate to match player ship direction ---
if (typeof ship !== "undefined" && ship && typeof ship.rotation === "number") {
// Smoothly rotate friend ship to match player ship rotation
var targetRotation = ship.rotation;
var rotDiff = targetRotation - self.rotation;
// Normalize angle difference to [-π, π]
while (rotDiff > Math.PI) {
rotDiff -= Math.PI * 2;
}
while (rotDiff < -Math.PI) {
rotDiff += Math.PI * 2;
}
// Apply smooth rotation with lerp factor
var rotLerp = 0.1; // Adjust for rotation smoothness
self.rotation += rotDiff * rotLerp;
}
// --- Saldırı kontrolü ---
if (self._attacking && self._attackTarget && typeof self._attackTarget.x === "number" && typeof self._attackTarget.y === "number") {
// 2 saniye gecikmeden sonra saldır
if (LK.ticks - self._attackStartTick >= self._attackDelay) {
// Hedef hala hayatta mı?
if (self._attackTarget.health > 0) {
// Her attackInterval tickte bir ateş et
if (LK.ticks - self._lastAttackTick > self._attackInterval) {
var bullet = new Bullet();
bullet.x = self.x;
bullet.y = self.y;
var angle = Math.atan2(self._attackTarget.y - self.y, self._attackTarget.x - self.x);
bullet.directionX = Math.cos(angle);
bullet.directionY = Math.sin(angle);
bullet.rotation = angle + Math.PI / 2;
bullet.damage = Math.round((ship.damage || 10) * 0.5);
bullets.push(bullet);
game.addChild(bullet);
self._lastAttackTick = LK.ticks;
// Calculate distance from ship to friend bullet spawn point and adjust volume
var dx = bullet.x - ship.x;
var dy = bullet.y - ship.y;
var distanceFromShip = Math.sqrt(dx * dx + dy * dy);
// Calculate volume based on distance (closer = louder, farther = quieter)
// Max distance for sound falloff is 500 pixels
var maxSoundDistance = 500;
var volumeMultiplier = Math.max(0.1, Math.min(1.0, 1.0 - distanceFromShip / maxSoundDistance));
// Get the sound and play with adjusted volume
var shootSound = LK.getSound('shoot');
var originalVolume = shootSound.volume || 0.3; // fallback to default volume
shootSound.volume = originalVolume * volumeMultiplier;
shootSound.play();
// Restore original volume for next play
shootSound.volume = originalVolume;
}
} else {
// Hedef öldü, saldırıyı bırak
self._attacking = false;
self._attackTarget = null;
}
}
}
};
// Saldırı başlat
self.startAttack = function (target) {
self._attackTarget = target;
self._attacking = true;
self._attackStartTick = LK.ticks;
self._lastAttackTick = LK.ticks;
};
// Saldırı bırak
self.stopAttack = function () {
self._attacking = false;
self._attackTarget = null;
};
return self;
});
// GrupKorsan (Group Pirate) class: 2-4 pirates moving and attacking together, with reward label under each
var GrupKorsan = Container.expand(function () {
var self = Container.call(this);
// Use randomly selected pirate asset for each group member
var pirateImageIndex = Math.floor(Math.random() * 20) + 1;
var pirateAssetName = 'pirate' + pirateImageIndex;
var pirateGraphics = self.attachAsset(pirateAssetName, {
anchorX: 0.5,
anchorY: 0.5
});
// Group member index and group size (set externally)
self.groupIndex = 0;
self.groupSize = 2;
// Level and stats (set externally)
self.level = 1;
self.health = 30;
self.speed = 2;
self.fireRate = 40;
self.lastFire = 0;
self.damage = 10;
self.defense = 5;
self.loot = 200;
self._isGrupKorsan = true;
// --- Enemy Text ---
self._enemyText = new Text2("Enemy", {
size: 18,
fill: 0xff4444,
align: "center"
});
self._enemyText.anchor.set(0.5, 0);
self._enemyText.x = 0;
self._enemyText.y = -50; // Above the pirate sprite
self.addChild(self._enemyText);
// --- Level Text ---
self._levelText = new Text2("Lv." + self.level, {
size: 22,
fill: 0xcccccc,
align: "center"
});
self._levelText.anchor.set(0.5, 0);
self._levelText.x = 0;
self._levelText.y = pirateGraphics.height / 2 + 4;
self.addChild(self._levelText);
// Reward label and coin value text removed for GrupKorsan
// --- Health bar ---
self._healthBar = new HealthBar();
self._healthBar.set({
maxValue: function maxValue() {
return self.health > 0 ? self.health : 0;
},
getValueFn: function getValueFn() {
return self.health > 0 ? self.health : 0;
},
getMaxFn: function getMaxFn() {
return 30 + (self.level - 1) * 10;
},
width: 60,
height: 10
});
self._healthBar.y = -60;
self.addChild(self._healthBar);
// --- Group movement: all members follow the same target/angle, set externally ---
self._groupTarget = null;
self._groupAngle = 0;
self._groupMoveSpeed = 2;
// --- Damage handler ---
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xffffff, 100);
};
// --- Update ---
self.update = function () {
// Update enemy text position in case of movement
if (self._enemyText) {
self._enemyText.x = 0;
self._enemyText.y = -50;
}
// Update level/reward text positions
if (self._levelText) {
self._levelText.x = 0;
self._levelText.y = pirateGraphics.height / 2 + 4;
self._levelText.setText("Lv." + self.level);
}
// Reward label and coin value update removed for GrupKorsan
// Health bar update
if (self._healthBar && self._healthBar.update) {
self._healthBar.update();
}
// Group movement: follow group target/angle if set
if (self._groupTarget && typeof self._groupTarget.x === "number" && typeof self._groupTarget.y === "number") {
var dx = self._groupTarget.x - self.x;
var dy = self._groupTarget.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 10) {
self.x += dx / dist * self._groupMoveSpeed;
self.y += dy / dist * self._groupMoveSpeed;
self._groupAngle = Math.atan2(dy, dx);
self.rotation = self._groupAngle + Math.PI / 2;
}
} else if (typeof self._groupAngle === "number") {
// Move in group direction if no target
self.x += Math.cos(self._groupAngle) * self._groupMoveSpeed;
self.y += Math.sin(self._groupAngle) * self._groupMoveSpeed;
self.rotation = self._groupAngle + Math.PI / 2;
}
};
return self;
});
// HealthBar class for displaying health above entities
var HealthBar = Container.expand(function () {
var self = Container.call(this);
// width, height, color, maxValue, getValueFn
self._width = 60;
self._height = 10;
self._color = 0x00ff00;
self._maxValue = 100;
self._getValueFn = null;
self._getMaxFn = null;
self._bg = self.attachAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: self._width / 40,
scaleY: self._height / 40,
tint: 0x222222
});
self._bar = self.attachAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: (self._width - 4) / 40,
scaleY: (self._height - 4) / 40,
tint: self._color
});
self._bar.x = 0;
self._bar.y = 0;
self.set = function (opts) {
self._maxValue = opts.maxValue || 100;
self._getValueFn = opts.getValueFn;
self._getMaxFn = opts.getMaxFn;
self._color = opts.color || 0x00ff00;
self._bar.tint = self._color;
if (opts.width) {
self._width = opts.width;
self._bg.scaleX = self._width / 40;
self._bar.scaleX = (self._width - 4) / 40;
}
if (opts.height) {
self._height = opts.height;
self._bg.scaleY = self._height / 40;
self._bar.scaleY = (self._height - 4) / 40;
}
};
self.update = function () {
var value = self._getValueFn ? self._getValueFn() : self._maxValue;
var maxValue = self._getMaxFn ? self._getMaxFn() : self._maxValue;
var ratio = Math.max(0, Math.min(1, value / maxValue));
// Color: green > yellow > red
if (ratio > 0.6) {
self._bar.tint = 0x00ff00;
} else if (ratio > 0.3) {
self._bar.tint = 0xffff00;
} else {
self._bar.tint = 0xff0000;
}
self._bar.scaleX = (self._width - 4) * ratio / 40;
};
return self;
});
// --- MECHANIC CLASS ---
// Mechanic: Automatically repairs ship when health is low
var Mechanic = Container.expand(function () {
var self = Container.call(this);
// Randomly select one of 2 NPC images for mechanic appearance
var npcImageIndex = Math.floor(Math.random() * 2) + 1;
var npcAssetName = 'npc' + npcImageIndex;
var mechanicGraphics = self.attachAsset(npcAssetName, {
anchorX: 0.5,
anchorY: 0.5,
tint: 0xff6600,
// Orange tint for mechanic
scaleX: 1.1,
scaleY: 1.1
});
self._followOffset = {
x: -120 + Math.random() * 40,
y: -80 + Math.random() * 40
};
self._repairDelay = 180; // 3 seconds between repairs
self._lastRepairTick = 0;
self.update = function () {
// Float animation like Dron
if (typeof self._floatBaseY === "undefined") {
self._floatBaseY = self.y;
}
if (typeof self._floatBaseX === "undefined") {
self._floatBaseX = self.x;
}
if (typeof self._floatPhase === "undefined") {
self._floatPhase = Math.random() * Math.PI * 2;
}
if (typeof self._floatPhaseX === "undefined") {
self._floatPhaseX = Math.random() * Math.PI * 2;
}
// Follow ship
if (typeof ship !== "undefined" && ship) {
var targetX = ship.x + self._followOffset.x;
var targetY = ship.y + self._followOffset.y;
var dx = targetX - self.x;
var dy = targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 10) {
self.x += dx / dist * 6;
self.y += dy / dist * 6;
}
self._floatBaseX = self.x;
self._floatBaseY = self.y;
}
// Auto repair ship when health is below 50%
if (ship && ship.health < ship.maxHealth * 0.5 && LK.ticks - self._lastRepairTick > self._repairDelay) {
var repairAmount = Math.round(ship.maxHealth * 0.1); // Repair 10% of max health
ship.health = Math.min(ship.health + repairAmount, ship.maxHealth);
self._lastRepairTick = LK.ticks;
// Visual effect
LK.effects.flashObject(ship, 0x00ff99, 300);
// Floating label
var label = new Text2("Mechanic Repair: +" + repairAmount + " HP", {
size: 32,
fill: 0x00ff99,
align: "center"
});
label.anchor.set(0.5, 0.5);
label.x = ship.x;
label.y = ship.y - 100;
if (typeof game !== "undefined" && game.addChild) {
game.addChild(label);
}
LK.setTimeout(function () {
if (label.parent) {
label.parent.removeChild(label);
}
}, 1200);
updateUI && updateUI();
}
// Float animation
var floatY = Math.sin(LK.ticks * 0.04 + self._floatPhase) * 6;
var floatX = Math.cos(LK.ticks * 0.03 + self._floatPhaseX) * 3;
self.x = self._floatBaseX + floatX;
self.y = self._floatBaseY + floatY;
};
return self;
});
// Helper: open Crew panel
var NPC = Container.expand(function () {
var self = Container.call(this);
// Randomly select one of 2 NPC images
var npcImageIndex = Math.floor(Math.random() * 2) + 1;
var npcAssetName = 'npc' + npcImageIndex;
var npcGraphics = self.attachAsset(npcAssetName, {
anchorX: 0.5,
anchorY: 0.5
});
self.isAI = true; // AI-driven
self.baseY = 0;
self.moveSpeed = 1 + Math.random() * 2;
self.moveAngle = Math.random() * Math.PI * 2;
self.wanderTimer = 0;
self.lastAIAction = 0;
self.targetX = null;
self.targetY = null;
self.rpgName = "NPC-" + Math.floor(Math.random() * 10000);
// --- NPC Level System ---
// Level range: wave 1 => 1-3, wave 2 => 3-5, wave 3 => 5-7, etc.
var minLevel = 1 + (typeof waveNumber !== "undefined" ? waveNumber : 0) * 2;
var maxLevel = minLevel + 2;
self.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1));
self.health = 100 + (self.level - 1) * 10;
// --- Level Text ---
self._levelText = new Text2("Lv." + self.level, {
size: 22,
fill: 0xcccccc,
align: "center"
});
self._levelText.anchor.set(0.5, 0);
self._levelText.x = 0;
self._levelText.y = 40; // Under the NPC sprite
self.addChild(self._levelText);
// Only floating AI logic, no quests/events/trade
// Add dummy interact method to prevent TypeError
self.interact = function () {
// No interaction for basic NPCs, return empty string
return "";
};
self.update = function () {
// Update level text position in case of movement
if (self._levelText) {
self._levelText.x = 0;
self._levelText.y = npcGraphics.height / 2 + 4;
self._levelText.setText("Lv." + self.level);
}
// Pause logic: If pauseUntil is set and not expired, skip movement
if (self.pauseUntil && LK.ticks < self.pauseUntil) {
// Still paused, only float animation
self.y += Math.sin(LK.ticks * 0.05) * 0.5;
return;
}
// AI: Sometimes move toward a random point, sometimes wander
self.wanderTimer++;
if (self.wanderTimer > 120 + Math.random() * 120) {
if (Math.random() < 0.5) {
// Pick a random point to move toward (simulate AI goal seeking)
self.targetX = self.x + (Math.random() - 0.5) * 800;
self.targetY = self.y + (Math.random() - 0.5) * 800;
} else {
self.targetX = null;
self.targetY = null;
}
self.moveAngle = Math.random() * Math.PI * 2;
self.wanderTimer = 0;
}
// Move toward target if set, else wander
if (self.targetX !== null && self.targetY !== null) {
var dx = self.targetX - self.x;
var dy = self.targetY - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 10) {
self.x += dx / dist * self.moveSpeed;
self.y += dy / dist * self.moveSpeed;
self.moveAngle = Math.atan2(dy, dx);
self._targetRotation = self.moveAngle + Math.PI / 2;
} else {
self.targetX = null;
self.targetY = null;
}
} else {
// Wander randomly with smooth gliding motion (no sharp turns)
if (typeof self._wanderVX === "undefined") {
self._wanderVX = Math.cos(self.moveAngle) * self.moveSpeed;
}
if (typeof self._wanderVY === "undefined") {
self._wanderVY = Math.sin(self.moveAngle) * self.moveSpeed;
}
// Target velocity for this wander direction
var targetVX = Math.cos(self.moveAngle) * self.moveSpeed;
var targetVY = Math.sin(self.moveAngle) * self.moveSpeed;
// Smoothly interpolate velocity for gliding effect
self._wanderVX += (targetVX - self._wanderVX) * 0.03;
self._wanderVY += (targetVY - self._wanderVY) * 0.03;
self.x += self._wanderVX;
self.y += self._wanderVY;
self._targetRotation = Math.atan2(self._wanderVY, self._wanderVX) + Math.PI / 2;
}
// Smoothly rotate toward target rotation
if (typeof self._targetRotation === "number") {
var diff = self._targetRotation - self.rotation;
while (diff > Math.PI) {
diff -= Math.PI * 2;
}
while (diff < -Math.PI) {
diff += Math.PI * 2;
}
var lerp = 0.05;
self.rotation += diff * lerp;
}
// Float animation on top of movement
self.y += Math.sin(LK.ticks * 0.05) * 0.5;
};
return self;
});
var Pirate = Container.expand(function () {
var self = Container.call(this);
// Randomly select one of 20 pirate images
var pirateImageIndex = Math.floor(Math.random() * 20) + 1;
var pirateAssetName = 'pirate' + pirateImageIndex;
var pirateGraphics = self.attachAsset(pirateAssetName, {
anchorX: 0.5,
anchorY: 0.5
});
// --- Pirate Level & Scaling ---
self.level = typeof waveNumber !== "undefined" ? 1 + waveNumber : 1;
self.health = 30 + (self.level - 1) * 10;
self.speed = 2 + Math.floor((self.level - 1) / 10) * 0.2; // Slight speed up every 10 waves
self.fireRate = 60 - Math.min((self.level - 1) * 2, 30); // Faster fire at higher levels
self.lastFire = 0;
self.loot = Math.floor(Math.random() * 30) + 10 + (self.level - 1) * 2;
self.moveAngle = Math.random() * Math.PI * 2;
self.patrolTimer = 0;
self.aggroRange = 600;
// Korsan saldırı ve savunma gücü seviyeye göre orantılı artar
self.damage = 5 + Math.floor((self.level - 1) * 1.5); // Saldırı gücü daha hızlı artar
self.defense = 2 + Math.floor((self.level - 1) * 1.2); // Savunma gücü de seviyeye göre artar
// --- Enemy Text ---
self._enemyText = new Text2("Enemy", {
size: 18,
fill: 0xff4444,
align: "center"
});
self._enemyText.anchor.set(0.5, 0);
self._enemyText.x = 0;
self._enemyText.y = -50; // Above the pirate sprite
self.addChild(self._enemyText);
// --- Level Text ---
self._levelText = new Text2("Lv." + self.level, {
size: 22,
fill: 0xcccccc,
align: "center"
});
self._levelText.anchor.set(0.5, 0);
self._levelText.x = 0;
self._levelText.y = 40; // Under the pirate sprite
self.addChild(self._levelText);
// Katil korsan için "Ödül:" yazısı ekle
self._rewardText = null;
if (self.rpgName === "Katil" || self._isKatil) {
self._rewardText = new Text2("Reward:", {
size: 22,
fill: 0xffcc00,
align: "center"
});
self._rewardText.anchor.set(0.5, 0);
self._rewardText.x = 0;
self._rewardText.y = self._levelText.y + 28; // Seviye yazısının hemen altında, aynı büyüklükte
self.addChild(self._rewardText);
}
// Level text only, defense text removed
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xffffff, 100);
};
self.update = function () {
// Update enemy text position in case of movement
if (self._enemyText) {
self._enemyText.x = 0;
self._enemyText.y = -50;
}
// Update level text position in case of movement
if (self._levelText) {
self._levelText.x = 0;
self._levelText.y = pirateGraphics.height / 2 + 4;
self._levelText.setText("Lv." + self.level);
}
// Katil korsan için ödül yazısı pozisyonunu güncel tut
if (self.rpgName === "Katil" || self._isKatil) {
if (!self._rewardText) {
self._rewardText = new Text2("Ödül:", {
size: 22,
fill: 0xffcc00,
align: "center"
});
self._rewardText.anchor.set(0.5, 0);
self.addChild(self._rewardText);
}
self._rewardText.x = 0;
self._rewardText.y = self._levelText.y + 28;
self._rewardText.visible = true;
// Katil korsan için coin miktarı yazısı ekle/güncelle
var rewardCoinValue = 0;
// Katil korsan için coin değeri, öldüğünde verilecek coin miktarı ile aynı olmalı
// Oyun kodunda coin.value = coin.value * (1 + Math.floor(waveNumber / 3)) * 10;
// Burada coin.value rastgele 5-14 arası, ama göstermek için ortalama değer (ör: 10) kullanılabilir
// Alternatif: self.level ve waveNumber'a göre hesapla
var baseCoin = 10; // ortalama coin
var waveNum = typeof waveNumber !== "undefined" ? waveNumber : 0;
rewardCoinValue = baseCoin * (1 + Math.floor(waveNum / 3)) * 10;
if (!self._rewardCoinText) {
self._rewardCoinText = new Text2(rewardCoinValue + " coin", {
size: 22,
fill: 0xffcc00,
align: "center"
});
self._rewardCoinText.anchor.set(0.5, 0);
self.addChild(self._rewardCoinText);
}
self._rewardCoinText.x = 0;
self._rewardCoinText.y = self._rewardText.y + 28;
self._rewardCoinText.setText(rewardCoinValue + " coin");
self._rewardCoinText.visible = true;
} else if (self._rewardText) {
self._rewardText.visible = false;
if (self._rewardCoinText) {
self._rewardCoinText.visible = false;
}
}
// Defense text removed
// Movement AI
self.patrolTimer++;
if (self.patrolTimer > 180) {
self.moveAngle = Math.random() * Math.PI * 2;
self.patrolTimer = 0;
}
// Patrol movement
if (self._squadTargetNPC && self._squadTargetNPC.questType === 'defend' && self._squadTargetNPC.questActive) {
// Korsan, NPC'yi hedefle
var target = self._squadTargetNPC;
// Eğer defend quest'te yardım kabul edildiyse, oyuncuya da saldırabilir
if (target._defendHelpAccepted === true && Math.random() < 0.5) {
// %50 ihtimalle oyuncuya saldır
target = Math.random() < 0.5 ? self._squadTargetNPC : ship;
}
var dx = target.x - self.x;
var dy = target.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist > 10) {
self.x += dx / dist * self.speed;
self.y += dy / dist * self.speed;
self.moveAngle = Math.atan2(dy, dx);
self._targetRotation = self.moveAngle + Math.PI / 2;
} else {
// Hedefe çok yaklaştıysa sadece dön
var angle = Math.atan2(target.y - self.y, target.x - self.x);
self._targetRotation = angle + Math.PI / 2;
}
// Ateş et
if (LK.ticks - self.lastFire > self.fireRate) {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y;
var angle = Math.atan2(target.y - self.y, target.x - self.x);
bullet.directionX = Math.cos(angle);
bullet.directionY = Math.sin(angle);
bullet.rotation = angle + Math.PI / 2;
enemyBullets.push(bullet);
if (game && game.addChild) {
game.addChild(bullet);
}
self.lastFire = LK.ticks;
// Keep pirate rotated to attack direction after firing
self._targetRotation = angle + Math.PI / 2;
}
} else {
// Wander randomly with smooth gliding motion (no sharp turns)
if (typeof self._wanderVX === "undefined") {
self._wanderVX = Math.cos(self.moveAngle) * self.speed * 0.5;
}
if (typeof self._wanderVY === "undefined") {
self._wanderVY = Math.sin(self.moveAngle) * self.speed * 0.5;
}
// Target velocity for this wander direction
var targetVX = Math.cos(self.moveAngle) * self.speed * 0.5;
var targetVY = Math.sin(self.moveAngle) * self.speed * 0.5;
// Smoothly interpolate velocity for gliding effect
self._wanderVX += (targetVX - self._wanderVX) * 0.03;
self._wanderVY += (targetVY - self._wanderVY) * 0.03;
self.x += self._wanderVX;
self.y += self._wanderVY;
self._targetRotation = Math.atan2(self._wanderVY, self._wanderVX) + Math.PI / 2;
}
// Smoothly rotate toward target rotation
if (typeof self._targetRotation === "number") {
var diff = self._targetRotation - self.rotation;
while (diff > Math.PI) {
diff -= Math.PI * 2;
}
while (diff < -Math.PI) {
diff += Math.PI * 2;
}
var lerp = 0.05;
self.rotation += diff * lerp;
}
};
return self;
});
// Pirate Base Class
var PirateBase = Container.expand(function () {
var self = Container.call(this);
// Use randomly selected pirate asset for base appearance
var pirateImageIndex = Math.floor(Math.random() * 20) + 1;
var pirateAssetName = 'pirate' + pirateImageIndex;
var baseGraphics = self.attachAsset(pirateAssetName, {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x990000,
scaleX: 3,
scaleY: 3
});
self.health = 300;
self.maxHealth = 300;
self.attackPower = 10;
self.defensePower = 10;
self.spawnRadius = 350;
self.pirates = [];
self.lastPirateSpawn = 0;
self.maxPirates = 10;
self._destroyed = false;
// Health bar
self._healthBar = new HealthBar();
self._healthBar.set({
maxValue: function maxValue() {
return self.maxHealth;
},
getValueFn: function getValueFn() {
return self.health;
},
getMaxFn: function getMaxFn() {
return self.maxHealth;
},
width: 120,
height: 16,
color: 0xff0000
});
self._healthBar.y = -120;
self.addChild(self._healthBar);
self.takeDamage = function (amount) {
self.health -= amount;
LK.effects.flashObject(self, 0xff0000, 120);
if (self.health <= 0 && !self._destroyed) {
self.health = 0;
self._destroyed = true;
// --- BAŞARIM: Korsan üssü yok etme ---
if (typeof checkPirateBaseAchievements === "function") {
checkPirateBaseAchievements();
}
// Remove all pirates around base
for (var i = 0; i < self.pirates.length; i++) {
if (self.pirates[i] && !self.pirates[i]._destroyed) {
self.pirates[i].destroy();
}
}
// Explosion effect
createExplosion(self.x, self.y);
// Floating label
var label = new Text2("Pirate Base Destroyed!", {
size: 48,
fill: 0xffcc00,
align: "center"
});
label.anchor.set(0.5, 0.5);
label.x = self.x;
label.y = self.y - 180;
if (game && game.addChild) {
game.addChild(label);
}
self._destroyed = true;
self.destroy();
}
};
self.update = function () {
// Health bar update
if (self._healthBar && self._healthBar.update) {
self._healthBar.update();
}
// Remove dead pirates from list
for (var i = self.pirates.length - 1; i >= 0; i--) {
if (!self.pirates[i] || self.pirates[i]._destroyed) {
self.pirates.splice(i, 1);
}
}
// Spawn pirates if less than max
if (!self._destroyed && self.pirates.length < self.maxPirates && LK.ticks - self.lastPirateSpawn > 90) {
var pirate = new Pirate();
var angle = Math.random() * Math.PI * 2;
pirate.x = self.x + Math.cos(angle) * (self.spawnRadius + Math.random() * 60);
pirate.y = self.y + Math.sin(angle) * (self.spawnRadius + Math.random() * 60);
pirate.health = 30 + self.attackPower * 2;
pirate.damage = 5 + self.attackPower;
pirate.rpgName = "BaseKorsan-" + Math.floor(Math.random() * 10000);
pirate._baseRef = self;
// Add health bar to pirate
pirate._healthBar = new HealthBar();
pirate._healthBar.set({
maxValue: function maxValue() {
return pirate.health > 0 ? pirate.health : 0;
},
getValueFn: function getValueFn() {
return pirate.health > 0 ? pirate.health : 0;
},
getMaxFn: function getMaxFn() {
return 30 + self.attackPower * 2;
},
width: 60,
height: 10
});
pirate._healthBar.y = -60;
pirate.addChild(pirate._healthBar);
pirates.push(pirate);
self.pirates.push(pirate);
if (game && game.addChild) {
game.addChild(pirate);
}
self.lastPirateSpawn = LK.ticks;
}
// Base defense: shoot at player if close
var distToShip = Math.sqrt(Math.pow(ship.x - self.x, 2) + Math.pow(ship.y - self.y, 2));
if (!self._destroyed && distToShip < self.spawnRadius + 80 && LK.ticks % 30 === 0) {
var bullet = new EnemyBullet();
bullet.x = self.x;
bullet.y = self.y;
var angle = Math.atan2(ship.y - self.y, ship.x - self.x);
bullet.directionX = Math.cos(angle);
bullet.directionY = Math.sin(angle);
bullet.rotation = angle + Math.PI / 2;
bullet.damage = 10 + self.attackPower;
enemyBullets.push(bullet);
if (game && game.addChild) {
game.addChild(bullet);
}
}
};
return self;
});
var Resource = Container.expand(function () {
var self = Container.call(this);
var resourceGraphics = self.attachAsset('resource', {
anchorX: 0.5,
anchorY: 0.5
});
// 10 farklı hammadde tipi
var resourceTypes = ['metal', 'energy', 'crystal', 'gas', 'ice', 'uranium', 'silicon', 'carbon', 'plasma', 'antimatter'];
// --- Değerli hammaddelerin daha az çıkmasını sağlamak için ağırlıklı seçim ---
// Market kaldırıldı, sabit fiyatlar kullanılıyor
var prices = [100, 80, 120, 90, 70, 200, 110, 60, 150, 300];
// Her hammaddenin ağırlığı: 1 / (fiyat^0.7) ile ters orantılı (daha değerli = daha az)
// 0.7 üssüyle daha yumuşak bir dağılım, istenirse 1.0 yapılabilir
var weights = [];
var totalWeight = 0;
for (var i = 0; i < prices.length; i++) {
var w = 1 / Math.pow(prices[i], 0.7);
weights.push(w);
totalWeight += w;
}
// Rastgele ağırlıklı seçim
var r = Math.random() * totalWeight;
var acc = 0,
idx = 0;
for (var i = 0; i < weights.length; i++) {
acc += weights[i];
if (r <= acc) {
idx = i;
break;
}
}
self.type = resourceTypes[idx];
// Miktar: Değerli hammaddelerde miktar da az olsun
// maxAmount = 1 + 4 * (ucuzluk ağırlığı)
var maxAmount = Math.max(1, Math.round(1 + 4 * (weights[idx] / totalWeight * prices.length)));
self.amount = Math.floor(Math.random() * maxAmount) + 1;
// --- Timed fade-out system ---
self._spawnTick = LK.ticks;
self._fadeStartTick = self._spawnTick + 180; // 3 seconds visible
self._expireTick = self._fadeStartTick + 120; // 2 seconds fade (total 5s)
self.update = function () {
self.rotation += 0.03;
// Timed fade-out logic
if (typeof self._expireTick === "number" && typeof self._fadeStartTick === "number") {
var now = LK.ticks;
if (now >= self._fadeStartTick) {
// Start fading out
var fadeProgress = Math.min(1, (now - self._fadeStartTick) / (self._expireTick - self._fadeStartTick));
self.alpha = 1 - fadeProgress;
if (now >= self._expireTick) {
// Mark for removal (actual removal in game.update)
self._shouldRemove = true;
}
} else {
self.alpha = 1;
}
}
};
return self;
});
var Ship = Container.expand(function () {
var self = Container.call(this);
var shipGraphics = self.attachAsset('ship', {
anchorX: 0.5,
anchorY: 0.5
});
self.health = 100;
self.maxHealth = 100;
self.shield = 50;
self.maxShield = 50;
self.shieldRegenDelay = 0;
self.speed = 5;
self.fireRate = 10;
self.damage = 10;
self.lastFire = 0;
self.takeDamage = function (amount) {
// Shield absorbs damage first
if (self.shield > 0) {
var shieldDamage = Math.min(amount, self.shield);
self.shield -= shieldDamage;
amount -= shieldDamage;
LK.effects.flashObject(self, 0x0088ff, 100);
self.shieldRegenDelay = 180; // 3 seconds delay before shield regen
}
if (amount > 0) {
self.health -= amount;
LK.effects.flashObject(self, 0xff0000, 200);
}
if (self.health <= 0) {
// --- Clear all world entities on respawn ---
var _destroyAll = function _destroyAll(arr) {
if (!arr) {
return;
}
for (var i = arr.length - 1; i >= 0; i--) {
if (arr[i] && typeof arr[i].destroy === "function") {
arr[i].destroy();
}
arr.splice(i, 1);
}
}; // Remove all pirates, npcs, asteroids, coins, resources, enemyBullets, bullets, stars, explosions, upgradeStations, pirateBases
self.health = 0;
// --- Respawn logic instead of Game Over ---
// 1. Lose 50% coins and 50% of each resource
var lostCoins = Math.floor(playerCoins * 0.5);
playerCoins = Math.floor(playerCoins * 0.5);
var lostResources = [];
for (var i = 0; i < playerResources.length; i++) {
var lost = Math.floor(playerResources[i] * 0.5);
lostResources.push(lost);
playerResources[i] = Math.floor(playerResources[i] * 0.5);
}
// 2. Respawn ship at a random far location, restore health/shield
var respawnRadius = 1200 + Math.random() * 1800; // 1200-3000 px away from last position
var respawnAngle = Math.random() * Math.PI * 2;
self.x = self.x + Math.cos(respawnAngle) * respawnRadius;
self.y = self.y + Math.sin(respawnAngle) * respawnRadius;
self.health = self.maxHealth;
self.shield = self.maxShield;
self.shieldRegenDelay = 180;
_destroyAll(pirates);
_destroyAll(npcs);
_destroyAll(asteroids);
_destroyAll(coins);
_destroyAll(resources);
_destroyAll(enemyBullets);
_destroyAll(bullets);
_destroyAll(stars);
_destroyAll(explosions);
_destroyAll(upgradeStations);
_destroyAll(pirateBases);
// Also clear any katil korsan respawn timer
if (typeof window._katilPirateRespawnTimer !== "undefined" && window._katilPirateRespawnTimer) {
LK.clearTimeout(window._katilPirateRespawnTimer);
window._katilPirateRespawnTimer = null;
}
window._katilPirateSpawned = false;
// Katil korsan öldü ve dünya sıfırlandıktan sonra tekrar spawn et
if (typeof window._katilPirateRespawnTimer !== "undefined" && window._katilPirateRespawnTimer) {
LK.clearTimeout(window._katilPirateRespawnTimer);
window._katilPirateRespawnTimer = null;
}
window._katilPirateRespawnTimer = LK.setTimeout(function () {
if (!window._katilPirateSpawned) {
var pirate = new Pirate();
// Katil korsan statlarını tekrar ayarla
var minLevel = 1 + waveNumber * 2;
pirate.level = minLevel + 10;
pirate.health = (30 + (pirate.level - 1) * 10) * 10;
pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10;
pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10;
pirate.rpgName = "Katil";
pirate._isKatil = true;
pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3));
pirate._attackRange = Math.floor(600 * 0.9); // Katil korsan için %10 daha kısa menzil
window._katilPirateSpawned = true;
// Diğer korsanlar gibi kenar spawn algoritmasıyla doğsun
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y - 1400;
break;
case 1:
pirate.x = ship.x + 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
case 2:
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y + 1400;
break;
case 3:
pirate.x = ship.x - 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
}
// Add health bar to katil pirate
pirate._healthBar = new HealthBar();
pirate._healthBar.set({
maxValue: function maxValue() {
return pirate.health > 0 ? pirate.health : 0;
},
getValueFn: function getValueFn() {
return pirate.health > 0 ? pirate.health : 0;
},
getMaxFn: function getMaxFn() {
return 30 + waveNumber * 10;
},
width: 60,
height: 10
});
pirate._healthBar.y = -60;
pirate.addChild(pirate._healthBar);
pirates.push(pirate);
if (typeof game !== "undefined" && game.addChild) {
game.addChild(pirate);
}
}
window._katilPirateRespawnTimer = null;
}, 6000);
// Optionally reset wave progress
enemiesKilled = 0;
// Respawn initial world elements
for (var i = 0; i < 135; i++) {
spawnStar(true);
}
for (var i = 0; i < 6; i++) {
spawnNPC();
}
for (var i = 0; i < 6; i++) {
spawnPirate();
}
for (var i = 0; i < 2; i++) {
spawnAsteroid();
}
// Track respawn tick for input blocking
window._lastRespawnTick = LK.ticks;
// 3. Show floating label for penalty
var lostText = "-%50 coin & hammadde kaybı! Yeniden doğdun.";
var label = new Text2(lostText, {
size: 48,
fill: 0xff4444,
align: "center"
});
label.anchor.set(0.5, 0.5);
label.x = 1024;
label.y = 1366 - 200;
if (typeof game !== "undefined" && game.addChild) {
game.addChild(label);
}
LK.setTimeout(function () {
if (label.parent) {
label.parent.removeChild(label);
}
}, 1800);
// 4. Update UI and save progress
if (typeof updateUI === "function") {
updateUI();
}
if (typeof saveProgress === "function") {
saveProgress();
}
}
};
self.update = function () {
// Shield regeneration
if (self.shieldRegenDelay > 0) {
self.shieldRegenDelay--;
} else if (self.shield < self.maxShield) {
self.shield = Math.min(self.shield + 0.1, self.maxShield);
}
// --- SHIP ROTATION: Keep ship rotated to last movement or attack direction, but turn smoothly ---
// If joystick is being dragged, rotate to movement direction
if (typeof isDragging !== "undefined" && isDragging && typeof joystickHandle !== "undefined" && typeof joystickBase !== "undefined") {
var dx = joystickHandle.x - joystickBase.x;
var dy = joystickHandle.y - joystickBase.y;
var distance = Math.sqrt(dx * dx + dy * dy);
if (distance > 10) {
self._lastMoveAngle = Math.atan2(dy, dx);
self._targetRotation = self._lastMoveAngle + Math.PI / 2;
}
} else if (typeof game !== "undefined" && typeof game._lastShipAttackAngle !== "undefined") {
// If not moving, but last attack angle exists, keep ship rotated to last attack direction
self._targetRotation = game._lastShipAttackAngle + Math.PI / 2;
}
// Smoothly rotate toward target rotation
if (typeof self._targetRotation === "number") {
// Calculate shortest angle difference
var diff = self._targetRotation - self.rotation;
while (diff > Math.PI) {
diff -= Math.PI * 2;
}
while (diff < -Math.PI) {
diff += Math.PI * 2;
}
// Lerp factor: lower = softer (0.05 = very soft, 0.2 = sharp)
var lerp = 0.05;
self.rotation += diff * lerp;
}
};
return self;
});
var Star = Container.expand(function () {
var self = Container.call(this);
var starGraphics = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5
});
self.speed = Math.random() * 2 + 0.5;
starGraphics.alpha = Math.random() * 0.8 + 0.2;
self.vx = (Math.random() - 0.5) * 0.5;
self.vy = (Math.random() - 0.5) * 0.5;
self.update = function () {
// Slow floating movement
self.x += self.vx;
self.y += self.vy;
};
return self;
});
// Trade Station Class
var TradeStation = Container.expand(function () {
var self = Container.call(this);
// Randomly select one of 2 NPC images for trade station appearance
var npcImageIndex = Math.floor(Math.random() * 2) + 1;
var npcAssetName = 'npc' + npcImageIndex;
var tradeGraphics = self.attachAsset(npcAssetName, {
anchorX: 0.5,
anchorY: 0.5,
tint: 0x00ff99,
scaleX: 2,
scaleY: 2
});
self.type = 'trade';
self.rotationSpeed = 0.01;
self.offerType = Math.random() < 0.5 ? 'buy' : 'sell'; // buy: station buys from player, sell: station sells to player
self.resourceType = Math.random() < 0.5 ? 'metal' : 'energy';
self.amount = Math.floor(Math.random() * 10) + 5;
self.price = Math.floor(Math.random() * 30) + 10;
self.specialOffer = Math.random() < 0.2; // 20% chance for special upgrade offer
self.specialUpgrade = null;
if (self.specialOffer) {
var upgrades = [{
name: 'maxHealth',
label: 'Max Health +30',
cost: 200
}, {
name: 'maxShield',
label: 'Max Shield +20',
cost: 180
}, {
name: 'damage',
label: 'Damage +5',
cost: 150
}, {
name: 'speed',
label: 'Speed +1',
cost: 120
}];
self.specialUpgrade = upgrades[Math.floor(Math.random() * upgrades.length)];
}
self.update = function () {
self.rotation += self.rotationSpeed;
// Pulse effect
var scale = 2 + Math.sin(LK.ticks * 0.05) * 0.1;
tradeGraphics.scaleX = scale;
tradeGraphics.scaleY = scale;
};
return self;
});
// Wormhole Teleporter Class
var Wormhole = Container.expand(function () {
var self = Container.call(this);
var whGraphics = self.attachAsset('star', {
anchorX: 0.5,
anchorY: 0.5,
scaleX: 5,
scaleY: 5,
tint: 0x00ffff
});
self.radius = 120;
self.cooldown = 0;
self.update = function () {
whGraphics.rotation += 0.07;
// Teleport ship if close and not on cooldown
var dx = ship.x - self.x;
var dy = ship.y - self.y;
var dist = Math.sqrt(dx * dx + dy * dy);
if (dist < self.radius && self.cooldown <= 0) {
// Teleport ship to a random far location
var tx = ship.x + (Math.random() - 0.5) * 4000;
var ty = ship.y + (Math.random() - 0.5) * 4000;
var dx2 = tx - ship.x;
var dy2 = ty - ship.y;
ship.x = tx;
ship.y = ty;
// Move all objects to keep camera effect
for (var i = 0; i < stars.length; i++) {
stars[i].x -= dx2;
stars[i].y -= dy2;
}
for (var i = 0; i < npcs.length; i++) {
npcs[i].x -= dx2;
npcs[i].y -= dy2;
}
for (var i = 0; i < pirates.length; i++) {
pirates[i].x -= dx2;
pirates[i].y -= dy2;
}
for (var i = 0; i < coins.length; i++) {
coins[i].x -= dx2;
coins[i].y -= dy2;
}
for (var i = 0; i < resources.length; i++) {
resources[i].x -= dx2;
resources[i].y -= dy2;
}
for (var i = 0; i < bullets.length; i++) {
bullets[i].x -= dx2;
bullets[i].y -= dy2;
}
for (var i = 0; i < enemyBullets.length; i++) {
enemyBullets[i].x -= dx2;
enemyBullets[i].y -= dy2;
}
for (var i = 0; i < asteroids.length; i++) {
asteroids[i].x -= dx2;
asteroids[i].y -= dy2;
}
for (var i = 0; i < upgradeStations.length; i++) {
upgradeStations[i].x -= dx2;
upgradeStations[i].y -= dy2;
}
// boostParticles removed (no boost system)
for (var i = 0; i < explosions.length; i++) {
explosions[i].x -= dx2;
explosions[i].y -= dy2;
}
for (var i = 0; i < blackHoles.length; i++) {
blackHoles[i].x -= dx2;
blackHoles[i].y -= dy2;
}
for (var i = 0; i < wormholes.length; i++) {
if (wormholes[i] !== self) {
wormholes[i].x -= dx2;
wormholes[i].y -= dy2;
}
}
self.cooldown = 180;
LK.effects.flashScreen(0x00ffff, 500);
}
if (self.cooldown > 0) {
self.cooldown--;
}
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x000033
});
/****
* Game Code
****/
// Her friend, en yakın pirate'ı (veya asteroid) bulup saldıracak, ship gibi
// --- FRIENDS: Dost birlikler NPC gibi düşmana karşı savaşa girsin ---
if (typeof friends !== "undefined" && Array.isArray(friends)) {
for (var i = 0; i < friends.length; i++) {
var friend = friends[i];
if (!friend) {
continue;
}
// En yakın pirate'ı bul
var nearestTarget = null;
var nearestDistance = Infinity;
for (var j = 0; j < pirates.length; j++) {
var pirate = pirates[j];
if (!pirate) {
continue;
}
var dist = Math.sqrt(Math.pow(pirate.x - friend.x, 2) + Math.pow(pirate.y - friend.y, 2));
if (dist < nearestDistance) {
nearestDistance = dist;
nearestTarget = pirate;
}
}
// Asteroidleri de hedef olarak eklemek isterseniz, aşağıdaki kodu açabilirsiniz:
// for (var j = 0; j < asteroids.length; j++) {
// var asteroid = asteroids[j];
// var dist = Math.sqrt(Math.pow(asteroid.x - friend.x, 2) + Math.pow(asteroid.y - friend.y, 2));
// if (dist < nearestDistance) {
// nearestDistance = dist;
// nearestTarget = asteroid;
// }
// }
// Saldırı menzili ship ile aynı olsun (ATTACK_RANGE)
var friendRange = typeof ATTACK_RANGE !== "undefined" ? ATTACK_RANGE : 220;
if (nearestTarget && nearestDistance < friendRange) {
// Eğer friend zaten bu hedefe saldırmıyorsa, saldırıyı başlat
if (!friend._attacking || friend._attackTarget !== nearestTarget) {
if (typeof friend.startAttack === "function") {
friend.startAttack(nearestTarget);
}
}
} else {
// Hedef yoksa saldırıyı bırak
if (typeof friend.stopAttack === "function") {
friend.stopAttack();
}
}
}
}
var ship;
var joystickBase;
var joystickHandle;
var isDragging = false;
var bullets = [];
var enemyBullets = [];
var npcs = [];
var pirates = [];
var coins = [];
var resources = [];
var stars = [];
var asteroids = [];
var upgradeStations = [];
var explosions = [];
var blackHoles = [];
var wormholes = [];
var tradeStations = [];
var pirateBases = [];
var tradeText = null; // No trade text needed
// Helper: spawn a pirate base at a random edge, with pirates around it
function spawnPirateBase() {
var base = new PirateBase();
// Spawn from edges of visible area
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
// Top
base.x = ship.x + (Math.random() - 0.5) * 2000;
base.y = ship.y - 1600;
break;
case 1:
// Right
base.x = ship.x + 1600;
base.y = ship.y + (Math.random() - 0.5) * 2000;
break;
case 2:
// Bottom
base.x = ship.x + (Math.random() - 0.5) * 2000;
base.y = ship.y + 1600;
break;
case 3:
// Left
base.x = ship.x - 1600;
base.y = ship.y + (Math.random() - 0.5) * 2000;
break;
}
;
pirateBases.push(base);
game.addChild(base);
// Spawn initial pirates around base
for (var i = 0; i < 10; i++) {
var pirate = new Pirate();
var angle = Math.PI * 2 * (i / 10);
pirate.x = base.x + Math.cos(angle) * (base.spawnRadius + Math.random() * 60);
pirate.y = base.y + Math.sin(angle) * (base.spawnRadius + Math.random() * 60);
// Level range: wave 1 => 1-3, wave 2 => 3-5, wave 3 => 5-7, etc.
var minLevel = 1 + waveNumber * 2;
var maxLevel = minLevel + 2;
pirate.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1));
pirate.health = 30 + (pirate.level - 1) * 10 + base.attackPower * 2;
pirate.damage = 5 + Math.floor((pirate.level - 1) * 1.5) + base.attackPower;
pirate.defense = 2 + Math.floor((pirate.level - 1) * 1.2) + base.defensePower;
pirate.rpgName = "BaseKorsan-" + Math.floor(Math.random() * 10000);
pirate._baseRef = base;
// Add health bar to pirate
pirate._healthBar = new HealthBar();
pirate._healthBar.set({
maxValue: function maxValue() {
return pirate.health > 0 ? pirate.health : 0;
},
getValueFn: function getValueFn() {
return pirate.health > 0 ? pirate.health : 0;
},
getMaxFn: function getMaxFn() {
return 30 + base.attackPower * 2;
},
width: 60,
height: 10
});
pirate._healthBar.y = -60;
pirate.addChild(pirate._healthBar);
pirates.push(pirate);
base.pirates.push(pirate);
game.addChild(pirate);
}
}
// tradeText UI removed (no tradeText in this version)
var camera = {
x: 0,
y: 0
};
// --- Helper for multi-pirate attack quest ---
// If groupCount >= 2, spawn GrupKorsan squad with reward label under each
function spawnPirateSquad(centerX, centerY, count, targetNPC) {
var squad = [];
if (count >= 2) {
// GrupKorsan: all move together, same target/angle
var minLevel = 1 + waveNumber * 2 + 5;
var maxLevel = minLevel + 2;
var groupAngle = Math.random() * Math.PI * 2;
var groupTarget = targetNPC || {
x: centerX + Math.cos(groupAngle) * 400,
y: centerY + Math.sin(groupAngle) * 400
};
for (var i = 0; i < count; i++) {
var gpirate = new GrupKorsan();
gpirate.groupIndex = i;
gpirate.groupSize = count;
gpirate.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1));
// Katil Korsan'ın statları: level = minLevel+10, health = (30 + (level-1)*10)*10, damage = (5 + ...)*10, defense = (2 + ...)*10
// GrupKorsan, Katil Korsan'ın statlarının %20 daha düşüğü olacak şekilde ayarlanır
var katilHealth = (30 + (gpirate.level - 1) * 10) * 10;
var katilDamage = (5 + Math.floor((gpirate.level - 1) * 1.5)) * 10;
var katilDefense = (2 + Math.floor((gpirate.level - 1) * 1.2)) * 10;
gpirate.health = Math.round(katilHealth * 0.8);
gpirate.damage = Math.round(katilDamage * 0.8);
gpirate.defense = Math.round(katilDefense * 0.8);
gpirate.loot = 200 + (gpirate.level - 1) * 50;
// --- GrupKorsan arası mesafe %15 oranında açıldı (200px yerine 200*1.15=230px) ---
// Her bir GrupKorsan'ın pozisyonu, merkezden eşit aralıklı ve %15 daha uzak olacak şekilde ayarlanır
var grupDistance = 200 * 1.15;
var angleOffset = Math.PI * 2 * i / count;
gpirate.x = centerX + Math.cos(groupAngle + angleOffset) * grupDistance + (Math.random() - 0.5) * 60;
gpirate.y = centerY + Math.sin(groupAngle + angleOffset) * grupDistance + (Math.random() - 0.5) * 60;
gpirate._groupTarget = groupTarget;
gpirate._groupAngle = groupAngle;
gpirate._groupMoveSpeed = 2 + Math.random() * 0.5;
gpirate.rpgName = "GrupKorsan-" + (i + 1) + "/" + count;
gpirate._isGrupKorsan = true;
pirates.push(gpirate);
game.addChild(gpirate);
squad.push(gpirate);
}
} else {
// Fallback: single pirate
for (var i = 0; i < count; i++) {
var pirate = new Pirate();
var angle = Math.PI * 2 * (i / count);
pirate.x = centerX + Math.cos(angle) * 200 + (Math.random() - 0.5) * 60;
pirate.y = centerY + Math.sin(angle) * 200 + (Math.random() - 0.5) * 60;
// Level range: wave 1 => 1-3, wave 2 => 3-5, wave 3 => 5-7, etc.
var minLevel = 1 + waveNumber * 2;
var maxLevel = minLevel + 2;
pirate.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1));
pirate.health = 30 + (pirate.level - 1) * 10;
pirate.damage = 5 + Math.floor((pirate.level - 1) * 1.5);
pirate.defense = 2 + Math.floor((pirate.level - 1) * 1.2);
pirate.rpgName = "Korsan-" + Math.floor(Math.random() * 10000);
pirate._squadTargetNPC = targetNPC;
pirates.push(pirate);
game.addChild(pirate);
squad.push(pirate);
}
}
return squad;
}
// Trade station tap detection (RPG/ticaret)
for (var i = 0; i < tradeStations.length; i++) {
var ts = tradeStations[i];
var dist = Math.sqrt(Math.pow(ts.x - ship.x, 2) + Math.pow(ts.y - ship.y, 2));
if (dist < 180) {
game._lastTradeStationTap = LK.ticks;
break;
}
}
var waveNumber = 0;
var enemiesKilled = 0;
var totalDistance = 0;
// Player stats
var playerCoins = storage.coins || 0;
// 10 hammadde için oyuncu envanteri
var playerResources = [storage.metal || 0,
// metal
storage.energy || 0,
// energy
storage.crystal || 0,
// crystal
storage.gas || 0,
// gas
storage.ice || 0,
// ice
storage.uranium || 0,
// uranium
storage.silicon || 0,
// silicon
storage.carbon || 0,
// carbon
storage.plasma || 0,
// plasma
storage.antimatter || 0 // antimatter
];
// Aliases for compatibility with old code
var playerMetal = playerResources[0];
var playerEnergy = playerResources[1];
var questsCompleted = storage.questsCompleted || 0;
// --- Player Level/EXP ---
var playerLevel = typeof storage.playerLevel !== "undefined" ? storage.playerLevel : 1;
var playerEXP = typeof storage.playerEXP !== "undefined" ? storage.playerEXP : 0;
function expToNext(level) {
// Simple EXP curve: next = 10 + 5*(level-1)
return 10 + 5 * (level - 1);
}
// UI Elements
// Calculate the unified font size (30% smaller than expText, then %10 büyüt)
var unifiedFontSize = Math.round(38 * 0.7 * 1.1);
// Add coin icon
var coinIcon = LK.getAsset('coin', {
anchorX: 0.5,
anchorY: 0.5,
x: 100,
y: 35,
scaleX: 1.2,
scaleY: 1.2
});
LK.gui.topLeft.addChild(coinIcon);
var coinText = new Text2('Coins: ' + playerCoins, {
size: unifiedFontSize,
fill: 0xFFFF00
});
coinText.anchor.set(0, 0);
coinText.x = 120;
coinText.y = 20;
LK.gui.topLeft.addChild(coinText);
// --- NPC ile savaş butonu ---
var npcFightBtnFont = Math.round(unifiedFontSize * 1.1);
window.npcFightBtn = new Text2("Fight NPCs", {
size: npcFightBtnFont,
fill: 0xff4444,
align: "center"
});
window.npcFightBtn.anchor.set(0.5, 0.5);
// Place at bottom center, above the bottom edge
window.npcFightBtn.x = 2048 / 2;
window.npcFightBtn.y = 2732 - 300; // moved 2 lines (about 120px) higher
LK.gui.bottom.addChild(window.npcFightBtn);
window.npcFightMode = false;
// --- UZAY İSTASYONU TESLİMAT BUTTON (above Reset, 2 rows up) ---
if (!window.stationDeliveryBtn) {
var deliveryBtnFont = Math.round(unifiedFontSize * 1.1);
// window.stationDeliveryBtn = new Text2("", { ... }); // BUTTON IS HIDDEN, DO NOT CREATE
// window.stationDeliveryBtn.anchor.set(1, 1);
// window.stationDeliveryBtn.x = -40;
// window.stationDeliveryBtn.y = -40 - 2 * 90;
// Add image above the button, 10% higher (yukarıya %10)
if (!window.stationDeliveryBtnImage) {
// Use the special image for the transparent button area
var imgAsset = LK.getAsset('stationDeliveryBtnAreaImg', {
anchorX: 1,
anchorY: 1,
scaleX: 0.7,
scaleY: 0.7
});
// Place image at the same x, but y is 10% higher (10% of image height above the button)
// Keep the station image fixed (do not move it up)
imgAsset.x = -40;
imgAsset.y = -40 - 2 * 90 - imgAsset.height * 1.1;
window.stationDeliveryBtnImage = imgAsset;
LK.gui.bottomRight.addChild(window.stationDeliveryBtnImage);
// Add a transparent button area exactly the size and position of the image for İstasyon Teslimat
var stationDeliveryBtnArea = new Text2("", {
size: 1,
fill: 0xffffff,
align: "center"
});
stationDeliveryBtnArea.anchor.set(1, 1);
// Place the button area exactly behind the image, matching its position and size
stationDeliveryBtnArea.x = imgAsset.x;
// Move the button area 1 row (imgAsset.height) above the image
stationDeliveryBtnArea.y = imgAsset.y - imgAsset.height;
stationDeliveryBtnArea.width = imgAsset.width;
stationDeliveryBtnArea.height = imgAsset.height;
stationDeliveryBtnArea.alpha = 0; // fully transparent, but still receives taps
window.stationDeliveryBtnArea = stationDeliveryBtnArea;
// Add the button area BEFORE the image so it is behind
LK.gui.bottomRight.addChild(stationDeliveryBtnArea);
LK.gui.bottomRight.addChild(window.stationDeliveryBtnImage);
}
// DO NOT ADD THE BUTTON TO THE GUI (HIDDEN)
// Add 'İstasyona Hammadde Gönder' text 2 rows below the button
if (!window.stationSendResourceText) {
var sendTextFont = Math.max(8, Math.round(unifiedFontSize * 1.0) - 2);
window.stationSendResourceText = new Text2("", {
size: sendTextFont,
fill: 0xffffff,
align: "center"
});
window.stationSendResourceText.anchor.set(1, 1);
// 2 rows below the stationDeliveryBtn (row = 90px)
// Use the same x/y as the image, but offset as if the button was there
window.stationSendResourceText.x = -40;
window.stationSendResourceText.y = -40 - 2 * 90 + 1 * 90;
LK.gui.bottomRight.addChild(window.stationSendResourceText);
}
}
// --- RESET BUTTON (bottom right) ---
if (!game._resetBtn) {
game._resetBtn = new Text2("Reset", {
size: Math.round(unifiedFontSize * 1.1),
fill: 0xff4444,
align: "center"
});
game._resetBtn.anchor.set(1, 1);
// Place at bottom right, with margin from edge
game._resetBtn.x = -40;
game._resetBtn.y = -40;
LK.gui.bottomRight.addChild(game._resetBtn);
}
// --- UZAY İSTASYONU TESLİMAT PANEL SYSTEM ---
// State for delivery system
if (!window.stationDeliveryState) {
// 10 hammadde için rastgele 50-200 arası istek, bonus %2-5 arası
window.stationDeliveryState = {
requests: [],
delivered: [],
completed: false,
bonus: 0
};
for (var i = 0; i < 10; i++) {
var req = 50 + Math.floor(Math.random() * 151); // 50-200
window.stationDeliveryState.requests.push(req);
window.stationDeliveryState.delivered.push(0);
}
window.stationDeliveryState.bonus = 2 + Math.floor(Math.random() * 4); // 2-5
window.stationDeliveryState.completed = false;
window.stationDeliveryState.rewardClaimed = false;
}
// Helper to open/close panel
window.openStationDeliveryPanel = function () {
if (window.stationDeliveryPanel) {
return;
}
// Increase font size by 2 for all panel texts/buttons
var panelFontSize = unifiedFontSize + 4;
var panelBtnFontSize = panelFontSize;
var labelFontSize = panelFontSize;
var closeBtnFontSize = panelFontSize;
var claimBtnFontSize = panelFontSize;
var bonusFontSize = panelFontSize;
var infoFontSize = panelFontSize;
var titleFontSize = panelFontSize;
// Calculate new panel size to fit enlarged text/buttons
var rowH = 80 + 8; // add 8px for extra font size
var panelW = 900 + 120; // add width for larger text/buttons
var panelH = 1100 + 2 * rowH; // add height for larger text/buttons
var px = 2048 / 2,
py = 2732 / 2;
// Panel BG
window.stationDeliveryPanelBG = LK.getAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
x: px,
y: py,
scaleX: panelW / 40,
scaleY: panelH / 40,
tint: 0x223355,
alpha: 0.97
});
// Title
window.stationDeliveryPanelTitle = new Text2("Space Station Delivery", {
size: titleFontSize,
fill: 0x00ffcc,
align: "center"
});
window.stationDeliveryPanelTitle.anchor.set(0.5, 0);
window.stationDeliveryPanelTitle.x = px;
window.stationDeliveryPanelTitle.y = py - panelH / 2 + 40;
// Info text
window.stationDeliveryPanelInfoText = new Text2("Complete the station's special resource requests!\nDeliver all to sell at +" + window.stationDeliveryState.bonus + "% bonus price.", {
size: infoFontSize,
fill: 0xffffff,
align: "center"
});
window.stationDeliveryPanelInfoText.anchor.set(0.5, 0);
window.stationDeliveryPanelInfoText.x = px;
window.stationDeliveryPanelInfoText.y = window.stationDeliveryPanelTitle.y + 60;
// Close button
window.stationDeliveryPanelCloseBtn = new Text2("Close", {
size: Math.round((unifiedFontSize + 4) * 1.3),
// 2 font larger than Coins, then 30% larger
fill: 0xff4444,
align: "center"
});
window.stationDeliveryPanelCloseBtn.anchor.set(0.5, 0.5);
// Move close button above the panel title, with extra margin
window.stationDeliveryPanelCloseBtn.x = px;
window.stationDeliveryPanelCloseBtn.y = window.stationDeliveryPanelTitle.y - 110; // further above title
// Resource request labels and delivery buttons
window.stationDeliveryLabels = [];
window.stationDeliveryDeliverBtns = [];
window.stationDeliveryStockLabels = [];
// Move all resource rows down by 3% of the panel height
var startY = window.stationDeliveryPanelInfoText.y + 80 + Math.round(panelH * 0.03);
for (var i = 0; i < window.resourceLabelOrder.length; i++) {
var resObj = window.resourceLabelOrder[i];
var req = window.stationDeliveryState.requests[resObj.origIdx];
var delivered = window.stationDeliveryState.delivered[resObj.origIdx];
var label = new Text2(resObj.name + ": " + delivered + " / " + req, {
size: labelFontSize,
fill: 0xffffff,
align: "left"
});
label.anchor.set(0, 0.5);
label.x = px - panelW / 2 + 60;
label.y = startY + i * rowH;
window.stationDeliveryLabels.push(label);
// Oyuncu stok durumu etiketi
var stockAmount = typeof playerResources[resObj.origIdx] !== "undefined" ? playerResources[resObj.origIdx] : 0;
var stockLabel = new Text2("Stock: " + stockAmount, {
size: Math.max(18, Math.round(labelFontSize * 0.85)),
fill: 0xcccccc,
align: "left"
});
stockLabel.anchor.set(0, 0.5);
stockLabel.x = label.x + 340;
stockLabel.y = label.y;
window.stationDeliveryStockLabels.push(stockLabel);
// Deliver button
var teslimBtn = new Text2("Deliver", {
size: panelBtnFontSize,
fill: 0x00ff99,
align: "center"
});
teslimBtn.anchor.set(0.5, 0.5);
teslimBtn.x = px + 220;
teslimBtn.y = label.y;
teslimBtn._stationResIdx = resObj.origIdx;
window.stationDeliveryDeliverBtns.push(teslimBtn);
}
// Bonus info
window.stationDeliveryPanelBonusText = new Text2("Complete all requests to sell at +" + window.stationDeliveryState.bonus + "% bonus price.", {
size: bonusFontSize,
fill: 0xffff00,
align: "center"
});
window.stationDeliveryPanelBonusText.anchor.set(0.5, 0);
window.stationDeliveryPanelBonusText.x = px;
window.stationDeliveryPanelBonusText.y = startY + rowH * 10 + 30;
// Complete delivery (claim reward) button
window.stationDeliveryPanelClaimBtn = new Text2("Deliver All & Claim Reward", {
size: claimBtnFontSize,
fill: 0xffcc00,
align: "center"
});
window.stationDeliveryPanelClaimBtn.anchor.set(0.5, 0.5);
window.stationDeliveryPanelClaimBtn.x = px;
window.stationDeliveryPanelClaimBtn.y = window.stationDeliveryPanelBonusText.y + 90;
// Add to game
game.addChild(window.stationDeliveryPanelBG);
game.addChild(window.stationDeliveryPanelTitle);
game.addChild(window.stationDeliveryPanelInfoText);
game.addChild(window.stationDeliveryPanelCloseBtn);
for (var i = 0; i < window.stationDeliveryLabels.length; i++) {
game.addChild(window.stationDeliveryLabels[i]);
}
for (var i = 0; i < window.stationDeliveryStockLabels.length; i++) {
game.addChild(window.stationDeliveryStockLabels[i]);
}
for (var i = 0; i < window.stationDeliveryDeliverBtns.length; i++) {
game.addChild(window.stationDeliveryDeliverBtns[i]);
}
game.addChild(window.stationDeliveryPanelBonusText);
game.addChild(window.stationDeliveryPanelClaimBtn);
window.stationDeliveryPanel = true;
};
window.closeStationDeliveryPanel = function () {
if (!window.stationDeliveryPanel) {
return;
}
if (window.stationDeliveryPanelBG && window.stationDeliveryPanelBG.parent) {
window.stationDeliveryPanelBG.parent.removeChild(window.stationDeliveryPanelBG);
}
if (window.stationDeliveryPanelTitle && window.stationDeliveryPanelTitle.parent) {
window.stationDeliveryPanelTitle.parent.removeChild(window.stationDeliveryPanelTitle);
}
if (window.stationDeliveryPanelInfoText && window.stationDeliveryPanelInfoText.parent) {
window.stationDeliveryPanelInfoText.parent.removeChild(window.stationDeliveryPanelInfoText);
}
if (window.stationDeliveryPanelCloseBtn && window.stationDeliveryPanelCloseBtn.parent) {
window.stationDeliveryPanelCloseBtn.parent.removeChild(window.stationDeliveryPanelCloseBtn);
}
if (window.stationDeliveryLabels) {
for (var i = 0; i < window.stationDeliveryLabels.length; i++) {
if (window.stationDeliveryLabels[i] && window.stationDeliveryLabels[i].parent) {
window.stationDeliveryLabels[i].parent.removeChild(window.stationDeliveryLabels[i]);
}
}
}
if (window.stationDeliveryDeliverBtns) {
for (var i = 0; i < window.stationDeliveryDeliverBtns.length; i++) {
if (window.stationDeliveryDeliverBtns[i] && window.stationDeliveryDeliverBtns[i].parent) {
window.stationDeliveryDeliverBtns[i].parent.removeChild(window.stationDeliveryDeliverBtns[i]);
}
}
}
if (window.stationDeliveryPanelBonusText && window.stationDeliveryPanelBonusText.parent) {
window.stationDeliveryPanelBonusText.parent.removeChild(window.stationDeliveryPanelBonusText);
}
if (window.stationDeliveryPanelClaimBtn && window.stationDeliveryPanelClaimBtn.parent) {
window.stationDeliveryPanelClaimBtn.parent.removeChild(window.stationDeliveryPanelClaimBtn);
}
window.stationDeliveryPanel = null;
window.stationDeliveryPanelBG = null;
window.stationDeliveryPanelTitle = null;
window.stationDeliveryPanelInfoText = null;
window.stationDeliveryPanelCloseBtn = null;
window.stationDeliveryLabels = null;
window.stationDeliveryDeliverBtns = null;
window.stationDeliveryPanelBonusText = null;
window.stationDeliveryPanelClaimBtn = null;
// Hide station stock labels when panel is closed
if (window.stationDeliveryStockLabels) {
for (var i = 0; i < window.stationDeliveryStockLabels.length; i++) {
if (window.stationDeliveryStockLabels[i] && window.stationDeliveryStockLabels[i].parent) {
window.stationDeliveryStockLabels[i].parent.removeChild(window.stationDeliveryStockLabels[i]);
}
}
window.stationDeliveryStockLabels = null;
}
};
// --- GAME TIME LABEL ---
// Track game start time in ticks
var gameStartTick = LK.ticks;
var gameTimeText = new Text2('Game Time: 0:00', {
size: unifiedFontSize,
fill: 0xcccccc
});
gameTimeText.anchor.set(0, 0);
gameTimeText.x = coinText.x + coinText.width + 40;
gameTimeText.y = coinText.y;
LK.gui.topLeft.addChild(gameTimeText);
// Add an image below the Game Time label
if (!window.gameTimeImage) {
// Use an existing image asset, e.g. 'teslimEtBtnBg' as a placeholder
// Move the Global Panel image and button to the row above the station image
var imgScale = 0.7;
var imgAsset = LK.getAsset('teslimEtBtnBg', {
anchorX: 1,
anchorY: 1,
scaleX: imgScale,
scaleY: imgScale
});
// Place the image at the same x as the stationDeliveryBtnImage, but 1 row above it
// If stationDeliveryBtnImage exists, align to it
var refImg = window.stationDeliveryBtnImage;
if (refImg) {
imgAsset.x = refImg.x;
imgAsset.y = refImg.y - refImg.height; // 1 row above station image
} else {
// fallback: 10% right from left edge, and 10px below the Game Time label
imgAsset.x = Math.round(2048 * 0.10);
imgAsset.y = gameTimeText.y + gameTimeText.height + 10;
}
window.gameTimeImage = imgAsset;
LK.gui.bottomRight.addChild(window.gameTimeImage);
// --- Add a new independent image file at the position 2 rows above Hammadde Takası (Resource Trade) image ---
// Remove the previously created image if it exists
if (window.resourceTradePanelImage && window.resourceTradePanelImage.parent) {
window.resourceTradePanelImage.parent.removeChild(window.resourceTradePanelImage);
window.resourceTradePanelImage = null;
}
// Create a new independent image at the same position (now 1 row above Hammadde Takası image)
if (!window.resourceTradePanelIndependentImage) {
var independentImgScale = 0.7;
var independentImgAsset = LK.getAsset('resourceTradePanelIndependentCus', {
anchorX: 1,
anchorY: 1,
scaleX: independentImgScale,
scaleY: independentImgScale
});
// Place 1 row above the Hammadde Takası (Resource Trade) image
independentImgAsset.x = imgAsset.x;
independentImgAsset.y = imgAsset.y - imgAsset.height * 1;
window.resourceTradePanelIndependentImage = independentImgAsset;
LK.gui.bottomRight.addChild(independentImgAsset);
// Move the Hammadde Stoğu panel button (tap area) 2 rows above the image, image stays fixed
if (!window.resourceTradePanelIndependentBtn) {
var btn = new Text2("", {
size: 1,
fill: 0xffffff,
align: "center"
});
btn.anchor.set(1, 1);
btn.x = independentImgAsset.x;
btn.y = independentImgAsset.y - independentImgAsset.height * 2; // 2 rows above the image
btn.width = independentImgAsset.width;
btn.height = independentImgAsset.height;
btn.alpha = 0; // fully transparent, but still receives taps
window.resourceTradePanelIndependentBtn = btn;
LK.gui.bottomRight.addChild(btn);
}
}
// Add a transparent button area exactly the size and position of the image for Global Market
var marketBtn = new Text2("Global Market", {
size: 1,
fill: 0xffffff,
align: "center"
});
marketBtn.anchor.set(1, 1);
marketBtn.x = imgAsset.x;
// Move the button area 2 rows (imgAsset.height * 2) above the image, image stays fixed
marketBtn.y = imgAsset.y - imgAsset.height * 2;
// Grow the button area 10% downward and shrink it 5% from the top
marketBtn.width = imgAsset.width;
marketBtn.height = imgAsset.height * 1.10 * 0.95; // first grow 10%, then shrink 5% from top
marketBtn.alpha = 0; // fully transparent, but still receives taps
window.globalMarketBtn = marketBtn;
LK.gui.bottomRight.addChild(marketBtn);
}
// --- RESOURCE TRADE PANEL STATE ---
if (!window.resourceTradePanelState) {
// 10 hammadde için değerler (1-10 arası, rastgele başlat)
window.resourceTradeValues = [];
for (var i = 0; i < 10; i++) {
window.resourceTradeValues.push(1 + Math.floor(Math.random() * 10));
}
window.resourceTradePanelState = {
open: false,
lastValueUpdateTick: LK.ticks
};
}
// Timer: her 5 saniyede bir (300 tick) değerleri değiştir
if (!window._resourceTradeValueTimer) {
window._resourceTradeValueTimer = LK.setInterval(function () {
if (!window.resourceTradeValues) {
return;
}
for (var i = 0; i < window.resourceTradeValues.length; i++) {
// Değeri -3/+3 arası değiştir, 1-10 aralığında tut
var delta = Math.floor(Math.random() * 7) - 3;
window.resourceTradeValues[i] = Math.max(1, Math.min(10, window.resourceTradeValues[i] + delta));
}
// Panel açıksa UI güncelle
if (window.resourceTradePanelState && window.resourceTradePanelState.open && typeof window.updateResourceTradePanel === "function") {
window.updateResourceTradePanel();
}
}, 5000);
}
// Resource UI: create 10 vertically stacked resource labels, left-aligned
// Sort resourceTypes by name length descending, keep original index for mapping to playerResources
var resourceTypes = ['Metal', 'Energy', 'Crystal', 'Gas', 'Ice', 'Uranium', 'Silicon', 'Carbon', 'Plasma', 'Antimatter'];
var resourceTypeObjs = [];
for (var i = 0; i < resourceTypes.length; i++) {
resourceTypeObjs.push({
name: resourceTypes[i],
origIdx: i
});
}
resourceTypeObjs.sort(function (a, b) {
// Sort by name length descending, then alphabetically for ties
if (b.name.length !== a.name.length) {
return b.name.length - a.name.length;
}
return a.name.localeCompare(b.name);
});
window.resourceLabels = [];
window.resourceLabelOrder = resourceTypeObjs; // Save for updateUI
var resourceIconMargin = 8;
var resourceLabelStartY = Math.round(2732 * 0.05); // 5% down from the top
for (var i = 0; i < resourceTypeObjs.length; i++) {
var label = new Text2(resourceTypeObjs[i].name + ': 0', {
size: unifiedFontSize,
fill: 0xFFFFFF
});
label.anchor.set(0, 0);
label.x = 20; // flush to left edge, but leave margin for menu icon
label.y = resourceLabelStartY + i * (unifiedFontSize + resourceIconMargin);
// Do NOT add to GUI, so resource stock text is hidden on main screen
// LK.gui.topLeft.addChild(label);
window.resourceLabels.push(label);
}
// --- RESOURCE TRADE PANEL LOGIC ---
window.openResourceTradePanel = function () {
if (window.resourceTradePanelState.open) {
return;
}
window.resourceTradePanelState.open = true;
// Increase font size by 2 for all panel texts/buttons
var panelFontSize = unifiedFontSize + 4;
var panelBtnFontSize = panelFontSize;
var labelFontSize = panelFontSize;
var closeBtnFontSize = panelFontSize;
var infoFontSize = panelFontSize;
var titleFontSize = panelFontSize;
// Panel boyutları ve konum (resize for larger text/buttons)
var rowH = 80 + 8;
var panelW = 1200 + 120;
var panelH = 1200 + 2 * rowH;
var px = 2048 / 2,
py = 2732 / 2;
// Panel BG
window.resourceTradePanelBG = LK.getAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
x: px,
y: py,
scaleX: panelW / 40,
scaleY: panelH / 40,
tint: 0x223355,
alpha: 0.97
});
// Başlık
window.resourceTradePanelTitle = new Text2("Resource Trading", {
size: titleFontSize,
fill: 0x00ffcc,
align: "center"
});
window.resourceTradePanelTitle.anchor.set(0.5, 0);
window.resourceTradePanelTitle.x = px;
window.resourceTradePanelTitle.y = py - panelH / 2 + 40;
// Info text
window.resourceTradePanelInfoText = new Text2("You can trade your resources.\nEach resource value changes between 5-30 and updates every 5 seconds.", {
size: infoFontSize,
fill: 0xffffff,
align: "center"
});
window.resourceTradePanelInfoText.anchor.set(0.5, 0);
window.resourceTradePanelInfoText.x = px;
window.resourceTradePanelInfoText.y = window.resourceTradePanelTitle.y + 60;
// Kapat butonu
window.resourceTradePanelCloseBtn = new Text2("Close", {
size: Math.round((unifiedFontSize + 4) * 1.3),
// 2 font larger than Coins, then 30% larger
fill: 0xff4444,
align: "center"
});
window.resourceTradePanelCloseBtn.anchor.set(0.5, 0.5);
window.resourceTradePanelCloseBtn.x = px;
window.resourceTradePanelCloseBtn.y = window.resourceTradePanelTitle.y - 110; // further above title
// Hammadde satırları ve butonları
window.resourceTradeLabels = [];
window.resourceTradeSellBtns = [];
window.resourceTradeSellAllBtns = [];
window.resourceTradeBuyBtns = [];
var startY = window.resourceTradePanelInfoText.y + 80 + rowH * 2; // move all rows down by 2 rows (1 row further)
for (var i = 0; i < window.resourceLabelOrder.length; i++) {
var resObj = window.resourceLabelOrder[i];
var idx = resObj.origIdx;
var label = new Text2(resObj.name + ": " + (playerResources[idx] || 0) + " | Value: " + (window.resourceTradeValues ? window.resourceTradeValues[idx] : 0), {
size: labelFontSize,
fill: 0xffffff,
align: "left"
});
label.anchor.set(0, 0.5);
label.x = px - panelW / 2 + 60;
label.y = startY + i * rowH;
window.resourceTradeLabels.push(label);
// Sell button
var sellBtn = new Text2("Sell", {
size: panelBtnFontSize,
fill: 0x00ff99,
align: "center"
});
sellBtn.anchor.set(0.5, 0.5);
sellBtn.x = px + 220;
sellBtn.y = label.y;
sellBtn._resourceIdx = idx;
window.resourceTradeSellBtns.push(sellBtn);
// Sell All button
var sellAllBtn = new Text2("Sell All", {
size: panelBtnFontSize,
fill: 0x00cc99,
align: "center"
});
sellAllBtn.anchor.set(0.5, 0.5);
sellAllBtn.x = px + 400;
sellAllBtn.y = label.y;
sellAllBtn._resourceIdx = idx;
window.resourceTradeSellAllBtns.push(sellAllBtn);
// Buy button
var buyBtn = new Text2("Buy", {
size: panelBtnFontSize,
fill: 0x0099ff,
align: "center"
});
buyBtn.anchor.set(0.5, 0.5);
buyBtn.x = px + 580;
buyBtn.y = label.y;
buyBtn._resourceIdx = idx;
window.resourceTradeBuyBtns.push(buyBtn);
}
// Add to game
game.addChild(window.resourceTradePanelBG);
game.addChild(window.resourceTradePanelTitle);
game.addChild(window.resourceTradePanelInfoText);
game.addChild(window.resourceTradePanelCloseBtn);
for (var i = 0; i < window.resourceTradeLabels.length; i++) {
game.addChild(window.resourceTradeLabels[i]);
}
for (var i = 0; i < window.resourceTradeSellBtns.length; i++) {
game.addChild(window.resourceTradeSellBtns[i]);
}
for (var i = 0; i < window.resourceTradeSellAllBtns.length; i++) {
game.addChild(window.resourceTradeSellAllBtns[i]);
}
for (var i = 0; i < window.resourceTradeBuyBtns.length; i++) {
game.addChild(window.resourceTradeBuyBtns[i]);
}
// Panel güncelleme fonksiyonu
window.updateResourceTradePanel = function () {
for (var i = 0; i < window.resourceLabelOrder.length; i++) {
var resObj = window.resourceLabelOrder[i];
var idx = resObj.origIdx;
if (window.resourceTradeLabels[i]) {
window.resourceTradeLabels[i].setText(resObj.name + ": " + (playerResources[idx] || 0) + " | Değer: " + (window.resourceTradeValues ? window.resourceTradeValues[idx] : 0));
}
}
};
window.updateResourceTradePanel();
};
window.closeResourceTradePanel = function () {
if (!window.resourceTradePanelState.open) {
return;
}
window.resourceTradePanelState.open = false;
if (window.resourceTradePanelBG && window.resourceTradePanelBG.parent) {
window.resourceTradePanelBG.parent.removeChild(window.resourceTradePanelBG);
}
if (window.resourceTradePanelTitle && window.resourceTradePanelTitle.parent) {
window.resourceTradePanelTitle.parent.removeChild(window.resourceTradePanelTitle);
}
if (window.resourceTradePanelInfoText && window.resourceTradePanelInfoText.parent) {
window.resourceTradePanelInfoText.parent.removeChild(window.resourceTradePanelInfoText);
}
if (window.resourceTradePanelCloseBtn && window.resourceTradePanelCloseBtn.parent) {
window.resourceTradePanelCloseBtn.parent.removeChild(window.resourceTradePanelCloseBtn);
}
if (window.resourceTradeLabels) {
for (var i = 0; i < window.resourceTradeLabels.length; i++) {
if (window.resourceTradeLabels[i] && window.resourceTradeLabels[i].parent) {
window.resourceTradeLabels[i].parent.removeChild(window.resourceTradeLabels[i]);
}
}
}
if (window.resourceTradeSellBtns) {
for (var i = 0; i < window.resourceTradeSellBtns.length; i++) {
if (window.resourceTradeSellBtns[i] && window.resourceTradeSellBtns[i].parent) {
window.resourceTradeSellBtns[i].parent.removeChild(window.resourceTradeSellBtns[i]);
}
}
}
if (window.resourceTradeSellAllBtns) {
for (var i = 0; i < window.resourceTradeSellAllBtns.length; i++) {
if (window.resourceTradeSellAllBtns[i] && window.resourceTradeSellAllBtns[i].parent) {
window.resourceTradeSellAllBtns[i].parent.removeChild(window.resourceTradeSellAllBtns[i]);
}
}
}
if (window.resourceTradeBuyBtns) {
for (var i = 0; i < window.resourceTradeBuyBtns.length; i++) {
if (window.resourceTradeBuyBtns[i] && window.resourceTradeBuyBtns[i].parent) {
window.resourceTradeBuyBtns[i].parent.removeChild(window.resourceTradeBuyBtns[i]);
}
}
}
window.resourceTradePanelBG = null;
window.resourceTradePanelTitle = null;
window.resourceTradePanelInfoText = null;
window.resourceTradePanelCloseBtn = null;
window.resourceTradeLabels = null;
window.resourceTradeSellBtns = null;
window.resourceTradeSellAllBtns = null;
window.resourceTradeBuyBtns = null;
};
var healthText = new Text2('Health: 100/100', {
size: unifiedFontSize,
fill: 0x00FF00
});
healthText.anchor.set(0.5, 0);
healthText._isHealthBar = true; // Mark for tap detection
LK.gui.top.addChild(healthText);
var shieldText = new Text2('Shield: 50/50', {
size: unifiedFontSize,
fill: 0x0088FF
});
shieldText.anchor.set(0.5, 0);
shieldText.y = 60;
LK.gui.top.addChild(shieldText);
// --- Add waveText and expText UI for wave/level display ---
// --- REPAIR PANEL WARNING SUPPRESSION ---
// When repair panel is closed, set a suppression timer for 30 seconds (1800 ticks)
if (window.repairPanel && !window._repairPanelSuppressUntil) {
window._repairPanelSuppressUntil = LK.ticks + 1800;
}
// (removed duplicate handler, see main game.down below)
var waveText = new Text2('Wave: 1', {
size: unifiedFontSize,
fill: 0xffffff
});
waveText.anchor.set(0.5, 0);
waveText.x = 1024;
waveText.y = 60; // Move down to make space for market button
LK.gui.top.addChild(waveText);
var expText = new Text2('Level: 1 EXP: 0/10', {
size: unifiedFontSize,
fill: 0xffffff
});
expText.anchor.set(0.5, 0);
expText.x = 1024;
expText.y = 70;
LK.gui.top.addChild(expText);
// --- Add image to right edge, 3 rows below waveText ---
if (!window.levelPanelBelowImg) {
// Use the 'levelPanelBelowImg' asset as the image
var imgAsset = LK.getAsset('levelPanelBelowImg', {
anchorX: 1,
// right edge
anchorY: 0,
// top
scaleX: 0.7,
scaleY: 0.7
});
// Place at right edge (x=0 in bottomRight, so x=-margin from right)
// LK.gui.topRight is anchored at (1,0), so x=0 is right edge, y=0 is top
// waveText.y is 60, so 3 rows below is 60 + 3*unifiedFontSize + 3*10 (10px margin per row)
var rowH = unifiedFontSize + 10;
imgAsset.x = -40; // 40px margin from right edge
imgAsset.y = waveText.y + 3 * rowH;
window.levelPanelBelowImg = imgAsset;
LK.gui.topRight.addChild(imgAsset);
// --- Achievements Panel Tap Area (transparent, over Achievements image) ---
if (!window.achievementsPanelBtn) {
var achBtn = new Text2("", {
size: 1,
fill: 0xffffff,
align: "center"
});
achBtn.anchor.set(1, 0);
achBtn.x = imgAsset.x;
achBtn.y = imgAsset.y;
achBtn.width = imgAsset.width;
achBtn.height = imgAsset.height;
achBtn.alpha = 0; // fully transparent, but receives taps
window.achievementsPanelBtn = achBtn;
LK.gui.topRight.addChild(achBtn);
}
// --- Add new image just below Achievements image (right edge, keep all others fixed) ---
if (!window.newImageAboveStorage) {
var newImgAsset = LK.getAsset('newImageAboveStorage', {
anchorX: 1,
anchorY: 0,
scaleX: 0.7,
scaleY: 0.7
});
// Place just below Achievements image (levelPanelBelowImg)
if (window.levelPanelBelowImg) {
newImgAsset.x = window.levelPanelBelowImg.x;
newImgAsset.y = window.levelPanelBelowImg.y + window.levelPanelBelowImg.height + 10;
} else if (typeof imgAsset !== "undefined") {
// fallback: just below the previous image
newImgAsset.x = imgAsset.x;
newImgAsset.y = imgAsset.y + imgAsset.height + 10;
} else {
// fallback: right edge, 2 rows from top
newImgAsset.x = -40;
newImgAsset.y = 10 + newImgAsset.height * 2 + 20;
}
window.newImageAboveStorage = newImgAsset;
LK.gui.topRight.addChild(newImgAsset);
}
// --- Add upg image to right edge, 3 rows below waveText, just below the previous image ---
if (!window.upgPanelBelowImg) {
var upgImgAsset = LK.getAsset('upg', {
anchorX: 1,
anchorY: 0,
scaleX: 0.7,
scaleY: 0.7
});
upgImgAsset.x = -40;
upgImgAsset.y = imgAsset.y + imgAsset.height + 10 + upgImgAsset.height + 10; // move 1 row (image height + 10px) further down
window.upgPanelBelowImg = upgImgAsset;
LK.gui.topRight.addChild(upgImgAsset);
// Add a transparent tap area over the upg image for opening the Upgrades panel
if (!window.upgPanelBtn) {
var upgBtn = new Text2("", {
size: 1,
fill: 0xffffff,
align: "center"
});
upgBtn.anchor.set(1, 0);
upgBtn.x = upgImgAsset.x;
// Move the button 1 row (image height + 10px) further down, image stays fixed
upgBtn.y = upgImgAsset.y + upgImgAsset.height + 10;
// Make the button area 1.5x wider and 1.5x taller than the image, centered on the image's position
upgBtn.width = upgImgAsset.width * 1.5;
upgBtn.height = upgImgAsset.height * 1.5;
// Shift the button left and up so it stays centered on the image
upgBtn.x = upgImgAsset.x - (upgBtn.width - upgImgAsset.width) / 2;
upgBtn.y = upgImgAsset.y + upgImgAsset.height + 10 - (upgBtn.height - upgImgAsset.height) / 2;
upgBtn.alpha = 0; // fully transparent, but receives taps
window.upgPanelBtn = upgBtn;
LK.gui.topRight.addChild(upgBtn);
}
}
}
// --- Achievements Panel Logic ---
// 50 achievements, with difficulty and rewards
if (!window.achievementsList) {
window.achievementsList = [
// id, name, desc, reward, difficulty
{
id: 1,
name: "First Blood",
desc: "Destroy your first pirate.",
reward: 50,
difficulty: "Easy"
}, {
id: 2,
name: "Miner",
desc: "Collect resources from an asteroid.",
reward: 30,
difficulty: "Easy"
}, {
id: 3,
name: "Trader",
desc: "Trade a resource.",
reward: 40,
difficulty: "Easy"
}, {
id: 5,
name: "Killer Pirate",
desc: "Destroy the killer pirate.",
reward: 500,
difficulty: "Hard"
}, {
id: 6,
name: "Rich",
desc: "Accumulate 1000 coins.",
reward: 100,
difficulty: "Medium"
}, {
id: 7,
name: "Mechanic",
desc: "Repair your ship.",
reward: 30,
difficulty: "Easy"
}, {
id: 8,
name: "Level Up",
desc: "Reach level 5.",
reward: 80,
difficulty: "Medium"
}, {
id: 9,
name: "Shield Master",
desc: "Fully charge your shield.",
reward: 40,
difficulty: "Easy"
}, {
id: 11,
name: "Pirate Hunter",
desc: "Destroy 10 pirates.",
reward: 120,
difficulty: "Medium"
}, {
id: 12,
name: "Asteroid Destroyer",
desc: "Destroy 5 asteroids.",
reward: 80,
difficulty: "Medium"
}, {
id: 13,
name: "Trade Master",
desc: "Complete 10 trades total.",
reward: 150,
difficulty: "Hard"
}, {
id: 14,
name: "Explorer",
desc: "Travel a total of 10,000 distance units.",
reward: 100,
difficulty: "Medium"
}, {
id: 15,
name: "Pirate King",
desc: "Destroy 50 pirates.",
reward: 300,
difficulty: "Hard"
}, {
id: 16,
name: "Resource King",
desc: "Collect 100 units of all resources.",
reward: 200,
difficulty: "Hard"
}, {
id: 17,
name: "Upgrader",
desc: "Purchase an upgrade.",
reward: 60,
difficulty: "Easy"
}, {
id: 18,
name: "Repair Specialist",
desc: "Repair 5 times.",
reward: 100,
difficulty: "Medium"
}, {
id: 19,
name: "Wave Passer",
desc: "Reach wave 5.",
reward: 80,
difficulty: "Medium"
}, {
id: 20,
name: "Wave Master",
desc: "Reach wave 10.",
reward: 200,
difficulty: "Hard"
}, {
id: 21,
name: "Shield King",
desc: "Upgrade shield to 200.",
reward: 120,
difficulty: "Hard"
}, {
id: 22,
name: "Beast",
desc: "Upgrade health to 300.",
reward: 120,
difficulty: "Hard"
}, {
id: 23,
name: "Fast",
desc: "Upgrade speed to 10.",
reward: 100,
difficulty: "Hard"
}, {
id: 24,
name: "Bullet Storm",
desc: "Reduce fire rate to 5.",
reward: 100,
difficulty: "Hard"
}, {
id: 25,
name: "Energy Master",
desc: "Collect a total of 500 energy.",
reward: 100,
difficulty: "Medium"
}, {
id: 26,
name: "Metal Worker",
desc: "Collect a total of 500 metal.",
reward: 100,
difficulty: "Medium"
}, {
id: 27,
name: "Ice Man",
desc: "Collect a total of 100 ice.",
reward: 80,
difficulty: "Medium"
}, {
id: 28,
name: "Plasma Hunter",
desc: "Collect a total of 50 plasma.",
reward: 80,
difficulty: "Medium"
}, {
id: 29,
name: "Antimatter Collector",
desc: "Collect a total of 10 antimatter.",
reward: 120,
difficulty: "Hard"
}, {
id: 33,
name: "Space Station",
desc: "Make a delivery to the station.",
reward: 100,
difficulty: "Medium"
}, {
id: 34,
name: "Big Delivery",
desc: "Complete all station requests.",
reward: 300,
difficulty: "Hard"
}, {
id: 35,
name: "Pirate Base",
desc: "Destroy a pirate base.",
reward: 200,
difficulty: "Hard"
}, {
id: 36,
name: "Pirate Base Hunter",
desc: "Destroy 3 pirate bases.",
reward: 400,
difficulty: "Very Hard"
}, {
id: 38,
name: "Resource Tycoon",
desc: "Collect 500 units of all resources.",
reward: 400,
difficulty: "Very Hard"
}, {
id: 39,
name: "Trade Tycoon",
desc: "Complete a total of 100 trades.",
reward: 400,
difficulty: "Very Hard"
}, {
id: 40,
name: "Level Master",
desc: "Reach level 20.",
reward: 400,
difficulty: "Very Hard"
}, {
id: 41,
name: "Wave Legend",
desc: "Reach wave 20.",
reward: 500,
difficulty: "Very Hard"
}, {
id: 42,
name: "Space Conqueror",
desc: "Travel a total of 100,000 distance units.",
reward: 500,
difficulty: "Very Hard"
}, {
id: 43,
name: "Pirate Slayer",
desc: "Destroy 100 pirates.",
reward: 600,
difficulty: "Very Hard"
}, {
id: 44,
name: "Asteroid King",
desc: "Destroy 50 asteroids.",
reward: 400,
difficulty: "Very Hard"
}, {
id: 45,
name: "Upgrade King",
desc: "Max out all upgrades.",
reward: 600,
difficulty: "Very Hard"
}, {
id: 46,
name: "Repair King",
desc: "Repair 20 times.",
reward: 300,
difficulty: "Very Hard"
},
// NEW: Active gameplay feature achievements
{
id: 51,
name: "Asteroid Hunter",
desc: "Destroy 20 asteroids.",
reward: 150,
difficulty: "Medium"
}, {
id: 52,
name: "Pirate Raider",
desc: "Destroy 10 pirates in one wave.",
reward: 200,
difficulty: "Hard"
}, {
id: 53,
name: "Fast Collector",
desc: "Collect 10 resources in one minute.",
reward: 120,
difficulty: "Medium"
}, {
id: 54,
name: "Rich Miner",
desc: "Have 1000 coins and 100 resources at the same time.",
reward: 250,
difficulty: "Hard"
}, {
id: 55,
name: "Upgrade Series",
desc: "Buy 3 upgrades in a row.",
reward: 180,
difficulty: "Medium"
}, {
id: 56,
name: "Achievement Hunter",
desc: "Complete all achievements.",
reward: 1000,
difficulty: "Legendary"
},
// --- NEW 10 ACHIEVEMENTS (latest features) ---
{
id: 57,
name: "GroupPirate Hunter",
desc: "Completely destroy a GroupPirate squad.",
reward: 250,
difficulty: "Hard"
}, {
id: 58,
name: "CoinBox Hunter",
desc: "Collect a CoinBox.",
reward: 80,
difficulty: "Easy"
}, {
id: 59,
name: "CoinBox Tycoon",
desc: "Collect a total of 10 CoinBoxes.",
reward: 300,
difficulty: "Hard"
}, {
id: 60,
name: "Wreck Hunter",
desc: "Collect a Derelict Wreck.",
reward: 60,
difficulty: "Easy"
}, {
id: 61,
name: "Wreck Tycoon",
desc: "Collect a total of 10 Derelict Wrecks.",
reward: 250,
difficulty: "Medium"
}, {
id: 62,
name: "Double Pirate",
desc: "Fight 2 pirates at the same time.",
reward: 120,
difficulty: "Medium"
}, {
id: 63,
name: "Triple GroupPirate",
desc: "Fight 3 GroupPirates at the same time.",
reward: 200,
difficulty: "Hard"
}, {
id: 64,
name: "Asteroid Rain",
desc: "Destroy 5 asteroids in one wave.",
reward: 180,
difficulty: "Hard"
}, {
id: 65,
name: "Resource Burst",
desc: "Collect more than 20 resources at once.",
reward: 220,
difficulty: "Hard"
}, {
id: 66,
name: "Pirate Swarm",
desc: "Fight more than 5 pirates in one wave.",
reward: 250,
difficulty: "Very Hard"
}];
// Save progress
if (!storage.achievements) {
storage.achievements = [];
}
window.achievementsProgress = storage.achievements;
}
// --- BAŞARIM MEKANİĞİ EKLENDİ ---
// Yardımcı fonksiyon: başarıma sahip mi?
function hasAchievement(id) {
return window.achievementsProgress && window.achievementsProgress.indexOf(id) !== -1;
}
// Yardımcı fonksiyon: başarıma ekle ve ödül ver
function grantAchievement(id) {
if (!window.achievementsProgress) {
window.achievementsProgress = [];
}
if (window.achievementsProgress.indexOf(id) !== -1) {
return;
} // Zaten kazanıldı
window.achievementsProgress.push(id);
storage.achievements = window.achievementsProgress;
// Ödül ver
var ach = null;
for (var i = 0; i < window.achievementsList.length; i++) {
if (window.achievementsList[i].id === id) {
ach = window.achievementsList[i];
break;
}
}
if (ach && typeof playerCoins !== "undefined") {
playerCoins += ach.reward;
updateUI && updateUI();
saveProgress && saveProgress();
// Show a short notification
var label = new Text2("Achievement Earned!\n" + ach.name + "\n+" + ach.reward + " coin", {
size: 44,
fill: 0x00ffcc,
align: "center"
});
label.anchor.set(0.5, 0.5);
label.x = 1024;
label.y = 400;
if (typeof game !== "undefined" && game.addChild) {
game.addChild(label);
}
LK.setTimeout(function () {
if (label.parent) {
label.parent.removeChild(label);
}
}, 1800);
}
// Tüm başarımlar kazanıldıysa 50. başarıma bak
if (window.achievementsProgress.length === 50 && !hasAchievement(50)) {
grantAchievement(50);
}
}
// --- BAŞARIMLARIN TAKİBİ İÇİN SAYACLAR ---
// Oyun içi sayaçlar
if (!window._achPirateKills) {
window._achPirateKills = 0;
}
if (!window._achAsteroidKills) {
window._achAsteroidKills = 0;
}
if (!window._achTradeCount) {
window._achTradeCount = 0;
}
if (!window._achRepairCount) {
window._achRepairCount = 0;
}
if (!window._achQuestComplete) {
window._achQuestComplete = 0;
}
if (!window._achNPCKills) {
window._achNPCKills = 0;
}
if (!window._achStationDeliver) {
window._achStationDeliver = 0;
}
if (!window._achStationAllDeliver) {
window._achStationAllDeliver = 0;
}
if (!window._achPirateBaseKills) {
window._achPirateBaseKills = 0;
}
if (!window._achPirateBaseKills3) {
window._achPirateBaseKills3 = 0;
}
if (!window._achUpgradeCount) {
window._achUpgradeCount = 0;
}
if (!window._achKatilEscape) {
window._achKatilEscape = false;
}
if (!window._achKatilKill) {
window._achKatilKill = false;
}
if (!window._achWormhole) {
window._achWormhole = false;
}
if (!window._achPirateTrade) {
window._achPirateTrade = false;
}
if (!window._achProtectNPC) {
window._achProtectNPC = false;
}
if (!window._ach3PirateFight) {
window._ach3PirateFight = false;
}
// --- BAŞARIM KONTROLLERİ ---
// 1. Korsan öldürme (İlk Kan, Korsan Avcısı, Korsan Kralı, Korsan Katili)
function checkPirateKillAchievements() {
window._achPirateKills++;
if (window._achPirateKills === 1) {
grantAchievement(1);
} // İlk Kan
if (window._achPirateKills === 10) {
grantAchievement(11);
} // Korsan Avcısı
if (window._achPirateKills === 50) {
grantAchievement(15);
} // Korsan Kralı
if (window._achPirateKills === 100) {
grantAchievement(43);
} // Korsan Katili
}
// 2. Katil korsan öldürme
function checkKatilKillAchievement() {
if (!window._achKatilKill) {
window._achKatilKill = true;
grantAchievement(5); // Katil Korsan
}
}
// 3. Asteroit patlatma
function checkAsteroidKillAchievements() {
window._achAsteroidKills++;
if (window._achAsteroidKills === 5) {
grantAchievement(12);
} // Asteroit Patlatıcı
if (window._achAsteroidKills === 50) {
grantAchievement(44);
} // Asteroit Kralı
}
// 4. Hammadde toplama (Madenci, Hammadde Kralı, Hammadde Zengini, Enerji Ustası, Metalci, Buz Adam, Plazma Avcısı, Antimaddeci)
function checkResourceCollectAchievements() {
// Madenci
grantAchievement(2);
// Enerji Ustası, Metalci, Buz Adam, Plazma Avcısı, Antimaddeci, Hammadde Kralı, Hammadde Zengini
var res = playerResources;
if (res[1] >= 500) {
grantAchievement(25);
} // Enerji Ustası
if (res[0] >= 500) {
grantAchievement(26);
} // Metalci
if (res[4] >= 100) {
grantAchievement(27);
} // Buz Adam
if (res[8] >= 50) {
grantAchievement(28);
} // Plazma Avcısı
if (res[9] >= 10) {
grantAchievement(29);
} // Antimaddeci
var all100 = true,
all500 = true;
for (var i = 0; i < res.length; i++) {
if (res[i] < 100) {
all100 = false;
}
if (res[i] < 500) {
all500 = false;
}
}
if (all100) {
grantAchievement(16);
} // Hammadde Kralı
if (all500) {
grantAchievement(38);
} // Hammadde Zengini
}
// 5. Takas (Tüccar, Ticaret Ustası, Takas Zengini)
function checkTradeAchievements() {
window._achTradeCount++;
if (window._achTradeCount === 1) {
grantAchievement(3);
} // Tüccar
if (window._achTradeCount === 10) {
grantAchievement(13);
} // Ticaret Ustası
if (window._achTradeCount === 100) {
grantAchievement(39);
} // Takas Zengini
}
// 6. Seviye atlama (Seviye Atla, Seviye Ustası)
function checkLevelAchievements() {
if (playerLevel >= 5) {
grantAchievement(8);
} // Seviye Atla
if (playerLevel >= 20) {
grantAchievement(40);
} // Seviye Ustası
}
// 7. Coin biriktirme (Zengin)
function checkCoinAchievements() {
if (playerCoins >= 1000) {
grantAchievement(6);
} // Zengin
}
// 8. Tamir (Mekanik, Tamirci, Tamir Kralı)
function checkRepairAchievements() {
window._achRepairCount++;
if (window._achRepairCount === 1) {
grantAchievement(7);
} // Mekanik
if (window._achRepairCount === 5) {
grantAchievement(18);
} // Tamirci
if (window._achRepairCount === 20) {
grantAchievement(46);
} // Tamir Kralı
}
// 9. Kalkanı doldurma (Kalkan Ustası, Kalkan Kralı)
function checkShieldAchievements() {
if (ship.shield >= ship.maxShield && ship.shield >= 50) {
grantAchievement(9);
} // Kalkan Ustası
if (ship.maxShield >= 200) {
grantAchievement(21);
} // Kalkan Kralı
}
// 10. Canı yükseltme (Canavar)
function checkHealthAchievements() {
if (ship.maxHealth >= 300) {
grantAchievement(22);
} // Canavar
}
// 11. Hızı yükseltme (Hızlı)
function checkSpeedAchievements() {
if (ship.speed >= 10) {
grantAchievement(23);
} // Hızlı
}
// 12. Atış hızını düşürme (Ateş Yağmuru)
function checkFireRateAchievements() {
if (ship.fireRate <= 5) {
grantAchievement(24);
} // Ateş Yağmuru
}
// 13. Görev tamamlama (Görev Adamı)
function checkQuestAchievements() {
window._achQuestComplete++;
if (window._achQuestComplete === 1) {
grantAchievement(10);
} // Görev Adamı
}
// 14. Dalga geçme (Dalga Geç, Dalga Ustası, Dalga Efsanesi)
function checkWaveAchievements() {
if (waveNumber + 1 >= 5) {
grantAchievement(19);
} // Dalga Geç
if (waveNumber + 1 >= 10) {
grantAchievement(20);
} // Dalga Ustası
if (waveNumber + 1 >= 20) {
grantAchievement(41);
} // Dalga Efsanesi
}
// 15. Yol katetme (Gezgin, Uzay Fatihi)
function checkDistanceAchievements() {
if (totalDistance >= 10000) {
grantAchievement(14);
} // Gezgin
if (totalDistance >= 100000) {
grantAchievement(42);
} // Uzay Fatihi
}
// 16. Upgrade satın alma (Upgradeci, Upgrade Kralı)
function checkUpgradeAchievements() {
window._achUpgradeCount++;
if (window._achUpgradeCount === 1) {
grantAchievement(17);
} // Upgradeci
// Tüm upgrade'ler maksimuma çıkarıldıysa
if (ship.maxHealth >= 300 && ship.maxShield >= 200 && ship.damage >= 50 && ship.speed >= 10 && ship.fireRate <= 5) {
grantAchievement(45); // Upgrade Kralı
}
}
// 17. Korsan üssü yok etme (Korsan Üssü, Korsan Üssü Avcısı)
function checkPirateBaseAchievements() {
window._achPirateBaseKills++;
if (window._achPirateBaseKills === 1) {
grantAchievement(35);
} // Korsan Üssü
if (window._achPirateBaseKills === 3) {
grantAchievement(36);
} // Korsan Üssü Avcısı
}
// 18. NPC öldürme (NPC Avcısı)
function checkNPCKillAchievements() {
window._achNPCKills++;
if (window._achNPCKills === 10) {
grantAchievement(48);
} // NPC Avcısı
}
// 19. İstasyon teslimatı (Uzay İstasyonu, Büyük Teslimat)
function checkStationDeliveryAchievements(allDone) {
window._achStationDeliver++;
grantAchievement(33); // Uzay İstasyonu
if (allDone) {
window._achStationAllDeliver++;
grantAchievement(34); // Büyük Teslimat
}
}
// 20. Solucan deliğinden geçme (Kaşif)
function checkWormholeAchievement() {
if (!window._achWormhole) {
window._achWormhole = true;
grantAchievement(4); // Kaşif
}
}
// 21. Katil korsandan kaçma (Katil Kaçışı)
function checkKatilEscapeAchievement() {
if (!window._achKatilEscape) {
window._achKatilEscape = true;
grantAchievement(37); // Katil Kaçışı
}
}
// 22. Bir korsandan kaçma (Korsan Kaçakçısı)
function checkPirateEscapeAchievement() {
grantAchievement(30); // Korsan Kaçakçısı
}
// 23. Bir NPC'yi koruma (Dost Canlısı)
function checkProtectNPCAchievement() {
if (!window._achProtectNPC) {
window._achProtectNPC = true;
grantAchievement(31); // Dost Canlısı
}
}
// 24. Aynı anda 3 korsanla savaş (Korsan Saldırısı)
function check3PirateFightAchievement() {
if (!window._ach3PirateFight) {
window._ach3PirateFight = true;
grantAchievement(32); // Korsan Saldırısı
}
}
// 25. Bir korsanla ticaret yapma (Korsan Dostu)
function checkPirateTradeAchievement() {
if (!window._achPirateTrade) {
window._achPirateTrade = true;
grantAchievement(47); // Korsan Dostu
}
}
// 26. 30 dakika hayatta kalma (Uzayda Hayatta Kal)
function checkSurvive30MinAchievement(elapsedSeconds) {
if (elapsedSeconds >= 1800) {
grantAchievement(49);
}
}
// --- Achievements Panel Open/Close ---
window.openAchievementsPanel = function (page) {
if (window.achievementsPanelBG) {
return;
}
var pageSize = 10;
var total = window.achievementsList.length;
var totalPages = Math.ceil(total / pageSize);
var currentPage = typeof page === "number" ? Math.max(1, Math.min(page, totalPages)) : 1;
window._achievementsPanelPage = currentPage;
var panelW = 1100,
panelH = 1200;
var px = 2048 / 2,
py = 2732 / 2;
// BG
var bg = LK.getAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
x: px,
y: py,
scaleX: panelW / 40,
scaleY: panelH / 40,
tint: 0x222244,
alpha: 0.97
});
// Title
var title = new Text2("Achievements", {
size: Math.round(unifiedFontSize * 1.3) + 2,
fill: 0x00ffcc,
align: "center"
});
title.anchor.set(0.5, 0);
title.x = px;
title.y = py - panelH / 2 + 40;
// Page info
var pageInfo = new Text2("Page " + currentPage + "/" + totalPages, {
size: Math.round(unifiedFontSize * 0.9) + 2,
fill: 0xffffff,
align: "center"
});
pageInfo.anchor.set(0.5, 0);
pageInfo.x = px;
pageInfo.y = title.y + 60;
// Achievements rows
var startY = pageInfo.y + 60;
var rowH = 90;
var achRows = [];
for (var i = 0; i < pageSize; i++) {
var idx = (currentPage - 1) * pageSize + i;
if (idx >= total) {
break;
}
var ach = window.achievementsList[idx];
var unlocked = window.achievementsProgress.indexOf(ach.id) !== -1;
var text = (unlocked ? "✅ " : "⬜ ") + ach.name + " - " + ach.desc + " [" + ach.difficulty + "]\nÖdül: " + ach.reward + " coin";
var color = unlocked ? 0x00ff99 : 0xffffff;
var row = new Text2(text, {
size: Math.round(unifiedFontSize * 0.9) + 2,
fill: color,
align: "left"
});
row.anchor.set(0, 0.5);
row.x = px - panelW / 2 + 40;
row.y = startY + i * rowH;
achRows.push(row);
}
// Page number buttons (1-2-3...)
var btnW = 45,
btnH = 35;
var pageBtns = [];
var pageBtnY = py + panelH / 2 - btnH / 2 - 20;
var pageBtnStartX = px - (totalPages - 1) * (btnW + 10) / 2;
for (var p = 1; p <= totalPages; p++) {
var isCurrent = p === currentPage;
var btn = new Text2("" + p, {
size: Math.round(unifiedFontSize * 0.6) + 2,
fill: isCurrent ? 0x00ffcc : 0xffffff,
align: "center"
});
btn.anchor.set(0.5, 0.5);
btn.x = pageBtnStartX + (p - 1) * (btnW + 10);
btn.y = pageBtnY;
btn.width = btnW;
btn.height = btnH;
btn._achPageBtn = p;
if (isCurrent) {
btn.fill = 0x00ffcc;
btn.setText("[" + p + "]");
}
pageBtns.push(btn);
}
// Close button
var closeBtn = new Text2("Close", {
size: Math.round((unifiedFontSize + 4) * 1.3),
fill: 0xff4444,
align: "center"
});
closeBtn.anchor.set(0.5, 0.5);
closeBtn.x = px;
closeBtn.y = title.y - 80;
closeBtn._achCloseBtn = true;
// Add to game
game.addChild(bg);
game.addChild(title);
game.addChild(pageInfo);
for (var i = 0; i < achRows.length; i++) {
game.addChild(achRows[i]);
}
for (var i = 0; i < pageBtns.length; i++) {
game.addChild(pageBtns[i]);
}
game.addChild(closeBtn);
// Store refs for tap logic/cleanup
window.achievementsPanelBG = bg;
window.achievementsPanelTitle = title;
window.achievementsPanelPageInfo = pageInfo;
window.achievementsPanelRows = achRows;
window.achievementsPanelPageBtns = pageBtns;
window.achievementsPanelCloseBtn = closeBtn;
};
window.closeAchievementsPanel = function () {
if (!window.achievementsPanelBG) {
return;
}
if (window.achievementsPanelBG.parent) {
window.achievementsPanelBG.parent.removeChild(window.achievementsPanelBG);
}
if (window.achievementsPanelTitle && window.achievementsPanelTitle.parent) {
window.achievementsPanelTitle.parent.removeChild(window.achievementsPanelTitle);
}
if (window.achievementsPanelPageInfo && window.achievementsPanelPageInfo.parent) {
window.achievementsPanelPageInfo.parent.removeChild(window.achievementsPanelPageInfo);
}
if (window.achievementsPanelRows) {
for (var i = 0; i < window.achievementsPanelRows.length; i++) {
if (window.achievementsPanelRows[i] && window.achievementsPanelRows[i].parent) {
window.achievementsPanelRows[i].parent.removeChild(window.achievementsPanelRows[i]);
}
}
}
if (window.achievementsPanelPageBtns) {
for (var i = 0; i < window.achievementsPanelPageBtns.length; i++) {
if (window.achievementsPanelPageBtns[i] && window.achievementsPanelPageBtns[i].parent) {
window.achievementsPanelPageBtns[i].parent.removeChild(window.achievementsPanelPageBtns[i]);
}
}
}
if (window.achievementsPanelCloseBtn && window.achievementsPanelCloseBtn.parent) {
window.achievementsPanelCloseBtn.parent.removeChild(window.achievementsPanelCloseBtn);
}
window.achievementsPanelBG = null;
window.achievementsPanelTitle = null;
window.achievementsPanelPageInfo = null;
window.achievementsPanelRows = null;
window.achievementsPanelPageBtns = null;
window.achievementsPanelCloseBtn = null;
window._achievementsPanelPage = null;
};
// --- CREW PANEL SYSTEM ---
// Global arrays for friends, dron, and mechanic
var friends = [];
var dron = null;
var mechanic = null;
// Helper: open Crew panel
window.openCrewPanel = function () {
if (window.crewPanelBG) {
return;
}
var px = 2048 / 2,
py = 2732 / 2;
var panelW = 900,
panelH = 900;
var rowH = 110;
var fontSize = Math.round(unifiedFontSize * 1.1) + 2;
// BG
var bg = LK.getAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
x: px,
y: py,
scaleX: panelW / 40,
scaleY: panelH / 40,
tint: 0x1a2dcb,
alpha: 0.97
});
// Title
var title = new Text2("Crew (Friends & Dron)", {
size: fontSize + 8,
fill: 0x00ffcc,
align: "center"
});
title.anchor.set(0.5, 0);
title.x = px;
title.y = py - panelH / 2 + 40;
// Info
var info = new Text2("You can buy up to 3 Friends. Each Friend attacks with 50% of your attack power.\nDron collects nearby resources/coins.\nMechanic automatically repairs when health drops below 50%.", {
size: fontSize - 2,
fill: 0xffffff,
align: "center"
});
info.anchor.set(0.5, 0);
info.x = px;
info.y = title.y + 60;
// Friend satın al butonları ve etiketleri
window.crewFriendBtns = [];
window.crewFriendLabels = [];
for (var i = 0; i < 3; i++) {
var label = new Text2("Friend #" + (i + 1) + (friends[i] ? " (Purchased)" : " (Empty)"), {
size: fontSize,
fill: friends[i] ? 0x00ff99 : 0xffffff,
align: "left"
});
label.anchor.set(0, 0.5);
label.x = px - panelW / 2 + 60;
label.y = info.y + 100 + rowH + i * rowH;
window.crewFriendLabels.push(label);
var btn = new Text2(friends[i] ? "Purchased" : "Buy (500 coin)", {
size: fontSize,
fill: friends[i] ? 0x888888 : 0x00ff99,
align: "center"
});
btn.anchor.set(0.5, 0.5);
btn.x = px + 220;
btn.y = label.y;
btn._friendIdx = i;
window.crewFriendBtns.push(btn);
}
// Dron satın al etiketi ve butonu
var dronLabel = new Text2("Dron: " + (dron ? "Purchased" : "None"), {
size: fontSize,
fill: dron ? 0x00ff99 : 0xffffff,
align: "left"
});
dronLabel.anchor.set(0, 0.5);
dronLabel.x = px - panelW / 2 + 60;
dronLabel.y = info.y + 100 + rowH + 3 * rowH;
var dronBtn = new Text2(dron ? "Purchased" : "Buy (1200 coin)", {
size: fontSize,
fill: dron ? 0x888888 : 0x00ff99,
align: "center"
});
dronBtn.anchor.set(0.5, 0.5);
dronBtn.x = px + 220;
dronBtn.y = dronLabel.y;
// Mechanic satın al etiketi ve butonu
var mechanicLabel = new Text2("Mechanic: " + (window.mechanic ? "Purchased" : "None"), {
size: fontSize,
fill: window.mechanic ? 0x00ff99 : 0xffffff,
align: "left"
});
mechanicLabel.anchor.set(0, 0.5);
mechanicLabel.x = px - panelW / 2 + 60;
mechanicLabel.y = info.y + 100 + rowH + 4 * rowH;
var mechanicBtn = new Text2(window.mechanic ? "Purchased" : "Buy (800 coin)", {
size: fontSize,
fill: window.mechanic ? 0x888888 : 0x00ff99,
align: "center"
});
mechanicBtn.anchor.set(0.5, 0.5);
mechanicBtn.x = px + 220;
mechanicBtn.y = mechanicLabel.y;
// Close button
var closeBtn = new Text2("Close", {
size: fontSize + 6,
fill: 0xff4444,
align: "center"
});
closeBtn.anchor.set(0.5, 0.5);
closeBtn.x = px;
closeBtn.y = title.y - 80;
// Add to game
game.addChild(bg);
game.addChild(title);
game.addChild(info);
for (var i = 0; i < window.crewFriendLabels.length; i++) {
game.addChild(window.crewFriendLabels[i]);
}
for (var i = 0; i < window.crewFriendBtns.length; i++) {
game.addChild(window.crewFriendBtns[i]);
}
game.addChild(dronLabel);
game.addChild(dronBtn);
game.addChild(mechanicLabel);
game.addChild(mechanicBtn);
game.addChild(closeBtn);
// Store refs for tap logic/cleanup
window.crewPanelBG = bg;
window.crewPanelTitle = title;
window.crewPanelInfo = info;
window.crewPanelDronLabel = dronLabel;
window.crewPanelDronBtn = dronBtn;
window.crewPanelMechanicLabel = mechanicLabel;
window.crewPanelMechanicBtn = mechanicBtn;
window.crewPanelCloseBtn = closeBtn;
};
window.closeCrewPanel = function () {
if (!window.crewPanelBG) {
return;
}
if (window.crewPanelBG.parent) {
window.crewPanelBG.parent.removeChild(window.crewPanelBG);
}
if (window.crewPanelTitle && window.crewPanelTitle.parent) {
window.crewPanelTitle.parent.removeChild(window.crewPanelTitle);
}
if (window.crewPanelInfo && window.crewPanelInfo.parent) {
window.crewPanelInfo.parent.removeChild(window.crewPanelInfo);
}
if (window.crewFriendLabels) {
for (var i = 0; i < window.crewFriendLabels.length; i++) {
if (window.crewFriendLabels[i] && window.crewFriendLabels[i].parent) {
window.crewFriendLabels[i].parent.removeChild(window.crewFriendLabels[i]);
}
}
}
if (window.crewFriendBtns) {
for (var i = 0; i < window.crewFriendBtns.length; i++) {
if (window.crewFriendBtns[i] && window.crewFriendBtns[i].parent) {
window.crewFriendBtns[i].parent.removeChild(window.crewFriendBtns[i]);
}
}
}
if (window.crewPanelDronLabel && window.crewPanelDronLabel.parent) {
window.crewPanelDronLabel.parent.removeChild(window.crewPanelDronLabel);
}
if (window.crewPanelDronBtn && window.crewPanelDronBtn.parent) {
window.crewPanelDronBtn.parent.removeChild(window.crewPanelDronBtn);
}
if (window.crewPanelMechanicLabel && window.crewPanelMechanicLabel.parent) {
window.crewPanelMechanicLabel.parent.removeChild(window.crewPanelMechanicLabel);
}
if (window.crewPanelMechanicBtn && window.crewPanelMechanicBtn.parent) {
window.crewPanelMechanicBtn.parent.removeChild(window.crewPanelMechanicBtn);
}
if (window.crewPanelCloseBtn && window.crewPanelCloseBtn.parent) {
window.crewPanelCloseBtn.parent.removeChild(window.crewPanelCloseBtn);
}
window.crewPanelBG = null;
window.crewPanelTitle = null;
window.crewPanelInfo = null;
window.crewFriendLabels = null;
window.crewFriendBtns = null;
window.crewPanelDronLabel = null;
window.crewPanelDronBtn = null;
window.crewPanelMechanicLabel = null;
window.crewPanelMechanicBtn = null;
window.crewPanelCloseBtn = null;
};
// --- Upgrades Panel Logic ---
if (!window.openUpgradesPanel) {
window.openUpgradesPanel = function () {
if (window.upgradesPanel) {
return;
}
// Panel size and position
var panelW = 900,
panelH = 1100;
// Center the panel on the screen
var px = 2048 / 2;
var py = 2732 / 2;
// Panel BG
var bg = LK.getAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
x: px,
y: py,
scaleX: panelW / 40,
scaleY: panelH / 40,
tint: 0x222244,
alpha: 0.97
});
// Title
var title = new Text2("Upgrades", {
size: Math.round(unifiedFontSize * 1.3),
fill: 0x00ffcc,
align: "center"
});
title.anchor.set(0.5, 0);
title.x = px;
title.y = py - panelH / 2 + 40;
// Upgrade options (example: maxHealth, maxShield, damage, speed, fireRate)
var upgFont = unifiedFontSize + 2;
var upgSpacing = 120;
var upgStartY = title.y + 120;
var upgBtns = [];
var upgDefs = [{
label: "Attack Range +5%\n(300 coin)",
type: "range",
fill: 0x99ff99,
cost: {
coins: 300,
metal: 0,
energy: 0
}
}, {
label: "Heal 5%\n(50 coin)",
type: "heal5",
fill: 0xff6699,
cost: {
coins: 50,
metal: 0,
energy: 0
}
}, {
label: "Max Health +20\n(600 coin, 60 metal)",
type: "maxHealth",
fill: 0x00ff99,
cost: {
coins: 600,
metal: 60,
energy: 0
}
}, {
label: "Max Shield +10\n(720 coin, 72 metal)",
type: "maxShield",
fill: 0x00ccff,
cost: {
coins: 720,
metal: 72,
energy: 0
}
}, {
label: "Damage +5\n(900 coin, 90 energy)",
type: "damage",
fill: 0xffcc00,
cost: {
coins: 900,
metal: 0,
energy: 90
}
}, {
label: "Speed +10%\n(1000 coin)",
type: "speed",
fill: 0x00ffff,
cost: {
coins: 1000,
metal: 0,
energy: 0
}
}, {
label: "Fire Rate +10%\n(1200 coin)",
type: "fireRate",
fill: 0xff99ff,
cost: {
coins: 1200,
metal: 0,
energy: 0
}
}];
for (var i = 0; i < upgDefs.length; i++) {
var upg = upgDefs[i];
var btn = new Text2(upg.label, {
size: upgFont,
fill: upg.fill,
align: "center"
});
btn.anchor.set(0.5, 0.5);
btn.x = px;
btn.y = upgStartY + i * upgSpacing;
btn._isUpgradesPanelBtn = true;
btn._upgradeType = upg.type;
btn._upgradeCost = upg.cost;
upgBtns.push(btn);
}
// Close button
var closeBtn = new Text2("Close", {
size: Math.round((unifiedFontSize + 4) * 1.3),
fill: 0xff4444,
align: "center"
});
closeBtn.anchor.set(0.5, 0.5);
closeBtn.x = px;
closeBtn.y = title.y - 80;
closeBtn._isUpgradesPanelCloseBtn = true;
// Add to game
game.addChild(bg);
game.addChild(title);
for (var i = 0; i < upgBtns.length; i++) {
game.addChild(upgBtns[i]);
}
game.addChild(closeBtn);
// Store refs for tap logic/cleanup
window.upgradesPanel = bg;
window.upgradesPanelTitle = title;
window.upgradesPanelBtns = upgBtns;
window.upgradesPanelCloseBtn = closeBtn;
};
window.closeUpgradesPanel = function () {
if (!window.upgradesPanel) {
return;
}
if (window.upgradesPanel && window.upgradesPanel.parent) {
window.upgradesPanel.parent.removeChild(window.upgradesPanel);
}
if (window.upgradesPanelTitle && window.upgradesPanelTitle.parent) {
window.upgradesPanelTitle.parent.removeChild(window.upgradesPanelTitle);
}
if (window.upgradesPanelBtns) {
for (var i = 0; i < window.upgradesPanelBtns.length; i++) {
if (window.upgradesPanelBtns[i] && window.upgradesPanelBtns[i].parent) {
window.upgradesPanelBtns[i].parent.removeChild(window.upgradesPanelBtns[i]);
}
}
}
if (window.upgradesPanelCloseBtn && window.upgradesPanelCloseBtn.parent) {
window.upgradesPanelCloseBtn.parent.removeChild(window.upgradesPanelCloseBtn);
}
window.upgradesPanel = null;
window.upgradesPanelTitle = null;
window.upgradesPanelBtns = null;
window.upgradesPanelCloseBtn = null;
};
}
// No wave, minimap, exp, or coordinate UI in this version
// Create joystick
joystickBase = game.addChild(LK.getAsset('joystickBase', {
anchorX: 0.5,
anchorY: 0.5,
x: 300,
y: 2432,
scaleX: 1.3,
scaleY: 1.3
}));
joystickBase.alpha = 0.5;
joystickHandle = game.addChild(LK.getAsset('joystickHandle', {
anchorX: 0.5,
anchorY: 0.5,
x: 300,
y: 2432,
scaleX: 1.3,
scaleY: 1.3
}));
joystickHandle.alpha = 0.7;
// Create ship
ship = game.addChild(new Ship());
ship.x = 1024;
ship.y = 1366;
// Add health bar to ship
ship._healthBar = new HealthBar();
ship._healthBar.set({
maxValue: function maxValue() {
return ship.maxHealth;
},
getValueFn: function getValueFn() {
return ship.health;
},
getMaxFn: function getMaxFn() {
return ship.maxHealth;
},
width: 80,
height: 12
});
ship._healthBar.y = -70;
ship.addChild(ship._healthBar);
// Start background music
LK.playMusic('bgmusic');
// Initialize star field (increased by 50%) and make them colorful
for (var i = 0; i < 135; i++) {
// 90 * 1.5 = 135
spawnStar(true); // pass true to enable colorful stars
}
// Spawn more map elements at start (20% more)
for (var i = 0; i < 6; i++) {
// was 5, now 6
spawnNPC();
}
for (var i = 0; i < 6; i++) {
// was 5, now 6
spawnPirate();
}
// Ensure at least one Katil korsan exists at game start
if (typeof window._katilPirateSpawned === "undefined" || !window._katilPirateSpawned) {
var pirate = new Pirate();
var minLevel = 1 + waveNumber * 2;
pirate.level = minLevel + 10;
pirate.health = (30 + (pirate.level - 1) * 10) * 10;
pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10;
pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10;
pirate.rpgName = "Katil";
pirate._isKatil = true;
pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3));
pirate._attackRange = Math.floor(600 * 0.9); // Katil korsan için %10 daha kısa menzil
window._katilPirateSpawned = true;
// Spawn from edge like other pirates
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y - 1400;
break;
case 1:
pirate.x = ship.x + 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
case 2:
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y + 1400;
break;
case 3:
pirate.x = ship.x - 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
}
// Add health bar to katil pirate
pirate._healthBar = new HealthBar();
pirate._healthBar.set({
maxValue: function maxValue() {
return pirate.health > 0 ? pirate.health : 0;
},
getValueFn: function getValueFn() {
return pirate.health > 0 ? pirate.health : 0;
},
getMaxFn: function getMaxFn() {
return 30 + waveNumber * 10;
},
width: 60,
height: 10
});
pirate._healthBar.y = -60;
pirate.addChild(pirate._healthBar);
pirates.push(pirate);
game.addChild(pirate);
}
for (var i = 0; i < 2; i++) {
// was 4, now 2 (50% reduction)
spawnAsteroid();
}
// Helper functions
function spawnStar(colorful) {
var star = new Star();
// If colorful is true, assign a random color
if (colorful) {
// Pick a random color from a vibrant palette
var palette = [0xffe066,
// yellow
0xff66cc,
// pink
0x66ffcc,
// aqua
0x66ccff,
// blue
0xcc66ff,
// purple
0xff6666,
// red
0x66ff66,
// green
0xffffff,
// white
0xffcc66,
// orange
0x66ffff // cyan
];
var color = palette[Math.floor(Math.random() * palette.length)];
if (star.children && star.children.length > 0 && star.children[0]) {
star.children[0].tint = color;
}
}
// Spawn at a random position within a large area around the ship
var radius = 1800 + Math.random() * 1200; // 1800-3000 px from ship
var angle = Math.random() * Math.PI * 2;
star.x = ship.x + Math.cos(angle) * radius;
star.y = ship.y + Math.sin(angle) * radius;
stars.push(star);
game.addChild(star);
}
function spawnNPC() {
var npc = new NPC();
// Spawn from edges of visible area
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
// Top
npc.x = ship.x + (Math.random() - 0.5) * 2000;
npc.y = ship.y - 1500;
break;
case 1:
// Right
npc.x = ship.x + 1500;
npc.y = ship.y + (Math.random() - 0.5) * 2000;
break;
case 2:
// Bottom
npc.x = ship.x + (Math.random() - 0.5) * 2000;
npc.y = ship.y + 1500;
break;
case 3:
// Left
npc.x = ship.x - 1500;
npc.y = ship.y + (Math.random() - 0.5) * 2000;
break;
}
npc.baseY = npc.y;
// --- Set NPC level and health to match pirate scaling ---
var minLevel = 1 + waveNumber * 2;
var maxLevel = minLevel + 2;
npc.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1));
npc.health = 100 + (npc.level - 1) * 10;
// Add health bar to NPC (for defend/hunt/escort/delivery/explore)
npc._healthBar = new HealthBar();
npc._healthBar.set({
maxValue: function maxValue() {
return npc.health !== undefined ? npc.health : 100;
},
getValueFn: function getValueFn() {
return npc.health !== undefined ? npc.health : 100;
},
getMaxFn: function getMaxFn() {
return 100 + (npc.level - 1) * 10;
},
width: 60,
height: 10
});
npc._healthBar.y = -60;
npc.addChild(npc._healthBar);
npcs.push(npc);
game.addChild(npc);
}
function spawnPirate() {
var pirate = new Pirate();
// Spawn from edges of visible area
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
// Top
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y - 1400;
break;
case 1:
// Right
pirate.x = ship.x + 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
case 2:
// Bottom
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y + 1400;
break;
case 3:
// Left
pirate.x = ship.x - 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
}
// Set pirate level and scale stats based on wave
// Level range: wave 1 => 1-3, wave 2 => 3-5, wave 3 => 5-7, etc.
var minLevel = 1 + waveNumber * 2;
var maxLevel = minLevel + 2;
// Katil korsan spawn etme şansı: Eğer henüz yoksa ve %20 şansla, bu korsanı Katil yap
if (typeof window._katilPirateSpawned === "undefined") {
window._katilPirateSpawned = false;
}
if (!window._katilPirateSpawned && Math.random() < 0.2) {
// Katil korsan!
pirate.level = minLevel + 10;
pirate.health = (30 + (pirate.level - 1) * 10) * 10;
pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10;
pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10;
pirate.rpgName = "Katil";
pirate._isKatil = true;
window._katilPirateSpawned = true;
// Katil korsan saldırı oranını 3 kat arttır
pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3));
pirate._attackRange = Math.floor(600 * 0.9); // Katil korsan için %10 daha kısa menzil
// Add Enemy text to Katil Korsan
if (!pirate._enemyText) {
pirate._enemyText = new Text2("Enemy", {
size: 18,
fill: 0xff4444,
align: "center"
});
pirate._enemyText.anchor.set(0.5, 0);
pirate._enemyText.x = 0;
pirate._enemyText.y = -50;
pirate.addChild(pirate._enemyText);
}
// Katil korsan için özel bir renk veya efekt istenirse burada eklenebilir
} else {
pirate.level = minLevel + Math.floor(Math.random() * (maxLevel - minLevel + 1));
pirate.health = 30 + (pirate.level - 1) * 10;
pirate.damage = 5 + Math.floor((pirate.level - 1) * 1.5);
pirate.defense = 2 + Math.floor((pirate.level - 1) * 1.2);
}
// Add health bar to pirate
pirate._healthBar = new HealthBar();
pirate._healthBar.set({
maxValue: function maxValue() {
return pirate.health > 0 ? pirate.health : 0;
},
getValueFn: function getValueFn() {
return pirate.health > 0 ? pirate.health : 0;
},
getMaxFn: function getMaxFn() {
return 30 + waveNumber * 10;
},
width: 60,
height: 10
});
pirate._healthBar.y = -60;
pirate.addChild(pirate._healthBar);
pirates.push(pirate);
game.addChild(pirate);
}
function spawnAsteroid() {
var asteroid = new Asteroid();
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
asteroid.x = ship.x + (Math.random() - 0.5) * 2200;
asteroid.y = ship.y - 1600;
break;
case 1:
asteroid.x = ship.x + 1600;
asteroid.y = ship.y + (Math.random() - 0.5) * 2200;
break;
case 2:
asteroid.x = ship.x + (Math.random() - 0.5) * 2200;
asteroid.y = ship.y + 1600;
break;
case 3:
asteroid.x = ship.x - 1600;
asteroid.y = ship.y + (Math.random() - 0.5) * 2200;
break;
}
// Add health bar to asteroid
asteroid._healthBar = new HealthBar();
asteroid._healthBar.set({
maxValue: function maxValue() {
return asteroid.health;
},
getValueFn: function getValueFn() {
return asteroid.health;
},
getMaxFn: function getMaxFn() {
return 50;
},
width: 50,
height: 8
});
asteroid._healthBar.y = -50;
asteroid.addChild(asteroid._healthBar);
asteroids.push(asteroid);
game.addChild(asteroid);
}
// Spawn an upgrade station at a random edge and add it to the world
function spawnUpgradeStation() {
// Artık UpgradeStation yerine DerelictDebris (enkaz) spawn ediyoruz
var debris = new DerelictDebris();
// Spawn from edges of visible area
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
debris.x = ship.x + (Math.random() - 0.5) * 1800;
debris.y = ship.y - 1400;
break;
case 1:
debris.x = ship.x + 1400;
debris.y = ship.y + (Math.random() - 0.5) * 1800;
break;
case 2:
debris.x = ship.x + (Math.random() - 0.5) * 1800;
debris.y = ship.y + 1400;
break;
case 3:
debris.x = ship.x - 1400;
debris.y = ship.y + (Math.random() - 0.5) * 1800;
break;
}
upgradeStations.push(debris);
game.addChild(debris);
}
// No trade stations in this version
function createExplosion(x, y) {
// %95 küçült: 8 * 0.05 = 0.4, en az 1 partikül bırakıyoruz
for (var i = 0; i < 1; i++) {
var particle = LK.getAsset('explosion', {
anchorX: 0.5,
anchorY: 0.5,
x: x,
y: y,
scaleX: 0.22,
// %95 küçült (1 - 0.95 = 0.05, yani 0.05x, ama çok küçük olur, 0.22 ile 8 partikülün toplam alanı korunur)
scaleY: 0.22
});
particle.vx = (Math.random() - 0.5) * 2; // Hızı da azalt, daha küçük efekt için
particle.vy = (Math.random() - 0.5) * 2;
particle.life = 12; // Daha kısa ömür
explosions.push(particle);
game.addChild(particle);
}
}
// No black holes or wormholes in this version
function updateUI() {
coinText.setText('Coins: ' + playerCoins);
// Keep playerMetal/playerEnergy in sync with playerResources
playerMetal = playerResources[0];
playerEnergy = playerResources[1];
// Use sorted resource label order for display
if (window.resourceLabels && window.resourceLabelOrder && window.resourceLabels.length === window.resourceLabelOrder.length) {
for (var i = 0; i < window.resourceLabelOrder.length; i++) {
var resObj = window.resourceLabelOrder[i];
window.resourceLabels[i].setText(resObj.name + ': ' + (playerResources[resObj.origIdx] || 0));
}
}
// (No market price display)
var healthPercent = Math.round(ship.health / ship.maxHealth * 100);
healthText.setText('Health: %' + healthPercent);
var shieldPercent = Math.round(ship.shield / ship.maxShield * 100);
shieldText.setText('Shield: %' + shieldPercent);
// Restore wave and exp UI
if (typeof waveText !== "undefined") {
waveText.setText('Wave: ' + (waveNumber + 1));
}
if (typeof expText !== "undefined") {
expText.setText('Level: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel));
}
waveText.setText('Wave: ' + (waveNumber + 1));
expText.setText('Level: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel));
// Show player level/exp in GUI (top right, above repText)
if (!window.levelText) {
window.levelText = new Text2('Seviye: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel), {
size: unifiedFontSize,
fill: 0xffffff
});
window.levelText.anchor.set(1, 0);
window.levelText.x = -20;
window.levelText.y = 20; // moved up by one row (40px)
LK.gui.topRight.addChild(window.levelText);
}
window.levelText.setText('Seviye: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel));
// Update player level/exp in top right
if (window.levelText) {
window.levelText.setText('Seviye: ' + playerLevel + ' EXP: ' + playerEXP + '/' + expToNext(playerLevel));
}
// Update health text color
if (ship.health > 60) {
healthText.fill = 0x00ff00;
} else if (ship.health > 30) {
healthText.fill = 0xffff00;
} else {
healthText.fill = 0xff0000;
}
// Update shield text color
if (ship.shield > 30) {
shieldText.fill = 0x0088ff;
} else if (ship.shield > 10) {
shieldText.fill = 0xffff00;
} else {
shieldText.fill = 0xff0000;
}
storage.playerLevel = playerLevel;
storage.playerEXP = playerEXP;
}
function saveProgress() {
storage.coins = playerCoins;
var resourceKeys = ['metal', 'energy', 'crystal', 'gas', 'ice', 'uranium', 'silicon', 'carbon', 'plasma', 'antimatter'];
for (var i = 0; i < resourceKeys.length; i++) {
storage[resourceKeys[i]] = playerResources[i] || 0;
}
// Keep playerMetal/playerEnergy in sync with playerResources
playerMetal = playerResources[0];
playerEnergy = playerResources[1];
storage.questsCompleted = questsCompleted;
}
// Event handlers
game.down = function (x, y, obj) {
// --- BLOCK ALL TAPS IF ANY PANEL IS OPEN (fixes tap blocking bug) ---
// --- ALSO BLOCK ALL INPUT IF SHIP IS DEAD ---
if (window.stationDeliveryPanel && window.stationDeliveryPanelBG && window.stationDeliveryPanelBG.parent || window.resourceTradePanelState && window.resourceTradePanelState.open || window._resourceStockPanel && window._resourceStockPanel.bg && window._resourceStockPanel.bg.parent || window.shipUpgradePanel && window.shipUpgradePanel.parent || window.repairPanel && window.repairPanel.parent || ship && ship.health <= 0) {
// If a panel is open or ship is dead, only allow taps on that panel's close/confirm buttons (handled below), block all other taps
// (Panel-specific tap logic is handled further down, so just return here to block all other UI/joystick/game taps)
// Exception: let panel close/confirm buttons work (handled in their own blocks)
// If ship is dead, block all input until respawn label is gone
if (ship && ship.health <= 0) {
return;
}
// (Do not return here, let the rest of the handler run for panel close/confirm buttons)
} else {
// --- PAUSE BUTTON (top left) tap detection: always allow pause tap, never block it ---
if (x < 100 && y < 100) {
// This is the reserved area for the platform pause/menu button.
// Let the LK engine handle the pause/menu tap, do not block it.
// Do not return here, let the engine handle it.
}
// --- GLOBAL MARKET BUTTON HANDLER (exact image area) ---
if (window.globalMarketBtn && window.globalMarketBtn.parent) {
var btn = window.globalMarketBtn;
var bx = btn.x,
by = btn.y;
var bw = btn.width || 320,
bh = btn.height || 80;
// LK.gui.bottomRight is anchored at (1,1), so x,y negative from bottom right
// The area is anchored at (1,1), so its (x,y) is the bottom right corner of the area
// So the area covers (bx-bw, by-bh) to (bx, by)
if (x > 2048 + bx - bw && x < 2048 + bx && y > 2732 + by - bh && y < 2732 + by) {
window.openResourceTradePanel();
return;
}
}
// --- CREW PANEL BUTTON HANDLER (just below Achievements image) ---
if (window.newImageAboveStorage) {
// Place a transparent tap area over the newImageAboveStorage image if not already present
if (!window.crewPanelBtn) {
var btn = new Text2("", {
size: 1,
fill: 0xffffff,
align: "center"
});
btn.anchor.set(1, 0);
btn.x = window.newImageAboveStorage.x;
// Move the Crew panel button to exactly the same y as the image
btn.y = window.newImageAboveStorage.y;
// Grow the button downward by the image's height (so it covers 2x the image height, starting at image.y)
btn.width = window.newImageAboveStorage.width;
btn.height = window.newImageAboveStorage.height * 2;
btn.alpha = 0;
window.crewPanelBtn = btn;
LK.gui.topRight.addChild(btn);
}
var btn = window.crewPanelBtn;
var bx = btn.x,
by = btn.y;
var bw = btn.width,
bh = btn.height;
// LK.gui.topRight is anchored at (1,0), so x negative from right, y positive from top
if (x > 2048 + bx - bw && x < 2048 + bx && y > by && y < by + bh) {
window.openCrewPanel();
return;
}
}
// --- ACHIEVEMENTS PANEL BUTTON HANDLER (Achievements image tap) ---
if (window.achievementsPanelBtn && window.achievementsPanelBtn.parent) {
var btn = window.achievementsPanelBtn;
var bx = btn.x,
by = btn.y;
var bw = btn.width,
bh = btn.height;
// LK.gui.topRight is anchored at (1,0), so x negative from right, y positive from top
// So the area covers (2048+bx-bw, by) to (2048+bx, by+bh)
if (x > 2048 + bx - bw && x < 2048 + bx && y > by && y < by + bh) {
window.openAchievementsPanel(1);
return;
}
}
// --- CREW PANEL HANDLER ---
if (window.crewPanelBG) {
// Close btn
if (window.crewPanelCloseBtn) {
var bx = window.crewPanelCloseBtn.x,
by = window.crewPanelCloseBtn.y;
var bw = window.crewPanelCloseBtn.width || 300,
bh = window.crewPanelCloseBtn.height || 80;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
window.closeCrewPanel();
return;
}
}
// Friend satın al btns
if (window.crewFriendBtns) {
for (var i = 0; i < window.crewFriendBtns.length; i++) {
var btn = window.crewFriendBtns[i];
var bx = btn.x,
by = btn.y;
var bw = btn.width || 220,
bh = btn.height || 60;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
var idx = btn._friendIdx;
if (!friends[idx] && playerCoins >= 500) {
playerCoins -= 500;
// Create Friend instance (see class below)
var friend = new Friend();
friend.x = ship.x + 80 + 60 * idx;
friend.y = ship.y + 80;
friends[idx] = friend;
game.addChild(friend);
btn.setText("Purchased");
btn.fill = 0x888888;
if (window.crewFriendLabels && window.crewFriendLabels[idx]) {
window.crewFriendLabels[idx].setText("Friend #" + (idx + 1) + " (Purchased)");
window.crewFriendLabels[idx].fill = 0x00ff99;
}
updateUI && updateUI();
saveProgress && saveProgress();
} else if (friends[idx]) {
btn.setText("Already Purchased");
btn.fill = 0x888888;
} else if (playerCoins < 500) {
btn.setText("Insufficient coins!");
btn.fill = 0xff4444;
}
// Reset label after short delay
(function (b, orig, origFill) {
LK.setTimeout(function () {
b.setText(friends[idx] ? "Purchased" : "Buy (500 coin)");
b.fill = friends[idx] ? 0x888888 : 0x00ff99;
}, 900);
})(btn, "Buy (500 coin)", 0x00ff99);
return;
}
}
}
// Dron satın al btn
if (window.crewPanelDronBtn) {
var btn = window.crewPanelDronBtn;
var bx = btn.x,
by = btn.y;
var bw = btn.width || 220,
bh = btn.height || 60;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
if (!dron && playerCoins >= 1200) {
playerCoins -= 1200;
dron = new Dron();
dron.x = ship.x - 100;
dron.y = ship.y + 80;
game.addChild(dron);
btn.setText("Purchased");
btn.fill = 0x888888;
if (window.crewPanelDronLabel) {
window.crewPanelDronLabel.setText("Dron: Purchased");
window.crewPanelDronLabel.fill = 0x00ff99;
}
updateUI && updateUI();
saveProgress && saveProgress();
} else if (dron) {
btn.setText("Already Purchased");
btn.fill = 0x888888;
} else if (playerCoins < 1200) {
btn.setText("Insufficient coins!");
btn.fill = 0xff4444;
}
(function (b, orig, origFill) {
LK.setTimeout(function () {
b.setText(dron ? "Purchased" : "Buy (1200 coin)");
b.fill = dron ? 0x888888 : 0x00ff99;
}, 900);
})(btn, "Buy (1200 coin)", 0x00ff99);
return;
}
}
// Mechanic satın al btn
if (window.crewPanelMechanicBtn) {
var btn = window.crewPanelMechanicBtn;
var bx = btn.x,
by = btn.y;
var bw = btn.width || 220,
bh = btn.height || 60;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
if (!window.mechanic && playerCoins >= 800) {
playerCoins -= 800;
window.mechanic = new Mechanic();
window.mechanic.x = ship.x - 120;
window.mechanic.y = ship.y - 80;
game.addChild(window.mechanic);
btn.setText("Purchased");
btn.fill = 0x888888;
if (window.crewPanelMechanicLabel) {
window.crewPanelMechanicLabel.setText("Mechanic: Purchased");
window.crewPanelMechanicLabel.fill = 0x00ff99;
}
updateUI && updateUI();
saveProgress && saveProgress();
} else if (window.mechanic) {
btn.setText("Already Purchased");
btn.fill = 0x888888;
} else if (playerCoins < 800) {
btn.setText("Insufficient coins!");
btn.fill = 0xff4444;
}
(function (b, orig, origFill) {
LK.setTimeout(function () {
b.setText(window.mechanic ? "Purchased" : "Buy (800 coin)");
b.fill = window.mechanic ? 0x888888 : 0x00ff99;
}, 900);
})(btn, "Buy (800 coin)", 0x00ff99);
return;
}
}
// Block all other taps when panel is open
return;
}
// --- UPGRADE PANEL BUTTON HANDLER (upg image tap) ---
if (window.upgPanelBtn && window.upgPanelBtn.parent) {
var btn = window.upgPanelBtn;
var bx = btn.x,
by = btn.y;
var bw = btn.width,
bh = btn.height;
// LK.gui.topRight is anchored at (1,0), so x negative from right, y positive from top
// So the area covers (2048+bx-bw, by) to (2048+bx, by+bh)
if (x > 2048 + bx - bw && x < 2048 + bx && y > by && y < by + bh) {
window.openUpgradesPanel();
return;
}
}
}
// --- RESOURCE TRADE PANEL INDEPENDENT BUTTON HANDLER ---
// If the independent button exists, check tap
if (window.resourceTradePanelIndependentBtn && window.resourceTradePanelIndependentBtn.parent) {
var btn = window.resourceTradePanelIndependentBtn;
var bx = btn.x,
by = btn.y;
var bw = btn.width,
bh = btn.height;
// LK.gui.bottomRight is anchored at (1,1), so x,y negative from bottom right
// The area is anchored at (1,1), so its (x,y) is the bottom right corner of the area
// So the area covers (bx-bw, by-bh) to (bx, by)
if (x > 2048 + bx - bw && x < 2048 + bx && y > 2732 + by - bh && y < 2732 + by) {
// Open 'Hammadde Stoğu' panel
if (typeof window.openResourceStockPanel === "function") {
window.openResourceStockPanel();
} else {
// Fallback: show a simple panel with player's resource stock
if (window._resourceStockPanel) {
return;
}
var panelW = 900,
panelH = 900;
var px = 2048 / 2,
py = 2732 / 2;
// Increase font size by 2 for all panel texts/buttons
var panelFontSize = unifiedFontSize + 4;
var labelFontSize = panelFontSize;
var closeBtnFontSize = panelFontSize;
var titleFontSize = panelFontSize;
// Resize panel background to fit enlarged text/buttons
var rowH = 70 + 8;
var panelW = 900 + 120;
var panelH = 900 + 2 * rowH;
var bg = LK.getAsset('resource', {
anchorX: 0.5,
anchorY: 0.5,
x: px,
y: py,
scaleX: panelW / 40,
scaleY: panelH / 40,
tint: 0x223355,
alpha: 0.97
});
var title = new Text2("Hammadde Stoğu", {
size: titleFontSize,
fill: 0x00ffcc,
align: "center"
});
title.anchor.set(0.5, 0);
title.x = px;
title.y = py - panelH / 2 + 40;
// List resources
var labels = [];
var startY = title.y + 80;
for (var i = 0; i < window.resourceLabelOrder.length; i++) {
var resObj = window.resourceLabelOrder[i];
var idx = resObj.origIdx;
var label = new Text2(resObj.name + ": " + (playerResources[idx] || 0), {
size: labelFontSize,
fill: 0xffffff,
align: "left"
});
label.anchor.set(0, 0.5);
label.x = px - panelW / 2 + 60;
label.y = startY + i * rowH;
labels.push(label);
}
// Close button
var closeBtn = new Text2("Kapat", {
size: Math.round((unifiedFontSize + 4) * 1.3),
// 2 font larger than Coins, then 30% larger
fill: 0xff4444,
align: "center"
});
closeBtn.anchor.set(0.5, 0.5);
closeBtn.x = px;
closeBtn.y = title.y - 110; // above the title
// Add to game
game.addChild(bg);
game.addChild(title);
for (var i = 0; i < labels.length; i++) {
game.addChild(labels[i]);
}
game.addChild(closeBtn);
// Store refs for tap logic/cleanup
window._resourceStockPanel = {
bg: bg,
title: title,
labels: labels,
closeBtn: closeBtn
};
// Add close logic
closeBtn._isResourceStockCloseBtn = true;
}
return;
}
}
// --- RESOURCE TRADE PANEL HANDLER ---
if (window._resourceStockPanel && window._resourceStockPanel.closeBtn) {
var btn = window._resourceStockPanel.closeBtn;
var bx = btn.x,
by = btn.y;
var bw = btn.width || 300,
bh = btn.height || 80;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
// Remove all panel elements
if (window._resourceStockPanel.bg && window._resourceStockPanel.bg.parent) {
window._resourceStockPanel.bg.parent.removeChild(window._resourceStockPanel.bg);
}
if (window._resourceStockPanel.title && window._resourceStockPanel.title.parent) {
window._resourceStockPanel.title.parent.removeChild(window._resourceStockPanel.title);
}
if (window._resourceStockPanel.labels) {
for (var i = 0; i < window._resourceStockPanel.labels.length; i++) {
if (window._resourceStockPanel.labels[i] && window._resourceStockPanel.labels[i].parent) {
window._resourceStockPanel.labels[i].parent.removeChild(window._resourceStockPanel.labels[i]);
}
}
}
if (window._resourceStockPanel.closeBtn && window._resourceStockPanel.closeBtn.parent) {
window._resourceStockPanel.closeBtn.parent.removeChild(window._resourceStockPanel.closeBtn);
}
window._resourceStockPanel = null;
return;
}
}
if (window.resourceTradePanelState && window.resourceTradePanelState.open) {
// Close btn
if (window.resourceTradePanelCloseBtn) {
var bx = window.resourceTradePanelCloseBtn.x,
by = window.resourceTradePanelCloseBtn.y;
var bw = window.resourceTradePanelCloseBtn.width || 300,
bh = window.resourceTradePanelCloseBtn.height || 80;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
window.closeResourceTradePanel();
return;
}
}
// Sat btns
if (window.resourceTradeSellBtns) {
for (var i = 0; i < window.resourceTradeSellBtns.length; i++) {
var btn = window.resourceTradeSellBtns[i];
var bx = btn.x,
by = btn.y;
var bw = btn.width || 120,
bh = btn.height || 60;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
var idx = btn._resourceIdx;
var amount = playerResources[idx] || 0;
var value = window.resourceTradeValues ? window.resourceTradeValues[idx] : 0;
if (amount > 0 && value > 0) {
playerCoins += value * amount;
playerResources[idx] = 0;
// --- BAŞARIM: Takas yapınca ---
checkTradeAchievements();
btn.setText("Satıldı!");
btn.fill = 0x00ffcc;
if (window.updateResourceTradePanel) {
window.updateResourceTradePanel();
}
updateUI();
saveProgress();
} else {
btn.setText("Yok!");
btn.fill = 0xff4444;
}
// Reset label after short delay
(function (b, orig, origFill) {
LK.setTimeout(function () {
b.setText("Sat");
b.fill = 0x00ff99;
}, 700);
})(btn, "Sat", 0x00ff99);
return;
}
}
}
// Tümünü Sat btns
if (window.resourceTradeSellAllBtns) {
for (var i = 0; i < window.resourceTradeSellAllBtns.length; i++) {
var btn = window.resourceTradeSellAllBtns[i];
var bx = btn.x,
by = btn.y;
var bw = btn.width || 180,
bh = btn.height || 60;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
var idx = btn._resourceIdx;
var amount = playerResources[idx] || 0;
var value = window.resourceTradeValues ? window.resourceTradeValues[idx] : 0;
if (amount > 0 && value > 0) {
playerCoins += value * amount;
playerResources[idx] = 0;
btn.setText("Hepsi Satıldı!");
btn.fill = 0x00ffcc;
if (window.updateResourceTradePanel) {
window.updateResourceTradePanel();
}
updateUI();
saveProgress();
} else {
btn.setText("Yok!");
btn.fill = 0xff4444;
}
(function (b, orig, origFill) {
LK.setTimeout(function () {
b.setText("Tümünü Sat");
b.fill = 0x00cc99;
}, 700);
})(btn, "Tümünü Sat", 0x00cc99);
return;
}
}
}
// Al btns
if (window.resourceTradeBuyBtns) {
for (var i = 0; i < window.resourceTradeBuyBtns.length; i++) {
var btn = window.resourceTradeBuyBtns[i];
var bx = btn.x,
by = btn.y;
var bw = btn.width || 120,
bh = btn.height || 60;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
var idx = btn._resourceIdx;
var value = window.resourceTradeValues ? window.resourceTradeValues[idx] : 0;
// Satın alma miktarı: 1 (veya istenirse artırılabilir)
var buyAmount = 1;
var totalCost = value * buyAmount;
if (playerCoins >= totalCost && value > 0) {
playerCoins -= totalCost;
playerResources[idx] = (playerResources[idx] || 0) + buyAmount;
btn.setText("Alındı!");
btn.fill = 0x00ffcc;
if (window.updateResourceTradePanel) {
window.updateResourceTradePanel();
}
updateUI();
saveProgress();
} else {
btn.setText("Yetersiz coin!");
btn.fill = 0xff4444;
}
(function (b, orig, origFill) {
LK.setTimeout(function () {
b.setText("Al");
b.fill = 0x0099ff;
}, 700);
})(btn, "Al", 0x0099ff);
return;
}
}
}
// Block all other taps when panel is open
return;
}
// --- UZAY İSTASYONU TESLİMAT BUTTON HANDLER ---
// Handle tap on stationDeliveryBtnArea (transparent area, exactly image size)
if (window.stationDeliveryBtnArea && window.stationDeliveryBtnArea.parent) {
var btn = window.stationDeliveryBtnArea;
var bx = btn.x,
by = btn.y;
var bw = btn.width,
bh = btn.height;
// LK.gui.bottomRight is anchored at (1,1), so x,y negative from bottom right
// The area is anchored at (1,1), so its (x,y) is the bottom right corner of the area
// So the area covers (bx-bw, by-bh) to (bx, by)
if (x > 2048 + bx - bw && x < 2048 + bx && y > 2732 + by - bh && y < 2732 + by) {
window.openStationDeliveryPanel();
return;
}
}
// LK.gui.bottomRight is anchored at (1,1), so x,y negative from bottom right
if (window.stationDeliveryBtn && window.stationDeliveryBtn.parent) {
var btn = window.stationDeliveryBtn;
var bx = btn.x,
by = btn.y;
// Button is about 180x80 px, so check if x is within 200px of right edge and y within 300-480px of bottom
if (x > 2048 - 200 && y > 2732 - 300 && y < 2732 - 100) {
window.openStationDeliveryPanel();
return;
}
}
// --- UZAY İSTASYONU TESLİMAT PANEL HANDLER ---
if (window.stationDeliveryPanel) {
// Close btn
if (window.stationDeliveryPanelCloseBtn) {
var bx = window.stationDeliveryPanelCloseBtn.x,
by = window.stationDeliveryPanelCloseBtn.y;
var bw = window.stationDeliveryPanelCloseBtn.width || 300,
bh = window.stationDeliveryPanelCloseBtn.height || 80;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
window.closeStationDeliveryPanel();
return;
}
}
// Teslim Et btns
if (window.stationDeliveryDeliverBtns) {
for (var i = 0; i < window.stationDeliveryDeliverBtns.length; i++) {
var btn = window.stationDeliveryDeliverBtns[i];
var bx = btn.x,
by = btn.y;
var bw = btn.width || 120,
bh = btn.height || 60;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
var idx = btn._stationResIdx;
var req = window.stationDeliveryState.requests[idx];
var delivered = window.stationDeliveryState.delivered[idx];
var canDeliver = Math.min(playerResources[idx], req - delivered);
if (canDeliver > 0) {
playerResources[idx] -= canDeliver;
window.stationDeliveryState.delivered[idx] += canDeliver;
btn.setText("Teslim Edildi!");
btn.fill = 0x00ffcc;
// Update label
if (window.stationDeliveryLabels && window.stationDeliveryLabels[i]) {
window.stationDeliveryLabels[i].setText(window.resourceLabelOrder[i].name + ": " + window.stationDeliveryState.delivered[idx] + " / " + req);
}
// Update stock label
if (window.stationDeliveryStockLabels && window.stationDeliveryStockLabels[i]) {
var stockAmount = typeof playerResources[idx] !== "undefined" ? playerResources[idx] : 0;
window.stationDeliveryStockLabels[i].setText("Stok: " + stockAmount);
}
updateUI();
saveProgress();
} else {
btn.setText("Yok!");
btn.fill = 0xff4444;
}
// Reset label after short delay
(function (b, orig, origFill) {
LK.setTimeout(function () {
b.setText(orig);
b.fill = origFill;
}, 700);
})(btn, "Teslim Et", 0x00ff99);
return;
}
}
}
// Teslimatı Tamamla (ödül al) butonu
if (window.stationDeliveryPanelClaimBtn) {
var btn = window.stationDeliveryPanelClaimBtn;
var bx = btn.x,
by = btn.y;
var bw = btn.width || 400,
bh = btn.height || 80;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
// Check if all delivered
var allDone = true;
for (var i = 0; i < 10; i++) {
if (window.stationDeliveryState.delivered[i] < window.stationDeliveryState.requests[i]) {
allDone = false;
break;
}
}
if (allDone && !window.stationDeliveryState.rewardClaimed) {
// Calculate total value (normal market price * delivered) * (1+bonus%)
var totalValue = 0;
for (var i = 0; i < 10; i++) {
var price = 1; // Market removed, use static price
totalValue += price * window.stationDeliveryState.requests[i];
}
var bonus = window.stationDeliveryState.bonus;
var reward = Math.round(totalValue * (1 + bonus / 100));
playerCoins += reward;
window.stationDeliveryState.rewardClaimed = true;
btn.setText("Ödül Alındı! +" + reward + " coin");
btn.fill = 0x00ffcc;
// Show floating label
var label = new Text2("İstasyon Teslimatı Tamamlandı!\n+" + reward + " coin", {
size: 48,
fill: 0xffcc00,
align: "center"
});
label.anchor.set(0.5, 0.5);
label.x = 1024;
label.y = 600;
game.addChild(label);
LK.setTimeout(function () {
if (label.parent) {
label.parent.removeChild(label);
}
}, 1800);
updateUI();
saveProgress();
} else if (window.stationDeliveryState.rewardClaimed) {
btn.setText("Zaten Alındı!");
btn.fill = 0x888888;
} else {
btn.setText("Eksik Teslimat!");
btn.fill = 0xff4444;
}
// Reset label after short delay
(function (b, orig, origFill) {
LK.setTimeout(function () {
b.setText("Tümünü Teslim Et & Ödülü Al");
b.fill = 0xffcc00;
}, 1200);
})(btn, "Tümünü Teslim Et & Ödülü Al", 0xffcc00);
return;
}
}
// Block all other taps when panel is open
return;
}
// --- RESET BUTTON HANDLER ---
if (game._resetBtn && game._resetBtn.parent) {
// Convert GUI coordinates for bottomRight
// LK.gui.bottomRight is anchored at (1,1) so x,y are negative from bottom right
var btn = game._resetBtn;
var bx = btn.x,
by = btn.y;
// Button is about 180x80 px, but let's use a safe area
// Since anchor is (1,1), bottom right, and x/y are negative from bottom right
// So, for a tap, check if x is within 200px of right edge and y within 100px of bottom
// LK.gui.bottomRight: x=0,y=0 is bottom right, so x,y negative
if (x > 2048 - 200 && y > 2732 - 100) {
// Clear all persistent storage, including achievements and all custom progress
storage.coins = 200; // Başlangıçta 200 coin ile başla
storage.metal = 0;
storage.energy = 0;
storage.crystal = 0;
storage.gas = 0;
storage.ice = 0;
storage.uranium = 0;
storage.silicon = 0;
storage.carbon = 0;
storage.plasma = 0;
storage.antimatter = 0;
storage.questsCompleted = 0;
storage.playerLevel = 1;
storage.playerEXP = 0;
// Clear all achievements
storage.achievements = [];
// Clear all custom progress keys
storage.rangeUpgradeLevel = 0;
storage.fireRateUpgradeLevel = 0;
// Remove any other custom keys that may have been set
// (defensive: remove all keys except built-in ones)
for (var k in storage) {
if (storage.hasOwnProperty(k) && ["coins", "metal", "energy", "crystal", "gas", "ice", "uranium", "silicon", "carbon", "plasma", "antimatter", "questsCompleted", "playerLevel", "playerEXP", "achievements", "rangeUpgradeLevel", "fireRateUpgradeLevel"].indexOf(k) === -1) {
try {
storage[k] = 0;
} catch (e) {}
}
}
// Show a quick feedback
btn.setText("Reset!");
btn.fill = 0x00ff99;
// Reload game (reset all progress)
LK.showGameOver();
return;
}
}
// --- NPC ile savaş butonu handler ---
if (window.npcFightBtn) {
var bx = window.npcFightBtn.x,
by = window.npcFightBtn.y;
var bw = window.npcFightBtn.width || 400,
bh = window.npcFightBtn.height || 100;
// Accept generous tap area
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
window.npcFightMode = !window.npcFightMode;
if (window.npcFightMode) {
window.npcFightBtn.setText("NPC Savaş: AÇIK");
window.npcFightBtn.fill = 0xff0000;
} else {
window.npcFightBtn.setText("NPC ile Savaş");
window.npcFightBtn.fill = 0xff4444;
}
return;
}
}
if (window.repairPanel && window.repairBtn && window.repairBtn.parent) {
// Check tap on repair button
var btn = window.repairBtn;
var bx = btn.x,
by = btn.y;
if (x > bx - 90 && x < bx + 90 && y > by - 40 && y < by + 40) {
// Try to repair
var costCoins = btn._repairCostCoins,
costMetal = btn._repairCostMetal,
costEnergy = btn._repairCostEnergy;
if (playerCoins >= costCoins && playerMetal >= costMetal && playerEnergy >= costEnergy) {
playerCoins -= costCoins;
playerMetal -= costMetal;
playerEnergy -= costEnergy;
ship.health = ship.maxHealth;
// --- BAŞARIM: Tamir ---
checkRepairAchievements();
updateUI();
saveProgress();
// Show feedback
btn.setText("Tamir Edildi!");
btn.fill = 0x00ffcc;
// Remove panel after short delay
var expire = LK.ticks + 40;
var origUpdate = game.update;
game.update = function () {
if (LK.ticks > expire) {
if (window.repairPanel && window.repairPanel.parent) {
window.repairPanel.parent.removeChild(window.repairPanel);
}
if (window.repairText && window.repairText.parent) {
window.repairText.parent.removeChild(window.repairText);
}
if (window.repairBtn && window.repairBtn.parent) {
window.repairBtn.parent.removeChild(window.repairBtn);
}
if (window.cancelRepairBtn && window.cancelRepairBtn.parent) {
window.cancelRepairBtn.parent.removeChild(window.cancelRepairBtn);
}
window.repairPanel = null;
window.repairText = null;
window.repairBtn = null;
window.cancelRepairBtn = null;
game.update = origUpdate;
}
if (origUpdate) {
origUpdate.apply(game, arguments);
}
};
} else {
// Not enough resources
btn.setText("Yetersiz kaynak!");
btn.fill = 0xff4444;
}
return;
}
// Check tap on cancel button
var cbtn = window.cancelRepairBtn;
if (cbtn) {
var cbx = cbtn.x,
cby = cbtn.y;
if (x > cbx - 90 && x < cbx + 90 && y > cby - 40 && y < cby + 40) {
// Remove all repair panel elements, allow closing even if not repaired
if (window.repairPanel && window.repairPanel.parent) {
window.repairPanel.parent.removeChild(window.repairPanel);
}
if (window.repairText && window.repairText.parent) {
window.repairText.parent.removeChild(window.repairText);
}
if (window.repairBtn && window.repairBtn.parent) {
window.repairBtn.parent.removeChild(window.repairBtn);
}
if (window.cancelRepairBtn && window.cancelRepairBtn.parent) {
window.cancelRepairBtn.parent.removeChild(window.cancelRepairBtn);
}
window.repairPanel = null;
window.repairText = null;
window.repairBtn = null;
window.cancelRepairBtn = null;
window._showRepairPanel = false;
return;
}
}
}
// --- Health bar tap to show repair panel ---
if (healthText && healthText.parent && healthText._isHealthBar && ship.health < ship.maxHealth && ship.health > 0) {
// Get healthText bounds in screen coordinates
// healthText is anchored at (0.5, 0), at top center
// Let's estimate its bounds
var hx = healthText.x,
hy = healthText.y;
var hw = healthText.width || 300;
var hh = healthText.height || 60;
// Accept generous tap area
if (x > hx - hw / 2 && x < hx + hw / 2 && y > hy && y < hy + hh) {
window._showRepairPanel = true;
return;
}
}
// --- SHIP TAP: Show upgrade/can-buy-health panel ---
// Oyuncu gemisi resmi üzerindeki buton GİZLENDİ. Artık bu panel gemi resmine tıklanarak açılamaz.
// (Bu alanı bilerek boş bırakıyoruz, panel açılmayacak.)
// --- Only allow joystick drag if ship is alive ---
if (ship && ship.health > 0 && Math.sqrt(Math.pow(x - joystickBase.x, 2) + Math.pow(y - joystickBase.y, 2)) < 130) {
isDragging = true;
joystickHandle.x = x;
joystickHandle.y = y;
return; // Prevent other interactions when dragging joystick
}
// --- If any panel is open, block all other game/ship input (except panel close/confirm buttons) ---
if (window.stationDeliveryPanel && window.stationDeliveryPanelBG && window.stationDeliveryPanelBG.parent || window.resourceTradePanelState && window.resourceTradePanelState.open || window._resourceStockPanel && window._resourceStockPanel.bg && window._resourceStockPanel.bg.parent || window.shipUpgradePanel && window.shipUpgradePanel.parent || window.repairPanel && window.repairPanel.parent) {
// Only allow panel close/confirm buttons (handled below), block all other game/ship input
// (Do not return here, let the rest of the handler run for panel close/confirm buttons)
} else {
// --- SHIP UPGRADE PANEL BUTTONS ---
if (window.shipUpgradePanel) {
// (existing ship upgrade panel logic unchanged)
// ... (no change here)
}
// --- ACHIEVEMENTS PANEL BUTTONS ---
if (window.achievementsPanelBG) {
// Close button
var cb = window.achievementsPanelCloseBtn;
if (cb) {
var bx = cb.x,
by = cb.y;
var bw = cb.width || 300,
bh = cb.height || 80;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
window.closeAchievementsPanel();
return;
}
}
// Page number buttons
var pageBtns = window.achievementsPanelPageBtns;
if (pageBtns) {
for (var i = 0; i < pageBtns.length; i++) {
var btn = pageBtns[i];
var bx = btn.x,
by = btn.y;
var bw = btn.width || 90,
bh = btn.height || 70;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
if (window._achievementsPanelPage !== btn._achPageBtn) {
window.closeAchievementsPanel();
window.openAchievementsPanel(btn._achPageBtn);
}
return;
}
}
}
// Block all other taps when panel is open
return;
}
// --- UPGRADE PANEL BUTTONS ---
if (window.upgradesPanel) {
// Upgrade buttons
var btns = window.upgradesPanelBtns || [];
for (var i = 0; i < btns.length; i++) {
var btn = btns[i];
if (!btn) {
continue;
}
var bx = btn.x,
by = btn.y;
var bw = btn.width || 400,
bh = btn.height || 80;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
// Try to buy upgrade
var upg = btn._upgradeType;
var cost = btn._upgradeCost;
if (upg === "range" && playerCoins >= cost.coins) {
// Increase attack range by 5%
if (typeof window._rangeUpgradeLevel === "undefined") {
window._rangeUpgradeLevel = 0;
}
window._rangeUpgradeLevel++;
// Store in storage for persistence
storage.rangeUpgradeLevel = window._rangeUpgradeLevel;
playerCoins -= cost.coins;
LK.effects.flashObject(ship, 0x99ff99, 400);
btn.setText("Alındı!");
btn.fill = 0x00ffcc;
// --- BAŞARIM: Upgrade satın alma ---
checkUpgradeAchievements();
updateUI();
saveProgress();
} else if (upg === "heal5" && playerCoins >= cost.coins) {
// Heal 5% of max health, but not above max
var healAmount = Math.round(ship.maxHealth * 0.05);
ship.health = Math.min(ship.health + healAmount, ship.maxHealth);
playerCoins -= cost.coins;
LK.effects.flashObject(ship, 0xff6699, 400);
btn.setText("Yenilendi!");
btn.fill = 0x00ffcc;
// --- BAŞARIM: Tamir ---
checkRepairAchievements();
updateUI();
saveProgress();
} else if (upg === "maxHealth" && playerCoins >= cost.coins && playerResources[0] >= cost.metal) {
ship.maxHealth += 20;
ship.health = ship.maxHealth;
playerCoins -= cost.coins;
playerResources[0] -= cost.metal;
LK.effects.flashObject(ship, 0x00ff99, 400);
btn.setText("Alındı!");
btn.fill = 0x00ffcc;
// --- BAŞARIM: Upgrade satın alma ---
checkUpgradeAchievements();
updateUI();
saveProgress();
} else if (upg === "maxShield" && playerCoins >= cost.coins && playerResources[0] >= cost.metal) {
ship.maxShield += 10;
ship.shield = ship.maxShield;
playerCoins -= cost.coins;
playerResources[0] -= cost.metal;
LK.effects.flashObject(ship, 0x00ccff, 400);
btn.setText("Alındı!");
btn.fill = 0x00ffcc;
// --- BAŞARIM: Upgrade satın alma ---
checkUpgradeAchievements();
updateUI();
saveProgress();
} else if (upg === "damage" && playerCoins >= cost.coins && playerResources[1] >= cost.energy) {
ship.damage += 5;
playerCoins -= cost.coins;
playerResources[1] -= cost.energy;
LK.effects.flashObject(ship, 0xffcc00, 400);
btn.setText("Alındı!");
btn.fill = 0x00ffcc;
// --- BAŞARIM: Upgrade satın alma ---
checkUpgradeAchievements();
updateUI();
saveProgress();
} else if (upg === "speed" && playerCoins >= cost.coins) {
ship.speed = Math.round(ship.speed * 1.1 * 100) / 100;
playerCoins -= cost.coins;
LK.effects.flashObject(ship, 0x00ffff, 400);
btn.setText("Alındı!");
btn.fill = 0x00ffcc;
// --- BAŞARIM: Upgrade satın alma ---
checkUpgradeAchievements();
updateUI();
saveProgress();
} else if (upg === "fireRate") {
if (typeof window._fireRateUpgradeLevel === "undefined") {
window._fireRateUpgradeLevel = 0;
}
if (window._fireRateUpgradeLevel >= 5) {
btn.setText("Maks. seviye!");
btn.fill = 0xff4444;
LK.setTimeout(function () {
btn.setText("Atış Hızı +10%\n(1200 coin)");
btn.fill = 0xff99ff;
}, 900);
} else if (playerCoins >= cost.coins) {
ship.fireRate = Math.max(2, Math.round(ship.fireRate * 0.9));
playerCoins -= cost.coins;
window._fireRateUpgradeLevel++;
storage.fireRateUpgradeLevel = window._fireRateUpgradeLevel;
LK.effects.flashObject(ship, 0xff99ff, 400);
btn.setText("Alındı!");
btn.fill = 0x00ffcc;
updateUI();
saveProgress();
} else {
btn.setText("Yetersiz kaynak!");
btn.fill = 0xff4444;
LK.setTimeout(function () {
btn.setText("Atış Hızı +10%\n(1200 coin)");
btn.fill = 0xff99ff;
}, 900);
}
} else {
var origText = "";
var origFill = btn.fill;
if (upg === "maxHealth") {
origText = "Max Can +20\n(600 coin, 60 metal)";
origFill = 0x00ff99;
} else if (upg === "maxShield") {
origText = "Max Kalkan +10\n(720 coin, 72 metal)";
origFill = 0x00ccff;
} else if (upg === "damage") {
origText = "Hasar +5\n(900 coin, 90 enerji)";
origFill = 0xffcc00;
} else if (upg === "speed") {
origText = "Hız +10%\n(1000 coin)";
origFill = 0x00ffff;
} else if (upg === "fireRate") {
origText = "Atış Hızı +10%\n(1200 coin)";
origFill = 0xff99ff;
} else {
origText = btn.text;
origFill = btn.fill;
}
btn.setText("Yetersiz kaynak!");
btn.fill = 0xff4444;
// Reset label after short delay
LK.setTimeout(function () {
btn.setText(origText);
btn.fill = origFill;
}, 900);
}
return;
}
}
// Close button
var cb = window.upgradesPanelCloseBtn;
if (cb) {
var bx = cb.x,
by = cb.y;
var bw = cb.width || 300,
bh = cb.height || 80;
if (x > bx - bw / 2 && x < bx + bw / 2 && y > by - bh / 2 && y < by + bh / 2) {
window.closeUpgradesPanel();
return;
}
}
}
// --- Handle Accept/Reject quest button taps ---
if (game._questDecisionAcceptBtn && game._questDecisionAcceptBtn.parent) {
var btn = game._questDecisionAcceptBtn;
var bx = btn.x,
by = btn.y;
// Button is about 180x80 px
if (x > bx - 90 && x < bx + 90 && y > by - 40 && y < by + 40) {
// Accept quest
var npc = btn._questNPC;
if (npc) {
// Defend quest özel: yardım kabul edildi
if (npc.questType === 'defend') {
npc._defendHelpAccepted = true;
// Show floating label for accepted
var acceptLabel = new Text2("Yardım kabul edildi!", {
size: 36,
fill: 0x00ff99,
align: "center"
});
acceptLabel.anchor.set(0.5, 0.5);
acceptLabel.x = npc.x;
acceptLabel.y = npc.y + 110;
game.addChild(acceptLabel);
npc._npcInteractionLabel = acceptLabel;
npc._npcInteractionLabelExpire = LK.ticks + 120;
npc.pauseUntil = LK.ticks + 120;
npc._npcText && npc._npcText.parent && npc._npcText.parent.removeChild(npc._npcText);
npc._npcText = new Text2(npc.rpgName + ": Teşekkürler! Beni korsanlardan koru!", {
size: 32,
fill: 0x00ffcc,
align: "center"
});
npc._npcText.anchor.set(0.5, 1);
npc._npcText.x = npc.x;
npc._npcText.y = npc.y - 70;
game.addChild(npc._npcText);
} else {
// Mark quest as accepted (progress 1 means started)
npc.questProgress = 1;
// Show NPC response
var acceptLabel = new Text2("Görev kabul edildi!", {
size: 36,
fill: 0x00ff99,
align: "center"
});
acceptLabel.anchor.set(0.5, 0.5);
acceptLabel.x = npc.x;
acceptLabel.y = npc.y + 110;
game.addChild(acceptLabel);
npc._npcInteractionLabel = acceptLabel;
npc._npcInteractionLabelExpire = LK.ticks + 120;
npc.pauseUntil = LK.ticks + 120;
npc._npcText && npc._npcText.parent && npc._npcText.parent.removeChild(npc._npcText);
npc._npcText = new Text2(npc.rpgName + ": Harika! Görev başladı.", {
size: 32,
fill: 0x00ffcc,
align: "center"
});
npc._npcText.anchor.set(0.5, 1);
npc._npcText.x = npc.x;
npc._npcText.y = npc.y - 70;
game.addChild(npc._npcText);
}
// Remove quest panel
if (npc._questDecisionPanel && npc._questDecisionPanel.parent) {
npc._questDecisionPanel.parent.removeChild(npc._questDecisionPanel);
}
if (npc._questDecisionText && npc._questDecisionText.parent) {
npc._questDecisionText.parent.removeChild(npc._questDecisionText);
}
if (npc._questDecisionAcceptBtn && npc._questDecisionAcceptBtn.parent) {
npc._questDecisionAcceptBtn.parent.removeChild(npc._questDecisionAcceptBtn);
}
if (npc._questDecisionRejectBtn && npc._questDecisionRejectBtn.parent) {
npc._questDecisionRejectBtn.parent.removeChild(npc._questDecisionRejectBtn);
}
npc._questDecisionPanel = null;
npc._questDecisionText = null;
npc._questDecisionAcceptBtn = null;
npc._questDecisionRejectBtn = null;
game._questDecisionPanel = null;
game._questDecisionAcceptBtn = null;
game._questDecisionRejectBtn = null;
game._questDecisionText = null;
game._questDecisionNPC = null;
}
return;
}
}
if (game._questDecisionRejectBtn && game._questDecisionRejectBtn.parent) {
var btn = game._questDecisionRejectBtn;
var bx = btn.x,
by = btn.y;
if (x > bx - 90 && x < bx + 90 && y > by - 40 && y < by + 40) {
// Reject quest
var npc = btn._questNPC;
if (npc) {
if (npc.questType === 'defend') {
npc._defendHelpAccepted = false;
// Show floating label for rejected
var rejectLabel = new Text2("Yardım edilmedi.", {
size: 36,
fill: 0xff4444,
align: "center"
});
rejectLabel.anchor.set(0.5, 0.5);
rejectLabel.x = npc.x;
rejectLabel.y = npc.y + 110;
game.addChild(rejectLabel);
npc._npcInteractionLabel = rejectLabel;
npc._npcInteractionLabelExpire = LK.ticks + 120;
npc.pauseUntil = LK.ticks + 120;
npc._npcText && npc._npcText.parent && npc._npcText.parent.removeChild(npc._npcText);
npc._npcText = new Text2(npc.rpgName + ": Anlaşıldı, kendi başıma savaşacağım!", {
size: 32,
fill: 0x00ffcc,
align: "center"
});
npc._npcText.anchor.set(0.5, 1);
npc._npcText.x = npc.x;
npc._npcText.y = npc.y - 70;
game.addChild(npc._npcText);
} else {
// Mark quest as inactive
npc.questActive = false;
// Show floating label for rejected
var rejectLabel = new Text2("Görev reddedildi.", {
size: 36,
fill: 0xff4444,
align: "center"
});
rejectLabel.anchor.set(0.5, 0.5);
rejectLabel.x = npc.x;
rejectLabel.y = npc.y + 110;
game.addChild(rejectLabel);
npc._npcInteractionLabel = rejectLabel;
npc._npcInteractionLabelExpire = LK.ticks + 120;
npc.pauseUntil = LK.ticks + 120;
npc._npcText && npc._npcText.parent && npc._npcText.parent.removeChild(npc._npcText);
npc._npcText = new Text2(npc.rpgName + ": Belki başka zaman!", {
size: 32,
fill: 0x00ffcc,
align: "center"
});
npc._npcText.anchor.set(0.5, 1);
npc._npcText.x = npc.x;
npc._npcText.y = npc.y - 70;
game.addChild(npc._npcText);
}
// Remove quest panel
if (npc._questDecisionPanel && npc._questDecisionPanel.parent) {
npc._questDecisionPanel.parent.removeChild(npc._questDecisionPanel);
}
if (npc._questDecisionText && npc._questDecisionText.parent) {
npc._questDecisionText.parent.removeChild(npc._questDecisionText);
}
if (npc._questDecisionAcceptBtn && npc._questDecisionAcceptBtn.parent) {
npc._questDecisionAcceptBtn.parent.removeChild(npc._questDecisionAcceptBtn);
}
if (npc._questDecisionRejectBtn && npc._questDecisionRejectBtn.parent) {
npc._questDecisionRejectBtn.parent.removeChild(npc._questDecisionRejectBtn);
}
npc._questDecisionPanel = null;
npc._questDecisionText = null;
npc._questDecisionAcceptBtn = null;
npc._questDecisionRejectBtn = null;
game._questDecisionPanel = null;
game._questDecisionAcceptBtn = null;
game._questDecisionRejectBtn = null;
game._questDecisionText = null;
game._questDecisionNPC = null;
}
return;
}
}
// Check for NPC tap (RPG/adventure/trade)
for (var i = 0; i < npcs.length; i++) {
var npc = npcs[i];
var dist = Math.sqrt(Math.pow(npc.x - ship.x, 2) + Math.pow(npc.y - ship.y, 2));
if (dist < 180) {
game._lastNPCTap = LK.ticks;
break;
}
}
// --- Distress event tap detection ---
if (window.distressEvent && window.distressEvent.helpBtn && window.distressEvent.ignoreBtn) {
game._lastDistressTapTick = LK.ticks;
game._lastDistressTapX = x;
game._lastDistressTapY = y;
}
}
};
game.move = function (x, y, obj) {
// Only allow joystick drag if ship is alive
if (isDragging && ship && ship.health > 0) {
var dx = x - joystickBase.x;
var dy = y - joystickBase.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Clamp handle to joystick base radius (130px for 30% larger size)
if (distance > 130) {
dx = dx / distance * 130;
dy = dy / distance * 130;
}
joystickHandle.x = joystickBase.x + dx;
joystickHandle.y = joystickBase.y + dy;
}
};
game.up = function (x, y, obj) {
// Only allow joystick release if ship is alive
isDragging = false;
if (ship && ship.health > 0) {
// Snap handle back to base
joystickHandle.x = joystickBase.x;
joystickHandle.y = joystickBase.y;
}
};
// Main game loop
game.update = function () {
// --- PERIODIC WORLD CLEANUP TO PREVENT LAG ---
// Every 600 ticks (~10 seconds), remove excess or orphaned entities
if (typeof window._lastWorldCleanupTick === "undefined") {
window._lastWorldCleanupTick = LK.ticks;
}
if (LK.ticks - window._lastWorldCleanupTick > 600) {
// Defensive: Remove destroyed, undefined, or offscreen objects from all arrays
var cleanupArray = function cleanupArray(arr, maxCount, offscreenDist) {
if (!arr) {
return;
}
for (var i = arr.length - 1; i >= 0; i--) {
var obj = arr[i];
// Remove if destroyed, undefined, or missing x/y
if (!obj || typeof obj.x !== "number" || typeof obj.y !== "number" || obj._destroyed) {
if (obj && typeof obj.destroy === "function") {
obj.destroy();
}
arr.splice(i, 1);
continue;
}
// Remove if far offscreen (relative to ship)
if (typeof ship !== "undefined" && ship && typeof ship.x === "number" && typeof ship.y === "number" && typeof offscreenDist === "number") {
var dx = obj.x - ship.x;
var dy = obj.y - ship.y;
if (Math.sqrt(dx * dx + dy * dy) > offscreenDist) {
if (typeof obj.destroy === "function") {
obj.destroy();
}
arr.splice(i, 1);
continue;
}
}
}
// Clamp array to maxCount (remove oldest if too many)
if (typeof maxCount === "number" && arr.length > maxCount) {
for (var j = arr.length - 1; j >= maxCount; j--) {
if (arr[j] && typeof arr[j].destroy === "function") {
arr[j].destroy();
}
arr.splice(j, 1);
}
}
}; // Clean up all major arrays
// Remove any nulls from arrays (defensive)
var compactArray = function compactArray(arr) {
if (!arr) {
return;
}
for (var i = arr.length - 1; i >= 0; i--) {
if (!arr[i]) {
arr.splice(i, 1);
}
}
};
cleanupArray(bullets, 100, 3000);
cleanupArray(enemyBullets, 100, 3000);
cleanupArray(pirates, 30, 4000);
cleanupArray(npcs, 20, 4000);
cleanupArray(asteroids, 10, 4000);
cleanupArray(coins, 30, 4000);
cleanupArray(resources, 30, 4000);
cleanupArray(stars, 200, 6000);
cleanupArray(upgradeStations, 5, 6000);
cleanupArray(explosions, 30, 4000);
cleanupArray(pirateBases, 5, 6000);
compactArray(bullets);
compactArray(enemyBullets);
compactArray(pirates);
compactArray(npcs);
compactArray(asteroids);
compactArray(coins);
compactArray(resources);
compactArray(stars);
compactArray(upgradeStations);
compactArray(explosions);
compactArray(pirateBases);
window._lastWorldCleanupTick = LK.ticks;
}
// --- SHIP MOVEMENT SYSTEM ---
// Defensive: always reset deltas
var shipDX = 0;
var shipDY = 0;
// Block all ship movement and input if ship is dead or just respawned
if (ship && ship.health <= 0) {
// Do not move ship, do not process joystick, do not update camera, do not fire, etc.
// Optionally, you could add a "dead" animation or effect here.
// (Handled by respawn logic)
return;
} else if (typeof window._lastRespawnTick !== "undefined" && LK.ticks - window._lastRespawnTick < 60) {
// Block all input/movement for 1 second after respawn
return;
} else {
// Block ship movement if ANY panel is open (but always allow joystick to move)
if (!(window.stationDeliveryPanel && window.stationDeliveryPanelBG && window.stationDeliveryPanelBG.parent) && !(window.resourceTradePanelState && window.resourceTradePanelState.open) && !(window._resourceStockPanel && window._resourceStockPanel.bg && window._resourceStockPanel.bg.parent) && !(window.shipUpgradePanel && window.shipUpgradePanel.parent) && !(window.repairPanel && window.repairPanel.parent)) {
// Only move if dragging joystick
if (isDragging) {
var dx = joystickHandle.x - joystickBase.x;
var dy = joystickHandle.y - joystickBase.y;
var distance = Math.sqrt(dx * dx + dy * dy);
// Only move if joystick is moved enough
if (distance > 10) {
// Normalize direction, scale by ship speed and joystick displacement
var norm = Math.min(distance, 130) / 130;
var effectiveSpeed = ship.speed * norm;
shipDX = dx / distance * effectiveSpeed;
shipDY = dy / distance * effectiveSpeed;
// Rotate ship to face movement direction
ship.rotation = Math.atan2(dy, dx) + Math.PI / 2;
}
}
}
// Actually move the ship's logical position, but keep it visually centered
ship.x += shipDX;
ship.y += shipDY;
}
// Track total distance traveled
totalDistance += Math.sqrt(shipDX * shipDX + shipDY * shipDY);
// --- Keep ship always visually centered ---
// Calculate the offset needed to keep the ship at the center of the screen
var centerX = 2048 / 2;
var centerY = 2732 / 2;
var offsetX = centerX - ship.x;
var offsetY = centerY - ship.y;
// Visually place the ship at the center
ship.x = centerX;
ship.y = centerY;
// Move all world objects by the offset so the ship appears to move through the world
for (var i = 0; i < stars.length; i++) {
stars[i].x += offsetX;
stars[i].y += offsetY;
}
for (var i = 0; i < npcs.length; i++) {
npcs[i].x += offsetX;
npcs[i].y += offsetY;
}
for (var i = 0; i < pirates.length; i++) {
pirates[i].x += offsetX;
pirates[i].y += offsetY;
}
for (var i = 0; i < coins.length; i++) {
coins[i].x += offsetX;
coins[i].y += offsetY;
}
for (var i = 0; i < resources.length; i++) {
resources[i].x += offsetX;
resources[i].y += offsetY;
}
for (var i = 0; i < bullets.length; i++) {
bullets[i].x += offsetX;
bullets[i].y += offsetY;
}
for (var i = 0; i < enemyBullets.length; i++) {
enemyBullets[i].x += offsetX;
enemyBullets[i].y += offsetY;
}
for (var i = 0; i < asteroids.length; i++) {
asteroids[i].x += offsetX;
asteroids[i].y += offsetY;
}
for (var i = 0; i < upgradeStations.length; i++) {
upgradeStations[i].x += offsetX;
upgradeStations[i].y += offsetY;
}
// --- DerelictDebris (enkaz) toplama ve fade-out sistemi ---
for (var i = upgradeStations.length - 1; i >= 0; i--) {
var debris = upgradeStations[i];
if (debris && debris.type === 'derelict' && !debris._collected && debris.intersects(ship)) {
debris._collected = true;
// 10 hammadde için rastgele bir tip seç
var resourceTypes = ['metal', 'energy', 'crystal', 'gas', 'ice', 'uranium', 'silicon', 'carbon', 'plasma', 'antimatter'];
var idx = Math.floor(Math.random() * resourceTypes.length);
if (debris._resourceAmount > 0) {
playerResources[idx] += debris._resourceAmount;
// Başarım kontrolü
checkResourceCollectAchievements && checkResourceCollectAchievements();
// Kısa bir label göster
var label = new Text2("+" + debris._resourceAmount + " " + resourceTypes[idx], {
size: 36,
fill: 0x00ff00,
align: "center"
});
label.anchor.set(0.5, 0.5);
label.x = ship.x;
label.y = ship.y - 120;
game.addChild(label);
LK.setTimeout(function () {
if (label.parent) {
label.parent.removeChild(label);
}
}, 900);
updateUI && updateUI();
saveProgress && saveProgress();
}
// Fade-out başlat
debris._fadeStartTick = LK.ticks;
debris._expireTick = debris._fadeStartTick + 60; // 1 saniyede kaybolsun
}
// Fade-out tamamlandıysa sil
if (debris && debris._shouldRemove) {
debris.destroy();
upgradeStations.splice(i, 1);
}
}
// boostParticles removed (no boost system)
for (var i = 0; i < explosions.length; i++) {
explosions[i].x += offsetX;
explosions[i].y += offsetY;
}
// Auto fire at very close pirates or asteroids
// Saldırı mesafesi %10 arttırıldı (200 -> 220)
// +%5 menzil yükseltmeleri uygula
var baseRange = 350;
var rangeBonus = (typeof window._rangeUpgradeLevel !== "undefined" ? window._rangeUpgradeLevel : storage.rangeUpgradeLevel || 0) * 0.05;
var ATTACK_RANGE = Math.round(baseRange * (1 + rangeBonus));
// %10 daha fazla menzil Katil Korsan ve GrupKorsan'a karşı
var ATTACK_RANGE_PIRATE_SPECIAL = Math.round(ATTACK_RANGE * 1.1);
if (LK.ticks - ship.lastFire > ship.fireRate && (pirates.length > 0 || asteroids.length > 0)) {
// Find nearest pirate or asteroid within 220 pixels
var nearestTarget = null;
var nearestDistance = Infinity;
var isPirate = false;
// Check pirates
for (var i = 0; i < pirates.length; i++) {
var pirate = pirates[i];
var dist = Math.sqrt(Math.pow(pirate.x - ship.x, 2) + Math.pow(pirate.y - ship.y, 2));
var isSpecialPirate = pirate._isKatil === true || pirate._isGrupKorsan === true;
var rangeToUse = isSpecialPirate ? ATTACK_RANGE_PIRATE_SPECIAL : ATTACK_RANGE;
if (dist < rangeToUse && dist < nearestDistance) {
nearestDistance = dist;
nearestTarget = pirate;
isPirate = true;
}
}
// Check asteroids
for (var i = 0; i < asteroids.length; i++) {
var dist = Math.sqrt(Math.pow(asteroids[i].x - ship.x, 2) + Math.pow(asteroids[i].y - ship.y, 2));
if (dist < ATTACK_RANGE && dist < nearestDistance) {
nearestDistance = dist;
nearestTarget = asteroids[i];
isPirate = false;
}
}
if (nearestTarget) {
var bullet = new Bullet();
bullet.x = ship.x;
bullet.y = ship.y;
var angle = Math.atan2(nearestTarget.y - ship.y, nearestTarget.x - ship.x);
bullet.directionX = Math.cos(angle);
bullet.directionY = Math.sin(angle);
bullet.rotation = angle + Math.PI / 2;
// Store last attack angle for ship rotation
game._lastShipAttackAngle = angle;
if (typeof ship !== "undefined") {
ship._lastMoveAngle = angle;
}
bullets.push(bullet);
game.addChild(bullet);
ship.lastFire = LK.ticks;
// Calculate distance from ship to bullet spawn point and adjust volume
var dx = bullet.x - ship.x;
var dy = bullet.y - ship.y;
var distanceFromShip = Math.sqrt(dx * dx + dy * dy);
// Calculate volume based on distance (closer = louder, farther = quieter)
// Max distance for sound falloff is 500 pixels
var maxSoundDistance = 500;
var volumeMultiplier = Math.max(0.1, Math.min(1.0, 1.0 - distanceFromShip / maxSoundDistance));
// Get the sound and play with adjusted volume
var shootSound = LK.getSound('shoot');
var originalVolume = shootSound.volume || 0.3; // fallback to default volume
shootSound.volume = originalVolume * volumeMultiplier;
shootSound.play();
// Restore original volume for next play
shootSound.volume = originalVolume;
// --- FRIENDS: Saldırı başlat ---
// Her friend, 1 saniye sonra saldırmaya başlar ve oyuncu saldırıyı bırakana kadar devam eder
for (var i = 0; i < friends.length; i++) {
if (friends[i] && typeof friends[i].startAttack === "function") {
// Eğer zaten bu hedefe saldırıyorsa, tekrar başlatma
if (!friends[i]._attacking || friends[i]._attackTarget !== nearestTarget) {
friends[i]._attackDelay = 60; // 1 saniye gecikme (her saldırı başlatıldığında sıfırla)
friends[i].startAttack(nearestTarget);
}
}
}
} else {
// --- FRIENDS: Saldırı bırak ---
for (var i = 0; i < friends.length; i++) {
if (friends[i] && typeof friends[i].stopAttack === "function") {
friends[i].stopAttack();
}
}
}
}
// Update bullets
for (var i = bullets.length - 1; i >= 0; i--) {
var bullet = bullets[i];
if (!bullet || typeof bullet.x === "undefined" || typeof bullet.y === "undefined") {
// Defensive: remove undefined or destroyed bullet
if (bullet && typeof bullet.destroy === "function") {
bullet.destroy();
}
bullets.splice(i, 1);
continue;
}
// Remove if marked for removal by fade-out
if (bullet._shouldRemove) {
bullet.destroy();
bullets.splice(i, 1);
continue;
}
// (No longer destroy instantly when out of range, fade-out will handle it)
// Check collision with pirates
for (var j = pirates.length - 1; j >= 0; j--) {
if (bullet.intersects(pirates[j])) {
// Small explosion effect at collision
createExplosion(bullet.x, bullet.y);
pirates[j].takeDamage(bullet.damage);
var killedByPlayer = !bullet._npcBullet;
var killedByNPC = !!bullet._npcBullet;
if (pirates[j].health <= 0) {
enemiesKilled++;
createExplosion(pirates[j].x, pirates[j].y);
// --- BAŞARIM: Korsan öldürme ---
if (killedByPlayer) {
checkPirateKillAchievements();
// Katil korsan öldürme
if (pirates[j]._isKatil) {
checkKatilKillAchievement();
}
}
// Drop loot
var coin = new Coin();
coin.x = pirates[j].x;
coin.y = pirates[j].y;
// Katil korsan ise 10 kat coin ver
if (pirates[j]._isKatil) {
coin.value = coin.value * (1 + Math.floor(waveNumber / 3)) * 10;
// Katil korsan öldü, tekrar spawn edilebilir
window._katilPirateSpawned = false;
// Katil korsan öldükten veya yok olduğunda tekrar spawn için timer başlat
if (typeof window._katilPirateRespawnTimer !== "undefined" && window._katilPirateRespawnTimer) {
LK.clearTimeout(window._katilPirateRespawnTimer);
}
window._katilPirateRespawnTimer = LK.setTimeout(function () {
if (!window._katilPirateSpawned) {
// Katil korsanı tekrar spawn et
var pirate = new Pirate();
// Katil korsan statlarını tekrar ayarla
var minLevel = 1 + waveNumber * 2;
pirate.level = minLevel + 10;
pirate.health = (30 + (pirate.level - 1) * 10) * 10;
pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10;
pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10;
pirate.rpgName = "Katil";
pirate._isKatil = true;
pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3));
pirate._attackRange = Math.floor(600 * 0.9); // Katil korsan için %10 daha kısa menzil
window._katilPirateSpawned = true;
// Diğer korsanlar gibi kenar spawn algoritmasıyla doğsun
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
// Top
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y - 1400;
break;
case 1:
// Right
pirate.x = ship.x + 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
case 2:
// Bottom
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y + 1400;
break;
case 3:
// Left
pirate.x = ship.x - 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
}
// Add health bar to katil pirate
pirate._healthBar = new HealthBar();
pirate._healthBar.set({
maxValue: function maxValue() {
return pirate.health > 0 ? pirate.health : 0;
},
getValueFn: function getValueFn() {
return pirate.health > 0 ? pirate.health : 0;
},
getMaxFn: function getMaxFn() {
return 30 + waveNumber * 10;
},
width: 60,
height: 10
});
pirate._healthBar.y = -60;
pirate.addChild(pirate._healthBar);
pirates.push(pirate);
game.addChild(pirate);
}
window._katilPirateRespawnTimer = null;
}, 6000); // 6 saniye sonra tekrar spawn
// Katil korsan yok olduğunda da tekrar spawn edilmesini sağla
for (var i = pirates.length - 1; i >= 0; i--) {
var p = pirates[i];
if (p && p._isKatil && (p._destroyed || typeof p.x === "undefined" || typeof p.y === "undefined")) {
// Katil korsan yok olduysa, tekrar spawn için timer başlat
window._katilPirateSpawned = false;
if (typeof window._katilPirateRespawnTimer !== "undefined" && window._katilPirateRespawnTimer) {
LK.clearTimeout(window._katilPirateRespawnTimer);
}
window._katilPirateRespawnTimer = LK.setTimeout(function () {
if (!window._katilPirateSpawned) {
var pirate = new Pirate();
var minLevel = 1 + waveNumber * 2;
pirate.level = minLevel + 10;
pirate.health = (30 + (pirate.level - 1) * 10) * 10;
pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10;
pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10;
pirate.rpgName = "Katil";
pirate._isKatil = true;
pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3));
pirate._attackRange = Math.floor(600 * 0.9); // Katil korsan için %10 daha kısa menzil
window._katilPirateSpawned = true;
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y - 1400;
break;
case 1:
pirate.x = ship.x + 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
case 2:
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y + 1400;
break;
case 3:
pirate.x = ship.x - 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
}
pirate._healthBar = new HealthBar();
pirate._healthBar.set({
maxValue: function maxValue() {
return pirate.health > 0 ? pirate.health : 0;
},
getValueFn: function getValueFn() {
return pirate.health > 0 ? pirate.health : 0;
},
getMaxFn: function getMaxFn() {
return 30 + waveNumber * 10;
},
width: 60,
height: 10
});
pirate._healthBar.y = -60;
pirate.addChild(pirate._healthBar);
pirates.push(pirate);
game.addChild(pirate);
}
window._katilPirateRespawnTimer = null;
}, 6000);
break; // Sadece bir kere tetiklenmeli
}
}
// Katil etiketiyle özel bir label göster
var katilLabel = new Text2("KATİL KORSAN ÖLDÜRÜLDÜ!\n+10x coin", {
size: 40,
fill: 0xff0000,
align: "center"
});
katilLabel.anchor.set(0.5, 0.5);
katilLabel.x = pirates[j].x;
katilLabel.y = pirates[j].y - 120;
game.addChild(katilLabel);
LK.setTimeout(function () {
if (katilLabel.parent) {
katilLabel.parent.removeChild(katilLabel);
}
}, 1500);
} else {
coin.value = coin.value * (1 + Math.floor(waveNumber / 3));
}
coins.push(coin);
game.addChild(coin);
// GrupKorsan öldüyse %25 ihtimalle CoinBox düşür
if (pirates[j]._isGrupKorsan && Math.random() < 0.25) {
var coinBox = new CoinBox();
coinBox.x = pirates[j].x + (Math.random() - 0.5) * 40;
coinBox.y = pirates[j].y + (Math.random() - 0.5) * 40;
coins.push(coinBox); // CoinBox da coins array'ine eklenir, toplama ve fade-out için
game.addChild(coinBox);
// Kısa bir label göster
var boxLabel = new Text2("COINBOX!", {
size: 32,
fill: 0xffd700,
align: "center"
});
boxLabel.anchor.set(0.5, 0.5);
boxLabel.x = coinBox.x;
boxLabel.y = coinBox.y - 60;
game.addChild(boxLabel);
LK.setTimeout(function () {
if (boxLabel.parent) {
boxLabel.parent.removeChild(boxLabel);
}
}, 900);
}
if (Math.random() < 0.3) {
var resource = new Resource();
resource.x = pirates[j].x + (Math.random() - 0.5) * 50;
resource.y = pirates[j].y + (Math.random() - 0.5) * 50;
resources.push(resource);
game.addChild(resource);
}
// Remove KILL label after 2 seconds if present
if (pirates[j]._killLabel) {
// If not already expiring, set expire tick
if (!pirates[j]._killLabel._expireTick) {
pirates[j]._killLabel._expireTick = LK.ticks + 120;
}
}
// Remove KILL label if expired
if (pirates[j]._killLabel && pirates[j]._killLabel._expireTick && LK.ticks > pirates[j]._killLabel._expireTick) {
if (pirates[j]._killLabel.parent) {
pirates[j]._killLabel.parent.removeChild(pirates[j]._killLabel);
}
pirates[j]._killLabel = null;
}
if (killedByPlayer) {
// Oyuncu öldürdü: sadece exp kazan
var pirateExp = 3 + Math.floor(Math.random() * 3);
playerEXP += pirateExp;
// Level up if enough EXP
while (playerEXP >= expToNext(playerLevel)) {
playerEXP -= expToNext(playerLevel);
playerLevel++;
LK.effects.flashObject(ship, 0x00ffcc, 800);
checkLevelAchievements();
}
} else if (killedByNPC) {
// NPC öldürdü: yoluna devam etsin, rep/exp yok
// Kısa bir etiket göster (isteğe bağlı)
var npcLabel = new Text2("NPC KILL", {
size: 28,
fill: 0xccccff,
align: "center"
});
npcLabel.anchor.set(0.5, 0.5);
npcLabel.x = pirates[j].x;
npcLabel.y = pirates[j].y - 80;
game.addChild(npcLabel);
// Fade out and remove after 700ms
LK.setTimeout(function () {
if (npcLabel.parent) {
npcLabel.parent.removeChild(npcLabel);
}
}, 700);
}
// Update UI after EXP gain
updateUI();
pirates[j].destroy();
pirates.splice(j, 1);
// Check for wave completion
if (enemiesKilled >= 10 + waveNumber * 5) {
waveNumber++;
enemiesKilled = 0;
// --- BAŞARIM: Dalga geçme ---
checkWaveAchievements();
// Spawn upgrade station every 3 waves
if (waveNumber % 3 === 0) {
spawnUpgradeStation();
}
}
}
bullet.destroy();
bullets.splice(i, 1);
LK.getSound('hit').play();
break;
}
}
// --- PLAYER CAN ATTACK NPCs IF FIGHT MODE IS ON ---
if (window.npcFightMode) {
for (var j = npcs.length - 1; j >= 0; j--) {
var npc = npcs[j];
if (bullet.intersects(npc)) {
// Small explosion effect at collision
createExplosion(bullet.x, bullet.y);
if (typeof npc.health === "undefined") {
npc.health = 100;
}
npc.health -= bullet.damage;
LK.effects.flashObject(npc, 0xff0000, 120);
// Floating label for NPC hit
var npcHitLabel = new Text2("NPC HIT", {
size: 24,
fill: 0xff8888,
align: "center"
});
npcHitLabel.anchor.set(0.5, 0.5);
npcHitLabel.x = npc.x;
npcHitLabel.y = npc.y - 60;
game.addChild(npcHitLabel);
LK.setTimeout(function (label) {
if (label.parent) {
label.parent.removeChild(label);
}
}.bind(null, npcHitLabel), 600);
// Remove NPC if dead
if (npc.health <= 0) {
// Floating label for NPC kill
var npcKillLabel = new Text2("NPC KILL", {
size: 28,
fill: 0xccccff,
align: "center"
});
npcKillLabel.anchor.set(0.5, 0.5);
npcKillLabel.x = npc.x;
npcKillLabel.y = npc.y - 80;
game.addChild(npcKillLabel);
LK.setTimeout(function (label) {
if (label.parent) {
label.parent.removeChild(label);
}
}.bind(null, npcKillLabel), 700);
// Oyuncu NPC öldürdü: coin kaybı uygula
var coinLoss = 10 + Math.floor(Math.random() * 41); // 10-50 arası coin kaybı
playerCoins = Math.max(0, playerCoins - coinLoss);
// Kısa bir etiket göster
var coinLossLabel = new Text2("-" + coinLoss + " coin", {
size: 28,
fill: 0xff4444,
align: "center"
});
coinLossLabel.anchor.set(0.5, 0.5);
coinLossLabel.x = npc.x;
coinLossLabel.y = npc.y - 120;
game.addChild(coinLossLabel);
LK.setTimeout(function (label) {
if (label.parent) {
label.parent.removeChild(label);
}
}, 900, coinLossLabel);
updateUI();
// Remove hostile label if present
if (npc._hostileLabel && npc._hostileLabel.parent) {
npc._hostileLabel.parent.removeChild(npc._hostileLabel);
npc._hostileLabel = null;
}
// Instantly remove Düşman! label if it exists (defensive, in case above missed)
if (npc._hostileLabel && npc._hostileLabel.parent) {
npc._hostileLabel.parent.removeChild(npc._hostileLabel);
npc._hostileLabel = null;
}
npc.destroy();
npcs.splice(j, 1);
}
bullet.destroy();
bullets.splice(i, 1);
LK.getSound('hit').play();
break;
}
}
}
// Check collision with asteroids
for (var j = asteroids.length - 1; j >= 0; j--) {
if (bullet.intersects(asteroids[j])) {
// Small explosion effect at collision
createExplosion(bullet.x, bullet.y);
asteroids[j].takeDamage(bullet.damage);
if (asteroids[j].health <= 0) {
// --- BAŞARIM: Asteroit patlatma ---
checkAsteroidKillAchievements();
// Drop multiple resources (reduced by 80%)
var dropCount = Math.max(1, Math.floor(asteroids[j].resources * 0.2));
for (var k = 0; k < dropCount; k++) {
var resource = new Resource();
resource.x = asteroids[j].x + (Math.random() - 0.5) * 100;
resource.y = asteroids[j].y + (Math.random() - 0.5) * 100;
resource.amount = Math.floor(Math.random() * 3) + 1;
resources.push(resource);
game.addChild(resource);
}
createExplosion(asteroids[j].x, asteroids[j].y);
asteroids[j].destroy();
asteroids.splice(j, 1);
}
bullet.destroy();
bullets.splice(i, 1);
LK.getSound('hit').play();
break;
}
}
}
// Update enemy bullets
for (var i = enemyBullets.length - 1; i >= 0; i--) {
var bullet = enemyBullets[i];
if (!bullet || typeof bullet.x === "undefined" || typeof bullet.y === "undefined") {
// Defensive: remove undefined or destroyed bullet
if (bullet && typeof bullet.destroy === "function") {
bullet.destroy();
}
enemyBullets.splice(i, 1);
continue;
}
// Remove if marked for removal by fade-out
if (bullet._shouldRemove) {
bullet.destroy();
enemyBullets.splice(i, 1);
continue;
}
// (No longer destroy instantly when out of range, fade-out will handle it)
if (bullet.intersects(ship)) {
// Small explosion effect at collision
createExplosion(bullet.x, bullet.y);
ship.takeDamage(bullet.damage);
updateUI();
bullet.destroy();
enemyBullets.splice(i, 1);
LK.getSound('hit').play();
continue;
}
// --- NEW: Pirate bullets can hit NPCs ---
for (var n = npcs.length - 1; n >= 0; n--) {
var npc = npcs[n];
if (bullet.intersects(npc)) {
// Small explosion effect at collision
createExplosion(bullet.x, bullet.y);
// Damage NPC
if (typeof npc.health === "undefined") {
npc.health = 100;
}
npc.health -= bullet.damage;
LK.effects.flashObject(npc, 0xff0000, 120);
// Floating label for NPC hit (optional)
var npcHitLabel = new Text2("NPC HIT", {
size: 24,
fill: 0xff8888,
align: "center"
});
npcHitLabel.anchor.set(0.5, 0.5);
npcHitLabel.x = npc.x;
npcHitLabel.y = npc.y - 60;
game.addChild(npcHitLabel);
LK.setTimeout(function (label) {
if (label.parent) {
label.parent.removeChild(label);
}
}.bind(null, npcHitLabel), 600);
// Remove NPC if dead
if (npc.health <= 0) {
// Floating label for NPC kill
var npcKillLabel = new Text2("NPC KILL", {
size: 28,
fill: 0xccccff,
align: "center"
});
npcKillLabel.anchor.set(0.5, 0.5);
npcKillLabel.x = npc.x;
npcKillLabel.y = npc.y - 80;
game.addChild(npcKillLabel);
LK.setTimeout(function (label) {
if (label.parent) {
label.parent.removeChild(label);
}
}.bind(null, npcKillLabel), 700);
// Remove hostile label if present
if (npc._hostileLabel && npc._hostileLabel.parent) {
npc._hostileLabel.parent.removeChild(npc._hostileLabel);
npc._hostileLabel = null;
}
// Remove NPC from world
npc.destroy();
npcs.splice(n, 1);
}
bullet.destroy();
enemyBullets.splice(i, 1);
LK.getSound('hit').play();
break;
}
}
}
// Update pirates
for (var i = 0; i < pirates.length; i++) {
var pirate = pirates[i];
if (pirate._healthBar && pirate._healthBar.update) {
pirate._healthBar.update();
}
// --- Pirate AI: Always attack the nearest target (NPC or player) ---
var nearestTarget = null;
var minDist = Infinity;
// Find nearest NPC
for (var n = 0; n < npcs.length; n++) {
var npc = npcs[n];
var distToNPC = Math.sqrt(Math.pow(npc.x - pirate.x, 2) + Math.pow(npc.y - pirate.y, 2));
if (distToNPC < minDist) {
minDist = distToNPC;
nearestTarget = npc;
}
}
// Compare with player distance
var distToPlayer = Math.sqrt(Math.pow(ship.x - pirate.x, 2) + Math.pow(ship.y - pirate.y, 2));
if (distToPlayer < minDist) {
minDist = distToPlayer;
nearestTarget = ship;
}
// If there is a target, move and attack
var pirateAttackRange = typeof pirate._attackRange === "number" ? pirate._attackRange : 600;
if (nearestTarget && minDist < pirateAttackRange) {
// --- 10% mesafe kuralı: hedefe %10 mesafe bırak, iç içe girme ---
var targetRadius = 40; // Varsayılan hedef yarıçapı (NPC/ship/pirate boyutu)
if (nearestTarget._healthBar && nearestTarget._healthBar._width) {
targetRadius = Math.max(targetRadius, nearestTarget._healthBar._width / 2);
}
var selfRadius = 35; // Korsan yarıçapı
if (pirate._healthBar && pirate._healthBar._width) {
selfRadius = Math.max(selfRadius, pirate._healthBar._width / 2);
}
var minBuffer = (minDist + selfRadius + targetRadius) * 0.15; // %15 buffer
var minAllowedDist = selfRadius + targetRadius + minBuffer;
var angle = Math.atan2(nearestTarget.y - pirate.y, nearestTarget.x - pirate.x);
// Sadece minAllowedDist'ten uzaktaysa yaklaş
if (minDist > minAllowedDist) {
var moveDist = Math.min(pirate.speed, minDist - minAllowedDist);
pirate.x += Math.cos(angle) * moveDist;
pirate.y += Math.sin(angle) * moveDist;
pirate.rotation = angle + Math.PI / 2;
} else {
// Hedefe çok yaklaştıysa sadece dön, hareket etme
pirate.rotation = angle + Math.PI / 2;
}
// Shoot at target
if (LK.ticks - pirate.lastFire > pirate.fireRate) {
var bullet = new EnemyBullet();
bullet.x = pirate.x;
bullet.y = pirate.y;
bullet.directionX = Math.cos(angle);
bullet.directionY = Math.sin(angle);
bullet.rotation = angle + Math.PI / 2;
bullet._pirateBullet = true;
enemyBullets.push(bullet);
game.addChild(bullet);
pirate.lastFire = LK.ticks;
// Calculate distance from ship to pirate bullet spawn point and adjust volume
var dx = bullet.x - ship.x;
var dy = bullet.y - ship.y;
var distanceFromShip = Math.sqrt(dx * dx + dy * dy);
// Calculate volume based on distance (closer = louder, farther = quieter)
// Max distance for sound falloff is 500 pixels
var maxSoundDistance = 500;
var volumeMultiplier = Math.max(0.1, Math.min(1.0, 1.0 - distanceFromShip / maxSoundDistance));
// Get the sound and play with adjusted volume
var shootSound = LK.getSound('shoot');
var originalVolume = shootSound.volume || 0.3; // fallback to default volume
shootSound.volume = originalVolume * volumeMultiplier;
shootSound.play();
// Restore original volume for next play
shootSound.volume = originalVolume;
}
} else {
// Wander randomly
pirate.x += Math.cos(pirate.moveAngle) * pirate.speed * 0.5;
pirate.y += Math.sin(pirate.moveAngle) * pirate.speed * 0.5;
}
// Remove pirates that are too far away
var dist = Math.sqrt(Math.pow(ship.x - pirate.x, 2) + Math.pow(ship.y - pirate.y, 2));
if (dist > 2000) {
// Katil korsan ise tekrar spawn için timer başlat
if (pirate._isKatil) {
window._katilPirateSpawned = false;
if (typeof window._katilPirateRespawnTimer !== "undefined" && window._katilPirateRespawnTimer) {
LK.clearTimeout(window._katilPirateRespawnTimer);
}
window._katilPirateRespawnTimer = LK.setTimeout(function () {
if (!window._katilPirateSpawned) {
var pirate = new Pirate();
var minLevel = 1 + waveNumber * 2;
pirate.level = minLevel + 10;
pirate.health = (30 + (pirate.level - 1) * 10) * 10;
pirate.damage = (5 + Math.floor((pirate.level - 1) * 1.5)) * 10;
pirate.defense = (2 + Math.floor((pirate.level - 1) * 1.2)) * 10;
pirate.rpgName = "Katil";
pirate._isKatil = true;
pirate.fireRate = Math.max(2, Math.floor((60 - Math.min((pirate.level - 1) * 2, 30)) / 3));
pirate._attackRange = Math.floor(600 * 0.9);
window._katilPirateSpawned = true;
var edge = Math.floor(Math.random() * 4);
switch (edge) {
case 0:
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y - 1400;
break;
case 1:
pirate.x = ship.x + 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
case 2:
pirate.x = ship.x + (Math.random() - 0.5) * 1800;
pirate.y = ship.y + 1400;
break;
case 3:
pirate.x = ship.x - 1400;
pirate.y = ship.y + (Math.random() - 0.5) * 1800;
break;
}
pirate._healthBar = new HealthBar();
pirate._healthBar.set({
maxValue: function maxValue() {
return pirate.health > 0 ? pirate.health : 0;
},
getValueFn: function getValueFn() {
return pirate.health > 0 ? pirate.health : 0;
},
getMaxFn: function getMaxFn() {
return 30 + waveNumber * 10;
},
width: 60,
height: 10
});
pirate._healthBar.y = -60;
pirate.addChild(pirate._healthBar);
pirates.push(pirate);
game.addChild(pirate);
}
window._katilPirateRespawnTimer = null;
}, 6000);
}
pirate.destroy();
pirates.splice(i, 1);
i--;
}
}
// Update NPCs
for (var i = npcs.length - 1; i >= 0; i--) {
var npc = npcs[i];
if (npc._healthBar && npc._healthBar.update) {
npc._healthBar.update();
}
// Remove NPCs that are too far away
var dist = Math.sqrt(Math.pow(npc.x - ship.x, 2) + Math.pow(npc.y - ship.y, 2));
if (dist > 2500) {
npc.destroy();
npcs.splice(i, 1);
continue;
}
// Remove any floating text if player is not close
if (npc._npcText && npc._npcText.parent) {
npc._npcText.parent.removeChild(npc._npcText);
npc._npcText = null;
}
// --- NPC ATTITUDE: Rep sistemi kaldırıldı, sadece NPC Saldırı butonuna göre saldırı ---
// Eğer NPC Saldırı aktifse, her durumda NPC'ler oyuncuya saldırır
var npcHostile = false;
if (window.npcSaldiriBtn && window.npcSaldiriBtn._active) {
npcHostile = true;
} else {
npcHostile = false;
}
// --- NPC AI: Always attack the nearest target (pirate or player) ---
// If NPC Saldırı is PASİF, NPCs never attack the player
var nearestTarget = null;
var minDist = Infinity;
// Find nearest pirate
for (var j = 0; j < pirates.length; j++) {
var p = pirates[j];
if (p._destroyed) {
continue;
}
var d = Math.sqrt(Math.pow(p.x - npc.x, 2) + Math.pow(p.y - npc.y, 2));
if (d < minDist) {
minDist = d;
nearestTarget = p;
}
}
// Compare with player distance only if NPC Saldırı is AKTİF
var npcSaldiriActive = window.npcSaldiriBtn && window.npcSaldiriBtn._active;
if (npcSaldiriActive) {
var distToPlayer = Math.sqrt(Math.pow(ship.x - npc.x, 2) + Math.pow(ship.y - npc.y, 2));
if (distToPlayer < minDist) {
minDist = distToPlayer;
nearestTarget = ship;
}
}
// If there is a target, move and attack
if (nearestTarget && minDist < 600) {
// --- 10% mesafe kuralı: hedefe %10 mesafe bırak, iç içe girme ---
var targetRadius = 40;
if (nearestTarget._healthBar && nearestTarget._healthBar._width) {
targetRadius = Math.max(targetRadius, nearestTarget._healthBar._width / 2);
}
var selfRadius = 30;
if (npc._healthBar && npc._healthBar._width) {
selfRadius = Math.max(selfRadius, npc._healthBar._width / 2);
}
var minBuffer = (minDist + selfRadius + targetRadius) * 0.15;
var minAllowedDist = selfRadius + targetRadius + minBuffer;
var dx = nearestTarget.x - npc.x;
var dy = nearestTarget.y - npc.y;
var d = Math.sqrt(dx * dx + dy * dy);
if (d > minAllowedDist) {
var moveDist = Math.min(npc.moveSpeed * 1.2, d - minAllowedDist);
npc.x += dx / d * moveDist;
npc.y += dy / d * moveDist;
}
// Shoot at target (use NPC bullet fire rate)
if (typeof npc._aiAttackTimer === "undefined") {
npc._aiAttackTimer = 0;
}
npc._aiAttackTimer++;
// Use the same fire rate as pirates (EnemyBullet speed)
var npcFireRate = 8;
if (typeof npc.fireRate === "number") {
npcFireRate = npc.fireRate;
} else {
npcFireRate = 40;
}
if (d < 300 && npc._aiAttackTimer > npcFireRate) {
var bullet;
var angle = Math.atan2(nearestTarget.y - npc.y, nearestTarget.x - npc.x);
if (nearestTarget === ship) {
bullet = new EnemyBullet();
bullet._npcBullet = true;
} else {
bullet = new Bullet();
bullet._npcBullet = true;
}
bullet.x = npc.x;
bullet.y = npc.y;
bullet.directionX = Math.cos(angle);
bullet.directionY = Math.sin(angle);
bullet.rotation = angle + Math.PI / 2;
if (nearestTarget === ship) {
enemyBullets.push(bullet);
} else {
bullets.push(bullet);
}
game.addChild(bullet);
npc._aiAttackTimer = 0;
// Calculate distance from ship to NPC bullet spawn point and adjust volume
var dx = bullet.x - ship.x;
var dy = bullet.y - ship.y;
var distanceFromShip = Math.sqrt(dx * dx + dy * dy);
// Calculate volume based on distance (closer = louder, farther = quieter)
// Max distance for sound falloff is 500 pixels
var maxSoundDistance = 500;
var volumeMultiplier = Math.max(0.1, Math.min(1.0, 1.0 - distanceFromShip / maxSoundDistance));
// Get the sound and play with adjusted volume
var shootSound = LK.getSound('shoot');
var originalVolume = shootSound.volume || 0.3; // fallback to default volume
shootSound.volume = originalVolume * volumeMultiplier;
shootSound.play();
// Restore original volume for next play
shootSound.volume = originalVolume;
}
// Show angry label above NPC if attacking player
if (nearestTarget === ship) {
if (!npc._hostileLabel || !npc._hostileLabel.parent) {
npc._hostileLabel = new Text2("Düşman!", {
size: 32,
fill: 0xff4444,
align: "center"
});
npc._hostileLabel.anchor.set(0.5, 0.5);
npc._hostileLabel.x = npc.x;
npc._hostileLabel.y = npc.y - 80;
npc._hostileLabel.alpha = 1;
game.addChild(npc._hostileLabel);
}
if (npc._hostileLabel) {
npc._hostileLabel.x = npc.x;
npc._hostileLabel.y = npc.y - 80;
npc._hostileLabel.alpha = 1;
npc._hostileLabel._expireTick = LK.ticks + 60; // 1 second
}
if (npc._hostileLabel && npc._hostileLabel._expireTick && LK.ticks > npc._hostileLabel._expireTick) {
if (npc._hostileLabel.parent) {
npc._hostileLabel.parent.removeChild(npc._hostileLabel);
}
npc._hostileLabel = null;
} else if (npc._hostileLabel && npc._hostileLabel._expireTick) {
var fadeStart = npc._hostileLabel._expireTick - 20;
if (LK.ticks > fadeStart) {
npc._hostileLabel.alpha = Math.max(0, (npc._hostileLabel._expireTick - LK.ticks) / 20);
} else {
npc._hostileLabel.alpha = 1;
}
}
} else {
// Remove hostile label if present
if (npc._hostileLabel && npc._hostileLabel.parent) {
npc._hostileLabel.parent.removeChild(npc._hostileLabel);
npc._hostileLabel = null;
}
}
} else {
// Remove hostile label if present
if (npc._hostileLabel && npc._hostileLabel.parent) {
npc._hostileLabel.parent.removeChild(npc._hostileLabel);
npc._hostileLabel = null;
}
}
}
// Update asteroids
for (var i = asteroids.length - 1; i >= 0; i--) {
var asteroid = asteroids[i];
if (asteroid._healthBar && asteroid._healthBar.update) {
asteroid._healthBar.update();
}
var dist = Math.sqrt(Math.pow(asteroid.x - ship.x, 2) + Math.pow(asteroid.y - ship.y, 2));
if (dist > 2500) {
asteroid.destroy();
asteroids.splice(i, 1);
}
}
// Update explosion particles
for (var i = explosions.length - 1; i >= 0; i--) {
var particle = explosions[i];
particle.x += particle.vx;
particle.y += particle.vy;
particle.vx *= 0.9;
particle.vy *= 0.9;
particle.life--;
particle.alpha = particle.life / 30;
particle.scaleX = particle.scaleY = 1 + (30 - particle.life) / 10;
if (particle.life <= 0) {
particle.destroy();
explosions.splice(i, 1);
}
}
// Collect items
for (var i = coins.length - 1; i >= 0; i--) {
// Remove if marked for removal by fade-out
if (coins[i]._shouldRemove) {
coins[i].destroy();
coins.splice(i, 1);
continue;
}
if (coins[i].intersects(ship)) {
// CoinBox ise özel toplama efekti ve 50-100 coin ver
if (typeof coins[i].value === "number" && coins[i].value >= 50 && coins[i].value <= 100 && coins[i].constructor && coins[i].constructor.name === "CoinBox") {
playerCoins += coins[i].value;
// Kısa bir label göster
var boxLabel = new Text2("+" + coins[i].value + " coin!", {
size: 36,
fill: 0xffd700,
align: "center"
});
boxLabel.anchor.set(0.5, 0.5);
boxLabel.x = ship.x;
boxLabel.y = ship.y - 120;
game.addChild(boxLabel);
LK.setTimeout(function () {
if (boxLabel.parent) {
boxLabel.parent.removeChild(boxLabel);
}
}, 900);
coins[i].destroy();
coins.splice(i, 1);
LK.getSound('collect').play();
updateUI();
saveProgress();
continue;
}
// Normal coin ise
playerCoins += coins[i].value;
coins[i].destroy();
coins.splice(i, 1);
LK.getSound('collect').play();
updateUI();
saveProgress();
}
}
for (var i = resources.length - 1; i >= 0; i--) {
// Remove if marked for removal by fade-out
if (resources[i]._shouldRemove) {
resources[i].destroy();
resources.splice(i, 1);
continue;
}
if (resources[i].intersects(ship)) {
// 10 tip için index bul
var resourceTypes = ['metal', 'energy', 'crystal', 'gas', 'ice', 'uranium', 'silicon', 'carbon', 'plasma', 'antimatter'];
var idx = resourceTypes.indexOf(resources[i].type);
if (idx >= 0) {
playerResources[idx] += resources[i].amount;
// --- BAŞARIM: Hammadde toplama ---
checkResourceCollectAchievements();
}
resources[i].destroy();
resources.splice(i, 1);
LK.getSound('collect').play();
updateUI();
saveProgress();
}
}
// --- BAŞARIM: Coin biriktirme ---
checkCoinAchievements();
// --- BAŞARIM: Seviye atlama ---
checkLevelAchievements();
// --- BAŞARIM: Kalkan doldurma ---
checkShieldAchievements();
// --- BAŞARIM: Can yükseltme ---
checkHealthAchievements();
// --- BAŞARIM: Hız yükseltme ---
checkSpeedAchievements();
// --- BAŞARIM: Atış hızı yükseltme ---
checkFireRateAchievements();
// --- BAŞARIM: Yol katetme ---
checkDistanceAchievements();
// --- BAŞARIM: 30 dakika hayatta kalma ---
var elapsedTicks = LK.ticks - gameStartTick;
var elapsedSeconds = Math.floor(elapsedTicks / 60);
checkSurvive30MinAchievement(elapsedSeconds);
// Update stars
for (var i = stars.length - 1; i >= 0; i--) {
var dist = Math.sqrt(Math.pow(stars[i].x - ship.x, 2) + Math.pow(stars[i].y - ship.y, 2));
if (dist > 2000) {
stars[i].destroy();
stars.splice(i, 1);
}
}
// Spawn entities (star spawn frequency is now random interval)
if (typeof window._nextStarSpawnTick === "undefined") {
window._nextStarSpawnTick = LK.ticks + Math.floor(20 + Math.random() * 60); // 20-80 ticks
}
if (LK.ticks >= window._nextStarSpawnTick) {
spawnStar(true);
window._nextStarSpawnTick = LK.ticks + Math.floor(20 + Math.random() * 60); // 20-80 ticks
}
// Spawn pirates and NPCs at intervals
if (pirates.length < 6 && Math.random() < 0.3) {
spawnPirate();
}
if (npcs.length < 4 && Math.random() < 0.2) {
spawnNPC();
}
// --- ASTEROID SYSTEM RESTORED ---
// Spawn asteroids if less than 3 in the world (50% reduction from 6)
// Also halve the spawn rate
if (asteroids.length < 3 && Math.random() < 0.0125) {
spawnAsteroid();
}
// --- GRUP KORSAN SQUAD SYSTEM (at least 1 squad always present, Katil Korsan mantığı ile) ---
if (typeof window._grupKorsanSpawned === "undefined") {
window._grupKorsanSpawned = false;
}
if (typeof window._grupKorsanRespawnTimer === "undefined") {
window._grupKorsanRespawnTimer = null;
}
// Count living GrupKorsan pirates
var grupKorsanCount = 0;
for (var i = 0; i < pirates.length; i++) {
if (pirates[i] && pirates[i]._isGrupKorsan) {
grupKorsanCount++;
}
}
// If none exist, spawn a new squad (2-4 members) after 6 seconds, like Katil Korsan
if (grupKorsanCount === 0 && !window._grupKorsanSpawned && !window._grupKorsanRespawnTimer) {
window._grupKorsanRespawnTimer = LK.setTimeout(function () {
if (!window._grupKorsanSpawned) {
// Pick squad size 2-4
var squadSize = 2 + Math.floor(Math.random() * 3); // 2,3,4
// Spawn from edge like Katil Korsan
var edge = Math.floor(Math.random() * 4);
var centerX = ship.x,
centerY = ship.y;
switch (edge) {
case 0:
centerX = ship.x + (Math.random() - 0.5) * 1800;
centerY = ship.y - 1400;
break;
case 1:
centerX = ship.x + 1400;
centerY = ship.y + (Math.random() - 0.5) * 1800;
break;
case 2:
centerX = ship.x + (Math.random() - 0.5) * 1800;
centerY = ship.y + 1400;
break;
case 3:
centerX = ship.x - 1400;
centerY = ship.y + (Math.random() - 0.5) * 1800;
break;
}
spawnPirateSquad(centerX, centerY, squadSize);
window._grupKorsanSpawned = true;
}
window._grupKorsanRespawnTimer = null;
}, 6000);
}
// If all GrupKorsan pirates are dead or removed, allow respawn again
if (grupKorsanCount === 0 && window._grupKorsanSpawned) {
window._grupKorsanSpawned = false;
// If timer is running, clear it to avoid double spawn
if (window._grupKorsanRespawnTimer) {
LK.clearTimeout(window._grupKorsanRespawnTimer);
window._grupKorsanRespawnTimer = null;
}
}
// Update score
LK.setScore(playerCoins + questsCompleted * 100 + enemiesKilled * 10 + Math.floor(totalDistance / 100));
if (ship._healthBar && ship._healthBar.update) {
ship._healthBar.update();
}
updateUI();
// --- AUTO-REPAIR PANEL WHEN HEALTH IS LOW OR HEALTH BAR TAPPED ---
// Show repair panel ONLY if explicitly requested (not automatically when health decreases)
if (!window.repairPanel && ship.health < ship.maxHealth && ship.health > 0 && window._showRepairPanel && (!window._repairPanelSuppressUntil || LK.ticks > window._repairPanelSuppressUntil)) {
// Do nothing: do not auto-open repair panel when health decreases
// The repair panel will only open if window._showRepairPanel is set by explicit user action (e.g. health bar tap)
} else if (window.repairPanel && ship.health >= ship.maxHealth * 0.7) {
// Remove repair panel if health is restored
if (window.repairPanel.parent) {
window.repairPanel.parent.removeChild(window.repairPanel);
}
if (window.repairText && window.repairText.parent) {
window.repairText.parent.removeChild(window.repairText);
}
if (window.repairBtn && window.repairBtn.parent) {
window.repairBtn.parent.removeChild(window.repairBtn);
}
if (window.cancelRepairBtn && window.cancelRepairBtn.parent) {
window.cancelRepairBtn.parent.removeChild(window.cancelRepairBtn);
}
window.repairPanel = null;
window.repairText = null;
window.repairBtn = null;
window.cancelRepairBtn = null;
window._showRepairPanel = false;
}
// If health < 50% and not dead, do NOT auto-show repair panel (disabled as per new requirement)
// (window._showRepairPanel is only set by explicit user action, e.g. health bar tap)
// --- GAME TIME UPDATE ---
// Calculate elapsed time in seconds
var elapsedTicks = LK.ticks - gameStartTick;
var elapsedSeconds = Math.floor(elapsedTicks / 60);
var minutes = Math.floor(elapsedSeconds / 60);
var seconds = elapsedSeconds % 60;
if (typeof gameTimeText !== "undefined") {
// Format as mm:ss
var timeStr = "Game Time: " + minutes + ":" + (seconds < 10 ? "0" : "") + seconds;
gameTimeText.setText(timeStr);
// Always keep gameTimeText to the right of coinText
gameTimeText.x = coinText.x + coinText.width + 40;
gameTimeText.y = coinText.y;
}
// --- Synchronize LK.ticks with real game time ---
// This ensures all time-based systems use real elapsed time, not just frame count
LK.ticks = gameStartTick + Math.floor((Date.now() - (window._gameStartRealTime || (window._gameStartRealTime = Date.now()))) / (1000 / 60));
};
Uzay temalı global Cevher ticareti, gerçekçi, Altında "Trade" yazsın. In-Game asset. High contrast. No shadows
Uzay temalı global Cevher ticareti, uzay istasyonu, gerçekçi, Altında "Space Stations" yazsın. In-Game asset. High contrast. No shadows
Uzay temalı global Cevher deposu, uzay kargosu, gerçekçi, Altında "Storage" yazsın. In-Game asset. High contrast. No shadows
Uzay temalı Gemi Geliştirme, Tamirci İstasyonu, gerçekçi, Altında "Upgrade" yazsın. In-Game asset. High contrast. No shadows
Uzay temalı global Achievements, gerçekçi, Altında "Achievements" yazsın. In-Game asset. High contrast. No shadows
Uzay temalı, içi altın dolu kapağı açık kasa, gerçekçi, In-Game asset. High contrast. No shadows
Uzay temalı Spaceship Crew, gerçekçi, Altında "crew" yazsın. In-Game asset. High contrast. No shadows
Uzay temalı Savaş Gemisi, gerçekçi, yazısız üstten görünüm. In-Game asset. High contrast. No shadows
Uzay temalı Savaş Gemisi, gerçekçi, yazısız üstten görünüm. In-Game asset. High contrast. No shadows
Uzay temalı altın coin, gerçekçi, yazısız üstten görünüm. In-Game asset. High contrast. No shadows
Uzay temalı Savaş Gemisi, gerçekçi, yazısız üstten görünüm. In-Game asset. High contrast. No shadows
Uzay temalı Savaş Gemisi, gerçekçi, yazısız üstten görünüm. In-Game asset. High contrast. No shadows
Uzay temalı Savaş Gemisi, gerçekçi, yazısız üstten görünüm. In-Game asset. High contrast. No shadows
Uzay temalı Savaş Gemisi, gerçekçi, yazısız üstten görünüm. In-Game asset. High contrast. No shadows
Uzay temalı Savaş Gemisi, gerçekçi, yazısız üstten görünüm. In-Game asset. High contrast. No shadows
Uzay temalı Savaş Gemisi, gerçekçi, yazısız üstten görünüm. In-Game asset. High contrast. No shadows
Uzay temalı Savaş Gemisi, gerçekçi, yazısız üstten görünüm. In-Game asset. High contrast. No shadows
Uzay temalı asteroid, üzerinde cevherler bulunsun, gerçekçi, yazısız üstten görünüm. In-Game asset. High contrast. No shadows