/****
* 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