Code edit (2 edits merged)
Please save this source code
User prompt
Karakterim level atladığında kart seçiminde (4 seçenek) iken tüm düşmanların durması, hareket etmemesi ve karakterime saldırmaması gerekiyor, karakterim kartını seçtikten sonra düşmanlar hareket etmeye ve saldırmaya devam etmeli. Ayrıca benezr şekilde karakterim level atladığında kart seçiminde (4 seçenek) iken karakterim de durmalı, hareket etmemeli ve düşmanlara saldırmamalı, karakterim kartını seçtikten sonra karakterim tekrar hareket edebilir ve düşmanlara saldırabilir olmalı.
User prompt
Bunu düzeltmeye çalış. Test amaçlı şimdilik görseli çok büyük yap, bakalım görebilecek miyim.
User prompt
O zaman bunu düzeltmeni istiyorum. 'zombie' değil 'normalZombie' olmalı bu zombi türüyle alakalı her şey çünkü benim eskiden sadece 'zombie' dediğim şey artık benim yeni 'normalZombie' dediğim şey. Ayrıca bu normalZombie'ler bana saldırınca normalZombieAttack görselini görmem gerekiyor 0.25 saniyeliğine ama şu anda bunu göremiyorum.
User prompt
Normal zombiler her saniyede bir normalZombieAttack görseli ile karakterime doğru bakarak saldırsın. Eğer zombinin saldırı menziline girerse karakterim hasar alır ama girmezse hasar almaz.
User prompt
Ama şu anda hala "LK.init.image('zombie', {width:140, height:162.17, id:'68404dda054b31d898d6154d'})" şeklinde yüklüyorsun normalZombie'nin assetini. Neden "zombie" yazıyor? "normalZombie" yazması gerekmiyor muydu? Ayrıca şu anda normalZombieAttack görselini hiçbir şekilde göremiyorum bu normal zombiler bana saldırırken, bunu düzeltmen şart! Ben mesela silahımla saldırınca silahımı görebiliyorum, o zombilerin silahı da kendi saldırı assetleri yani normal zombinin silahi normalZombieAttack asseti ve bana onunla hasar veriyor olmalı.
User prompt
Normal zombilerin saldırıları "normalZombieAttack" asseti ile olmalı. Yani normal zombiler bana saldırınca ben bu asseti görmeliyim ve bu asset bana temas edince hasar almalıyım. Şu anda normal zombiyi (normalZombie) olarak değil (zombie) olarak yazmışsın kodda galiba, onu da normalZombie olacak şekilde güncellemen lazım.
User prompt
Şu anda normal zombilerin (ilk dalgada gelen zombi türü) saldırı assetlerini göremiyorum. Zombiler bana saldırabilecek menzile gelince saldırıyor ve hasar alıyorum fakat bana saldırdıkları saldırı assetini göremiyorum.
User prompt
Şu anda normal zombiler bana saldırı yaptığı zaman saldırı assetlerini göremiyorum ama hasar alıyorum. Zombiler bana saldırdıkları zaman ilgili zombilerin saldırı assetlerini görmem gerekiyor. Yani düşmanlar bana saldırırken saldırı assetlerini görebilmeliyim düşmanların.
Code edit (1 edits merged)
Please save this source code
User prompt
Şimdi istiyorum ki ilk dalgada her zombi türünden 1 tane gelsin.
User prompt
"Şu anda molotofu fırlattıktan sonra molotof patlayınca bu molotofun yakma alanından etkilenen zombiler toplamda 1 saniye olacak şekilde her 0.5 saniyede bir hasar almalı. Bu molotofun patladıktan sonraki yakma alanı 1 saniye boyunca yanmaya devam etmeli. Bu süre boyunca bu alana temas eden zombilere de toplamda 1 saniye olacak şekilde her 0.5 saniyede bir hasar vermeli." Şu anda molotof patladıktan sonra molotovFlame'den hasar alan rakipler 1 saniye boyunca her 0.5 saniyede bir hasar almıyor. Sadece bir kere hasar alıyor o kadar. Düzeltmemiz lazım.
User prompt
Şu anda molotofu fırlattıktan sonra molotof patlayınca bu molotofun yakma alanından etkilenen zombiler toplamda 1 saniye olacak şekilde her 0.5 saniyede bir hasar almalı. Bu molotofun patladıktan sonraki yakma alanı 1 saniye boyunca yanmaya devam etmeli. Bu süre boyunca bu alana temas eden zombilere de toplamda 1 saniye olacak şekilde her 0.5 saniyede bir hasar vermeli.
User prompt
MolotovFlame'den hasar alan düşmanlar bu assetten bir kere bile hasar alsa yanarak 1 saniye boyunca 0.5 saniyede bir hasar almalı.
User prompt
Şimdi ben zombinin adını (normalZombie) olarak değiştirmek istiyorum ve bu normal zombinin yanında ekstra olarak tank zombi (tankZombie), hızlı zombi (fastZombie), menzilli zombi (rangedZombie) ve patron zombi (bossZombie) tarzında çeşitli zombi türleri de eklemek istiyorum. 1. Dalgada: 5 normal zombi. (Her dalgadan sonra +1 artar) 3. Dalgada: Normal zombilere ek olarak menzilli zombiler de gelmeye başlar. Normal zombiler + 1 menzilli zombi. (Her dalgadan sonra menzilli zombi sayısı +1 artar) 6. Dalgada: Normal ve menzilli zombilere ek olarak hızlı zombiler de gelmeye başlar. Normal zombiler + menzilli zombiler + 1 hızlı zombi. (Her dalgadan sonra hızlı zombi sayısı +1 artar) 9. Dalgada: Normal, menzilli ve hızlı zombilere ek olarak tank zombi de gelmeye başlar. Normal zombiler + menzilli zombiler + hızlı zombiler + 1 tank zombi. (Her dalgadan sonra tank zombi sayısı +1 artar) 10. Dalgada: Normal, menzilli, hızlı ve tank zombilere ek olarak patron zombi gelir. Normal zombiler + menzilli zombiler + hızlı zombiler + tank zombiler + 1 patron zombi. (Patron zombi her 10 dalgada bir gelir ve her 10 dalgada bir sayısı +1 artar) Şimdi zombilerin özelliklerini söylüyorum: normalZombie: HP: 16 Saldırı Gücü: 8 Hareket Hızı: Normal Saldırı Menzili: 150 Saldırı Hızı: 1 saniyede 1 vuruş. Bu zombi karakterime saldırabileceği menzile girdikten sonra durup 1 saniyede bir karakterime doğru saldırır. Bu saldırı (normalZombieAttack) asseti karakterime çarpınca hasar verir. rangedZombie: HP: 12 Saldırı Gücü: 20 Hareket Hızı: Normal Saldırı Menzili: 800 Saldırı Hızı: 2 saniyede 1 vuruş. Bu zombi karakterime saldırabileceği menzile girdikten sonra durup 2 saniyede bir karakterime doğru dümdüz ilerleyen bir tükürük fırlatır. Bu tükürük (rangedZombieSaliva) asseti hızlı ilerler ve karakterime çarpınca hasar verir. fastZombie: HP: 8 Saldırı Gücü: 10 Hareket Hızı: Çok Hızlı Saldırı Menzili: 100 Saldırı Hızı: 0.5 saniyede 1 vuruş. Bu zombi karakterime saldırabileceği menzile girdikten sonra durup 0.5 saniyede bir karakterime doğru saldırır. Bu saldırı (fastZombieAttack) asseti karakterime çarpınca hasar verir. tankZombie: HP: 80 Saldırı Gücü: 20 Hareket Hızı: Yavaş Saldırı Menzili: 200 Saldırı Hızı: 3 saniyede 1 vuruş. Bu zombi karakterime saldırabileceği menzile girdikten sonra durup 3 saniyede bir karakterime doğru saldırır. Bu saldırı (tankZombieAttack) asseti karakterime çarpınca hasar verir. bossZombie: HP: 400 Saldırı Gücü: Yakın Dövüş modunda 40, Uzak Dövüş modunda 5. Hareket Hızı: Yakın Dövüş modunda Yavaş, Uzak Dövüş modunda Normal. Saldırı Menzili: Yakın Dövüş modunda 200, Uzak Dövüş modunda 1600. Saldırı Hızı: Yakın Dövüş modunda 5 saniyede 1, Uzak Dövüş modunda 0.2 saniyede 1. Bu zombi 10 saniyeliğine yakın dövüşçü, 3 saniyeliğine uzak dövüşçü formu arasında sürekli geçiş yapar. Her geçiş sırasında 1 saniyeliğine hareketsiz kalır. Bu zombi yakın dövüş modunda karakterime saldırabileceği menzile girdikten sonra durup 5 saniyede bir karakterime doğru saldırır. Bu saldırı (bossZombieAttack1) asseti karakterime çarpınca hasar verir. Bu zombi uzak dövüş modunda karakterime saldırabileceği menzile girdikten sonra durup 3 saniye boyunca her 0.2 saniyede bir karakterime doğru kemik fırlatır. Bu kemik (bossZombieAttack2) asseti çok hızlı ilerler ve karakterime çarpınca hasar verir. Ayrıca karakterim bu zombiden çok uzaklaşırsa (2000 menzil kadar) oyun boss'tan çok uzaktasın geri dön diye uyarı vermeli ve 5'den geriye doğru saymalı, eğer 0 olana kadar tekrar boss'a 2000 menzil kadar yaklaşmazsam oyun bitmeli ama yaklaşırsam oyun devam etmeli.
User prompt
Ben poisonZombie diye bir şey istemedim öncelikle. Onu siler misin? Sonrasında sana istediklerimi daha detaylı olarak söyleyeceğim.
User prompt
Diğer zombi türleri için de asset eklemen lazım. Her tür zombinin kendi özel asseti olmalı.
User prompt
Şu anda oyundaki düşman karakterim olan zombiyi zombie olarak mı enemy olarak mı çağırıyorum? Zombie dediğim şey Enemy sınıfının (yani düşmanları içeren sınıf) bir alt sınıfı (Enemey sınıfıntan türetilmiş) olmalı. Çünkü ben oyunda ilerleyen zamanlarda çeşitli çeşitli birbirinden farklı düşmanlar yaratmak isteyebilirim o yüzden bunu düzeltmemde fayda var. Örneğin ben ilerleyen zamanlarda zombinin yanında ekstra olarak tank zombi, hızlı zombi, zehirli zombi, patron zombi tarzında çeşitli zombi türleri de eklemek isteyebilirim. Dolayısıyla bu karakterleri Enemy (düşman) sınıfıntan türetmem daha sağlıklı olur bence. Ona göre kodumu düzenlemen lazım.
User prompt
Oyun başındaki silah seçim ekranı şu an iyi gibi ama level atladığımda gelen seçim kısmı (4 seçenek sunan) kartlar biraz daha (azıcık daha) yakın olması lazım birbirine şu an arada çok mesafe var.
User prompt
Oyunum hala tam ekran değil. Hiçbir şey değişmedi.
User prompt
Şu anda oyunu mobil telefonumda oynamaya çalışıyorum fakat oyun bu senine konuştuğum sekmenin sağındaki küçük bir sekmede başlıyor. Yani oyunu telefonumun tam ekranında oynayamıyorum. Bunu düzeltebilmemin bir yolu var mı?
User prompt
Kartların diklemesine olan mesafesini biraz azalt, yatay olan mesafesini birazcık arttır. Ayrıca oyunumu tam ekran yapabileceğim bir seçenek olmasını istiyorum.
User prompt
Diklemesine olan mesafeyi çok az daha azalt. Ayrıca ilk dalga ben silah seçimini yaptıktan sonra spawn olup gelmeye başlasın.
User prompt
Diklemesine olan mesafeyi biraz daha arttır. Yanlamasına olan mesafeyi biraz daha azalt.
User prompt
Oyunun başlangıcında silah seçimi yaparken kartlar birbirine çok yakın duruyor. Diklemesine ve yanlamasına mesafeyi biraz arttır.
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var storage = LK.import("@upit/storage.v1"); /**** * Classes ****/ var Bomb = Container.expand(function () { var self = Container.call(this); self.active = true; var bombGraphics = self.attachAsset('bomb', { anchorX: 0.5, anchorY: 0.5, width: 60, height: 60, tint: 0xff0000 }); self.update = function () { if (!player || !self.active) { return; } var dx = player.x - self.x; var dy = player.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 80) { self.active = false; // Deal 20 damage to all non-boss enemies on screen for (var i = enemies.length - 1; i >= 0; i--) { var enemy = enemies[i]; var screenDx = Math.abs(enemy.x - player.x); var screenDy = Math.abs(enemy.y - player.y); if (screenDx < 1024 && screenDy < 1366) { // Deal 20 damage, drop exp and remove if dead if (enemy.takeDamage(20)) { dropExpOrb(enemy.x, enemy.y); enemy.destroy(); enemies.splice(i, 1); } } } LK.effects.flashScreen(0xff0000, 300); if (self.parent) { self.destroy(); } return true; } }; return self; }); var Bullet = Container.expand(function () { var self = Container.call(this); self.damage = 10; self.speed = 15; self.dirX = 0; self.dirY = 0; self.piercing = 1; self.hitEnemies = []; var bulletGraphics = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { // Seviye seçim ekranı açıkken hiçbir şey yapma if (skillSelectionActive) { return; } // Hareket this.x += this.dirX * this.speed; this.y += this.dirY * this.speed; // Kameranın (görünen alanın) dışına çıktıysa sil var camMinX = camera.x; var camMaxX = camera.x + 2048; var camMinY = camera.y; var camMaxY = camera.y + 2732; if (this.x < camMinX || this.x > camMaxX || this.y < camMinY || this.y > camMaxY) { this.destroy(); var bi = bullets.indexOf(this); if (bi >= 0) { bullets.splice(bi, 1); } return; } // “Menzil” filtresi: bu top silahının menzili WEAPONS.ball.range var maxRange = WEAPONS.ball.range * (player.weapons && player.weapons.ball ? player.weapons.ball.rngMul : 1); // (Eğer o envanterde yoksa rngMul=1 varsaydık) // Düşmana çarpma kontrolü & menzil filtresi for (var i = enemies.length - 1; i >= 0; i--) { var e = enemies[i]; if (this.intersects(e) && isWithinRange(e, maxRange)) { damageEnemy(e, this.damage); this.destroy(); var bi2 = bullets.indexOf(this); if (bi2 >= 0) { bullets.splice(bi2, 1); } return; } } }; return self; }); // ChickenLeg pickup class var ChickenLeg = Container.expand(function () { var self = Container.call(this); self.active = true; var chickenLegGraphics = self.attachAsset('chickenLeg', { anchorX: 0.5, anchorY: 0.5, width: 60, height: 60 }); self.update = function () { if (!player || !self.active) { return; } var dx = player.x - self.x; var dy = player.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 80) { self.active = false; // Heal player for 30% of max HP, but not above max HP var healAmount = Math.floor(player.maxHp * 0.3); var oldHp = player.hp; player.hp = Math.min(player.hp + healAmount, player.maxHp); var actualHealed = player.hp - oldHp; if (actualHealed > 0) { LK.effects.flashObject(player, 0x00ff00, 300); // Show green HP gain text with black border var healText = new Text2('+' + actualHealed.toFixed(1), { size: 48, fill: 0x00ff00, stroke: 0x000000, strokeThickness: 4 }); healText.anchor.set(0.5); healText.x = player.x; healText.y = player.y - (player.height ? player.height / 2 : 60); healText.alpha = 1; gameContainer.addChild(healText); tween(healText, { y: healText.y - 60, alpha: 0 }, { duration: 500, easing: tween.linear, onFinish: function onFinish() { if (healText.parent) { healText.destroy(); } } }); } LK.getSound('pickup').play(); if (self.parent) { self.destroy(); } return true; } }; return self; }); // Enemy base class var Enemy = Container.expand(function () { var self = Container.call(this); self.hp = 20; self.maxHp = 20; self.damage = 10; self.speed = 2; self.expValue = 5; self.type = 'normal'; self.critChance = 0.25; // Düşmanlar da %25 kritik vuruş şansı ile başlar // Enemy does not attach a graphic directly; subclasses should do this self.takeDamage = function (amount) { self.hp -= amount; LK.effects.flashObject(self, 0xff0000, 200); if (self.hp <= 0) { return true; // Enemy died } return false; // Enemy still alive }; self.update = function () { // Kart seçim ekranı aktifken düşman hareket etmesin ve saldırmasın if (!player || skillSelectionActive) { return; } // Subclasses should implement facing logic if needed // Debuff: Stun if (self.stunBuff && self.stunBuffTime !== undefined) { var stunDuration = Math.floor(self.stunBuff * 60); // seconds to frames if (gameTime - self.stunBuffTime < stunDuration) { // Stunned: do not move or attack return; } else { self.stunBuff = 0; self.stunBuffTime = undefined; } } // 1) Oyuncuya olan vektörü bul var dx = player.x - self.x; var dy = player.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); // Debuff: Slow var speedMul = 1; if (self.slowBuff && self.slowBuffTime !== undefined) { var slowDuration = 120; // 2 seconds if (gameTime - self.slowBuffTime < slowDuration) { speedMul = Math.max(0, 1 - self.slowBuff / 100); } else { self.slowBuff = 0; self.slowBuffTime = undefined; } } // 2) Eğer düşman, oyuncuya 150 pikselden yakınsa hareket etme if (dist > 150) { // 3) Aksi halde 50px uzaktan hareket et self.x += dx / dist * self.speed * speedMul; self.y += dy / dist * self.speed * speedMul; } // Bleeding+ if (self.bleedBuff && self.bleedBuffTime !== undefined) { var bleedDuration = 240; // 4 seconds (60fps) if (gameTime - self.bleedBuffTime < bleedDuration) { // Every 30 frames (0.5s), apply bleed damage if (!self.bleedTicks) { self.bleedTicks = 0; } var ticks = Math.floor((gameTime - self.bleedBuffTime) / 30); while (self.bleedTicks < ticks) { var bleedDmg = self.maxHp * (self.bleedBuff / 100); // allow fractional self.hp -= bleedDmg; LK.effects.flashObject(self, 0xaa0000, 100); // Show damage text for bleeding (always, even if <1) var dmgText = new Text2(bleedDmg.toFixed(1), { size: 48, fill: 0x00ff00, // green for regen, but for bleed use red stroke: 0x000000, strokeThickness: 4 }); dmgText.setText('-' + bleedDmg.toFixed(1)); dmgText.fill = 0xff3333; dmgText.anchor.set(0.5); dmgText.x = self.x; dmgText.y = self.y - (self.height ? self.height / 2 : 60); dmgText.alpha = 1; gameContainer.addChild(dmgText); tween(dmgText, { y: dmgText.y - 60, alpha: 0 }, { duration: 500, easing: tween.linear, onFinish: function onFinish() { if (dmgText.parent) { dmgText.destroy(); } } }); if (self.hp <= 0) { if (self.parent) { self.destroy(); } break; } self.bleedTicks++; } } else { self.bleedBuff = 0; self.bleedBuffTime = undefined; self.bleedTicks = 0; } } // Grit Wound+ if (self.gritWound && self.lastGritWoundTime !== undefined) { var gritDuration = 240; // 4 seconds if (gameTime - self.lastGritWoundTime < gritDuration) { // No regen logic here, but you can use self.gritWound for regen reduction elsewhere // Example: If you add enemy regen, show green text here if (self.enemyRegen && self.hp < self.maxHp) { var regenAmount = self.maxHp * (self.enemyRegen / 100); if (self.gritWound > 0) { regenAmount = regenAmount * (1 - self.gritWound / 100); } var oldHp = self.hp; self.hp = Math.min(self.hp + regenAmount, self.maxHp); var actualHealed = self.hp - oldHp; if (actualHealed > 0) { // Show green HP gain text with black border var healText = new Text2('+' + actualHealed.toFixed(1), { size: 48, fill: 0x00ff00, stroke: 0x000000, strokeThickness: 4 }); healText.anchor.set(0.5); healText.x = self.x; healText.y = self.y - (self.height ? self.height / 2 : 60); healText.alpha = 1; gameContainer.addChild(healText); tween(healText, { y: healText.y - 60, alpha: 0 }, { duration: 500, easing: tween.linear, onFinish: function onFinish() { if (healText.parent) { healText.destroy(); } } }); } } } else { self.gritWound = 0; self.lastGritWoundTime = undefined; } } // --- Enemy HP regen after wave 5, scaling with wave --- if (self.enemyRegen && self.enemyRegen > 0 && (!self.gritWound || self.gritWound === 0)) { if (!self.lastEnemyRegenTime) { self.lastEnemyRegenTime = 0; } if (gameTime - self.lastEnemyRegenTime >= 300) { // every 5s var regenAmount = self.maxHp * (self.enemyRegen / 100); var oldHp = self.hp; self.hp = Math.min(self.hp + regenAmount, self.maxHp); var actualHealed = self.hp - oldHp; if (actualHealed > 0) { // Show green HP gain text with black border var healText = new Text2('+' + actualHealed.toFixed(1), { size: 48, fill: 0x00ff00, stroke: 0x000000, strokeThickness: 4 }); healText.anchor.set(0.5); healText.x = self.x; healText.y = self.y - (self.height ? self.height / 2 : 60); healText.alpha = 1; gameContainer.addChild(healText); tween(healText, { y: healText.y - 60, alpha: 0 }, { duration: 500, easing: tween.linear, onFinish: function onFinish() { if (healText.parent) { healText.destroy(); } } }); } self.lastEnemyRegenTime = gameTime; } } // Burada asla destroy() yok; sadece hareketi sınırlıyoruz }; return self; }); // Tank Zombie subclass var TankZombie = Enemy.expand(function () { var self = Enemy.call(this); var tankGraphics = self.attachAsset('tankZombie', { anchorX: 0.5, anchorY: 0.5 }); self.maxHp = 80; self.hp = 80; self.damage = 20; self.speed = 1.0; self.attackRange = 200; self.attackCooldown = 180; // 3 saniye (60fps) self.attackTimer = 0; self.type = 'tankZombie'; self.update = function (origUpdate) { return function () { // Kart seçim ekranı aktifken tank zombi hareket etmesin ve saldırmasın if (skillSelectionActive) { return; } if (self.x < player.x) { tankGraphics.scaleX = 1; } else if (self.x > player.x) { tankGraphics.scaleX = -1; } var dx = player.x - self.x; var dy = player.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > self.attackRange) { var speedMul = 1; if (self.slowBuff && self.slowBuffTime !== undefined) { var slowDuration = 120; if (gameTime - self.slowBuffTime < slowDuration) { speedMul = Math.max(0, 1 - self.slowBuff / 100); } } self.x += dx / dist * self.speed * speedMul; self.y += dy / dist * self.speed * speedMul; self.attackTimer = 0; } else { self.attackTimer++; if (self.attackTimer >= self.attackCooldown) { self.attackTimer = 0; var atk = gameContainer.addChild(new Container()); atk.attachAsset('tankZombieAttack', { anchorX: 0.5, anchorY: 0.5 }); atk.x = self.x + dx / dist * 70; atk.y = self.y + dy / dist * 70; atk.life = 16; atk.update = function () { this.life--; if (this.life <= 0) { if (this.parent) { this.destroy(); } return; } if (this.intersects(player)) { player.takeDamage(self.damage); if (this.parent) { this.destroy(); } } }; } } origUpdate.call(self); }; }(self.update); return self; }); // Ranged Zombie subclass var RangedZombie = Enemy.expand(function () { var self = Enemy.call(this); var rangedGraphics = self.attachAsset('rangedZombie', { anchorX: 0.5, anchorY: 0.5 }); self.maxHp = 12; self.hp = 12; self.damage = 20; self.speed = 2.0; self.attackRange = 1000; self.attackCooldown = 120; // 2 saniye (60fps) self.attackTimer = 0; self.type = 'rangedZombie'; self.update = function (origUpdate) { return function () { // Kart seçim ekranı aktifken menzilli zombi hareket etmesin ve saldırmasın if (skillSelectionActive) { return; } // Flip to face player if (self.x < player.x) { rangedGraphics.scaleX = 1; } else if (self.x > player.x) { rangedGraphics.scaleX = -1; } var dx = player.x - self.x; var dy = player.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > self.attackRange) { // Move towards player var speedMul = 1; if (self.slowBuff && self.slowBuffTime !== undefined) { var slowDuration = 120; if (gameTime - self.slowBuffTime < slowDuration) { speedMul = Math.max(0, 1 - self.slowBuff / 100); } } self.x += dx / dist * self.speed * speedMul; self.y += dy / dist * self.speed * speedMul; self.attackTimer = 0; } else { // Attack every 2s self.attackTimer++; if (self.attackTimer >= self.attackCooldown) { self.attackTimer = 0; // Fire saliva projectile towards player var saliva = gameContainer.addChild(new Container()); saliva.attachAsset('rangedZombieSaliva', { anchorX: 0.5, anchorY: 0.5 }); saliva.x = self.x; saliva.y = self.y; var dirX = dx / dist; var dirY = dy / dist; saliva.speed = 18; saliva.update = function () { this.x += dirX * saliva.speed; this.y += dirY * saliva.speed; // If hits player, deal damage if (this.intersects(player)) { player.takeDamage(self.damage); if (this.parent) { this.destroy(); } } // Remove if out of camera var camMinX = camera.x; var camMaxX = camera.x + 2048; var camMinY = camera.y; var camMaxY = camera.y + 2732; if (this.x < camMinX || this.x > camMaxX || this.y < camMinY || this.y > camMaxY) { if (this.parent) { this.destroy(); } } }; } } origUpdate.call(self); }; }(self.update); return self; }); // Normal Zombie subclass (renamed from Zombie) var NormalZombie = Enemy.expand(function () { var self = Enemy.call(this); var zombieGraphics = self.attachAsset('normalZombie', { anchorX: 0.5, anchorY: 0.5 }); self.maxHp = 16; self.hp = 16; self.damage = 8; self.speed = 2.0; self.attackRange = 150; self.attackCooldown = 60; // 1 saniye (60fps) self.attackTimer = 0; self.type = 'normalZombie'; self.update = function (origUpdate) { return function () { // Kart seçim ekranı aktifken normal zombi hareket etmesin ve saldırmasın if (skillSelectionActive) { return; } // Flip to face player if (self.x < player.x) { zombieGraphics.scaleX = 1; } else if (self.x > player.x) { zombieGraphics.scaleX = -1; } // Move or attack var dx = player.x - self.x; var dy = player.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > self.attackRange) { // Move towards player var speedMul = 1; if (self.slowBuff && self.slowBuffTime !== undefined) { var slowDuration = 120; if (gameTime - self.slowBuffTime < slowDuration) { speedMul = Math.max(0, 1 - self.slowBuff / 100); } } self.x += dx / dist * self.speed * speedMul; self.y += dy / dist * self.speed * speedMul; self.attackTimer = 0; } else { // Attack every 1s self.attackTimer++; if (self.attackTimer >= self.attackCooldown) { self.attackTimer = 0; // Attack: spawn attack asset towards player var atk = gameContainer.addChild(new Container()); atk.attachAsset('normalZombieAttack', { anchorX: 0.5, anchorY: 0.5 }); atk.x = self.x + dx / dist * 60; atk.y = self.y + dy / dist * 60; atk.life = 15; // 0.25 saniye (15 frame) atk.update = function () { this.life--; if (this.life <= 0) { if (this.parent) { this.destroy(); } return; } // If hits player, deal damage if (this.intersects(player)) { player.takeDamage(self.damage); if (this.parent) { this.destroy(); } } }; } } origUpdate.call(self); }; }(self.update); return self; }); // Fast Zombie subclass var FastZombie = Enemy.expand(function () { var self = Enemy.call(this); var fastGraphics = self.attachAsset('fastZombie', { anchorX: 0.5, anchorY: 0.5 }); self.maxHp = 8; self.hp = 8; self.damage = 10; self.speed = 5.0; self.attackRange = 100; self.attackCooldown = 30; // 0.5 saniye (60fps) self.attackTimer = 0; self.type = 'fastZombie'; self.update = function (origUpdate) { return function () { // Kart seçim ekranı aktifken hızlı zombi hareket etmesin ve saldırmasın if (skillSelectionActive) { return; } if (self.x < player.x) { fastGraphics.scaleX = 1; } else if (self.x > player.x) { fastGraphics.scaleX = -1; } var dx = player.x - self.x; var dy = player.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > self.attackRange) { var speedMul = 1; if (self.slowBuff && self.slowBuffTime !== undefined) { var slowDuration = 120; if (gameTime - self.slowBuffTime < slowDuration) { speedMul = Math.max(0, 1 - self.slowBuff / 100); } } self.x += dx / dist * self.speed * speedMul; self.y += dy / dist * self.speed * speedMul; self.attackTimer = 0; } else { self.attackTimer++; if (self.attackTimer >= self.attackCooldown) { self.attackTimer = 0; var atk = gameContainer.addChild(new Container()); atk.attachAsset('fastZombieAttack', { anchorX: 0.5, anchorY: 0.5 }); atk.x = self.x + dx / dist * 50; atk.y = self.y + dy / dist * 50; atk.life = 8; atk.update = function () { this.life--; if (this.life <= 0) { if (this.parent) { this.destroy(); } return; } if (this.intersects(player)) { player.takeDamage(self.damage); if (this.parent) { this.destroy(); } } }; } } origUpdate.call(self); }; }(self.update); return self; }); // Boss Zombie subclass var BossZombie = Enemy.expand(function () { var self = Enemy.call(this); var bossGraphics = self.attachAsset('bossZombie', { anchorX: 0.5, anchorY: 0.5 }); self.maxHp = 400; self.hp = 400; self.type = 'bossZombie'; self.mode = 'melee'; // 'melee' or 'ranged' self.modeTimer = 0; self.modeSwitchDelay = 0; self.attackTimer = 0; self.attackCooldownMelee = 300; // 5s self.attackCooldownRanged = 24; // 0.4s self.attackRangeMelee = 200; self.attackRangeRanged = 1600; self.damageMelee = 40; self.damageRanged = 5; self.speedMelee = 1.0; self.speedRanged = 2.0; self.modeDurationMelee = 600; // 10s self.modeDurationRanged = 180; // 3s self.modeSwitchPause = 60; // 1s self.lastWarningTime = 0; self.warningCountdown = 0; self.warningActive = false; self.warningText = null; self.update = function (origUpdate) { return function () { // Kart seçim ekranı aktifken boss hareket etmesin ve saldırmasın if (skillSelectionActive) { return; } // Flip to face player if (self.x < player.x) { bossGraphics.scaleX = 1; } else if (self.x > player.x) { bossGraphics.scaleX = -1; } // Mode switching logic if (self.modeSwitchDelay > 0) { self.modeSwitchDelay--; // 1s pause, do not move or attack origUpdate.call(self); return; } self.modeTimer++; if (self.mode === 'melee' && self.modeTimer >= self.modeDurationMelee) { self.mode = 'ranged'; self.modeTimer = 0; self.modeSwitchDelay = self.modeSwitchPause; } else if (self.mode === 'ranged' && self.modeTimer >= self.modeDurationRanged) { self.mode = 'melee'; self.modeTimer = 0; self.modeSwitchDelay = self.modeSwitchPause; } var dx = player.x - self.x; var dy = player.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); // Boss warning if player is too far var bossDist = Math.sqrt((self.x - player.x) * (self.x - player.x) + (self.y - player.y) * (self.y - player.y)); if (bossDist > 2000) { if (!self.warningActive) { self.warningActive = true; self.warningCountdown = 5; self.lastWarningTime = gameTime; if (!self.warningText) { self.warningText = new Text2('Boss\'tan çok uzaktasın! 5', { size: 80, fill: 0xff0000 }); self.warningText.anchor.set(0.5, 0); self.warningText.y = 300; LK.gui.top.addChild(self.warningText); } } else { if (gameTime - self.lastWarningTime >= 60) { self.warningCountdown--; self.lastWarningTime = gameTime; if (self.warningText) { self.warningText.setText('Boss\'tan çok uzaktasın! ' + self.warningCountdown); } } if (self.warningCountdown <= 0) { if (self.warningText && self.warningText.parent) { self.warningText.destroy(); } LK.showGameOver(); return; } } } else if (self.warningActive) { self.warningActive = false; if (self.warningText && self.warningText.parent) { self.warningText.destroy(); } self.warningText = null; } // Movement and attack logic if (self.mode === 'melee') { // Melee mode: slow, short range, high damage if (dist > self.attackRangeMelee) { var speedMul = 1; if (self.slowBuff && self.slowBuffTime !== undefined) { var slowDuration = 120; if (gameTime - self.slowBuffTime < slowDuration) { speedMul = Math.max(0, 1 - self.slowBuff / 100); } } self.x += dx / dist * self.speedMelee * speedMul; self.y += dy / dist * self.speedMelee * speedMul; self.attackTimer = 0; } else { self.attackTimer++; if (self.attackTimer >= self.attackCooldownMelee) { self.attackTimer = 0; var atk = gameContainer.addChild(new Container()); atk.attachAsset('bossZombieAttack1', { anchorX: 0.5, anchorY: 0.5 }); atk.x = self.x + dx / dist * 90; atk.y = self.y + dy / dist * 90; atk.life = 20; atk.update = function () { this.life--; if (this.life <= 0) { if (this.parent) { this.destroy(); } return; } if (this.intersects(player)) { player.takeDamage(self.damageMelee); if (this.parent) { this.destroy(); } } }; } } } else if (self.mode === 'ranged') { // Ranged mode: normal speed, long range, low damage, fires bones if (dist > self.attackRangeRanged) { var speedMul = 1; if (self.slowBuff && self.slowBuffTime !== undefined) { var slowDuration = 120; if (gameTime - self.slowBuffTime < slowDuration) { speedMul = Math.max(0, 1 - self.slowBuff / 100); } } self.x += dx / dist * self.speedRanged * speedMul; self.y += dy / dist * self.speedRanged * speedMul; self.attackTimer = 0; } else { self.attackTimer++; if (self.attackTimer % self.attackCooldownRanged === 0) { // Fire bone projectile var bone = gameContainer.addChild(new Container()); bone.attachAsset('bossZombieAttack2', { anchorX: 0.5, anchorY: 0.5 }); bone.x = self.x; bone.y = self.y; var dirX = dx / dist; var dirY = dy / dist; bone.speed = 28; bone.update = function () { this.x += dirX * bone.speed; this.y += dirY * bone.speed; if (this.intersects(player)) { player.takeDamage(self.damageRanged); if (this.parent) { this.destroy(); } } var camMinX = camera.x; var camMaxX = camera.x + 2048; var camMinY = camera.y; var camMaxY = camera.y + 2732; if (this.x < camMinX || this.x > camMaxX || this.y < camMinY || this.y > camMaxY) { if (this.parent) { this.destroy(); } } }; } } } origUpdate.call(self); }; }(self.update); return self; }); var ExpOrb = Container.expand(function () { var self = Container.call(this); // 1) spawnTime kaydet self.spawnTime = gameTime; self.value = 1; // Each orb always gives 1 EXP self.magnetSpeed = 0; var orbGraphics = self.attachAsset('expOrb', { anchorX: 0.5, anchorY: 0.5 }); self.update = function () { // ORB YAŞI (frame cinsinden) var age = gameTime - self.spawnTime; // 2) Eğer 10 saniye geçmişse (600 frame), orb’u yok et ve listeden çıkar if (age >= 600) { // yok et if (self.parent) { self.destroy(); } var idx = expOrbs.indexOf(self); if (idx >= 0) { expOrbs.splice(idx, 1); } return; } // 3) Son 5 saniyede (age >= 300), yanıp sönme uygulaması if (age >= 300) { // kalan süre var remaining = 600 - age; // 300’den 0’a kadar azalır // blink periyodu: remaining / 50’ye bağlı (en hızlı 1 frame’lik periyot) var blinkInterval = Math.ceil(remaining / 50); if (blinkInterval < 1) { blinkInterval = 1; } // yaşa göre çift/tek periyoda bölerek görünürlüğü değiştir var phase = Math.floor(age / blinkInterval); self.visible = phase % 2 === 0; } else { // son 5 saniye değilse görünür olsun self.visible = true; } // 4) Normal manyetik çekim hareketi if (!player || self.magnetSpeed === 0) { return; } var dx = player.x - self.x; var dy = player.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { self.x += dx / dist * self.magnetSpeed; self.y += dy / dist * self.magnetSpeed; } }; return self; }); var Joystick = Container.expand(function () { var self = Container.call(this); self.active = false; self.dirX = 0; self.dirY = 0; var base = self.attachAsset('joystickBase', { anchorX: 0.5, anchorY: 0.5 }); base.alpha = 0.5; var knob = self.attachAsset('joystickKnob', { anchorX: 0.5, anchorY: 0.5 }); knob.alpha = 0.7; self.setKnobPosition = function (x, y) { var dx = x - base.x; var dy = y - base.y; var dist = Math.sqrt(dx * dx + dy * dy); var maxDist = 60; if (dist > maxDist) { dx = dx / dist * maxDist; dy = dy / dist * maxDist; } knob.x = dx; knob.y = dy; // Defensive: avoid division by zero if (maxDist === 0) { self.dirX = 0; self.dirY = 0; } else { self.dirX = dx / maxDist; self.dirY = dy / maxDist; } }; self.reset = function () { knob.x = 0; knob.y = 0; self.dirX = 0; self.dirY = 0; self.active = false; }; return self; }); var Magnet = Container.expand(function () { var self = Container.call(this); self.active = true; var magnetGraphics = self.attachAsset('magnet', { anchorX: 0.5, anchorY: 0.5, width: 60, height: 60, tint: 0x0099ff }); self.update = function () { if (!player || !self.active) { return; } var dx = player.x - self.x; var dy = player.y - self.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 80) { self.active = false; // Magnetize all exp orbs on screen for (var i = 0; i < expOrbs.length; i++) { var orb = expOrbs[i]; var orbDx = Math.abs(orb.x - player.x); var orbDy = Math.abs(orb.y - player.y); if (orbDx < 1024 && orbDy < 1366) { orb.magnetSpeed = 20; } } if (self.parent) { self.destroy(); } return true; } }; return self; }); var Player = Container.expand(function () { var self = Container.call(this); self.level = 1; self.exp = 0; self.expToNext = 10; self.hp = 100; self.maxHp = 100; self.damage = 10.0; self.fireRate = 30; self.bulletSpeed = 15; self.moveSpeed = 5; self.pickupRange = 100; self.lastFire = 0; self.critChance = 0.25; // Oyuncu başlangıçta %25 kritik vuruş şansı /* ===== BUFFS ===== */ self.buffs = { damageReduction: 0, // percent, e.g. 10 means 10% moveSpeed: 0, // percent, e.g. 20 means 20% hpRegen: 0, // percent per 5s damageReflection: 0, // percent gritWound: 0, // percent enemySlow: 0, // percent enemyStun: 0, // seconds bleeding: 0, // percent per tick critChance: 0 // Critical Hit Chance+ buff, percent (15 means +15%) }; self.lastRegenTime = 0; /* ===== WEAPON INVENTORY ===== */ self.weapons = {}; // key → instance var playerGraphics = self.attachAsset('player', { anchorX: 0.5, anchorY: 0.5 }); self.takeDamage = function (amount) { // Damage Reduction+ var finalAmount = amount; if (self.buffs.damageReduction > 0) { finalAmount = Math.ceil(amount * (1 - self.buffs.damageReduction / 100)); } self.hp -= finalAmount; LK.effects.flashObject(self, 0xff0000, 300); LK.getSound('damageTaken').play(); // Damage Reflection+ if (self.buffs.damageReflection > 0 && enemies.length > 0) { // Find nearest enemy in contact (or just first in range) for (var i = 0; i < enemies.length; i++) { var e = enemies[i]; var dx = e.x - self.x; var dy = e.y - self.y; var distSq = dx * dx + dy * dy; if (distSq <= 22500) { // same as contact check var reflectDmg = Math.ceil(finalAmount * self.buffs.damageReflection / 100); if (reflectDmg > 0) { damageEnemy(e, reflectDmg); // Show damage text for reflection var dmgText = new Text2(reflectDmg.toFixed(1), { size: 48, fill: 0xffffff, stroke: 0x000000, strokeThickness: 4 }); dmgText.anchor.set(0.5); dmgText.x = e.x; dmgText.y = e.y - (e.height ? e.height / 2 : 60); dmgText.alpha = 1; gameContainer.addChild(dmgText); tween(dmgText, { y: dmgText.y - 60, alpha: 0 }, { duration: 500, easing: tween.linear, onFinish: function onFinish() { if (dmgText.parent) { dmgText.destroy(); } } }); } break; } } } if (self.hp <= 0) { LK.showGameOver(); } }; self.gainExp = function (amount) { // Add exp orbs self.exp += amount; // Level up if exp orbs >= current level while (self.exp >= self.level) { self.exp -= self.level; self.levelUp(); } }; self.update = function () { // Kart seçim ekranı aktifken karakter hareket etmesin ve saldırmasın if (skillSelectionActive) { return; } // Flip player graphic based on movement direction if (self.lastX === undefined) { self.lastX = self.x; } if (self.x > self.lastX) { playerGraphics.scaleX = 1; } else if (self.x < self.lastX) { playerGraphics.scaleX = -1; } self.lastX = self.x; // Movement Speed+ if (self.buffs.moveSpeed > 0) { self.moveSpeed = 5 * (1 + self.buffs.moveSpeed / 100); } else { self.moveSpeed = 5; } // HP Regeneration+ if (self.buffs.hpRegen > 0 && gameTime - (self.lastRegenTime || 0) >= 300) { var regenAmount = self.maxHp * (self.buffs.hpRegen / 100); // allow fractional regen if (regenAmount > 0 && self.hp < self.maxHp) { var oldHp = self.hp; self.hp = Math.min(self.hp + regenAmount, self.maxHp); var actualHealed = self.hp - oldHp; if (actualHealed > 0) { LK.effects.flashObject(self, 0x00ff00, 200); // Show green HP gain text with black border var healText = new Text2('+' + actualHealed.toFixed(1), { size: 48, fill: 0x00ff00, stroke: 0x000000, strokeThickness: 4 }); healText.anchor.set(0.5); healText.x = self.x; healText.y = self.y - (self.height ? self.height / 2 : 60); healText.alpha = 1; gameContainer.addChild(healText); tween(healText, { y: healText.y - 60, alpha: 0 }, { duration: 500, easing: tween.linear, onFinish: function onFinish() { if (healText.parent) { healText.destroy(); } } }); } } self.lastRegenTime = gameTime; } for (var k in self.weapons) { var inst = self.weapons[k], meta = WEAPONS[k]; if (--inst.cdCnt <= 0) { fireWeapon(k, inst, meta); // inst.cd muhtemelen kesirsiz olmayabilir; istersen Math.floor ile yuvarla: inst.cdCnt = Math.floor(inst.cd); } } }; self.levelUp = function () { self.level++; // self.exp is now handled in gainExp, do not reset here self.expToNext = self.level * 15; // self.maxHp += 10; // Artık seviye atlayınca max HP artmıyor // Do NOT restore full health on level up // self.hp = self.maxHp; // Increase movement speed by 4% per level up self.moveSpeed *= 1.04; LK.getSound('levelup').play(); showSkillSelection(); }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x111111 }); /**** * Game Code ****/ // --- New zombie attack/projectile assets --- /* ==== UNIVERSAL HELPERS ==== */ function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } function isWithinRange(e, maxRange) { var dx = e.x - player.x; var dy = e.y - player.y; return dx * dx + dy * dy <= maxRange * maxRange; } function addWeapon(key) { // <-- artık global if (player && Object.keys(player.weapons).length >= 6) { return; // Zaten 6 silah varsa yeni eklemeye izin verme } var m = WEAPONS[key]; var target = player ? player : { weapons: {} }; // sınıf kurulumunda da çalışsın target.weapons[key] = { cd: m.cd, cdCnt: m.cd, dmgMul: 1, rngMul: 1, amount: 1, upgrade: 0, enchTaken: [] }; } function rand(min, max) { return min + Math.random() * (max - min); } function distSq(a, b) { var dx = a.x - b.x, dy = a.y - b.y; return dx * dx + dy * dy; } function unitDir(from, to) { var dx = to.x - from.x, dy = to.y - from.y, d = Math.sqrt(dx * dx + dy * dy); return { dx: dx / d, dy: dy / d, len: d }; } /* ==== WEAPON META TABLE ==== */ var WEAPONS = { sword: { cd: 120, dmg: 6, range: 200, type: 'melee', ench: ["Sword Damage+", "Sword Attack Speed+", "Sword Range+"], up: ["Sword +1", "Sword +2", "Sword +3"] }, boomerang: { cd: 180, dmg: 6, range: 750, type: 'boomerang', ench: ["Boomerang Damage+", "Boomerang Attack Speed+", "Boomerang Range+"], up: ["Boomerang +1", "Boomerang +2", "Boomerang +3"] }, ball: { cd: 180, dmg: 6, range: 800, type: 'ball', ench: ["Ball Damage+", "Ball Attack Speed+", "Ball Range+"], up: ["Ball +1", "Ball +2", "Ball +3"] }, rocket: { cd: 480, dmg: 16, range: 900, type: 'rocket', ench: ["Rocket Damage+", "Rocket Attack Speed+", "Rocket Range+"], up: ["Rocket +1", "Rocket +2", "Rocket +3"] }, brick: { cd: 240, dmg: 8, range: 650, type: 'brick', ench: ["Brick Damage+", "Brick Attack Speed+", "Brick Range+"], up: ["Back 75°", "Front 45°", "Back 45°"] }, lightning: { cd: 240, dmg: 16, range: 900, type: 'lightning', ench: ["Lightning Damage+", "Lightning Attack Speed+", "Lightning Range+"], up: ["Lightning +1", "Lightning +2", "Lightning +3"] }, aura: { cd: 60, dmg: 2, range: 250, type: 'aura', ench: ["Aura Damage+", "Aura Attack Speed+", "Aura Range+"], up: ["Slow 25%", "Slow 50%", "Slow 75%"] }, molotov: { cd: 180, dmg: 6, range: 600, type: 'molotov', ench: ["Molotov Damage+", "Molotov Attack Speed+", "Molotov Range+"], up: ["Molotov +1", "Molotov +2", "Molotov +3"] }, shuriken: { cd: 180, dmg: 6, range: 350, type: 'shuriken', ench: ["Shuriken Damage+", "Shuriken Attack Speed+", "Shuriken Range+"], up: ["Shuriken +1", "Shuriken +2", "Shuriken +3"] } }; var player; var enemies = []; var bullets = []; var expOrbs = []; var joystick; var gameTime = 0; var waveNumber = 1; // bossSpawned variable removed; boss is now spawned every 10th wave var selectedSkills = []; var camera = { x: 0, y: 0 }; var gameContainer; var skillSelectionActive = false; var pickups = []; var lastPickupSpawn = 0; // Initialize game world gameContainer = game.addChild(new Container()); // Tile seamless background image to cover the visible area and follow the camera var bgTileAsset = LK.getAsset('seamlessBackGround', { anchorX: 0, anchorY: 0 }); var bgTileWidth = bgTileAsset.width; var bgTileHeight = bgTileAsset.height; var bgTilesX = Math.ceil(2048 / bgTileWidth) + 2; // +2 for safe margin var bgTilesY = Math.ceil(2732 / bgTileHeight) + 2; var backgroundTiles = []; for (var i = 0; i < bgTilesX; i++) { for (var j = 0; j < bgTilesY; j++) { var tile = LK.getAsset('seamlessBackGround', { anchorX: 0, anchorY: 0 }); tile.x = i * bgTileWidth; tile.y = j * bgTileHeight; gameContainer.addChild(tile); backgroundTiles.push(tile); } } // Create player player = gameContainer.addChild(new Player()); player.x = 1024; player.y = 1366; // --- Weapon selection overlay at game start --- var weaponSelectionActive = true; var weaponSelectionOverlay = game.addChild(new Container()); weaponSelectionOverlay.x = 1024; weaponSelectionOverlay.y = 1366; // List of all 9 weapons var allWeaponKeys = Object.keys(WEAPONS); // Show all 9 weapons as selectable cards for (var i = 0; i < allWeaponKeys.length; i++) { (function (idx) { var key = allWeaponKeys[idx]; var card = weaponSelectionOverlay.addChild(new Container()); card.attachAsset('skillCard', { anchorX: 0.5, anchorY: 0.5 }); // Arrange in 3x3 grid, adjust spacing: horizontal 420, vertical 700 (slightly more horizontal, less vertical) var col = idx % 3; var row = Math.floor(idx / 3); card.x = (col - 1) * 420; card.y = (row - 1) * 700; // Weapon name var txt = new Text2(key.charAt(0).toUpperCase() + key.slice(1), { size: 48, fill: 0xffffff }); txt.anchor.set(0.5); card.addChild(txt); // On select: give only this weapon, remove overlay, start game card.down = function () { // Remove all weapons player.weapons = {}; addWeapon(key); weaponSelectionActive = false; weaponSelectionOverlay.destroy(); skillSelectionActive = false; // Spawn first wave after weapon selection spawnWave(); }; })(i); } // Block all input except weapon selection until chosen skillSelectionActive = true; // --- Health bar for player --- // Rectangle health bar (depletes right to left) player.hpBar = new Container(); // Remove shadow/overlay: set bg alpha lower so it doesn't affect fg color player.hpBarBg = LK.getAsset('hpBarRect', { anchorX: 0, anchorY: 0.5 }); player.hpBarBg.alpha = 0.3; // Lower alpha so fg color is not affected player.hpBarFg = LK.getAsset('hpBarRectFg', { anchorX: -0.02, anchorY: 0.5 }); player.hpBar.addChild(player.hpBarBg); player.hpBar.addChild(player.hpBarFg); player.hpBar.y = -140; player.hpBar.x = -110; // align left edge to player center player.addChild(player.hpBar); // --- Health bar for enemies will be created on spawnEnemy --- // Create joystick joystick = new Joystick(); joystick.x = 0; joystick.y = -200; LK.gui.bottom.addChild(joystick); // UI Elements var timerText = new Text2('00:00', { size: 80, fill: 0xFFFFFF }); timerText.anchor.set(0.5, 0); LK.gui.top.addChild(timerText); // Add wave number display to top center var waveText = new Text2('Wave 1', { size: 80, fill: 0x00FFFF }); waveText.anchor.set(0.5, 0); waveText.y = 100; LK.gui.top.addChild(waveText); // (Fullscreen button removed: not supported in sandbox/mobile, and does not affect mobile fullscreen) var levelText = new Text2('Level 1', { size: 60, fill: 0xFFFF00 }); levelText.anchor.set(0, 0); levelText.y = 10; levelText.x = 200; LK.gui.top.addChild(levelText); var hpText = new Text2('HP: 100/100', { size: 60, fill: 0xFF0000 }); hpText.anchor.set(1, 0); hpText.y = 10; hpText.x = -200; LK.gui.top.addChild(hpText); // Spawn functions function spawnEnemy(type, x, y) { var enemy; if (type === 'normal' || type === 'normalZombie') { enemy = new NormalZombie(); } else if (type === 'rangedZombie') { enemy = new RangedZombie(); } else if (type === 'fastZombie') { enemy = new FastZombie(); } else if (type === 'tankZombie') { enemy = new TankZombie(); } else if (type === 'bossZombie') { enemy = new BossZombie(); } else { enemy = new NormalZombie(); } enemy.x = x; enemy.y = y; // --- Düşman can yenileme: 5. dalgadan sonra, her 5 dalgada +1% artar --- if (waveNumber > 5) { var regenPercent = 5 + Math.floor((waveNumber - 6) / 5) + 0; enemy.enemyRegen = regenPercent; } else { enemy.enemyRegen = 0; } enemies.push(enemy); gameContainer.addChild(enemy); // --- Health bar for enemy --- enemy.hpBar = new Container(); enemy.hpBarBg = LK.getAsset('enemyHpBarRect', { anchorX: 0.0, anchorY: 0.5 }); enemy.hpBarBg.alpha = 0.3; enemy.hpBarFg = LK.getAsset('enemyHpBarRectFg', { anchorX: -0.04, anchorY: 0.5 }); enemy.hpBar.addChild(enemy.hpBarBg); enemy.hpBar.addChild(enemy.hpBarFg); enemy.hpBar.y = -110; enemy.hpBar.x = -80; enemy.addChild(enemy.hpBar); } function spawnWave() { var spawnRadius = 1500; // 1. dalga: 5 normal zombi, her dalgada +1 artar var normalCount = 5 + (waveNumber - 1); // 3. dalgada menzilli zombi başlar, her dalgada +1 artar var rangedCount = waveNumber >= 3 ? waveNumber - 2 : 0; // 6. dalgada hızlı zombi başlar, her dalgada +1 artar var fastCount = waveNumber >= 6 ? waveNumber - 5 : 0; // 9. dalgada tank zombi başlar, her dalgada +1 artar var tankCount = waveNumber >= 9 ? waveNumber - 8 : 0; // 10. dalgada boss zombi başlar, her 10 dalgada bir +1 artar var bossCount = waveNumber % 10 === 0 ? Math.floor(waveNumber / 10) : 0; // Normal zombiler for (var i = 0; i < normalCount; i++) { var angle = Math.random() * Math.PI * 2; var x = player.x + Math.cos(angle) * spawnRadius; var y = player.y + Math.sin(angle) * spawnRadius; spawnEnemy('normalZombie', x, y); } // Ranged zombiler for (var i = 0; i < rangedCount; i++) { var angle = Math.random() * Math.PI * 2; var x = player.x + Math.cos(angle) * spawnRadius; var y = player.y + Math.sin(angle) * spawnRadius; spawnEnemy('rangedZombie', x, y); } // Fast zombiler for (var i = 0; i < fastCount; i++) { var angle = Math.random() * Math.PI * 2; var x = player.x + Math.cos(angle) * spawnRadius; var y = player.y + Math.sin(angle) * spawnRadius; spawnEnemy('fastZombie', x, y); } // Tank zombiler for (var i = 0; i < tankCount; i++) { var angle = Math.random() * Math.PI * 2; var x = player.x + Math.cos(angle) * spawnRadius; var y = player.y + Math.sin(angle) * spawnRadius; spawnEnemy('tankZombie', x, y); } // Boss zombiler for (var i = 0; i < bossCount; i++) { var angle = Math.random() * Math.PI * 2; var x = player.x + Math.cos(angle) * spawnRadius; var y = player.y + Math.sin(angle) * spawnRadius; spawnEnemy('bossZombie', x, y); } } function dropExpOrb(x, y) { var orb = new ExpOrb(); orb.x = x; orb.y = y; // orb.value is always 1 in ExpOrb class expOrbs.push(orb); gameContainer.addChild(orb); } /* =============================================================== */ /* NINE-WEAPON FIRE / PROJECTILES */ /* =============================================================== */ function nearestEnemy() { var best = null, bestD = 1e9; for (var i = 0; i < enemies.length; i++) { var d = distSq(player, enemies[i]); if (d < bestD) { best = enemies[i]; bestD = d; } } return best; } function damageEnemy(e, dmg, attacker) { // attacker: optional, can be player or enemy // Play hit sound on every hit LK.getSound('hit').play(); // Determine crit chance and crit type var critChance = 0.25; // default var critType = null; // null, "low", "high" var isPlayerAttack = false; if (attacker === undefined) { attacker = player; } if (attacker === player) { isPlayerAttack = true; critChance = player.critChance !== undefined ? player.critChance : 0.25; } else if (attacker && attacker.type === 'normal') { critChance = attacker.critChance !== undefined ? attacker.critChance : 0.25; } var critRoll = Math.random(); var critValue = 1; if (critRoll < critChance) { // Crit! Now decide low or high if (Math.random() < 0.5) { critType = "low"; critValue = 1.2; } else { critType = "high"; critValue = 1.4; } } var finalDmg = dmg * critValue; // Apply debuffs if player has buffs if (player && player.buffs) { // Grit Wound+ if (player.buffs.gritWound > 0) { e.gritWound = player.buffs.gritWound; e.lastGritWoundTime = gameTime; } // Enemy Slow+ if (player.buffs.enemySlow > 0) { e.slowBuff = player.buffs.enemySlow; e.slowBuffTime = gameTime; } // Enemy Stun+ if (player.buffs.enemyStun > 0) { e.stunBuff = player.buffs.enemyStun; e.stunBuffTime = gameTime; } // Bleeding+ if (player.buffs.bleeding > 0) { e.bleedBuff = player.buffs.bleeding; e.bleedBuffTime = gameTime; e.bleedTicks = 0; } } // Hasar yazısı göster if (typeof finalDmg === "number") { var fillColor = 0xffffff; var strokeColor = 0x000000; if (isPlayerAttack) { if (critType === "low") { fillColor = 0xffe066; // sarı } else if (critType === "high") { fillColor = 0xff3333; // kırmızı } else { fillColor = 0xffffff; // beyaz } } else { // Enemy attack, use default fillColor = 0xff3333; } var dmgText = new Text2(finalDmg.toFixed(1), { size: 48, fill: fillColor, stroke: strokeColor, strokeThickness: 4 }); dmgText.anchor.set(0.5); dmgText.x = e.x; dmgText.y = e.y - (e.height ? e.height / 2 : 60); dmgText.alpha = 1; gameContainer.addChild(dmgText); // Yavaşça yukarı çıkıp kaybolsun tween(dmgText, { y: dmgText.y - 60, alpha: 0 }, { duration: 500, easing: tween.linear, onFinish: function onFinish() { if (dmgText.parent) { dmgText.destroy(); } } }); } if (e.takeDamage(finalDmg)) { // Play death sound (if you have a death sound asset, e.g. 'death') if (LK.getSound('death')) { LK.getSound('death').play(); } // Ölünce exp orb düşür: dropExpOrb(e.x, e.y); // Sonra düşmanı kaldır ve yok et: var idx = enemies.indexOf(e); enemies.splice(idx, 1); e.destroy(); } } /* ---- sword (melee slash) ---- */ function swingSword(inst) { var sorted = enemies.slice().sort(function (a, b) { return distSq(player, a) - distSq(player, b); }); if (sorted.length === 0) { return; } for (var n = 0; n < inst.amount; n++) { var idx = n < sorted.length ? n : 0; var target = sorted[idx]; var dir = unitDir(player, target); var arc = gameContainer.addChild(new Container()); arc.x = player.x; arc.y = player.y; arc.attachAsset('swordHit', { anchorX: 0, anchorY: 0.5, rotation: Math.atan2(dir.dy, dir.dx), scaleX: inst.rngMul, scaleY: inst.rngMul }); arc.life = 10; arc.dmg = WEAPONS.sword.dmg * inst.dmgMul; arc.hitList = []; arc.update = function () { if (--this.life <= 0) { if (this.parent) { this.destroy(); } return; } var maxRange = WEAPONS.sword.range * inst.rngMul; for (var i = enemies.length - 1; i >= 0; i--) { var e = enemies[i]; // hem arc.intersects hem oyuncudan menzil kontrolü if (this.hitList.indexOf(e) === -1 && this.intersects(e) && isWithinRange(e, maxRange)) { this.hitList.push(e); damageEnemy(e, this.dmg); } } }; } } /* ---- boomerang ---- */ function fireBoomerang(inst) { var target = nearestEnemy(); if (!target) { return; } var dir = unitDir(player, target); var maxRange = WEAPONS.boomerang.range * inst.rngMul; for (var n = 0; n < inst.amount; n++) { var p = new Container(); var boomerangAsset = p.attachAsset('boomerang', { anchorX: 0.5, anchorY: 0.5, scaleX: inst.rngMul, scaleY: inst.rngMul }); p._rotationAngle = 0; p._boomerangAsset = boomerangAsset; p.x = player.x; p.y = player.y; p._startX = player.x; p._startY = player.y; p._dirX = dir.dx; p._dirY = dir.dy; p._phase = "out"; // "out" = gidiş, "back" = dönüş p._distance = 0; p._maxRange = maxRange; p._speed = 18; p.dmg = WEAPONS.boomerang.dmg * inst.dmgMul; p.hitEnemies = []; p.update = function () { if (skillSelectionActive) { return; } // Boomerang assetini döndür if (this._boomerangAsset) { this._rotationAngle = (this._rotationAngle || 0) + 0.3; this._boomerangAsset.rotation = this._rotationAngle; } // Hareket if (this._phase === "out") { this.x += this._dirX * this._speed; this.y += this._dirY * this._speed; this._distance += this._speed; if (this._distance >= this._maxRange) { this._phase = "back"; } } else if (this._phase === "back") { // Dönüşte, oyuncuya doğru hareket et var dx = player.x - this.x; var dy = player.y - this.y; var d = Math.sqrt(dx * dx + dy * dy); if (d < this._speed) { // Oyuncuya ulaştıysa yok et if (this.parent) { this.destroy(); } var idx4 = bullets.indexOf(this); if (idx4 >= 0) { bullets.splice(idx4, 1); } return; } this.x += dx / d * this._speed; this.y += dy / d * this._speed; } // Düşmanlara çarpma kontrolü, menzil bakmaksızın, yok olmadan biçip geçmeli for (var i = enemies.length - 1; i >= 0; i--) { var e = enemies[i]; if (this.hitEnemies.indexOf(e) === -1 && this.intersects(e)) { damageEnemy(e, this.dmg); this.hitEnemies.push(e); } // Eğer boomerang artık düşmanla temas etmiyorsa, tekrar vurabilmesi için hitEnemies'den çıkar if (this.hitEnemies.indexOf(e) !== -1 && !this.intersects(e)) { this.hitEnemies.splice(this.hitEnemies.indexOf(e), 1); } } // Kameranın (görünen alanın) dışına çıktıysa sil (fail-safe) var camMinX = camera.x; var camMaxX = camera.x + 2048; var camMinY = camera.y; var camMaxY = camera.y + 2732; if (this.x < camMinX || this.x > camMaxX || this.y < camMinY || this.y > camMaxY) { if (this.parent) { this.destroy(); } var idx = bullets.indexOf(this); if (idx >= 0) { bullets.splice(idx, 1); } return; } }; bullets.push(p); gameContainer.addChild(p); } } /* ---- top (rebound ball) ---- */ function fireBall(inst) { var target = nearestEnemy(); // Ball should always fire, even if no enemy exists. If no target, fire straight up. var dir; if (!target) { dir = { dx: 0, dy: -1, len: 1 }; // fire straight up if no enemy } else { dir = unitDir(player, target); } for (var n = 0; n < inst.amount; n++) { var p = new Container(); // Attach the 'ball' asset, scale by range multiplier var ballGraphics = p.attachAsset('ball', { anchorX: 0.5, anchorY: 0.5, scaleX: inst.rngMul, scaleY: inst.rngMul }); p._rotationAngle = 0; p._ballAsset = ballGraphics; p.x = player.x; p.y = player.y; p.dirX = dir.dx; p.dirY = dir.dy; p.damage = 12 * inst.dmgMul; p.speed = 14; p.bounced = false; p.hitEnemies = []; p.update = function () { // Seviye seçim ekranı açıkken hiçbir şey yapma if (skillSelectionActive) { return; } // Hareket this.x += this.dirX * this.speed; this.y += this.dirY * this.speed; // Rotate ball asset if (this._ballAsset) { this._rotationAngle = (this._rotationAngle || 0) + 0.3; this._ballAsset.rotation = this._rotationAngle; } // Kamera (görünen alan) sınırlarını belirle var camMinX = camera.x; var camMaxX = camera.x + 2048; var camMinY = camera.y; var camMaxY = camera.y + 2732; // Kameranın dışına çıktıysa yok et if (this.x < camMinX || this.x > camMaxX || this.y < camMinY || this.y > camMaxY) { if (this.parent) { this.destroy(); } var bi = bullets.indexOf(this); if (bi >= 0) { bullets.splice(bi, 1); } return; } // Düşmana çarpma kontrolü (menzilsiz, ekranda nerde olursa olsun vurmalı ve sekmeli) for (var i = enemies.length - 1; i >= 0; i--) { var e = enemies[i]; if (this.hitEnemies.indexOf(e) === -1 && this.intersects(e)) { damageEnemy(e, this.damage); this.hitEnemies.push(e); // Sadece ilk çarpmada sekmeli if (!this.bounced) { this.dirX = -this.dirX; this.dirY = -this.dirY; this.bounced = true; } } // Eğer ball artık düşmanla temas etmiyorsa, tekrar vurabilmesi için hitEnemies'den çıkar if (this.hitEnemies.indexOf(e) !== -1 && !this.intersects(e)) { this.hitEnemies.splice(this.hitEnemies.indexOf(e), 1); } } }; bullets.push(p); gameContainer.addChild(p); } } /* ---- rocket ---- */ function fireRocket(inst) { var target = nearestEnemy(); if (!target) { return; } var dir = unitDir(player, target); // Bu silahın menzili: var maxRange = WEAPONS.rocket.range * inst.rngMul; for (var n = 0; n < inst.amount; n++) { var p = gameContainer.addChild(new Container()); p.attachAsset('rocket', { anchorX: 0.5, anchorY: 0.5, scaleX: inst.rngMul, scaleY: inst.rngMul }); p.x = player.x; p.y = player.y; p.speed = 10; p.dmg = WEAPONS.rocket.dmg * inst.dmgMul; // Track target enemy at fire time p._targetEnemy = target; p._exploded = false; p.update = function () { if (skillSelectionActive) { return; } // Defensive: if target enemy is dead, just fly straight var targetEnemy = this._targetEnemy; if (!this._exploded && targetEnemy && enemies.indexOf(targetEnemy) !== -1) { // Home in on target enemy's center var dx = targetEnemy.x - this.x; var dy = targetEnemy.y - this.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist > 0) { this.x += dx / dist * this.speed; this.y += dy / dist * this.speed; } // Check if reached center (use <= speed for precision) if (dist <= this.speed) { this.x = targetEnemy.x; this.y = targetEnemy.y; this._exploded = true; LK.getSound('rocketBoom').play(); // a) Dairesel patlama efekti: rocketBoom asseti kullanılmalı var explosion = gameContainer.addChild(new Container()); var gfx = LK.getAsset('rocketBoom', { anchorX: 0.5, anchorY: 0.5, // Restore original scale (no 2x) scaleX: inst.rngMul * (80 / 70), scaleY: inst.rngMul * (80 / 70), alpha: 0.6 }); explosion.addChild(gfx); explosion.x = this.x; explosion.y = this.y; explosion.life = 30; explosion.update = function () { if (--this.life <= 0) { this.destroy(); } else { this.children[0].alpha = this.life / 30 * 0.6; } }; // b) AoE hasar: sadece menzil içindekileri vur for (var k = enemies.length - 1; k >= 0; k--) { var e2 = enemies[k]; // Restore original AoE radius (radius^2 = 16000) if (distSq(this, e2) < 16000) { // Rocket patlaması hasar versin damageEnemy(e2, this.dmg); } } // c) Bu roketi sil if (this.parent) { this.destroy(); } var bi = bullets.indexOf(this); if (bi >= 0) { bullets.splice(bi, 1); } return; } } else if (!this._exploded) { // If target is gone, fly straight in last direction if (typeof this.dirX === "undefined" || typeof this.dirY === "undefined") { // Set direction to original target var origDir = unitDir({ x: player.x, y: player.y }, { x: this.x, y: this.y }); this.dirX = origDir.dx; this.dirY = origDir.dy; } this.x += this.dirX * this.speed; this.y += this.dirY * this.speed; } // 3) Kameranın (görünen alanın) dışına çıkarsa sil var camMinX = camera.x; var camMaxX = camera.x + 2048; var camMinY = camera.y; var camMaxY = camera.y + 2732; if (this.x < camMinX || this.x > camMaxX || this.y < camMinY || this.y > camMaxY) { if (this.parent) { this.destroy(); } var bi2 = bullets.indexOf(this); if (bi2 >= 0) { bullets.splice(bi2, 1); } return; } }; bullets.push(p); } } /* ---- brick ---- */ function throwBrick(inst, angDeg) { var rad = angDeg * Math.PI / 180; var p = gameContainer.addChild(new Container()); var brickAsset = p.attachAsset('brick', { anchorX: 0.5, anchorY: 0.5, scaleX: inst.rngMul, scaleY: inst.rngMul }); p._rotationAngle = 0; p._brickAsset = brickAsset; p.x = player.x; p.y = player.y; // Brick should be thrown upwards at 75 degrees (not downwards) p.vx = Math.cos(rad) * 14; p.vy = -Math.abs(Math.sin(rad) * 14); // always upwards p.g = 0.3; p.dmg = WEAPONS.brick.dmg * inst.dmgMul; // Bu silahın menzili: var maxRange = WEAPONS.brick.range * inst.rngMul; p._hitEnemies = []; p.update = function () { if (skillSelectionActive) { return; } // 1) Yerçekimi ve hareket this.vy += this.g; this.x += this.vx; this.y += this.vy; // Rotate brick asset if (this._brickAsset) { this._rotationAngle = (this._rotationAngle || 0) + 0.3; this._brickAsset.rotation = this._rotationAngle; } // 2) Düşmana çarpma kontrolü, brick yok olmasın, her düşmana bir kez vurabilsin, menzil bakmaksızın ekrandan çıkana kadar vurmalı for (var i = enemies.length - 1; i >= 0; i--) { var e = enemies[i]; if (this._hitEnemies.indexOf(e) === -1 && this.intersects(e)) { damageEnemy(e, this.dmg); this._hitEnemies.push(e); } // Eğer brick artık düşmanla temas etmiyorsa, tekrar vurabilmesi için _hitEnemies'den çıkar if (this._hitEnemies.indexOf(e) !== -1 && !this.intersects(e)) { this._hitEnemies.splice(this._hitEnemies.indexOf(e), 1); } } // 3) Kameranın (görünen alanın) dışına çıkarsa yok et var camMinX = camera.x; var camMaxX = camera.x + 2048; var camMinY = camera.y; var camMaxY = camera.y + 2732; if (this.x < camMinX || this.x > camMaxX || this.y < camMinY || this.y > camMaxY) { if (this.parent) { this.destroy(); } return; } }; } /* ---- lightning ---- */ function castLightning(inst) { // 1) Menzildeki düşmanları filtrele var maxRange = WEAPONS.lightning.range * inst.rngMul; var candidates = enemies.filter(function (e) { var dx = e.x - player.x; var dy = e.y - player.y; return dx * dx + dy * dy <= maxRange * maxRange; }); if (candidates.length === 0) { return; } // 2) Rastgele birini seç var victim = candidates[Math.floor(Math.random() * candidates.length)]; // 3) Efekti ekle var l = gameContainer.addChild(new Container()); l.attachAsset('lightning', { anchorX: 0.5, anchorY: 1, scaleX: inst.rngMul, scaleY: inst.rngMul }); l.x = victim.x; l.y = victim.y; l.life = 15; l.update = function () { if (skillSelectionActive) { return; } if (--this.life === 14) { damageEnemy(victim, WEAPONS.lightning.dmg * inst.dmgMul); } if (this.life <= 0) { if (this.parent) { this.destroy(); } } }; } /* ---- aura ---- */ var auraField = null; function ensureAura(inst) { // 1) Sadece ilk defa oluşturulacak: if (!auraField) { auraField = player.addChild(new Container()); auraField.attachAsset('auraField', { anchorX: 0.5, anchorY: 0.5, alpha: 0.3, scaleX: inst.rngMul, scaleY: inst.rngMul }); // Oyuncunun tam ortası (yerel koordinat): auraField.x = 0; auraField.y = 0; } // Her frame aura görselini güncelle (range buff alınırsa anında büyüsün/küçülsün) if (auraField && auraField.children && auraField.children.length > 0) { auraField.children[0].scaleX = inst.rngMul; auraField.children[0].scaleY = inst.rngMul; // Rotate aura asset if (typeof auraField._rotationAngle === "undefined") { auraField._rotationAngle = 0; } auraField._rotationAngle += 0.3; // 10 kat daha hızlı döndür auraField.children[0].rotation = auraField._rotationAngle; } // 2) Hasar uygulama kısmı (oyuncudan r uzaklıktaysa vur): var r = WEAPONS.aura.range * inst.rngMul; for (var i = enemies.length - 1; i >= 0; i--) { var e = enemies[i]; var dx = e.x - player.x; var dy = e.y - player.y; if (dx * dx + dy * dy < r * r) { damageEnemy(e, inst.dmgMul * 5); } } } /* ---- molotov ---- */ function throwMolotov(inst) { // Find nearest enemy var target = nearestEnemy(); if (!target) { return; } var dir = unitDir(player, target); var p = gameContainer.addChild(new Container()); var molotovAsset = p.attachAsset('molotov', { anchorX: 0.5, anchorY: 0.5, scaleX: inst.rngMul, scaleY: inst.rngMul }); p._rotationAngle = 0; p._molotovAsset = molotovAsset; p.x = player.x; p.y = player.y; p.dirX = dir.dx; p.dirY = dir.dy; p.speed = 16; p.life = 120; // Give enough life to reach enemy p._targetEnemy = target; p.update = function () { if (skillSelectionActive) { return; } this.x += this.dirX * this.speed; this.y += this.dirY * this.speed; // Rotate molotov asset if (this._molotovAsset) { this._rotationAngle = (this._rotationAngle || 0) + 0.3; this._molotovAsset.rotation = this._rotationAngle; } // Check for collision with target enemy if (this._targetEnemy && this.intersects(this._targetEnemy)) { // Explode at enemy position spawnFlame(this._targetEnemy.x, this._targetEnemy.y, inst); if (this.parent) { this.destroy(); } return; } // If life runs out, just destroy (fail-safe) if (--this.life <= 0) { if (this.parent) { this.destroy(); } } }; bullets.push(p); } /* ---- molotov flame ---- */ function spawnFlame(x, y, inst) { var f = gameContainer.addChild(new Container()); f.attachAsset('molotovFlame', { anchorX: 0.5, anchorY: 0.5, alpha: 0.4, scaleX: inst.rngMul, scaleY: inst.rngMul }); f.x = x; f.y = y; f.life = 60; // 1s at 60fps f.dmg = WEAPONS.molotov.dmg * inst.dmgMul; var maxFlameRange = 70 * inst.rngMul; f._flameTick = 0; // Her zombi için: {enemyId: {tickCount: kaç kere hasar aldı, lastTick: son hasar frame'i}} f._flameTickedEnemies = {}; f.update = function () { if (skillSelectionActive) { return; } if (typeof this._flameTick === "undefined") { this._flameTick = 0; } this._flameTick++; // Her frame, alev alanında olan zombileri bul for (var i = enemies.length - 1; i >= 0; i--) { var e = enemies[i]; var dx = e.x - f.x; var dy = e.y - f.y; var dist2 = dx * dx + dy * dy; if (dist2 < maxFlameRange * maxFlameRange && f.intersects(e)) { // Her zombi için benzersiz bir id kullan if (typeof e._molotovFlameId === "undefined") { e._molotovFlameId = Math.random().toString(36).substr(2, 9) + "_" + Math.floor(Math.random() * 1000000); } if (!f._flameTickedEnemies[e._molotovFlameId]) { f._flameTickedEnemies[e._molotovFlameId] = { tickCount: 0, lastTick: -100, enteredTick: this._flameTick }; } var tickInfo = f._flameTickedEnemies[e._molotovFlameId]; // Zombi alev alanına yeni girdiyse enteredTick güncelle if (typeof tickInfo.enteredTick === "undefined") { tickInfo.enteredTick = this._flameTick; } // 1 saniye boyunca (2 tick) her 0.5 saniyede bir hasar uygula // Yani: enteredTick'ten itibaren 0. ve 30. frame'de hasar almalı // tickCount < 2 ve (this._flameTick - tickInfo.enteredTick) % 30 === 0 ise hasar uygula if (tickInfo.tickCount < 2) { var tickFrame = this._flameTick - tickInfo.enteredTick; if (tickFrame === 0 && tickInfo.tickCount === 0 || tickFrame === 30 && tickInfo.tickCount === 1) { damageEnemy(e, 3); // 3 damage per tick, 2 ticks = 6 total tickInfo.tickCount++; tickInfo.lastTick = this._flameTick; } } } else { // Zombi alev alanından çıktıysa, tekrar girerse yeni bir enteredTick başlatabilsin diye kaydını sil if (typeof e._molotovFlameId !== "undefined" && f._flameTickedEnemies[e._molotovFlameId]) { delete f._flameTickedEnemies[e._molotovFlameId]; } } } // 2) Lifetime if (--this.life <= 0) { if (this.parent) { this.destroy(); } return; } // 3) Kameranın (görünen alanın) dışına çıkarsa sil var camMinX = camera.x; var camMaxX = camera.x + 2048; var camMinY = camera.y; var camMaxY = camera.y + 2732; if (this.x < camMinX || this.x > camMaxX || this.y < camMinY || this.y > camMaxY) { if (this.parent) { this.destroy(); } return; } }; } /* ---- shuriken ring ---- */ var shurikenObjs = []; // Shuriken now always visible, orbits player, speed = attack speed, range+ increases both orbit radius and asset size var _maintainShurikens = function maintainShurikens(inst) { // Always create shurikens if not present or if amount changed if (shurikenObjs.length !== inst.amount) { shurikenObjs.forEach(function (s) { if (s.parent) { s.destroy(); } }); shurikenObjs = []; var n = inst.amount; var r = WEAPONS.shuriken.range * inst.rngMul; var sizeMul = inst.rngMul; // Range+ increases both orbit and asset size for (var i = 0; i < n; i++) { var s = gameContainer.addChild(new Container()); s.attachAsset('shuriken', { anchorX: 0.5, anchorY: 0.5, scaleX: sizeMul, scaleY: sizeMul }); s.angle = i / n * Math.PI * 2; s.hitEnemies = []; s.update = function () { if (skillSelectionActive) { return; } var curR = WEAPONS.shuriken.range * inst.rngMul; var curSizeMul = inst.rngMul; // Orbit around player, speed = attack speed (cd) var baseCd = WEAPONS.shuriken.cd; var curCd = inst.cd || baseCd; // Higher attack speed = lower cd = faster orbit var speed = 0.15 * (baseCd / curCd); this.angle += speed; this.x = player.x + Math.cos(this.angle) * curR; this.y = player.y + Math.sin(this.angle) * curR; // Update asset size if range+ changes if (this.children && this.children.length > 0) { this.children[0].scaleX = curSizeMul; this.children[0].scaleY = curSizeMul; if (typeof this._rotationAngle === "undefined") { this._rotationAngle = 0; } this._rotationAngle += 0.4; this.children[0].rotation = this._rotationAngle; } // Damage enemies if intersecting and within range for (var k = enemies.length - 1; k >= 0; k--) { var e = enemies[k]; if (this.hitEnemies.indexOf(e) === -1 && this.intersects(e) && isWithinRange(e, curR)) { damageEnemy(e, 6); // Shuriken temas ettiği düşmana 6 hasar versin this.hitEnemies.push(e); } // Reset hitEnemies if not intersecting anymore (so shuriken can hit again on next contact) if (this.hitEnemies.indexOf(e) !== -1 && (!this.intersects(e) || !isWithinRange(e, curR))) { this.hitEnemies.splice(this.hitEnemies.indexOf(e), 1); } } }; shurikenObjs.push(s); } } // Always update shuriken positions even if already created for (var i = 0; i < shurikenObjs.length; i++) { if (typeof shurikenObjs[i].update === "function") { shurikenObjs[i].update(); } } }; /* ---- ana fireWeapon anahtarı ---- */ function fireWeapon(key, inst, meta) { switch (meta.type) { case 'melee': swingSword(inst); break; case 'boomerang': if (player.weapons && player.weapons.boomerang) { fireBoomerang(inst); } break; case 'ball': fireBall(inst); break; case 'rocket': fireRocket(inst); break; case 'brick': // Brick en yakındaki düşman ekranın sağında mı solunda mı ona göre fırlatılmalı var nearest = nearestEnemy(); var throwRight = true; if (nearest) { // Kameranın sol üst köşesi (camera.x, camera.y) // Ekranın ortası: camera.x + 1024 // Düşman ekranın sağında mı solunda mı? if (nearest.x < player.x) { throwRight = false; } else { throwRight = true; } } else { // Düşman yoksa, sağa fırlat throwRight = true; } // Sağdaysa: 75 derece yukarı sağdan (75°), soldaysa: 75 derece yukarı soldan (255°) if (throwRight) { throwBrick(inst, 75); if (inst.upgrade > 0) { throwBrick(inst, 105); } if (inst.upgrade > 1) { throwBrick(inst, 45); } if (inst.upgrade > 2) { throwBrick(inst, 135); } } else { throwBrick(inst, 255); if (inst.upgrade > 0) { throwBrick(inst, 225); } if (inst.upgrade > 1) { throwBrick(inst, 285); } if (inst.upgrade > 2) { throwBrick(inst, 195); } } break; case 'lightning': for (var n = 0; n < inst.amount; n++) { castLightning(inst); } break; case 'aura': ensureAura(inst); break; case 'molotov': for (var n = 0; n < inst.amount; n++) { throwMolotov(inst); } break; case 'shuriken': _maintainShurikens(inst); break; } } function showSkillSelection() { skillSelectionActive = true; // 1) Yeni bir Overlay (seçim ekranı) oluşturuyoruz var ov = game.addChild(new Container()); ov.x = 1024; ov.y = 1366; var choices = []; // 2) Eğer oyuncu seviyesi 5'in katı ise (5,10,15,...) if (player.level % 5 === 0) { var ownedCount = Object.keys(player.weapons).length; // 2.a) Hâlâ < 6 silah sahibi ise: yeni silah açma seçenekleri if (ownedCount < 6) { // Kilidi açılmamış silahlar havuzunu al var pool = Object.keys(WEAPONS).filter(function (key) { return !player.weapons[key]; }); // Havuzdan rastgele 3 silah göster, her biri benzersiz while (choices.length < 3 && pool.length > 0) { var rnd = Math.floor(Math.random() * pool.length); choices.push(pool.splice(rnd, 1)[0]); } } else { // 2.b) Zaten 6 silah sahibi ise: önce silahların upgrade durumlarını kontrol et var upgradePool = []; Object.keys(player.weapons).forEach(function (key) { var inst = player.weapons[key]; if (inst.upgrade < 3) { // Eğer bu silah hâlâ < 3 aşama güçlendirilmişse, // “bu silahın bir sonraki upgrade aşaması” havuza eklenir. upgradePool.push({ w: key }); } }); if (upgradePool.length > 0) { // 2.b.i) En az bir silah yükseltmesi kalmış demektir: // Rastgele en fazla 3 tane “silah güçlendirme” seçeneği göster, her biri benzersiz while (choices.length < 3 && upgradePool.length > 0) { var rnd2 = Math.floor(Math.random() * upgradePool.length); choices.push({ w: upgradePool[rnd2].w, type: "upgrade" }); upgradePool.splice(rnd2, 1); } } else { // 2.b.ii) Tüm 6 silah zaten upgrade === 3 ise: // Bu aşamadan sonra artık sadece efsun kartı göster. // Her kart benzersiz olmalı (hem silah hem efsun kombinasyonu) var ownedKeys2 = Object.keys(player.weapons); var usedPairs = []; var attempts = 0; while (choices.length < 3 && attempts < 20) { var randomKey2 = ownedKeys2[Math.floor(Math.random() * ownedKeys2.length)]; var enchList = WEAPONS[randomKey2].ench; var rnd3 = enchList[Math.floor(Math.random() * enchList.length)]; var pairKey = randomKey2 + "|" + rnd3; if (usedPairs.indexOf(pairKey) === -1) { choices.push({ w: randomKey2, e: rnd3 }); usedPairs.push(pairKey); } attempts++; } } } } else { // 3) Seviye 5'in katı değilse: doğrudan efsun kartları göster // Her kart benzersiz olmalı (hem silah hem efsun kombinasyonu) var ownedKeys = Object.keys(player.weapons); var usedPairs2 = []; var attempts2 = 0; while (choices.length < 3 && attempts2 < 20) { var randomKey = ownedKeys[Math.floor(Math.random() * ownedKeys.length)]; var enchList = WEAPONS[randomKey].ench; var rnd4 = enchList[Math.floor(Math.random() * enchList.length)]; var pairKey2 = randomKey + "|" + rnd4; if (usedPairs2.indexOf(pairKey2) === -1) { choices.push({ w: randomKey, e: rnd4 }); usedPairs2.push(pairKey2); } attempts2++; } } // 4) choices dizisini “3 adet kart” olarak UI’da çizelim // --- BUFF havuzu --- // --- Buff/efsun/buff limit tracking --- if (!player.buffCounts) { player.buffCounts = {}; } if (!player.efsunCounts) { player.efsunCounts = {}; } // --- BUFF havuzu --- var buffPool = [{ key: "damageReduction", label: "Damage Reduction+", desc: "Take 10% less damage per level" }, { key: "moveSpeed", label: "Movement Speed Increase+", desc: "Move 20% faster per level" }, { key: "hpRegen", label: "HP Regeneration+", desc: "Regenerate 1% max HP every 5s per level" }, { key: "damageReflection", label: "Damage Reflection+", desc: "Reflect 5% damage per level" }, { key: "gritWound", label: "Grit Wound+", desc: "Reduce enemy regen by 10% per level" }, { key: "enemySlow", label: "Enemy Slow+", desc: "Slow enemies by 10% per level" }, { key: "enemyStun", label: "Enemy Stun+", desc: "Stun enemies for 0.1s per level" }, { key: "bleeding", label: "Bleeding+", desc: "Bleed: 4s, 0.5s tick, 5% max HP per tick per level" }, { key: "critChance", label: "Critical Hit Chance+", desc: "Increase critical hit chance by 15% per level" }, { key: "maxHpPlus", label: "Maximum HP+", desc: "Increase max HP by 25 per level" }]; // Remove buffs that have been taken 5 times buffPool = buffPool.filter(function (buff) { var k = buff.key; return (player.buffCounts[k] || 0) < 5; }); // Rastgele bir buff seç (her zaman 4. seçenek) var buffIdx = Math.floor(Math.random() * buffPool.length); var buffChoice = buffPool[buffIdx]; choices = choices.slice(0, 3); // Sadece ilk 3'ü tut choices.push({ type: "buff", buff: buffChoice }); // 4. seçenek buff // Remove efsun options that have been taken 5 times choices = choices.filter(function (c) { if (_typeof(c) === "object" && c.e) { var key = c.w + "|" + c.e; return (player.efsunCounts[key] || 0) < 5; } return true; }); choices.forEach(function (c, idx) { var card = ov.addChild(new Container()); // 4. kartı mavi yap if (idx === 3) { card.attachAsset('skillCard', { anchorX: 0.5, anchorY: 0.5, tint: 0x3399ff // mavi renk }); } else { card.attachAsset('skillCard', { anchorX: 0.5, anchorY: 0.5 }); } // Kartlar arası mesafeyi azalt (ör: 440px), dikey mesafeyi azalt (ör: 0) card.x = (idx - 1.5) * 440; card.y = 0; var label, fn, desc = ""; if (typeof c === "string") { // c, yeni kilidi açılacak silahın anahtarı (ör. "boomerang") var key = c; var own = !!player.weapons[key]; if (!own) { // Hâlâ envanterde yoksa “Unlock <silah adı>” label = "Unlock " + key; fn = function fn() { addWeapon(key); }; } else if (player.weapons[key].upgrade < 3) { // Silah zaten varsa ve upgrade < 3 ise: label = WEAPONS[key].up[player.weapons[key].upgrade]; fn = function fn() { player.weapons[key].upgrade++; player.weapons[key].amount++; }; } else { // Eğer upgrade === 3’e ulaşmışsa: label = "No Option"; fn = function fn() {}; } } else if (c.type === "upgrade") { // c = { w: "sword", type:"upgrade" } var wkey = c.w; label = WEAPONS[wkey].up[player.weapons[wkey].upgrade]; fn = function fn() { player.weapons[wkey].upgrade++; player.weapons[wkey].amount++; }; } else if (c.type === "buff") { label = c.buff.label; desc = c.buff.desc; fn = function fn() { // Buff mantığı var k = c.buff.key; if (!player.buffCounts[k]) { player.buffCounts[k] = 0; } if (player.buffCounts[k] >= 5) { return; } // Defensive: do not allow more than 5 switch (k) { case "damageReduction": player.buffs.damageReduction += 10; break; case "moveSpeed": player.buffs.moveSpeed += 20; break; case "hpRegen": player.buffs.hpRegen += 1; break; case "damageReflection": player.buffs.damageReflection += 5; break; case "gritWound": player.buffs.gritWound += 10; break; case "enemySlow": player.buffs.enemySlow += 10; break; case "enemyStun": player.buffs.enemyStun += 0.1; break; case "bleeding": player.buffs.bleeding += 1; break; case "critChance": player.buffs.critChance += 15; player.critChance = 0.25 + player.buffs.critChance / 100; if (player.critChance > 1) { player.critChance = 1; } break; case "maxHpPlus": player.maxHp += 25; break; } player.buffCounts[k]++; }; } else { // c = { w: "<silahAnahtarı>", e: "<SilahName> Damage+" veya "<SilahName> Attack Speed+" vb. } var w2 = c.w; var efsun = c.e; label = efsun; // Efsun açıklaması if (efsun.endsWith(" Damage+")) { desc = "Increases weapon damage by 20%"; } else if (efsun.endsWith(" Attack Speed+")) { desc = "Increases weapon attack speed by 20%"; } else if (efsun.endsWith(" Range+")) { desc = "Increases weapon range and size by 20%"; } else { desc = ""; } fn = function fn() { var key = w2 + "|" + efsun; if (!player.efsunCounts[key]) { player.efsunCounts[key] = 0; } if (player.efsunCounts[key] >= 5) { return; } // Defensive: do not allow more than 5 var inst = player.weapons[w2]; // Efsun metni "<SilahName> Damage+" ile bitiyorsa hasar çarpanını %20 artır: if (efsun.endsWith(" Damage+")) { inst.dmgMul *= 1.2; } // Efsun metni "<SilahName> Attack Speed+" ile bitiyorsa cd süresini %20 azalt: if (efsun.endsWith(" Attack Speed+")) { var meta = WEAPONS[w2]; if (meta && typeof meta.cd === "number") { if (!inst._baseCd) { inst._baseCd = meta.cd; inst._atkSpdMul = 1; } inst._atkSpdMul *= 0.8; inst.cd = inst._baseCd * inst._atkSpdMul; } else { inst.cd *= 0.8; } // cdCnt’yi de yeni cd’ye çekiyoruz if (typeof inst.cdCnt === "number") { if (inst.cdCnt > inst.cd) { inst.cdCnt = inst.cd; } } else { inst.cdCnt = Math.floor(inst.cd); } } // Efsun metni "<SilahName> Range+" ile bitiyorsa hem menzil hem boyut çarpanını %20 artır: if (efsun.endsWith(" Range+")) { inst.rngMul *= 3.2; inst.sizeMul *= 3.2; } // "Size+" efsunu artık yok, hiçbir şey yapma player.efsunCounts[key]++; }; } var txt = new Text2(label, { size: 40, fill: 0xffffff }); txt.anchor.set(0.5); card.addChild(txt); if (desc) { // Card width is 400, so max text width should be less (e.g. 340) var descTxt = new Text2(desc, { size: 28, fill: 0xcccccc, wordWrap: true, wordWrapWidth: 340, align: "center" }); descTxt.anchor.set(0.5, 0); descTxt.y = 60; card.addChild(descTxt); } card.down = function () { fn(); ov.destroy(); skillSelectionActive = false; levelText.setText('Level ' + player.level); }; }); } // Input handling game.down = function (x, y, obj) { if (skillSelectionActive) { return; } var localPos = game.toLocal({ x: x, y: y }); if (localPos.y > 2000) { joystick.active = true; joystick.setKnobPosition(0, 0); } }; game.move = function (x, y, obj) { if (!joystick.active || skillSelectionActive) { return; } var localPos = LK.gui.bottom.toLocal({ x: x, y: y }); var dx = localPos.x - joystick.x; var dy = localPos.y - joystick.y; joystick.setKnobPosition(dx, dy); }; game.up = function (x, y, obj) { joystick.reset(); }; // Game update game.update = function () { if (skillSelectionActive) { return; } gameTime++; // Update timer → geçen süreyi göster var elapsedSeconds = Math.floor(gameTime / 60); var minutes = Math.floor(elapsedSeconds / 60); var seconds = elapsedSeconds % 60; timerText.setText((minutes < 10 ? '0' + minutes : minutes) + ':' + (seconds < 10 ? '0' + seconds : seconds)); // Update UI hpText.setText('HP: ' + player.hp.toFixed(1) + '/' + player.maxHp.toFixed(1)); // Show exp orbs and required exp for next level levelText.setText('Level ' + player.level + ' EXP: ' + player.exp + '/' + player.level); // Show current wave waveText.setText('Wave ' + waveNumber); // Player movement if (joystick.active) { player.x += joystick.dirX * player.moveSpeed; player.y += joystick.dirY * player.moveSpeed; } /* Fire weapons / aura etc. */ player.update(); // --- Update player health bar --- if (player.hpBar && player.hpBarFg) { var hpRatio = Math.max(0, Math.min(1, player.hp / player.maxHp)); player.hpBarFg.width = 212 * hpRatio; // Move foreground bar's x so it depletes from right to left player.hpBarFg.x = 0; // Player HP bar color: green >80%, yellow 50-80%, orange 20-50%, red+blink <20% if (hpRatio > 0.8) { player.hpBarFg.tint = 0x00ff44; // green player.hpBarFg.alpha = 1; } else if (hpRatio > 0.5) { player.hpBarFg.tint = 0xffe066; // yellow player.hpBarFg.alpha = 1; } else if (hpRatio > 0.2) { player.hpBarFg.tint = 0xff9900; // orange player.hpBarFg.alpha = 1; } else { // <20%: red and blinking // Blink: alternate between red and transparent every 6 frames (~10Hz) player.hpBarFg.tint = 0xff0000; // pure red for critical if (gameTime % 12 < 6) { player.hpBarFg.alpha = 1; } else { player.hpBarFg.alpha = 0.3; } } } // --- Update enemy health bars --- for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; if (enemy.hpBar && enemy.hpBarFg) { var ehpRatio = Math.max(0, Math.min(1, enemy.hp / enemy.maxHp)); enemy.hpBarFg.width = 152 * ehpRatio; enemy.hpBarFg.x = 0; // Enemy HP bar color: always red, regardless of HP enemy.hpBarFg.tint = 0xff4444; enemy.hpBarFg.alpha = 1; } } // Camera follow player camera.x = player.x - 1024; camera.y = player.y - 1366; gameContainer.x = -camera.x; gameContainer.y = -camera.y; // Update background tiles to always cover the visible area if (backgroundTiles && backgroundTiles.length) { var startX = Math.floor(camera.x / bgTileWidth) * bgTileWidth; var startY = Math.floor(camera.y / bgTileHeight) * bgTileHeight; var idx = 0; for (var i = 0; i < bgTilesX; i++) { for (var j = 0; j < bgTilesY; j++) { var tile = backgroundTiles[idx++]; tile.x = startX + i * bgTileWidth; tile.y = startY + j * bgTileHeight; } } } // Progress to next wave only when all enemies are cleared if (typeof nextWaveTimeout === "undefined") { nextWaveTimeout = null; } if (enemies.length === 0 && !nextWaveTimeout) { nextWaveTimeout = LK.setTimeout(function () { waveNumber++; spawnWave(); nextWaveTimeout = null; }, 3000); } if (enemies.length > 0 && nextWaveTimeout) { LK.clearTimeout(nextWaveTimeout); nextWaveTimeout = null; } // Update enemies for (var i = 0; i < enemies.length; i++) { var enemy = enemies[i]; var dx = enemy.x - player.x; var dy = enemy.y - player.y; var distSq = dx * dx + dy * dy; // 150 piksel mesafe için distSq <= 22500 kullanıyoruz. if (distSq <= 22500) { if (enemy.lastHitTime === undefined || gameTime - enemy.lastHitTime >= 60) { // Enemy crit logic var critChance = enemy.critChance !== undefined ? enemy.critChance : 0.25; var critType = null; var critValue = 1; if (Math.random() < critChance) { if (Math.random() < 0.5) { critType = "low"; critValue = 1.2; } else { critType = "high"; critValue = 1.4; } } // Calculate enemy's final damage (base * crit multiplier) var finalDmg = enemy.damage * critValue; // Apply player damage reduction buff before taking damage var reducedDmg = finalDmg; if (player.buffs && player.buffs.damageReduction > 0) { reducedDmg = Math.ceil(finalDmg * (1 - player.buffs.damageReduction / 100)); } player.takeDamage(reducedDmg); // Show damage text for enemy crits on player var fillColor = 0xff3333; if (critType === "low") { fillColor = 0xffe066; } if (critType === "high") { fillColor = 0xff3333; } var dmgText = new Text2(reducedDmg.toFixed(1), { size: 48, fill: fillColor, stroke: 0x000000, strokeThickness: 4 }); dmgText.anchor.set(0.5); dmgText.x = player.x; dmgText.y = player.y - (player.height ? player.height / 2 : 60); dmgText.alpha = 1; gameContainer.addChild(dmgText); tween(dmgText, { y: dmgText.y - 60, alpha: 0 }, { duration: 500, easing: tween.linear, onFinish: function onFinish() { if (dmgText.parent) { dmgText.destroy(); } } }); enemy.lastHitTime = gameTime; } } } // Update exp orbs for (var i = expOrbs.length - 1; i >= 0; i--) { var orb = expOrbs[i]; var dx = orb.x - player.x; var dy = orb.y - player.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < player.pickupRange) { orb.magnetSpeed = 10; } if (orb.intersects(player)) { player.gainExp(orb.value); orb.destroy(); expOrbs.splice(i, 1); LK.getSound('pickup').play(); } } // Spawn pickups randomly if (gameTime - lastPickupSpawn > 1800 && Math.random() < 0.1) { lastPickupSpawn = gameTime; var pickup; var angle = Math.random() * Math.PI * 2; var distance = 300 + Math.random() * 500; var pickupX = player.x + Math.cos(angle) * distance; var pickupY = player.y + Math.sin(angle) * distance; var randVal = Math.random(); if (randVal < 0.4) { pickup = new Bomb(); } else if (randVal < 0.8) { pickup = new Magnet(); } else { pickup = new ChickenLeg(); } pickup.x = pickupX; pickup.y = pickupY; pickups.push(pickup); gameContainer.addChild(pickup); } // Update pickups for (var i = pickups.length - 1; i >= 0; i--) { var pickup = pickups[i]; if (!pickup.active) { pickups.splice(i, 1); } } }; // Initial spawn // spawnWave();//{jb} // Delay first wave spawn until after weapon selection // Start background music at game start LK.playMusic('backGroundMusic'); // Tank Zombie asset (bigger, armored) // Fast Zombie asset (slimmer, agile) // Poison Zombie asset (greenish, toxic) // Boss Zombie asset (huge, menacing)
===================================================================
--- original.js
+++ change.js
@@ -180,8 +180,9 @@
}
return false; // Enemy still alive
};
self.update = function () {
+ // Kart seçim ekranı aktifken düşman hareket etmesin ve saldırmasın
if (!player || skillSelectionActive) {
return;
}
// Subclasses should implement facing logic if needed
@@ -377,8 +378,12 @@
self.attackTimer = 0;
self.type = 'tankZombie';
self.update = function (origUpdate) {
return function () {
+ // Kart seçim ekranı aktifken tank zombi hareket etmesin ve saldırmasın
+ if (skillSelectionActive) {
+ return;
+ }
if (self.x < player.x) {
tankGraphics.scaleX = 1;
} else if (self.x > player.x) {
tankGraphics.scaleX = -1;
@@ -447,8 +452,12 @@
self.attackTimer = 0;
self.type = 'rangedZombie';
self.update = function (origUpdate) {
return function () {
+ // Kart seçim ekranı aktifken menzilli zombi hareket etmesin ve saldırmasın
+ if (skillSelectionActive) {
+ return;
+ }
// Flip to face player
if (self.x < player.x) {
rangedGraphics.scaleX = 1;
} else if (self.x > player.x) {
@@ -529,8 +538,12 @@
self.attackTimer = 0;
self.type = 'normalZombie';
self.update = function (origUpdate) {
return function () {
+ // Kart seçim ekranı aktifken normal zombi hareket etmesin ve saldırmasın
+ if (skillSelectionActive) {
+ return;
+ }
// Flip to face player
if (self.x < player.x) {
zombieGraphics.scaleX = 1;
} else if (self.x > player.x) {
@@ -605,8 +618,12 @@
self.attackTimer = 0;
self.type = 'fastZombie';
self.update = function (origUpdate) {
return function () {
+ // Kart seçim ekranı aktifken hızlı zombi hareket etmesin ve saldırmasın
+ if (skillSelectionActive) {
+ return;
+ }
if (self.x < player.x) {
fastGraphics.scaleX = 1;
} else if (self.x > player.x) {
fastGraphics.scaleX = -1;
@@ -689,8 +706,12 @@
self.warningActive = false;
self.warningText = null;
self.update = function (origUpdate) {
return function () {
+ // Kart seçim ekranı aktifken boss hareket etmesin ve saldırmasın
+ if (skillSelectionActive) {
+ return;
+ }
// Flip to face player
if (self.x < player.x) {
bossGraphics.scaleX = 1;
} else if (self.x > player.x) {
@@ -1092,8 +1113,12 @@
self.levelUp();
}
};
self.update = function () {
+ // Kart seçim ekranı aktifken karakter hareket etmesin ve saldırmasın
+ if (skillSelectionActive) {
+ return;
+ }
// Flip player graphic based on movement direction
if (self.lastX === undefined) {
self.lastX = self.x;
}
@@ -1180,10 +1205,10 @@
/****
* Game Code
****/
-/* ==== UNIVERSAL HELPERS ==== */
// --- New zombie attack/projectile assets ---
+/* ==== UNIVERSAL HELPERS ==== */
function _typeof(o) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) {
return typeof o;
Survivor.io style 2D sword swing effect made by HABBY PTE. LTD.. In-Game asset. 2d. High contrast. No shadows. It should only have a slash effect, no swords. The slash effect should also be in the shape of a half moon.
Survivor.io style 2D round soccer ball made by HABBY PTE. LTD.. In-Game asset. 2d. High contrast. No shadows
A 2D Survivor.io style lightning strike from a cloud in the sky to the ground, made by HABBY PTE. LTD.. In-Game asset. 2d. High contrast. No shadows
A red and blue Survivor.io style 2D U-shaped (with N and S) magnet made by HABBY PTE. LTD.. In-Game asset. 2d. High contrast. No shadows
Survivor.io style 2D shuriken made by HABBY PTE. LTD.. In-Game asset. 2d. High contrast. No shadows
Survivor.io style 2D brick made by HABBY PTE. LTD.. In-Game asset. 2d. High contrast. No shadows
Survivor.io style 2D missile rocket made by HABBY PTE. LTD.
A 2D green radiating circular aura in the Survivor.io style made by HABBY PTE. LTD.. In-Game asset. 2d. High contrast. No shadows
A 2D bomb in the style of Survivor.io, made by HABBY PTE. LTD.. In-Game asset. 2d. High contrast. No shadows
A 2D circular burning effect in Survivor.io style made by HABBY PTE. LTD. (not only the surroundings but also the inside burns) In-Game asset. 2d. High contrast. No shadows
A 2D molotov in the Survivor.io style made by HABBY PTE. LTD.. In-Game asset. 2d. High contrast. No shadows
Survivor.io style 2D greenish exp sphere made by HABBY PTE. LTD. No exp written on it. In-Game asset. 2d. High contrast. No shadows
Survivor.io style 2D half-moon orange boomerang made by HABBY PTE. LTD. In-Game asset. 2d. High contrast. No shadows
Survivor.io style 2D 1 chicken leg.. In-Game asset. 2d. High contrast. No shadows
2D survivor.io game style atomic boom effect front view. No text written on it.
2D. Ranged zombie. attacks with poisonous saliva. In-Game asset. 2d. High contrast. No shadows
2D. Child (small) zombie. He has a small saw in his hand.. In-Game asset. 2d. High contrast. No shadows
2D. Fat zombie. His hands are too big.. In-Game asset. 2d. High contrast. No shadows
Poisonous green circular saliva. 2D. Top View.. In-Game asset. 2d. High contrast. No shadows
Small green claw slash effect. 2D. Top View.. In-Game asset. 2d. High contrast. No shadows
Giant boss angry reddish zombie. 2D.. In-Game asset. 2d. High contrast. No shadows
Small saw slash effect. 2D. Top View.. In-Game asset. 2d. High contrast. No shadows
Big red fist slash effect. 2D. Top View.. In-Game asset. 2d. High contrast. No shadows
Kanlı kemik 2D. Top View.. In-Game asset. 2d. High contrast. No shadows
2D character that looks like a prophet and holds a holy book in his hand.. In-Game asset. 2d. High contrast. No shadows
A background image (wallpaper) representing an old prophet-like man with white hair and beard, wearing a priest's robe (hooded) and holding a holy book (christianity, cross) in his hand, fighting against zombies.. In-Game asset. 2d. High contrast. No shadows
Zombie flesh and bone themed 2D cardboard hollow (without text) horizontal rectangular button.. In-Game asset. 2d. High contrast. No shadows
2D. Healer zombie. Like a female zombie in a healer costume.. In-Game asset. 2d. High contrast. No shadows
2D. Brain illustrated healing potion.. In-Game asset. 2d. High contrast. No shadows
A healing blood pool with circular zombie brain and bone particles. Green + (healing) symbols on top. 2D.. In-Game asset. 2d. High contrast. No shadows
2D. Survivor.io game style skill card. No text written on it. No symbols on it. Just the blank card. Green.. In-Game asset. 2d. High contrast. No shadows
2D. Cartoon. The rise of the zombie ghost spirit from the ground.. In-Game asset. 2d. High contrast. No shadows
levelup
Sound effect
hit
Sound effect
rocketBoom
Sound effect
pickup
Sound effect
backGroundMusic
Music
death
Sound effect
damageTaken
Sound effect
mainMenuMusic
Music
deathScreenMusic
Music
swordSoundEffect
Sound effect
bumerangSoundEffect
Sound effect
brickSoundEffect
Sound effect
lightningSoundEffect
Sound effect
ballSoundEffect
Sound effect
rocketSoundEffect
Sound effect
auraSoundEffect
Sound effect
molotovSoundEffect
Sound effect
molotovBoom
Sound effect
introSpeech
Sound effect
bombBoomSound
Sound effect