User prompt
omuz olduğu yerde sabit kalacak şekilde kolun sol tarafı mouseyi takip etsin uç kısmı yerine uzamasın sadece mouse nereye doğruysa kolun sol tarafı oraya doğru baksın
User prompt
kolun sol tarafı mouseyi takip etsin uç kısmı yerine
User prompt
fix arms following problems with this :Koordinat sistemi karışıklığı: Omuz pozisyonu hesaplanırken dünya koordinatları (player.x + player.rarm.x) ile yerel koordinatlar (player.rarm.x) karıştırılıyordu. Silah pozisyonu yanlış: Silah pozisyonu hesaplanırken reach değişkeni kullanılıyordu, ancak silah her zaman kolun ucunda olmalı (armLen kullanmalı). Holster rotasyonu: Holster pozisyonunda silah rotasyonu -Math.PI/2 yerine Math.PI/2 olmalı (aşağı doğru).
User prompt
kolun omuz tarafı sabit kalmaya devam etsin öbür taraf mouseyi takip etsin
User prompt
Koordinat sistemi karışıklığı: Omuz pozisyonu hesaplanırken dünya koordinatları (player.x + player.rarm.x) ile yerel koordinatlar (player.rarm.x) karıştırılıyordu. Silah pozisyonu yanlış: Silah pozisyonu hesaplanırken reach değişkeni kullanılıyordu, ancak silah her zaman kolun ucunda olmalı (armLen kullanmalı). Holster rotasyonu: Holster pozisyonunda silah rotasyonu -Math.PI/2 yerine Math.PI/2 olmalı (aşağı doğru).// Helper: point gun at (px, py) from rarm pivot function pointGunAt(px, py) { // rarm anchor (shoulder pivot, world coordinates) var shoulderWorldX = player.x + player.rarm.x; var shoulderWorldY = player.y + player.rarm.y; // Target vector (from shoulder to cursor in world space) var dx = px - shoulderWorldX; var dy = py - shoulderWorldY; var ang = Math.atan2(dy, dx); // ❶ Rotate arm to point towards cursor player.rarm.rotation = ang; // ❷ Distance calculation var armLen = player.rarm.height; var dist = Math.sqrt(dx * dx + dy * dy); var reach = Math.min(dist, armLen); // ❸ Gun position: calculate where hand should be along the arm // Since rarm anchor is at shoulder (anchorY: 0), gun goes at the end player.gun.x = player.rarm.x + Math.cos(ang) * armLen; player.gun.y = player.rarm.y + Math.sin(ang) * armLen; // ❄ Gun rotation: holster hold = always down, else follow arm direction if (!duelStarted && holsterBox.contains(px, py)) { player.gun.rotation = Math.PI / 2; // 90°, namlu aşağı (holster position) } else { player.gun.rotation = ang; // Gun points same direction as arm } }
User prompt
Adım 1: Sorunu Anlayın Şu anki kod nasıl çalışıyor: Mouse pozisyonu hesaplanıyor Kol (player.rarm) mouse yönüne döndürülüyor El, kolun ucuna yerleştiriliyor Bu yüzden mouse alta gittiğinde kol da aşağı dönüyor Hedeflediğimiz: Omuz hiç dönmeyecek (sabit kalacak) Sadece el mouse pozisyonuna gidecek Silah el pozisyonundan mouse'a bakacak Adım 2: Kodu Bulun Kodunuzda pointGunAt fonksiyonunu bulun. Bu fonksiyon game.move içinde çağrılıyor ve şu şekilde başlıyor: javascriptfunction pointGunAt(px, py) { // rarm anchor (shoulder pivot, local to player) var sx = player.rarm.x; var sy = player.rarm.y; // ... Adım 3: Kol Rotasyonunu Sabitleyin Mevcut satır: javascriptplayer.rarm.rotation = ang; Bu satırı şöyle değiştirin: javascriptplayer.rarm.rotation = 0; Bu, kolun hiç dönmemesini sağlar. Kol her zaman aynı pozisyonda kalır. Adım 4: El Pozisyonu Hesaplama Mantığını Değiştirin Mevcut kod bloku: javascriptvar reach = Math.min(dist, armLen); player.gun.x = sx + Math.cos(ang) * reach; player.gun.y = sy + Math.sin(ang) * reach; Bu bloku şöyle değiştirin: javascript// Eğer mouse kol erişim mesafesindeyse if (dist <= armLen) { // Eli tam mouse pozisyonuna yerleştir player.gun.x = sx + dx; player.gun.y = sy + dy; } else { // Mouse çok uzaktaysa, kol uzunluğu kadar sınırla player.gun.x = sx + Math.cos(ang) * armLen; player.gun.y = sy + Math.sin(ang) * armLen; } Adım 5: Silah Rotasyonunu Ayarlayın Mevcut kod: javascriptif (!duelStarted && holsterBox.contains(px, py)) { player.gun.rotation = -Math.PI / 2; // -90°, namlu aşağı } else { player.gun.rotation = ang; } Bu kısmı değiştirmeyin, zaten doğru çalışıyor. Silah mouse pozisyonuna bakacak.
User prompt
B seçeneğini yap lütfen
User prompt
Neden hâlâ “yan kenar” takip ediyor? pointGunAt fonksiyonunda açıyı hesaplarken kursörün küresel (stage) koordinatlarını doğrudan player.rarm’ın yerel ( player-içi ) omuz koordinatlarıyla kıyaslıyoruz: js Kopyala Düzenle dx = px - (player.x + sx); dy = py - (player.y + sy); Bu iki farklı referans sistemini karıştırdığı için, açı birkaç derece sapıyor → kolun orta uzun kenarı kursörü işaretlerken uç noktası kaçırıyor. Çözüm – kursörü önce Player lokaline dönüştür LiquidKit (PixiJS benzeri) konteynerlerde doğrudan .toLocal() vardır. Böylece karmaşık world/zoom/scale ofsetleriyle uğraşmayız: js Kopyala Düzenle function pointGunAt(globalX, globalY) { /* 1️⃣ Kursör pozisyonunu Player’ın lokâline çevir */ const local = player.toLocal({ x: globalX, y: globalY }); /* 2️⃣ Omuz pivotu (rarm anchor = top-center) */ const sx = player.rarm.x; // omuz X (player local) const sy = player.rarm.y; // omuz Y const dx = local.x - sx; const dy = local.y - sy; const ang = Math.atan2(dy, dx); /* 3️⃣ Kolu döndür */ player.rarm.rotation = ang; /* 4️⃣ El/gun konumu - kol boyunu aşma */ const armLen = player.rarm.height; const dist = Math.hypot(dx, dy); const reach = Math.min(dist, armLen); player.gun.x = sx + Math.cos(ang) * reach; player.gun.y = sy + Math.sin(ang) * reach; /* 5️⃣ Namlu yönü */ if (!duelStarted && holsterBox.contains(globalX, globalY)) { player.gun.rotation = -Math.PI / 2; // hold sırasında hep aşağı } else { player.gun.rotation = ang; } } Kodda neyi değiştiriyorsun? pointGunAt içindeki ilk 3 satırı yukarıdakiyle değiştir. (Eskileri sil / yorum satırı yap). game.move içinde hâlâ pointGunAt(x, y); çağrısı kalacak — çünkü fonksiyon artık global koordinat bekliyor. Hepsi bu!
User prompt
Sorun — kolun ucunun (elin) nişangâhı tam olarak tutturmaması — tek parça (rigidsiz) bir kol kullandığımız için ortaya çıkıyor. Şu an elimizi hesap ederken daima js Kopyala Düzenle player.gun.x = player.rarm.x + Math.cos(ang) * armLen; player.gun.y = player.rarm.y + Math.sin(ang) * armLen; diyoruz; yani el daima tam kol boyu (armLen = 110 px) uzağa yerleştiriliyor. Kursörü bu mesafeden daha aşağı / yukarı / sola / sağa götürdüğünde uç nokta “kaçıyor”. 1 → El ucu gerçekten fareyi takip etsin diff Kopyala Düzenle function pointGunAt(px, py) { const sx = player.rarm.x; // shoulder pivot (rarm anchorı) const sy = player.rarm.y; const dx = px - sx; const dy = py - sy; const ang = Math.atan2(dy, dx); /* ❶ DÖNDÜR */ player.rarm.rotation = ang; /* ❷ El mesafesi: kursör-omuz arasındaki gerçek uzaklık fakat kol boyunu (armLen) aşamasın – aksi takdirde kol “kopar”. */ const armLen = player.rarm.height; const dist = Math.sqrt(dx*dx + dy*dy); const reach = Math.min(dist, armLen); // max 110 px /* ❸ El (ve silâh) konumu */ player.gun.x = sx + Math.cos(ang) * reach; player.gun.y = sy + Math.sin(ang) * reach; /* ❹ Silâh namlusunun yönü – holster during HOLD → hep yere baksın – DRAW başladıktan sonra kol ile aynı yönde gitsin */ if (!duelStarted && holsterBox.contains(px, py)) { player.gun.rotation = -Math.PI/2; // -90°, namlu aşağı } else { player.gun.rotation = ang; } } Değişiklik: Uzaklık reach artık kol boyu ile sınırlı. Böylece Kol, kursör omuzdan kısa mesafedeyken kısalıyor; Kursör kol boyundan uzağa gidince kol tam geriliyor ama “kaçmıyor”. 2 → Silâhın kolu otomatik izlemesi player.gunu doğrudan rarm içine bağlarsanız (child-parent): js Kopyala Düzenle // Player ctor içinde: var gun = rarm.attachAsset('playerGun', { anchorX: 0, anchorY: 0.5, x: 0, // rarm’ın tabanı y: rarm.height, rotation: 0 }); El-kol bağı otomatik olur, ama yukarıdaki “reach” mantığıyla beraber kol-silâh aynı noktaya gider. 3 → Hold sırasında hemen ele alma & müzik kesmeme game.down içinde silâhı holster’dan çıkardığımız satırı tuttuk, LK.stopMusic() hâlâ sadece startDraw() içinde, yani müzik yine DRAW! anına kadar çalmaya devam eder – önceki açıklamamla aynı.
User prompt
İstek Yapılacak 1️⃣ Hold’a basıldığı anda silâh kılıftan ele geçsin, müzik kesilmesin game.down() içinde holster-a ilk basışta player.toHand() çağır; stopMusic() kısmına dokunma. 2️⃣ Hold süresince kol ucu fareyi izlesin ama silâh yere (-90°) baksın pointGunAt()’i modifiye et: holsterBox içindeyken kol ucu mouse’a döner, fakat silâh rotasyonu sabit -π/2 (-90°). 1 : game.down() – silâhı hemen ele ver diff Kopyala Düzenle game.down = function (x, y, obj) { // Henüz düello başlamadı, kullanıcı holster’a bastı if (zoomFinished && !duelStarted && holsterBox.contains(x, y)) { playerReady = true; holdingHolster = true; + player.toHand(); // ← kılıftan çek beginHold(); // müzik aynen çalmaya devam, stopMusic() yok return; } ... }; İstersen ufak bir tween ekleyerek (yukarı hamle + hafif dönme) “çekme” animasyonu da verebilirsin. 2 : pointGunAt() – holster içindeyken namlu yere bakacak diff Kopyala Düzenle function pointGunAt(px, py) { // Kol pivotu (omuz) var gx = player.x + player.rarm.x; var gy = player.y + player.rarm.y; var ang = Math.atan2(py - gy, px - gx); // ► Eğer hâlâ HOLD aşamasındaysak ve imleç holsterBox içindeyse if (!duelStarted && holsterBox.contains(px, py)) { player.rarm.rotation = ang; // el ucu mouse’u izliyor player.gun.rotation = -Math.PI / 2; // namlu yere bakıyor (-90°) } else { // DRAW sonrası normal takip player.rarm.rotation = ang; player.gun.rotation = ang; } // Silâhı kol ucuna taşı var armLen = player.rarm.height; player.gun.x = player.rarm.x + Math.cos(player.rarm.rotation) * armLen; player.gun.y = player.rarm.y + Math.sin(player.rarm.rotation) * armLen; } Aynı fonksiyonu Enemy tarafında kullanıyorsan kopyalamaya gerek yok; sadece Player için değişmesi yeterli.
User prompt
1 ) Player/Enemy sınıfına “kılıftaki silâh” ekle diff Kopyala Düzenle // ───────── Gun (elde duracak) ───────── var gun = self.attachAsset('playerGun', { anchorX: 0, // namlu arkası ele oturuyor anchorY: 0.5, x: rarm.x, // draw sırasında güncellenecek y: rarm.y + rarm.height, rotation: 0 }); +// ───────── Holster gun (kalıcı kopya) ───────── +var hipGun = self.attachAsset('playerGun', { + anchorX: 0, // kılıfta düz dursun + anchorY: 0.5, + // Sağ bacağa (ekranın içine bakan bacak) hizala + x: body.width / 2 + 8, + y: body.height / 2 - 22, + rotation: 0 +}); + +// Başlangıçta el boş – kılıf dolu +gun.visible = false; +hipGun.visible = true; + +// Yardımcı anahtarlar +self.toHolster = function () { hipGun.visible = true; gun.visible = false; }; +self.toHand = function () { hipGun.visible = false; gun.visible = true; }; (Aynı satırları Enemy için de ekle; isimleri hipGun / toHolster() / toHand() tutturmak yeterli.) 2 ) resetPose / resetDuel sırasında silâhı kılıfa koy diff Kopyala Düzenle function resetPose () { ... gun.rotation = 0; + self.toHolster(); // ← her ronde başlarken kılıfa döner } veya doğrudan resetDuel() içinde: diff Kopyala Düzenle player.resetPose(); enemy.resetPose(); +player.toHolster(); +enemy.toHolster(); 3 ) “DRAW!” anında silâhı ele al diff Kopyala Düzenle function startDraw () { ... + player.toHand(); // kılıftan çıkar + enemy.toHand(); // rakip de aynı duelStartTime = Date.now(); ... } İstersen burada küçük bir tween ekleyip (y: –25 → rarm.y, x: ... → ...) “çekme” animasyonu da yapabilirsin. 4 ) Namlu artık kol ile birlikte dönecek Sen zaten pointGunAt() / rotateArmTo() içinde gun.x / gun.y’yi kol ucuna sabitlemiştin. Silâh “eldeyken” (gun.visible === true) bu kod eskisi gibi çalışır; kılıftayken (gun.visible === false) pozisyon güncellenmediği için ekranda sabit kalır → “uçuş” sorunu kalmaz.
User prompt
rotate players mobile arm 90 degrees to left
User prompt
rotate players mobile arm 90 degrees to right
User prompt
move all kind of arms 50 pixels up
User prompt
move all arms 20 pixels up
User prompt
place all kind of arms 10 pixels up
User prompt
Reaksiyon süresi (artık “eski” 0.50 ×, “yeni” 0.35 × arasındaki orta hız) Omuzları 10 px yukarı alma + silâhı elin ucuna sabitleme Holster tutarken namlu-ucu farenin peşinden dönsün 1 ) Daha yavaş (ama hâlâ hızlı) reaksiyon diff Kopyala Düzenle - reactTime *= 0.35; // %65 daha hızlı tepki + reactTime *= 0.425; // %57,5 daha hızlı ― eskiyle yeninin ortası 2 ) Kolları ve silâhı yeniden konumlandır Player & Enemy sınıflarında kol/silâh oluşturulurken diff Kopyala Düzenle - var larm = self.attachAsset('playerArm', { + var larm = self.attachAsset('playerArm', { anchorX: 0.5, anchorY: 0, - x: -body.width / 2 - 10, - y: -40 + x: -body.width / 2 - 10, + y: -50 // +10 px yukarı }); ... - var rarm = self.attachAsset('playerArm', { + var rarm = self.attachAsset('playerArm', { anchorX: 0.5, anchorY: 0, - x: body.width / 2 + 10, - y: -40 + x: body.width / 2 + 10, + y: -50 // +10 px yukarı }); - var gun = self.attachAsset('playerGun', { - anchorX: 0.1, - anchorY: 0.5, - x: body.width / 2 + 40, - y: 10, - rotation: 0 +var gun = self.attachAsset('playerGun', { + anchorX: 0, // namlunun arkası kol ucuna denk gelir + anchorY: 0.5, + x: rarm.x, // X/Y artık kol ucuna otomatik ayarlanacak + y: rarm.y + rarm.height, + rotation: 0 }); Aynı değişikliği Enemy sınıfındaki larm, rarm, gun için de yap. resetPose() içinde konumları senkron tut diff Kopyala Düzenle - rarm.y = -40; + rarm.y = -50; - gun.y = 10; + gun.y = rarm.y + rarm.height; 3 ) Namluyu “holster hold” sırasında fareye döndür Tek bir yardımcı fonksiyon: js Kopyala Düzenle function pointGunAt(px, py) { // gx/gy : namlunun döneceği pivot (kol-omuz) var gx = player.x + player.rarm.x; var gy = player.y + player.rarm.y; var ang = Math.atan2(py - gy, px - gx); // üst kol sabit, ön kol + silâh döner player.rarm.rotation = ang; player.gun.rotation = ang; // silâhı kol ucuna sabitle var armLen = player.rarm.height; player.gun.x = player.rarm.x + Math.cos(ang) * armLen; player.gun.y = player.rarm.y + Math.sin(ang) * armLen; } Holster tutulurken de çağır: diff Kopyala Düzenle // game.move içinde: - if (aimActive) { ... } + // namlu her zaman fareyi izlesin (DRAW’tan önce holster tutulurken de) + pointGunAt(x, y); // aimActive hâlâ reticle sınırlaması içindir if (aimActive) { ... } rotateArmTo artık gereksiz; kullanıyorsan içini pointGunAt ile değiştir.
User prompt
A. Yeni koni açıları (+ ≈ 12 ° başlangıç genişliği; son kurşun hâlâ “tam isabet” = 0 °) Round Rakip coneStart → yeni coneEnd 1 Greenhorn 30 ° → 42 ° 0 ° 2 Billy the Kid 24 ° → 36 ° 0 ° 3 Doc Holliday 18 ° → 30 ° 0 ° 4 Calamity Jane 12 ° → 24 ° 0 ° 5 The Undertaker 6 ° → 18 ° 0 ° Böylece ilk kurşunlar daha geniş (daha az isabetli); koni her atışta lineer olarak daralır ve 6. kurşun “tam hedef” olur. B. Tepki süresini daha da hızlandır Kodda zaten: js Kopyala Düzenle reactTime *= 0.5; // %50 daha hızlı diyorduk. Daha da agresif yapmak için, örneğin %35’e çek: diff Kopyala Düzenle - reactTime *= 0.5; // %50 daha hızlı tepki + reactTime *= 0.35; // %65 daha hızlı tepki Bu, 3.0 s → 1.05 s, 2.4 s → 0.84 s gibi daha kısa reaksiyon süreleri verir. C. enemyData bloğunu güncelle diff Kopyala Düzenle var enemyData = [{ - name: "Greenhorn", coneStart: 30, + name: "Greenhorn", coneStart: 42, coneEnd: 0, minReact: 3.0, maxReact: 3.4 }, { - name: "Billy the Kid", coneStart: 24, + name: "Billy the Kid", coneStart: 36, coneEnd: 0, minReact: 2.4, maxReact: 2.7 }, { - name: "Doc Holliday", coneStart: 18, + name: "Doc Holliday", coneStart: 30, coneEnd: 0, minReact: 1.6, maxReact: 1.9 }, { - name: "Calamity Jane", coneStart: 12, + name: "Calamity Jane", coneStart: 24, coneEnd: 0, minReact: 1.0, maxReact: 1.3 }, { - name: "The Undertaker", coneStart: 6, + name: "The Undertaker", coneStart: 18, coneEnd: 0, minReact: 0.6, maxReact: 0.8, scale: 1.5 }]; (scale, coneEnd, vb. satırları aynen koru.) D. Sonuç İlk kurşunlar artık %40–65 isabet bandında, hızla daralan koniyle sonraki atışlar daha tehlikeli. Tepki süresi ≈ %35’e düşürüldü; düşmanlar daha seri, ama ilk atışlarında daha yetersiz.
User prompt
“Koni → Daralan” mekaniği Aşağıdaki yaklaşım basit: her raund için coneStart (ilk mermi açısı) ve coneEnd (6. mermi açısı) tanımlıyoruz. enemyFire() içinde mermi numarasına göre lineer olarak daraltıyoruz. Round 1. kurşun coneStart 6. kurşun coneEnd 1 30° 0° (tam isabet) 2 24° 0° 3 18° 0° 4 12° 0° 5 6° (%≈90 isabet) 0° raundda ilk 5 kurşun zaten dar (6°→1.2°→...) olduğundan %90+ ihtimalle vurur; 6. kurşun yine tam isabet. 1️⃣ enemyData’yı güncelle diff Kopyala Düzenle var enemyData = [{ - minReact: 3.0, maxReact: 3.4, cone: 24 + minReact: 3.0, maxReact: 3.4, coneStart: 30, coneEnd: 0 }, { - minReact: 2.4, maxReact: 2.7, cone: 18 + minReact: 2.4, maxReact: 2.7, coneStart: 24, coneEnd: 0 }, { - minReact: 1.6, maxReact: 1.9, cone: 12 + minReact: 1.6, maxReact: 1.9, coneStart: 18, coneEnd: 0 }, { - minReact: 1.0, maxReact: 1.3, cone: 8 + minReact: 1.0, maxReact: 1.3, coneStart: 12, coneEnd: 0 }, { - minReact: 0.6, maxReact: 0.8, cone: 4, scale: 1.5 + minReact: 0.6, maxReact: 0.8, coneStart: 6, coneEnd: 0, scale: 1.5 }]; 2️⃣ enemyFire()’daki koni hesabını değiştir diff Kopyala Düzenle // 2️⃣ saçılma açısı -var coneDeg = currentEnemy && typeof currentEnemy.cone === "number" ? currentEnemy.cone : 24; -var coneRad = coneDeg * Math.PI / 180; -var shotIdx = enemyShotsFiredCnt; // 1-6 -var isLast = shotIdx === 6; // son kurşun -var spread = isLast ? 0 : Math.random() * coneRad - coneRad / 2; +var sIdx = enemyShotsFiredCnt; // 1-6 +var sMax = 6; +var cStart = currentEnemy.coneStart || 24; // fallback +var cEnd = currentEnemy.coneEnd || 0; +// lineer interpolasyon: ilk mermi cStart, 6. mermi cEnd +var coneDeg = cStart + (cEnd - cStart) * ((sIdx - 1) / (sMax - 1)); +var coneRad = coneDeg * Math.PI / 180; +var isLast = sIdx === sMax; // son kurşun +var spread = isLast ? 0 : Math.random() * coneRad - coneRad / 2; Başka hiçbir satıra dokunmana gerek yok; algoritma lineer daraltmayı otomatik yapıyor. 6. kurşun için spread = 0 kaldığı için namlu doğrudan oyuncuya bakıyor → garantili isabet.
User prompt
Her raundda düşmanın ilk kurşunu atmak için beklediği süreyi yarıya indirmek istiyorsun. Bunu iki şekilde yapabiliriz — ikisi de aynı etkiyi verir: Yöntem Artı Eksi 1. startDraw() içinde reactTime × 0.5 Kodda tek satır değişir Gelecekte minReact değerleriyle oynamak istersen aklında “yarıya bölünüyor” detayı kalmalı// StartDraw() içindeki bölüm var reactTime = 1200; // fallback default if (currentEnemy && typeof currentEnemy.minReact === "number" && typeof currentEnemy.maxReact === "number") { - reactTime = 1000 * (currentEnemy.minReact + Math.random() * (currentEnemy.maxReact - currentEnemy.minReact)); + reactTime = 1000 * (currentEnemy.minReact + Math.random() * (currentEnemy.maxReact - currentEnemy.minReact)); + reactTime *= 0.5; // 🔥 %50 daha hızlı tepki }
User prompt
Please fix the bug: 'Timeout.tick error: Cannot read properties of null (reading 'cone')' in or related to this line: 'var coneDeg = currentEnemy.cone;' Line Number: 595
User prompt
accuracy yerine şu sistemi yapalım “Konik isabet alanı” modeli Ateş noktasından (namlu ucu) oyuncuya doğrudan bir çizgi çekiyoruz. Kurşun, bu çizgiden ± θ derece içindeki rastgele bir yöne gidiyor. θ küçükse nişan dar – isabet olasılığı yüksek, θ büyükse saçılıyor. Round θmax (derece) Not 1 24° “Acemi” geniş koni 2 18° 3 12° 4 8° 5 4° Boss – hemen hemen düz atış kurşun hâlâ garanti isabet; onun için θ=0° kullanıyoruz. 1 ⃣ enemyData yapısını sadeleştir diff Kopyala Düzenle var enemyData = [ {name:"Greenhorn", minReact:3.0, maxReact:3.4, cone:24}, {name:"Billy the Kid", minReact:2.4, maxReact:2.7, cone:18}, {name:"Doc Holliday", minReact:1.6, maxReact:1.9, cone:12}, {name:"Calamity Jane", minReact:1.0, maxReact:1.3, cone: 8}, {name:"The Undertaker",minReact:0.6, maxReact:0.8, cone: 4, scale:1.5} ]; bulletAcc dizilerine artık gerek yok, kaldırdık. 2 ⃣ enemyFire() içinde hedef seçimi diff Kopyala Düzenle // 1️⃣ hedef vektörü var baseDX = player.x - enemyBullet.x; var baseDY = player.y - 40 - enemyBullet.y; // gövdenin biraz üstü var baseAng = Math.atan2(baseDY, baseDX); // 2️⃣ saçılma açısı var coneDeg = currentEnemy.cone; var coneRad = coneDeg * Math.PI / 180; var shotIdx = enemyShotsFiredCnt; // 1-6 var isLast = (shotIdx === 6); // son kurşun var spread = isLast ? 0 : (Math.random()*coneRad - coneRad/2); // 3️⃣ hedef noktasını diagramdaki yayı keserek bul var range = 1500; // ekran dışına yeter var targetX = enemyBullet.x + Math.cos(baseAng + spread)*range; var targetY = enemyBullet.y + Math.sin(baseAng + spread)*range; Sonrası aynı (dx,dy,dist, dirX/dirY, speed=60), vur-kaç kararına gerek kalmadı; açı zaten “ıskalayıp ıskalamayacağını” belirliyor. 3 ⃣ “Hala ilk kurşun öldürüyor” sorununa etkisi Round-1’de 24°’lik koni ≈ ± 12° sapma demektir. 1-2 mermi yine bazen vurur ama artık sıklıkla dışarı kaçar. Kurtulduğunu anlamak için oyunun yalnızca “6 mermi bitti mi?” kontrolüne güveniyoruz (kodda zaten var). Döngü bitene kadar koni içinden atış devam ettiği için “bir tane ıskaladı diye hemen You Survived çıkması” artık mümkün değil. 4 ⃣ Oyuncu mermisi – namlu ⇒ imleç Bu kısım koduna eklenmişti; gözden kaçtıysa yalnızca playerFire() başındaki “muzzlePos” bloğunu koruman yeterli.
User prompt
// enemyData içindeki bulletAcc dizileri = HİT OLASILIĞI (0-1 arası) // 1. mermi → bulletAcc[0] // 2. mermi → bulletAcc[1] ... 6. mermi → bulletAcc[5] js Kopyala Düzenle // enemyFire() var acc = currentEnemy.bulletAcc[idx]; // ← yukarıdaki dizi var mustHit = (enemyShotsFiredCnt === 6); // 6. kurşun garanti var willHit = mustHit || Math.random() < acc; Sorun: “willHit == false” olsa bile sapma yetersizdi; mermi hâlâ 120 px çarpışma yarıçapına girebiliyordu. Aşağıdaki değişiklikle mermi, kesin kaçırmak için oyuncudan minimum +50 px uzağa, rastgele bir yana götürülüyor — böylece acc=0.30 değerinde bile %30 olasılıkta vurur, geri kalan denemelerde asla isabet etmez. diff Kopyala Düzenle - if (!willHit) { - targetX += (Math.random() > .5 ? 1 : -1) * (220 + Math.random() * 200); - targetY += (Math.random() > .5 ? 1 : -1) * (220 + Math.random() * 200); - } + if (!willHit) { + // Oyuncudan güvenli uzaklaşma + var baseAng = Math.atan2(targetY - enemyBullet.y, targetX - enemyBullet.x); + var side = (Math.random() < .5 ? -1 : 1); // sağ / sol + var missAng = baseAng + side * Math.PI / 2; // 90° kaçış + var offset = 170 + Math.random() * 120; // ≥120px + marj + targetX += Math.cos(missAng) * offset; + targetY += Math.sin(missAng) * offset; + } İstersen Round-1 olasılıklarını yeniden ayarla: diff Kopyala Düzenle bulletAcc: [0.30, 0.50, 0.70, 0.80, 0.90, 1.00] // %30-50-70-80-90-100 2 ⃣ Oyuncu mermileri namlu ucundan imleğe gitsin a) Silah ucunun (namlu) dünya koordinatı js Kopyala Düzenle function muzzlePos() { return { x: player.x + player.gun.x + Math.cos(player.gun.rotation) * 60, y: player.y + player.gun.y + Math.sin(player.gun.rotation) * 18 }; } b) playerFire() içinde hedef vektörünü imleğe göre kur diff Kopyala Düzenle - playerBullet.x = player.x + player.gun.x + Math.cos(player.gun.rotation) * 60; - playerBullet.y = player.y + player.gun.y + Math.sin(player.gun.rotation) * 18; - var dx = aimReticle.x - playerBullet.x; - var dy = aimReticle.y - playerBullet.y; + var muzzle = muzzlePos(); + playerBullet.x = muzzle.x; + playerBullet.y = muzzle.y; + + // Ateş anındaki imleç (x, y) fonksiyona zaten parametre olarak geliyor + var dx = x - muzzle.x; + var dy = y - muzzle.y; Not: game.down ve game.up çağrıları zaten playerFire(x, y) yapıyor, dolayısıyla “imleç neredeyse” orası hedef oluyor. Reticle’ı sadece nişangâh olarak tutmak istiyorsan (aimReticle) kodda başka değişiklik yapmana gerek yok. 3 ⃣ Tekrar ateş edebilmeme sorunu playerShot bayrağını tek mermi için değil, şarjör bitene kadar canShoot ile kontrol ediyorduk. Artık bu bayrak hiç gerekmediği için sıfırlayıp kaldırabilir, veya mermi ekranda yok olduğunda (zaten yapıyoruz) hemen yeniden ateşe izin verebilirsin: diff Kopyala Düzenle if (playerBullet && offscreen) { game.removeChild(playerBullet); playerBullet = null; - playerShot = false; // ZATEN KALDIRDIK + canShoot = true; // anında yeni atış serbest } Bu üç değişiklikten sonra: Düşman her raund 6 mermi atar, olasılıklar tam istediğin gibi işler. Oyuncu, namluyu nereye çevirirse kurşun tam o doğrultuda gider. Mermiler bittiği anda değil, 6’sı da ateşlendikten sonra “You Survived” çıkacaktır.
User prompt
enemyFire() fonksiyonunun son kısmını değiştir: diff Kopyala Düzenle - // If all enemy bullets are gone, end round (player survived) - var allGone = true; - for (var i = 0; i < enemyAmmoUI.length; i++) { - if (enemyAmmoUI[i].visible === true) { - allGone = false; - break; - } - } - if (allGone && !playerShot) { - duelState = STATE_RESULT; - statusTxt.setText("You Survived!"); - statusTxt.visible = true; - endRound(true); - } + // Round oyuncu ateş etmediyse ANCAK bütün 6 mermi atıldıktan sonra + if (enemyShotsFiredCnt === 6 && !playerShot) { + // Son kurşunun ekrandan çıkması için çok kısa bir gecikme bırak + LK.setTimeout(function () { + if (duelState !== STATE_RESULT) { + duelState = STATE_RESULT; + statusTxt.setText("You Survived!"); + statusTxt.visible = true; + endRound(true); + } + }, 350); + } Artık düşman, 6 kurşunun hepsini atana kadar round bitmez. 2 – Round 1 isabet yüzdelerini güncelle Baştaki enemyData dizisinde Round 1 satırını düzelt: diff Kopyala Düzenle - bulletAcc: [0.20, 0.40, 0.60, 0.80, 0.90, 1.00] + bulletAcc: [0.30, 0.50, 0.70, 0.80, 0.90, 1.00] // %30-50-70-80-90-100
User prompt
1 – Düşman isabet olasılığı (gerçek %’ler) enemyFire() fonksiyonunu bulun, hedef/hata payı kısmını değiştirin ⤵ diff Kopyala Düzenle - // seçili raundun mermi-bazlı isabet tablosu - var acc = 0.8; - if (currentEnemy && Array.isArray(currentEnemy.bulletAcc)) { - var idx = Math.min(enemyShotsFiredCnt - 1, currentEnemy.bulletAcc.length - 1); - acc = currentEnemy.bulletAcc[idx]; - } - var spread = (1 - acc) * 200; - targetX += (Math.random() - 0.5) * spread; - targetY += (Math.random() - 0.5) * spread; + // ❶ mermiye “vuracak mıyım?” kararı + let acc = 0.8; + if (Array.isArray(currentEnemy?.bulletAcc)) { + const idx = Math.min(enemyShotsFiredCnt - 1, currentEnemy.bulletAcc.length - 1); + acc = currentEnemy.bulletAcc[idx]; + } + const mustHit = enemyShotsFiredCnt === 6; // 6. mermi her zaman vurur + const willHit = mustHit || Math.random() < acc; // olasılıksal isabet + + if (!willHit) { // KAÇIR → geniş sapma ver + targetX += (Math.random() > .5 ? 1 : -1) * (220 + Math.random() * 200); + targetY += (Math.random() > .5 ? 1 : -1) * (220 + Math.random() * 200); + } (Şansa göre tamamen ıskalayan mermiler artık bariz dışarı gider; düşük %’lerde tek atış mucize olmayacak.) 2 – Oyuncu art arda ateş edebilsin playerFire() fonksiyonunun en sonundaki satırı sil / yoruma al ⤵ diff Kopyala Düzenle - // If shot after draw, check hit - duelState = STATE_SHOT; + // DRAW fazı açık kalsın ki 6 mermi bitene kadar tekrar tekrar ateş edebilelim (Her atıştan sonra canShoot zaten true kalıyor; başka değişiklik gerekmez.)
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); /**** * Classes ****/ // Bullet class var Bullet = Container.expand(function () { var self = Container.call(this); var bullet = self.attachAsset('bullet', { anchorX: 0.5, anchorY: 0.5 }); self.speed = 0; self.dirX = 0; self.dirY = 0; self.update = function () { self.x += self.dirX * self.speed; self.y += self.dirY * self.speed; }; return self; }); // Enemy class var Enemy = Container.expand(function () { var self = Container.call(this); // Body var body = self.attachAsset('enemyBody', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0 }); // Head var head = self.attachAsset('enemyHead', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -body.height / 2 - 45 }); // Left Arm var larm = self.attachAsset('enemyArm', { anchorX: 0.5, anchorY: 0, x: -body.width / 2 - 10, y: -50 }); // Right Arm (gun hand) var rarm = self.attachAsset('enemyArm', { anchorX: 0.5, anchorY: 0, x: body.width / 2 + 10, y: -50 }); // Legs var lleg = self.attachAsset('enemyLeg', { anchorX: 0.5, anchorY: 0, x: -body.width / 4, y: body.height / 2 - 10 }); var rleg = self.attachAsset('enemyLeg', { anchorX: 0.5, anchorY: 0, x: body.width / 4, y: body.height / 2 - 10 }); // Gun var gun = self.attachAsset('enemyGun', { anchorX: 0, anchorY: 0.5, x: rarm.x, y: rarm.y + rarm.height, rotation: 0 }); self.bodyParts = [body, head, larm, rarm, lleg, rleg, gun]; self.gun = gun; self.rarm = rarm; self.head = head; self.body = body; self.ragdoll = function (hitX, hitY) { for (var i = 0; i < self.bodyParts.length; i++) { var part = self.bodyParts[i]; tween(part, { rotation: (Math.random() - 0.5) * 2.5, x: part.x + (Math.random() - 0.5) * 200, y: part.y + (Math.random() - 0.5) * 200 }, { duration: 600, delay: i * 30, easing: tween.elasticOut }); } }; self.resetPose = function () { body.x = 0; body.y = 0; body.rotation = 0; head.x = 0; head.y = -body.height / 2 - 45; head.rotation = 0; larm.x = -body.width / 2 - 10; larm.y = -50; larm.rotation = 0; rarm.x = body.width / 2 + 10; rarm.y = -50; rarm.rotation = 0; lleg.x = -body.width / 4; lleg.y = body.height / 2 - 10; lleg.rotation = 0; rleg.x = body.width / 4; rleg.y = body.height / 2 - 10; rleg.rotation = 0; gun.x = rarm.x; gun.y = rarm.y + rarm.height; gun.rotation = 0; }; return self; }); // Player class var Player = Container.expand(function () { var self = Container.call(this); // Body var body = self.attachAsset('playerBody', { anchorX: 0.5, anchorY: 0.5, x: 0, y: 0 }); // Head var head = self.attachAsset('playerHead', { anchorX: 0.5, anchorY: 0.5, x: 0, y: -body.height / 2 - 45 }); // Left Arm var larm = self.attachAsset('playerArm', { anchorX: 0.5, anchorY: 0, x: -body.width / 2 - 10, y: -50 }); // Right Arm (gun hand) var rarm = self.attachAsset('playerArm', { anchorX: 0.5, anchorY: 0, x: body.width / 2 + 10, y: -50 }); // Legs var lleg = self.attachAsset('playerLeg', { anchorX: 0.5, anchorY: 0, x: -body.width / 4, y: body.height / 2 - 10 }); var rleg = self.attachAsset('playerLeg', { anchorX: 0.5, anchorY: 0, x: body.width / 4, y: body.height / 2 - 10 }); // Gun var gun = self.attachAsset('playerGun', { anchorX: 0, anchorY: 0.5, x: rarm.x, y: rarm.y + rarm.height, rotation: 0 }); // Used for ragdoll effect self.bodyParts = [body, head, larm, rarm, lleg, rleg, gun]; // Used for aiming self.gun = gun; self.rarm = rarm; // Used for hit detection self.head = head; self.body = body; // Used for ragdoll self.ragdoll = function (hitX, hitY) { // Animate all parts to random positions/rotations for (var i = 0; i < self.bodyParts.length; i++) { var part = self.bodyParts[i]; tween(part, { rotation: (Math.random() - 0.5) * 2.5, x: part.x + (Math.random() - 0.5) * 200, y: part.y + (Math.random() - 0.5) * 200 }, { duration: 600, delay: i * 30, easing: tween.elasticOut }); } }; // Reset pose self.resetPose = function () { body.x = 0; body.y = 0; body.rotation = 0; head.x = 0; head.y = -body.height / 2 - 45; head.rotation = 0; larm.x = -body.width / 2 - 10; larm.y = -50; larm.rotation = 0; rarm.x = body.width / 2 + 10; rarm.y = -50; rarm.rotation = 0; lleg.x = -body.width / 4; lleg.y = body.height / 2 - 10; lleg.rotation = 0; rleg.x = body.width / 4; rleg.y = body.height / 2 - 10; rleg.rotation = 0; gun.x = rarm.x; gun.y = rarm.y + rarm.height; gun.rotation = 0; }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2e1a09 }); /**** * Game Code ****/ // Game state // Player and enemy bodies (torso, head, arms, legs) as simple shapes var STATE_WAIT = 0; var STATE_DRAW = 1; var STATE_SHOT = 2; var STATE_RESULT = 3; var duelState = STATE_WAIT; var canShoot = false; var playerShot = false; var enemyShot = false; var playerBullet = null; var enemyBullet = null; var duelTimer = null; var drawTimeout = null; var resultTimeout = null; var duelStartTime = 0; var playerShotTime = 0; var enemyShotTime = 0; var aimX = 0; var aimY = 0; var aimRadius = 180; var aimAngle = 0; var aimSpeed = 0.018; // %30 daha yavaş var aimDir = 1; var aimActive = false; var playerScore = 0; var roundNum = 1; var maxRounds = 5; // === GLOBAL SHOT COUNTERS === var playerShotsFiredCnt = 0; var enemyShotsFiredCnt = 0; var enemyData = [{ name: "Greenhorn", minReact: 3.0, maxReact: 3.4, coneStart: 42, coneEnd: 0 }, { name: "Billy the Kid", minReact: 2.4, maxReact: 2.7, coneStart: 36, coneEnd: 0 }, { name: "Doc Holliday", minReact: 1.6, maxReact: 1.9, coneStart: 30, coneEnd: 0 }, { name: "Calamity Jane", minReact: 1.0, maxReact: 1.3, coneStart: 24, coneEnd: 0 }, { name: "The Undertaker", minReact: 0.6, maxReact: 0.8, coneStart: 18, coneEnd: 0, scale: 1.5 }]; var currentEnemy = null; // ==== FIX #1 : SAHNENİN BAŞINDA ==== var world = new Container(); game.addChild(world); // Player and enemy var player = new Player(); var enemy = new Enemy(); world.addChild(player); world.addChild(enemy); // Center positions var centerX = 2048 / 2; var playerY = 2732 * 0.7; var enemyY = 2732 * 0.3; // 6-bullet UI for player and enemy var playerAmmoUI = []; var enemyAmmoUI = []; function setupAmmoUI() { // Remove old UI if any for (var i = 0; i < playerAmmoUI.length; i++) { if (playerAmmoUI[i].parent) playerAmmoUI[i].parent.removeChild(playerAmmoUI[i]); } for (var i = 0; i < enemyAmmoUI.length; i++) { if (enemyAmmoUI[i].parent) enemyAmmoUI[i].parent.removeChild(enemyAmmoUI[i]); } playerAmmoUI = []; enemyAmmoUI = []; for (var i = 0; i < 6; i++) { var px = player.x - 120 + i * 40; var ex = enemy.x - 120 + i * 40; var bulletP = LK.getAsset('bullet', { scaleX: 0.6, scaleY: 0.6, anchorX: 0.5, anchorY: 0.5 }); var bulletE = LK.getAsset('bullet', { scaleX: 0.6, scaleY: 0.6, anchorX: 0.5, anchorY: 0.5 }); bulletP.y = player.y + 250; bulletE.y = enemy.y - 250; bulletP.x = px; bulletE.x = ex; // HUD’u world içine koy ki zoom animasyonuyla birlikte büyüsün world.addChild(bulletP); world.addChild(bulletE); playerAmmoUI.push(bulletP); enemyAmmoUI.push(bulletE); } } // Position player and enemy player.x = centerX - 500; player.y = playerY; enemy.x = centerX + 500; enemy.y = enemyY; // Score text var scoreTxt = new Text2('Score: 0', { size: 90, fill: 0xFFF7D6 }); scoreTxt.anchor.set(0.5, 0); LK.gui.top.addChild(scoreTxt); // Round text var roundTxt = new Text2('', { size: 70, fill: 0xFFE4B5 }); roundTxt.anchor.set(0.5, 0); LK.gui.top.addChild(roundTxt); roundTxt.y = 110; // Duel status text var statusTxt = new Text2('', { size: 110, fill: 0xFFECB3 }); statusTxt.anchor.set(0.5, 0.5); LK.gui.center.addChild(statusTxt); // Aim reticle var aimReticle = LK.getAsset('bullet', { anchorX: 0.5, anchorY: 0.5, scaleX: 1.5, scaleY: 1.5, tint: 0xff0000 }); aimReticle.visible = false; game.addChild(aimReticle); // Helper: reset all state for a new round function resetDuel() { duelState = STATE_WAIT; canShoot = false; playerShot = false; enemyShot = false; playerBullet = null; enemyBullet = null; duelStartTime = 0; playerShotTime = 0; enemyShotTime = 0; aimAngle = Math.random() * Math.PI * 2; aimDir = Math.random() > 0.5 ? 1 : -1; aimActive = false; aimReticle.visible = false; player.resetPose(); enemy.resetPose(); setupAmmoUI(); // === RESET SHOT COUNTERS === playerShotsFiredCnt = 0; enemyShotsFiredCnt = 0; // === RESET HOLSTER FLAGS === playerReady = false; // yeni ← round’a nötr gir holdingHolster = false; // yeni holsterHoldTime = 0; // güvenlik duelStarted = false; // <-- YENİ: her raunda nötr gir cancelHold(); // 1. raunddan kalma musicTimeout varsa temizle zoomFinished = false; // yeni animasyona hazırlan var roundMusic = roundNum === 1 ? 'duelmusic' : roundNum === 2 ? 'round2music' : roundNum === 3 ? 'round3music' : roundNum === 4 ? 'round4music' : 'round5music'; startZoomIn(roundMusic); statusTxt.setText("Wait for the music..."); statusTxt.visible = true; // Set up enemy for this round currentEnemy = enemyData[roundNum - 1]; roundTxt.setText("Round " + roundNum + ": " + currentEnemy.name); // Boss scale (if any) enemy.scaleX = enemy.scaleY = currentEnemy.scale || 1; // Play duel music // (music now only plays during zoom-in, do not play here) // drawTimeout is now set after zoom-in, not here if (drawTimeout) LK.clearTimeout(drawTimeout); } // Start the "DRAW!" phase function startDraw() { if (!playerReady) { earlyLose(); return; } duelState = STATE_DRAW; canShoot = true; aimActive = true; aimReticle.visible = true; statusTxt.setText("DRAW!"); statusTxt.visible = true; LK.stopMusic(); LK.getSound('draw').play(); duelStartTime = Date.now(); // Enemy will shoot after their reaction time var reactTime = 1200; // fallback default if (currentEnemy && typeof currentEnemy.minReact === "number" && typeof currentEnemy.maxReact === "number") { reactTime = 1000 * (currentEnemy.minReact + Math.random() * (currentEnemy.maxReact - currentEnemy.minReact)); reactTime *= 0.425; // %57.5 daha hızlı tepki (eski 0.5 ve yeni 0.35 arası) } if (duelTimer) LK.clearTimeout(duelTimer); duelTimer = LK.setTimeout(enemyFire, reactTime); } // Player fires function playerFire(x, y) { // 6 mermi limiti if (playerShotsFiredCnt >= 6) { statusTxt.setText("Out of ammo!"); statusTxt.visible = true; return; } if (!canShoot) return; // playerShot kısıtlamasını kaldır playerShot = true; playerShotsFiredCnt++; // sayaç ↑ playerShotTime = Date.now(); // Animate gun tween(player.gun, { rotation: -0.5 }, { duration: 80, easing: tween.cubicOut }); tween(player.rarm, { rotation: -0.3 }, { duration: 80, easing: tween.cubicOut }); LK.getSound('gunshot').play(); // Create bullet playerBullet = new Bullet(); // Muzzle position helper function muzzlePos() { return { x: player.x + player.gun.x + Math.cos(player.gun.rotation) * 60, y: player.y + player.gun.y + Math.sin(player.gun.rotation) * 18 }; } var muzzle = muzzlePos(); playerBullet.x = muzzle.x; playerBullet.y = muzzle.y; // Ateş anındaki imleç (x, y) fonksiyona zaten parametre olarak geliyor var dx = x - muzzle.x; var dy = y - muzzle.y; var dist = Math.sqrt(dx * dx + dy * dy); playerBullet.dirX = dx / dist; playerBullet.dirY = dy / dist; playerBullet.speed = 60; game.addChild(playerBullet); // Decrement player bullet UI for (var i = 0; i < playerAmmoUI.length; i++) { if (playerAmmoUI[i].visible !== false) { playerAmmoUI[i].visible = false; break; } } // If shot before draw, instant loss if (duelState === STATE_WAIT) { duelState = STATE_RESULT; statusTxt.setText("You shot too early!\nYou Lose!"); statusTxt.visible = true; LK.getSound('fail').play(); player.ragdoll(); enemy.ragdoll(); endRound(false); return; } // bir sonraki mermiye izin ver if (duelState === STATE_DRAW && playerShotsFiredCnt < 6) { canShoot = true; aimActive = true; aimReticle.visible = true; } // DRAW fazı açık kalsın ki 6 mermi bitene kadar tekrar tekrar ateş edebilelim } // Enemy fires function enemyFire() { var _currentEnemy; if (enemyShotsFiredCnt >= 6 || duelState === STATE_RESULT) return; if (!enemyShot) enemyShot = true; // ilk mermi için eski bayrak enemyShotsFiredCnt++; enemyShotTime = Date.now(); // Animate gun tween(enemy.gun, { rotation: 0.5 }, { duration: 80, easing: tween.cubicOut }); tween(enemy.rarm, { rotation: 0.3 }, { duration: 80, easing: tween.cubicOut }); LK.getSound('gunshot').play(); // Create bullet enemyBullet = new Bullet(); enemyBullet.x = enemy.x + enemy.gun.x + Math.cos(enemy.gun.rotation) * 60; enemyBullet.y = enemy.y + enemy.gun.y + Math.sin(enemy.gun.rotation) * 18; // Decrement enemy bullet UI for (var i = 0; i < enemyAmmoUI.length; i++) { if (enemyAmmoUI[i].visible !== false) { enemyAmmoUI[i].visible = false; break; } } // Konik isabet modeliyle hedef seçimi // 1️⃣ hedef vektörü var baseDX = player.x - enemyBullet.x; var baseDY = player.y - 40 - enemyBullet.y; // gövdenin biraz üstü var baseAng = Math.atan2(baseDY, baseDX); // 2️⃣ saçılma açısı (daralan koni) var sIdx = enemyShotsFiredCnt; // 1-6 var sMax = 6; var cStart = currentEnemy && typeof currentEnemy.coneStart === "number" ? currentEnemy.coneStart : 24; // fallback var cEnd = currentEnemy && typeof currentEnemy.coneEnd === "number" ? currentEnemy.coneEnd : 0; var coneDeg = cStart + (cEnd - cStart) * ((sIdx - 1) / (sMax - 1)); var coneRad = coneDeg * Math.PI / 180; var isLast = sIdx === sMax; // son kurşun var spread = isLast ? 0 : Math.random() * coneRad - coneRad / 2; // 3️⃣ hedef noktasını diagramdaki yayı keserek bul var range = 1500; // ekran dışına yeter var targetX = enemyBullet.x + Math.cos(baseAng + spread) * range; var targetY = enemyBullet.y + Math.sin(baseAng + spread) * range; var dx = targetX - enemyBullet.x; var dy = targetY - enemyBullet.y; var dist = Math.sqrt(dx * dx + dy * dy); enemyBullet.dirX = dx / dist; enemyBullet.dirY = dy / dist; enemyBullet.speed = 60; game.addChild(enemyBullet); // sonraki mermi (tak-tak) 0.6 sn sonra if (enemyShotsFiredCnt < 6) { LK.setTimeout(enemyFire, 600); } // Round oyuncu ateş etmediyse ANCAK bütün 6 mermi atıldıktan sonra if (enemyShotsFiredCnt === 6 && !playerShot) { // Son kurşunun ekrandan çıkması için çok kısa bir gecikme bırak LK.setTimeout(function () { if (duelState !== STATE_RESULT) { duelState = STATE_RESULT; statusTxt.setText("You Survived!"); statusTxt.visible = true; endRound(true); } }, 350); } } // End round, win: true/false function endRound(win) { canShoot = false; aimActive = false; aimReticle.visible = false; if (drawTimeout) LK.clearTimeout(drawTimeout); if (duelTimer) LK.clearTimeout(duelTimer); if (resultTimeout) LK.clearTimeout(resultTimeout); drawTimeout = null; duelTimer = null; resultTimeout = null; // Next round or end game resultTimeout = LK.setTimeout(function () { if (win) { playerScore += 1; scoreTxt.setText("Score: " + playerScore); roundNum += 1; if (roundNum > maxRounds) { statusTxt.setText("You Win!\nScore: " + playerScore); statusTxt.visible = true; LK.showYouWin(); } else { resetDuel(); } } else { statusTxt.setText("You Lose!\nScore: " + playerScore); statusTxt.visible = true; LK.showGameOver(); } }, 1600); } // Handle aiming reticle movement (circular motion) function updateAimReticle() { if (!aimActive) return; aimAngle += aimSpeed * aimDir; var r = aimRadius; var ex = enemy.x; var ey = enemy.y; // Reticle orbits enemy's body aimReticle.x = ex + Math.cos(aimAngle) * r; aimReticle.y = ey + Math.sin(aimAngle) * r * 0.7; } // Handle bullet collisions and duel result function checkBullets() { // Player bullet hits enemy if (playerBullet) { // Check if bullet reached enemy var dx = playerBullet.x - enemy.x; var dy = playerBullet.y - enemy.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 120) { // Hit! LK.getSound('hit').play(); enemy.ragdoll(playerBullet.x, playerBullet.y); game.removeChild(playerBullet); playerBullet = null; duelState = STATE_RESULT; statusTxt.setText("You Win the Duel!"); statusTxt.visible = true; endRound(true); } } // Enemy bullet hits player if (enemyBullet) { var dx = enemyBullet.x - player.x; var dy = enemyBullet.y - player.y; var dist = Math.sqrt(dx * dx + dy * dy); if (dist < 120) { LK.getSound('hit').play(); player.ragdoll(enemyBullet.x, enemyBullet.y); game.removeChild(enemyBullet); enemyBullet = null; duelState = STATE_RESULT; statusTxt.setText("You were shot!"); statusTxt.visible = true; endRound(false); } } // Both bullets exist (simultaneous fire) if (playerBullet && enemyBullet) { // Check which bullet hits first var pdx = playerBullet.x - enemy.x; var pdy = playerBullet.y - enemy.y; var pdist = Math.sqrt(pdx * pdx + pdy * pdy); var edx = enemyBullet.x - player.x; var edy = enemyBullet.y - player.y; var edist = Math.sqrt(edx * edx + edy * edy); if (pdist < 120 && edist < 120) { // Both hit at same time: draw LK.getSound('hit').play(); player.ragdoll(); enemy.ragdoll(); game.removeChild(playerBullet); game.removeChild(enemyBullet); playerBullet = null; enemyBullet = null; duelState = STATE_RESULT; statusTxt.setText("Draw!\nBoth shot!"); statusTxt.visible = true; endRound(false); } else if (pdist < 120) { LK.getSound('hit').play(); enemy.ragdoll(playerBullet.x, playerBullet.y); game.removeChild(playerBullet); playerBullet = null; duelState = STATE_RESULT; statusTxt.setText("You Win the Duel!"); statusTxt.visible = true; endRound(true); } else if (edist < 120) { LK.getSound('hit').play(); player.ragdoll(enemyBullet.x, enemyBullet.y); game.removeChild(enemyBullet); enemyBullet = null; duelState = STATE_RESULT; statusTxt.setText("You were shot!"); statusTxt.visible = true; endRound(false); } } // Remove bullets if off screen if (playerBullet && (playerBullet.x < 0 || playerBullet.x > 2048 || playerBullet.y < 0 || playerBullet.y > 2732)) { game.removeChild(playerBullet); playerBullet = null; canShoot = true; // anında yeni atış serbest } if (enemyBullet && (enemyBullet.x < 0 || enemyBullet.x > 2048 || enemyBullet.y < 0 || enemyBullet.y > 2732)) { game.removeChild(enemyBullet); enemyBullet = null; enemyShot = false; // yeni atışa izin ver } } // ==== FIX #2 : holster konumu ==== var holsterOffset = 250; var holsterBox = new Rectangle(player.x - 200, player.y + player.body.height / 2 + holsterOffset, 400, 200); // Show holster box as a semi-transparent rectangle var holsterVisual = LK.getAsset('playerBody', { width: 400, height: 200, color: 0x333333, alpha: 0.25, anchorX: 0, anchorY: 0 }); holsterVisual.x = holsterBox.x; holsterVisual.y = holsterBox.y; world.addChild(holsterVisual); // Holster hold/zoom/duel state var zoomFinished = false, duelStarted = false, holsterHoldTime = 0; var playerReady = false; var holdingHolster = false; // Track if holster is being held var musicTimeout = null; // Timer for music/draw phase function beginHold() { if (musicTimeout) { LK.clearTimeout(musicTimeout); } var extra = 4000 + Math.random() * 6000; // 4-10 s musicTimeout = LK.setTimeout(stopMusicAndDraw, extra); } function cancelHold() { if (musicTimeout) { LK.clearTimeout(musicTimeout); } musicTimeout = null; } function stopMusicAndDraw() { LK.stopMusic(); // müzik sustu duelStarted = true; // <-- ERKEN KAYIP KONTROLÜ ARTIK PASİF holsterHoldTime = 0; // güvenlik: sayaç sıfırla startDraw(); // DRAW fazına geç } // Touch/click to fire or start holster hold game.down = function (x, y, obj) { // No fire until duel actually started if (zoomFinished && !duelStarted && holsterBox.contains(x, y)) { playerReady = true; holdingHolster = true; // holster pressed beginHold(); // HOLD başlatılırken zamanlayıcı kuruluyor return; } if (!duelStarted) return; if (duelState === STATE_DRAW && canShoot && aimActive) { playerFire(x, y); } else if (duelState === STATE_WAIT && !playerShot) { playerFire(x, y); } }; // On pointer up, check for early leave from holster // ==== FIX #4c : tetikleyici ===== game.up = function (x, y, obj) { // HOLSTER EARLY RELEASE CHECK holdingHolster = false; // holster released cancelHold(); // el çekildi, zamanlayıcı iptal if (!duelStarted && playerReady && duelState === STATE_WAIT) { earlyLose(); return; } if (duelState === STATE_DRAW && canShoot) { playerFire(x, y); return; } }; // Move reticle with finger (optional: drag to aim) game.move = function (x, y, obj) { // Holster hold/early leave logic if (zoomFinished && !duelStarted) { if (holsterBox.contains(x, y)) { // handled in update for dt-accurate timing } else if (holdingHolster) { cancelHold(); earlyLose(); } } if (aimActive) { // Clamp reticle to a circle around enemy var ex = enemy.x; var ey = enemy.y; var dx = x - ex; var dy = y - ey; var d = Math.sqrt(dx * dx + dy * dy); if (d > aimRadius) { dx = dx * (aimRadius / d); dy = dy * (aimRadius / d); } aimReticle.x = ex + dx; aimReticle.y = ey + dy; // Update angle for next frame aimAngle = Math.atan2(aimReticle.y - ey, aimReticle.x - ex); } // Helper: point gun at (px, py) from rarm pivot function pointGunAt(px, py) { var gx = player.x + player.rarm.x; var gy = player.y + player.rarm.y; var ang = Math.atan2(py - gy, px - gx); player.rarm.rotation = ang; player.gun.rotation = ang; var armLen = player.rarm.height; player.gun.x = player.rarm.x + Math.cos(ang) * armLen; player.gun.y = player.rarm.y + Math.sin(ang) * armLen; } // Namlu her zaman fareyi izlesin (DRAW’tan önce holster tutulurken de) pointGunAt(x, y); }; // Helper to update holster box position under player's feet function updateHolsterBox() { holsterBox.x = player.x - 200; holsterBox.y = player.y + player.body.height / 2 + holsterOffset; holsterVisual.x = holsterBox.x; holsterVisual.y = holsterBox.y; } // Main update loop game.update = function (dt) { // Hide status text during zoom-in if (!zoomFinished) { statusTxt.visible = false; return; } // Always update holster position to follow player updateHolsterBox(); // Animate aim reticle updateAimReticle(); // Animate bullets if (playerBullet) playerBullet.update(); if (enemyBullet) enemyBullet.update(); // Check for bullet collisions checkBullets(); // ==== FIX #3 : statusTxt ayarı ==== // Holster hold logic after zoom if (zoomFinished && !duelStarted) { // Use pointerX/Y for current pointer var px = typeof game.pointerX === "number" ? game.pointerX : 0; var py = typeof game.pointerY === "number" ? game.pointerY : 0; var dtMs = typeof dt === "number" ? dt : 16.7; if (holsterBox.contains(px, py) && holdingHolster) { holsterHoldTime += dtMs; } else { if (holsterHoldTime > 0 && holdingHolster) { earlyLose(); } holsterHoldTime = 0; } if (!duelStarted) { if (!playerReady) { statusTxt.setText("Hold holster to start duel"); } else { statusTxt.setText("Waiting for music to end…\nthen aim & release"); } } // if (holsterHoldTime >= 4000 && !duelStarted) { // duelStarted = true; // holsterHoldTime = 0; // resetDuel(); // } statusTxt.visible = true; } else { if (duelState === STATE_DRAW) { statusTxt.setText("Aim and SHOOT!"); } statusTxt.visible = true; } }; // Early lose function function earlyLose() { if (duelState === STATE_RESULT) return; // only trigger once holdingHolster = false; holsterHoldTime = 0; duelStarted = false; LK.stopMusic(); if (drawTimeout) LK.clearTimeout(drawTimeout); statusTxt.setText("Too early!\nYou lose!"); statusTxt.visible = true; LK.getSound('gunshot').play(); // yere ateş LK.getSound('fail').play(); // Optionally, also play gunshot sound: // LK.getSound('gunshot').play(); // Show game over after 1 second LK.setTimeout(function () { LK.showGameOver(); }, 1000); } // Camera zoom-in before duel starts function startZoomIn(music) { // ==== FIX #1b : zoom fonksiyonunda ==== var startScale = 0.4; world.scaleX = world.scaleY = startScale; world.x = centerX * (1 - startScale); world.y = 2732 / 2 * (1 - startScale); LK.playMusic(music || 'duelmusic'); tween(world, { scaleX: 1, scaleY: 1, x: 0, y: 0 }, { duration: 4000, easing: tween.quadInOut, onFinish: function onFinish() { zoomFinished = true; } }); } // Start zoom-in and music immediately on boot startZoomIn();
===================================================================
--- original.js
+++ change.js
@@ -43,16 +43,16 @@
var larm = self.attachAsset('enemyArm', {
anchorX: 0.5,
anchorY: 0,
x: -body.width / 2 - 10,
- y: -40
+ y: -50
});
// Right Arm (gun hand)
var rarm = self.attachAsset('enemyArm', {
anchorX: 0.5,
anchorY: 0,
x: body.width / 2 + 10,
- y: -40
+ y: -50
});
// Legs
var lleg = self.attachAsset('enemyLeg', {
anchorX: 0.5,
@@ -67,12 +67,12 @@
y: body.height / 2 - 10
});
// Gun
var gun = self.attachAsset('enemyGun', {
- anchorX: 0.1,
+ anchorX: 0,
anchorY: 0.5,
- x: body.width / 2 + 40,
- y: 10,
+ x: rarm.x,
+ y: rarm.y + rarm.height,
rotation: 0
});
self.bodyParts = [body, head, larm, rarm, lleg, rleg, gun];
self.gun = gun;
@@ -100,21 +100,21 @@
head.x = 0;
head.y = -body.height / 2 - 45;
head.rotation = 0;
larm.x = -body.width / 2 - 10;
- larm.y = -40;
+ larm.y = -50;
larm.rotation = 0;
rarm.x = body.width / 2 + 10;
- rarm.y = -40;
+ rarm.y = -50;
rarm.rotation = 0;
lleg.x = -body.width / 4;
lleg.y = body.height / 2 - 10;
lleg.rotation = 0;
rleg.x = body.width / 4;
rleg.y = body.height / 2 - 10;
rleg.rotation = 0;
- gun.x = body.width / 2 + 40;
- gun.y = 10;
+ gun.x = rarm.x;
+ gun.y = rarm.y + rarm.height;
gun.rotation = 0;
};
return self;
});
@@ -139,16 +139,16 @@
var larm = self.attachAsset('playerArm', {
anchorX: 0.5,
anchorY: 0,
x: -body.width / 2 - 10,
- y: -40
+ y: -50
});
// Right Arm (gun hand)
var rarm = self.attachAsset('playerArm', {
anchorX: 0.5,
anchorY: 0,
x: body.width / 2 + 10,
- y: -40
+ y: -50
});
// Legs
var lleg = self.attachAsset('playerLeg', {
anchorX: 0.5,
@@ -163,12 +163,12 @@
y: body.height / 2 - 10
});
// Gun
var gun = self.attachAsset('playerGun', {
- anchorX: 0.1,
+ anchorX: 0,
anchorY: 0.5,
- x: body.width / 2 + 40,
- y: 10,
+ x: rarm.x,
+ y: rarm.y + rarm.height,
rotation: 0
});
// Used for ragdoll effect
self.bodyParts = [body, head, larm, rarm, lleg, rleg, gun];
@@ -202,21 +202,21 @@
head.x = 0;
head.y = -body.height / 2 - 45;
head.rotation = 0;
larm.x = -body.width / 2 - 10;
- larm.y = -40;
+ larm.y = -50;
larm.rotation = 0;
rarm.x = body.width / 2 + 10;
- rarm.y = -40;
+ rarm.y = -50;
rarm.rotation = 0;
lleg.x = -body.width / 4;
lleg.y = body.height / 2 - 10;
lleg.rotation = 0;
rleg.x = body.width / 4;
rleg.y = body.height / 2 - 10;
rleg.rotation = 0;
- gun.x = body.width / 2 + 40;
- gun.y = 10;
+ gun.x = rarm.x;
+ gun.y = rarm.y + rarm.height;
gun.rotation = 0;
};
return self;
});
@@ -443,9 +443,9 @@
// Enemy will shoot after their reaction time
var reactTime = 1200; // fallback default
if (currentEnemy && typeof currentEnemy.minReact === "number" && typeof currentEnemy.maxReact === "number") {
reactTime = 1000 * (currentEnemy.minReact + Math.random() * (currentEnemy.maxReact - currentEnemy.minReact));
- reactTime *= 0.35; // %65 daha hızlı tepki
+ reactTime *= 0.425; // %57.5 daha hızlı tepki (eski 0.5 ve yeni 0.35 arası)
}
if (duelTimer) LK.clearTimeout(duelTimer);
duelTimer = LK.setTimeout(enemyFire, reactTime);
}
@@ -825,19 +825,21 @@
aimReticle.y = ey + dy;
// Update angle for next frame
aimAngle = Math.atan2(aimReticle.y - ey, aimReticle.x - ex);
}
- // ==== FIX #4b : imleç açısı ====
- if (duelState >= STATE_DRAW) {
- var rotateArmTo = function rotateArmTo(x, y) {
- var gx = player.x + player.gun.x;
- var gy = player.y + player.gun.y;
- var ang = Math.atan2(y - gy, x - gx);
- player.gun.rotation = ang;
- player.rarm.rotation = ang + 0.15;
- };
- rotateArmTo(x, y);
+ // Helper: point gun at (px, py) from rarm pivot
+ function pointGunAt(px, py) {
+ var gx = player.x + player.rarm.x;
+ var gy = player.y + player.rarm.y;
+ var ang = Math.atan2(py - gy, px - gx);
+ player.rarm.rotation = ang;
+ player.gun.rotation = ang;
+ var armLen = player.rarm.height;
+ player.gun.x = player.rarm.x + Math.cos(ang) * armLen;
+ player.gun.y = player.rarm.y + Math.sin(ang) * armLen;
}
+ // Namlu her zaman fareyi izlesin (DRAW’tan önce holster tutulurken de)
+ pointGunAt(x, y);
};
// Helper to update holster box position under player's feet
function updateHolsterBox() {
holsterBox.x = player.x - 200;