User prompt
move enemy legs 3 pixels right
User prompt
move enemy legs 5 pixels left
User prompt
move enemy legs behind enemy body
User prompt
move enemy head 4 pixels up 4 pixels left
User prompt
move enemy head 5 pixels up 5 pixels right
User prompt
move enemy left arm behin enemy body
User prompt
Please fix the bug: 'Cannot read properties of undefined (reading 'width')' in or related to this line: 'var larm = self.attachAsset('enemyArm', {' Line Number: 58
User prompt
move enemy left arm behind enemy body
User prompt
move enemy left arm 10 pixels right
User prompt
move enemy left arm 30 pixels right
User prompt
move enemygun assets when music is not stopped 5 pixels down 10 pixels left
User prompt
move enemygun assets when music is not stopped 10 pixels down 5 pixels left
User prompt
move enemygun assets when in pocket 10 pixels down 5 pixels left
User prompt
move enemygunpocket 5 pixels left
User prompt
make enemygunpocket disappear at the moment music stops
User prompt
move it 30 pixels left
User prompt
mov it 150 pixels down 50 pixels right
User prompt
move enemygunpocket 600 px upper 550 pixels right
User prompt
move enemygunpocket 300 px upper 350 pixels right
User prompt
move it 200 pixels up and 400 pixel right
User prompt
move it 300 pixels up and 300 pixel right
User prompt
move it 200 pixels up and 200 pixel right
User prompt
place enemygunpocket middle of the screen
User prompt
move enemy legs 5 pixels right 3 pixels up
User prompt
move enemy legs 5 pixels up
/**** * 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: 10, y: -15 //{h} // 5px daha aşağı, 10px sağa taşındı }); // Head var head = self.attachAsset('enemyHead', { anchorX: 0.5, anchorY: 0.5, x: -4, y: -body.height / 2 - 45 - 4 }); // Left Arm var larm = self.attachAsset('enemyArm', { anchorX: 0.5, anchorY: 0, x: -body.width / 2 + 5, // 5px closer to body y: -80 //{t} // 30px up (10px further down) }); // Right Arm (gun hand) var rarm = self.attachAsset('enemyArm', { anchorX: 0.5, anchorY: 0, x: body.width / 2 - 5, // 5px closer to body y: -80 //{z} // 30px up (10px further down) }); // Legs var lleg = self.attachAsset('enemyLeg', { anchorX: 0.5, anchorY: 0, x: -body.width / 4, y: body.height / 2 - 15 }); var rleg = self.attachAsset('enemyLeg', { anchorX: 0.5, anchorY: 0, x: body.width / 4, y: body.height / 2 - 15 }); // Gun (in hand) var gun = self.attachAsset('enemyGun', { anchorX: 0.5, // 0.5 yerine 0.5 anchorY: 0.5, x: rarm.x, y: rarm.y + rarm.height, rotation: 0 }); // Holster gun (on hip, always visible unless drawn) var hipGun = self.attachAsset('enemyGun', { anchorX: 0.5, anchorY: 0.5, x: body.width / 2 + 8, y: body.height / 2 - 22, rotation: Math.PI / 2 }); // Start with gun in holster, hand empty gun.visible = false; hipGun.visible = true; // Helper methods to switch gun/holster visibility self.toHolster = function () { hipGun.visible = true; gun.visible = false; }; self.toHand = function () { hipGun.visible = false; gun.visible = true; }; 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 = 10; body.y = -15; //{1w} // 5px daha aşağı, 10px sağa taşındı body.rotation = 0; head.x = -4; head.y = -body.height / 2 - 45 - 4; head.rotation = 0; larm.x = -body.width / 2 + 5; // 5px closer to body larm.y = -80; //{1k} // 30px up (10px further down) larm.rotation = 0; rarm.x = body.width / 2 - 5; // 5px closer to body rarm.y = -80; //{1n} // 30px up (10px further down) rarm.rotation = 0; lleg.x = -body.width / 4; lleg.y = body.height / 2 - 15; lleg.rotation = 0; rleg.x = body.width / 4; rleg.y = body.height / 2 - 15; rleg.rotation = 0; gun.x = rarm.x; gun.y = rarm.y + rarm.height; gun.rotation = 0; self.toHolster(); // always start with gun in holster }; 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: -100 //{1S} // 50px up }); // Right Arm (gun hand) var rarm = self.attachAsset('playerArm', { anchorX: 0.5, anchorY: 0, x: body.width / 2 + 10, y: -100 //{1Y} // 50px up }); // 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 (in hand) var gun = self.attachAsset('playerGun', { anchorX: 0.5, // 0.5 yerine 0.5 anchorY: 0.5, x: rarm.x, y: rarm.y + rarm.height, rotation: 0 }); // Holster gun (on hip, always visible unless drawn) var hipGun = self.attachAsset('playerGun', { anchorX: 0.5, anchorY: 0.5, x: body.width / 2 + 8, y: body.height / 2 - 22, rotation: Math.PI / 2 }); // Start with gun in holster, hand empty gun.visible = false; hipGun.visible = true; // Helper methods to switch gun/holster visibility self.toHolster = function () { hipGun.visible = true; gun.visible = false; }; self.toHand = function () { hipGun.visible = false; gun.visible = true; }; // 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 = -100; //{2J} // 50px up larm.rotation = 0; rarm.x = body.width / 2 + 10; rarm.y = -100; //{2M} // 50px up 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; self.toHolster(); // always start with gun in holster }; return self; }); /**** * Initialize Game ****/ var game = new LK.Game({ backgroundColor: 0x2e1a09 }); /**** * Game Code ****/ // Player and enemy bodies (torso, head, arms, legs) as simple shapes // Game state 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; // === PLAYER SHOT COOLDOWN === var playerShotCooldown = 0; // ms left until next shot allowed 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 + 60; // Düşman: yalnızca 5. raundda 75 px yukarı var enemyExtraY = roundNum === 5 ? -75 : 0; bulletE.y = enemy.y - 250 + enemyExtraY; 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 removed) // 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 removed) player.resetPose(); enemy.resetPose(); player.toHolster(); enemy.toHolster(); holsterFull.visible = true; holsterEmpty.visible = false; 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 removed) statusTxt.setText("DRAW!"); statusTxt.visible = true; LK.stopMusic(); LK.getSound('draw').play(); player.toHand(); // draw from holster enemy.toHand(); // enemy draws too aimEnemyGunAt(player.x, player.y - 40); // hemen hizala 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; } // === COOLDOWN: Block if cooldown active === if (playerShotCooldown > 0) { return; } playerShot = true; playerShotsFiredCnt++; // sayaç ↑ playerShotTime = Date.now(); // === COOLDOWN: Set cooldown after shot === playerShotCooldown = 300; // 0.3 seconds in ms // 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() { // Use full gun width/height for muzzle (center anchor) var gunW = player.gun.width || 60; var gunH = player.gun.height || 15; return { x: player.x + player.gun.x + Math.cos(player.gun.rotation) * gunW, y: player.y + player.gun.y + Math.sin(player.gun.rotation) * gunH }; } 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 removed) } // 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(); // Ateşten önce hedefe çevir aimEnemyGunAt(player.x, player.y - 40); // 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(); var gunW = enemy.gun.width || 60; var gunH = enemy.gun.height || 18; enemyBullet.x = enemy.x + enemy.gun.x + Math.cos(enemy.gun.rotation) * gunW; enemyBullet.y = enemy.y + enemy.gun.y + Math.sin(enemy.gun.rotation) * gunH; // 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 removed) 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() { // (aim reticle removed) } // 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; // === COOLDOWN: Only allow new shot if cooldown expired === if (playerShotCooldown <= 0) { canShoot = true; } } 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 ==== // Use two holster sprites: full and empty, toggle visibility var holsterFull = LK.getAsset('kilif', { width: 300, height: 300, anchorX: -0.2, anchorY: 0 }); var holsterEmpty = LK.getAsset('kilifbos', { width: 300, height: 300, anchorX: -0.2, anchorY: 0 }); holsterFull.visible = true; holsterEmpty.visible = false; var holsterOffset = 250; holsterFull.x = player.x - 200; holsterFull.y = player.y + player.body.height / 2 + holsterOffset; holsterEmpty.x = holsterFull.x; holsterEmpty.y = holsterFull.y; world.addChild(holsterFull); world.addChild(holsterEmpty); // Helper: check if global point is inside holsterFull's bounds (always use holsterFull for hit area) function holsterContains(globalX, globalY) { // Global (stage) → world-içi (local) koordinata çevir var localX = (globalX - world.x) / world.scaleX; var localY = (globalY - world.y) / world.scaleY; // kilif sprite’ının local kutusu var b = { x: holsterFull.x, y: holsterFull.y, w: holsterFull.width, h: holsterFull.height }; return localX >= b.x && localX <= b.x + b.w && localY >= b.y && localY <= b.y + b.h; } // 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 && holsterContains(x, y)) { playerReady = true; holdingHolster = true; // holster pressed player.toHand(); // ← kılıftan çek (hemen ele ver) // Play holdsound when gun is drawn to hand LK.getSound('holdsound').play(); // --- holster assetini kilifbos ile değiştir (Çözüm 2: iki sprite, görünürlük) --- holsterFull.visible = false; holsterEmpty.visible = true; pointGunAt(x, y); // artık global ve erişilebilir 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 (holsterContains(x, y)) { // handled in update for dt-accurate timing } else if (holdingHolster) { cancelHold(); earlyLose(); } } // (aim reticle removed) // Namlu her zaman fareyi izlesin (DRAW’tan önce holster tutulurken de) pointGunAt(x, y); }; /**** GLOBAL YARDIMCI : Silahı imlece çevir ****/ function pointGunAt(globalX, globalY) { // 1) Omuz pivotu – dünya koordinatı var shX = player.x + player.rarm.x; var shY = player.y + player.rarm.y; // 2) Omuz → imleç vektörü ve açı (0 rad = sağ) var dx = globalX - shX; var dy = globalY - shY; var ang = Math.atan2(dy, dx); // 3) Kol sprite’ı AŞAĞI bakıyor → –90° (-π/2) düzelt var armRot = ang - Math.PI / 2; player.rarm.rotation = armRot; // 4) El / silah konumu (player LOCAL) var L = player.rarm.height; // ≈110 px // ❗️ Doğru ofset: (-sin, +cos) player.gun.x = player.rarm.x - Math.sin(armRot) * L; player.gun.y = player.rarm.y + Math.cos(armRot) * L; // 5) Silah namlu yönü if (!duelStarted && holsterContains(globalX, globalY)) { player.gun.rotation = Math.PI / 2; // kılıfta ⇒ namlu yere } else { player.gun.rotation = ang; // hedefe bak } } /**** GLOBAL : Enemy silahını hedefe çevir ****/ function aimEnemyGunAt(globalX, globalY) { // 1) Omuz pivotu (world) var shX = enemy.x + enemy.rarm.x; var shY = enemy.y + enemy.rarm.y; // 2) Omuz→hedef vektörü var dx = globalX - shX; var dy = globalY - shY; var ang = Math.atan2(dy, dx); // 3) Kol sprite’ı AŞAĞI bakıyor → –90° (–π/2) düzeltme var armRot = ang - Math.PI / 2; enemy.rarm.rotation = armRot; // 4) El / silah konumu (enemy LOCAL) **ayna!** var L = enemy.rarm.height; // ≈145 px enemy.gun.x = enemy.rarm.x - Math.sin(armRot) * L; enemy.gun.y = enemy.rarm.y + Math.cos(armRot) * L; // 5) Namluyu hedefe döndür enemy.gun.rotation = ang; } // Helper to update holster box position under player's feet function updateHolsterBox() { holsterFull.x = player.x - 200; holsterFull.y = player.y + player.body.height / 2 + holsterOffset; holsterEmpty.x = holsterFull.x; holsterEmpty.y = holsterFull.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(); // (aim reticle removed) // Rakip silahı eldeyse oyuncuyu takip etsin if (enemy.gun.visible) { aimEnemyGunAt(player.x, player.y - 40); } // Animate bullets if (playerBullet) { playerBullet.update(); } if (enemyBullet) { enemyBullet.update(); } // === PLAYER SHOT COOLDOWN TIMER === if (playerShotCooldown > 0) { var dtMs = typeof dt === "number" ? dt : 16.7; playerShotCooldown -= dtMs; if (playerShotCooldown < 0) playerShotCooldown = 0; } // 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 (holsterContains(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("Wait 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();
/****
* 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: 10,
y: -15 //{h} // 5px daha aşağı, 10px sağa taşındı
});
// Head
var head = self.attachAsset('enemyHead', {
anchorX: 0.5,
anchorY: 0.5,
x: -4,
y: -body.height / 2 - 45 - 4
});
// Left Arm
var larm = self.attachAsset('enemyArm', {
anchorX: 0.5,
anchorY: 0,
x: -body.width / 2 + 5,
// 5px closer to body
y: -80 //{t} // 30px up (10px further down)
});
// Right Arm (gun hand)
var rarm = self.attachAsset('enemyArm', {
anchorX: 0.5,
anchorY: 0,
x: body.width / 2 - 5,
// 5px closer to body
y: -80 //{z} // 30px up (10px further down)
});
// Legs
var lleg = self.attachAsset('enemyLeg', {
anchorX: 0.5,
anchorY: 0,
x: -body.width / 4,
y: body.height / 2 - 15
});
var rleg = self.attachAsset('enemyLeg', {
anchorX: 0.5,
anchorY: 0,
x: body.width / 4,
y: body.height / 2 - 15
});
// Gun (in hand)
var gun = self.attachAsset('enemyGun', {
anchorX: 0.5,
// 0.5 yerine 0.5
anchorY: 0.5,
x: rarm.x,
y: rarm.y + rarm.height,
rotation: 0
});
// Holster gun (on hip, always visible unless drawn)
var hipGun = self.attachAsset('enemyGun', {
anchorX: 0.5,
anchorY: 0.5,
x: body.width / 2 + 8,
y: body.height / 2 - 22,
rotation: Math.PI / 2
});
// Start with gun in holster, hand empty
gun.visible = false;
hipGun.visible = true;
// Helper methods to switch gun/holster visibility
self.toHolster = function () {
hipGun.visible = true;
gun.visible = false;
};
self.toHand = function () {
hipGun.visible = false;
gun.visible = true;
};
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 = 10;
body.y = -15; //{1w} // 5px daha aşağı, 10px sağa taşındı
body.rotation = 0;
head.x = -4;
head.y = -body.height / 2 - 45 - 4;
head.rotation = 0;
larm.x = -body.width / 2 + 5; // 5px closer to body
larm.y = -80; //{1k} // 30px up (10px further down)
larm.rotation = 0;
rarm.x = body.width / 2 - 5; // 5px closer to body
rarm.y = -80; //{1n} // 30px up (10px further down)
rarm.rotation = 0;
lleg.x = -body.width / 4;
lleg.y = body.height / 2 - 15;
lleg.rotation = 0;
rleg.x = body.width / 4;
rleg.y = body.height / 2 - 15;
rleg.rotation = 0;
gun.x = rarm.x;
gun.y = rarm.y + rarm.height;
gun.rotation = 0;
self.toHolster(); // always start with gun in holster
};
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: -100 //{1S} // 50px up
});
// Right Arm (gun hand)
var rarm = self.attachAsset('playerArm', {
anchorX: 0.5,
anchorY: 0,
x: body.width / 2 + 10,
y: -100 //{1Y} // 50px up
});
// 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 (in hand)
var gun = self.attachAsset('playerGun', {
anchorX: 0.5,
// 0.5 yerine 0.5
anchorY: 0.5,
x: rarm.x,
y: rarm.y + rarm.height,
rotation: 0
});
// Holster gun (on hip, always visible unless drawn)
var hipGun = self.attachAsset('playerGun', {
anchorX: 0.5,
anchorY: 0.5,
x: body.width / 2 + 8,
y: body.height / 2 - 22,
rotation: Math.PI / 2
});
// Start with gun in holster, hand empty
gun.visible = false;
hipGun.visible = true;
// Helper methods to switch gun/holster visibility
self.toHolster = function () {
hipGun.visible = true;
gun.visible = false;
};
self.toHand = function () {
hipGun.visible = false;
gun.visible = true;
};
// 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 = -100; //{2J} // 50px up
larm.rotation = 0;
rarm.x = body.width / 2 + 10;
rarm.y = -100; //{2M} // 50px up
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;
self.toHolster(); // always start with gun in holster
};
return self;
});
/****
* Initialize Game
****/
var game = new LK.Game({
backgroundColor: 0x2e1a09
});
/****
* Game Code
****/
// Player and enemy bodies (torso, head, arms, legs) as simple shapes
// Game state
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;
// === PLAYER SHOT COOLDOWN ===
var playerShotCooldown = 0; // ms left until next shot allowed
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 + 60;
// Düşman: yalnızca 5. raundda 75 px yukarı
var enemyExtraY = roundNum === 5 ? -75 : 0;
bulletE.y = enemy.y - 250 + enemyExtraY;
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 removed)
// 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 removed)
player.resetPose();
enemy.resetPose();
player.toHolster();
enemy.toHolster();
holsterFull.visible = true;
holsterEmpty.visible = false;
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 removed)
statusTxt.setText("DRAW!");
statusTxt.visible = true;
LK.stopMusic();
LK.getSound('draw').play();
player.toHand(); // draw from holster
enemy.toHand(); // enemy draws too
aimEnemyGunAt(player.x, player.y - 40); // hemen hizala
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;
}
// === COOLDOWN: Block if cooldown active ===
if (playerShotCooldown > 0) {
return;
}
playerShot = true;
playerShotsFiredCnt++; // sayaç ↑
playerShotTime = Date.now();
// === COOLDOWN: Set cooldown after shot ===
playerShotCooldown = 300; // 0.3 seconds in ms
// 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() {
// Use full gun width/height for muzzle (center anchor)
var gunW = player.gun.width || 60;
var gunH = player.gun.height || 15;
return {
x: player.x + player.gun.x + Math.cos(player.gun.rotation) * gunW,
y: player.y + player.gun.y + Math.sin(player.gun.rotation) * gunH
};
}
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 removed)
}
// 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();
// Ateşten önce hedefe çevir
aimEnemyGunAt(player.x, player.y - 40);
// 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();
var gunW = enemy.gun.width || 60;
var gunH = enemy.gun.height || 18;
enemyBullet.x = enemy.x + enemy.gun.x + Math.cos(enemy.gun.rotation) * gunW;
enemyBullet.y = enemy.y + enemy.gun.y + Math.sin(enemy.gun.rotation) * gunH;
// 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 removed)
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() {
// (aim reticle removed)
}
// 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;
// === COOLDOWN: Only allow new shot if cooldown expired ===
if (playerShotCooldown <= 0) {
canShoot = true;
}
}
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 ====
// Use two holster sprites: full and empty, toggle visibility
var holsterFull = LK.getAsset('kilif', {
width: 300,
height: 300,
anchorX: -0.2,
anchorY: 0
});
var holsterEmpty = LK.getAsset('kilifbos', {
width: 300,
height: 300,
anchorX: -0.2,
anchorY: 0
});
holsterFull.visible = true;
holsterEmpty.visible = false;
var holsterOffset = 250;
holsterFull.x = player.x - 200;
holsterFull.y = player.y + player.body.height / 2 + holsterOffset;
holsterEmpty.x = holsterFull.x;
holsterEmpty.y = holsterFull.y;
world.addChild(holsterFull);
world.addChild(holsterEmpty);
// Helper: check if global point is inside holsterFull's bounds (always use holsterFull for hit area)
function holsterContains(globalX, globalY) {
// Global (stage) → world-içi (local) koordinata çevir
var localX = (globalX - world.x) / world.scaleX;
var localY = (globalY - world.y) / world.scaleY;
// kilif sprite’ının local kutusu
var b = {
x: holsterFull.x,
y: holsterFull.y,
w: holsterFull.width,
h: holsterFull.height
};
return localX >= b.x && localX <= b.x + b.w && localY >= b.y && localY <= b.y + b.h;
}
// 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 && holsterContains(x, y)) {
playerReady = true;
holdingHolster = true; // holster pressed
player.toHand(); // ← kılıftan çek (hemen ele ver)
// Play holdsound when gun is drawn to hand
LK.getSound('holdsound').play();
// --- holster assetini kilifbos ile değiştir (Çözüm 2: iki sprite, görünürlük) ---
holsterFull.visible = false;
holsterEmpty.visible = true;
pointGunAt(x, y); // artık global ve erişilebilir
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 (holsterContains(x, y)) {
// handled in update for dt-accurate timing
} else if (holdingHolster) {
cancelHold();
earlyLose();
}
}
// (aim reticle removed)
// Namlu her zaman fareyi izlesin (DRAW’tan önce holster tutulurken de)
pointGunAt(x, y);
};
/**** GLOBAL YARDIMCI : Silahı imlece çevir ****/
function pointGunAt(globalX, globalY) {
// 1) Omuz pivotu – dünya koordinatı
var shX = player.x + player.rarm.x;
var shY = player.y + player.rarm.y;
// 2) Omuz → imleç vektörü ve açı (0 rad = sağ)
var dx = globalX - shX;
var dy = globalY - shY;
var ang = Math.atan2(dy, dx);
// 3) Kol sprite’ı AŞAĞI bakıyor → –90° (-π/2) düzelt
var armRot = ang - Math.PI / 2;
player.rarm.rotation = armRot;
// 4) El / silah konumu (player LOCAL)
var L = player.rarm.height; // ≈110 px
// ❗️ Doğru ofset: (-sin, +cos)
player.gun.x = player.rarm.x - Math.sin(armRot) * L;
player.gun.y = player.rarm.y + Math.cos(armRot) * L;
// 5) Silah namlu yönü
if (!duelStarted && holsterContains(globalX, globalY)) {
player.gun.rotation = Math.PI / 2; // kılıfta ⇒ namlu yere
} else {
player.gun.rotation = ang; // hedefe bak
}
}
/**** GLOBAL : Enemy silahını hedefe çevir ****/
function aimEnemyGunAt(globalX, globalY) {
// 1) Omuz pivotu (world)
var shX = enemy.x + enemy.rarm.x;
var shY = enemy.y + enemy.rarm.y;
// 2) Omuz→hedef vektörü
var dx = globalX - shX;
var dy = globalY - shY;
var ang = Math.atan2(dy, dx);
// 3) Kol sprite’ı AŞAĞI bakıyor → –90° (–π/2) düzeltme
var armRot = ang - Math.PI / 2;
enemy.rarm.rotation = armRot;
// 4) El / silah konumu (enemy LOCAL) **ayna!**
var L = enemy.rarm.height; // ≈145 px
enemy.gun.x = enemy.rarm.x - Math.sin(armRot) * L;
enemy.gun.y = enemy.rarm.y + Math.cos(armRot) * L;
// 5) Namluyu hedefe döndür
enemy.gun.rotation = ang;
}
// Helper to update holster box position under player's feet
function updateHolsterBox() {
holsterFull.x = player.x - 200;
holsterFull.y = player.y + player.body.height / 2 + holsterOffset;
holsterEmpty.x = holsterFull.x;
holsterEmpty.y = holsterFull.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();
// (aim reticle removed)
// Rakip silahı eldeyse oyuncuyu takip etsin
if (enemy.gun.visible) {
aimEnemyGunAt(player.x, player.y - 40);
}
// Animate bullets
if (playerBullet) {
playerBullet.update();
}
if (enemyBullet) {
enemyBullet.update();
}
// === PLAYER SHOT COOLDOWN TIMER ===
if (playerShotCooldown > 0) {
var dtMs = typeof dt === "number" ? dt : 16.7;
playerShotCooldown -= dtMs;
if (playerShotCooldown < 0) playerShotCooldown = 0;
}
// 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 (holsterContains(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("Wait 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();