<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>3D Runner - Gelişmiş Sürüm</title>
    <style>
        body { margin: 0; overflow: hidden; font-family: sans-serif; background: #0f0c29; user-select: none; touch-action: none; }
        #canvas-container { width: 100vw; height: 100vh; display: block; }
        #ui-overlay { position: absolute; top: 20px; left: 20px; color: #fff; pointer-events: none; z-index: 10; }
        .stat-box { font-size: 22px; font-weight: 800; text-shadow: 2px 2px 8px rgba(0,0,0,0.8); margin-bottom: 5px; }
        #powerup-bar-container { position: absolute; top: 20px; right: 20px; display: flex; flex-direction: column; gap: 8px; z-index: 10; }
        .powerup-indicator { background: rgba(15, 12, 41, 0.85); border: 1px solid rgba(255,255,255,0.2); padding: 8px 14px; border-radius: 12px; color: white; font-weight: bold; display: flex; align-items: center; gap: 10px; width: 160px; }
        .p-bar { height: 6px; background: #2ed573; border-radius: 3px; width: 100%; }
        #game-over { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; color: white; background: rgba(15, 12, 41, 0.96); padding: 40px 50px; border-radius: 24px; display: none; z-index: 30; }
        #game-over h1 { margin: 0 0 10px 0; color: #ff4757; font-size: 42px; }
        #restart-btn { margin-top: 20px; padding: 14px 40px; font-size: 20px; font-weight: bold; background: #2ed573; color: white; border: none; border-radius: 30px; cursor: pointer; }
        #on-screen-controls { position: absolute; bottom: 25px; left: 50%; transform: translateX(-50%); display: flex; gap: 15px; z-index: 10; }
        .ctrl-btn { width: 70px; height: 70px; background: rgba(255, 255, 255, 0.12); border: 2px solid rgba(255, 255, 255, 0.25); border-radius: 50%; color: white; font-size: 26px; font-weight: bold; display: flex; align-items: center; justify-content: center; cursor: pointer; }
        #shop-btn { position: absolute; top: 110px; left: 20px; background: #ff9f43; color: white; border: none; padding: 10px 20px; font-size: 15px; font-weight: bold; border-radius: 20px; cursor: pointer; z-index: 15; }
        #shop-modal { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 400px; background: rgba(15, 12, 41, 0.98); border-radius: 24px; padding: 25px; color: white; display: none; z-index: 30; }
        .character-card { background: rgba(255, 255, 255, 0.06); padding: 12px; border-radius: 14px; display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; }
        .char-action-btn { padding: 8px 16px; font-weight: bold; border: none; border-radius: 10px; cursor: pointer; }
        .btn-buy { background: #ff9f43; color: white; }
        .btn-select { background: #2ed573; color: white; }
        .btn-selected { background: #57606f; color: white; }
        #close-shop { width: 100%; margin-top: 10px; padding: 12px; background: #ff4757; color: white; border: none; border-radius: 12px; font-weight: bold; cursor: pointer; }
    </style>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
</head>
<body>
    <div id="ui-overlay">
        <div class="stat-box">🏁 Skor: <span id="score">0</span></div>
        <div class="stat-box">🪙 Para: <span id="coins">0</span></div>
        <div class="stat-box" style="font-size: 15px; color: #feca57;">🏆 En Yüksek: <span id="high-score">0</span></div>
    </div>
    <button id="shop-btn" onclick="toggleShop()">🛒 Karakter Mağazası</button>
    <div id="powerup-bar-container"></div>
    <div id="shop-modal">
        <h2 style="color:#feca57; text-align:center;">Karakter Mağazası</h2>
        <div id="character-list"></div>
        <button id="close-shop" onclick="toggleShop()">Kapat</button>
    </div>
    <div id="game-over">
        <h1>OYUN BİTTİ</h1>
        <p>Skor: <span id="final-score">0</span></p>
        <p style="color: #feca57;">En Yüksek Skor: <span id="final-highscore">0</span></p>
        <button id="restart-btn" onclick="resetGame()">Yeniden Başla</button>
    </div>
    <div id="on-screen-controls">
        <div class="ctrl-btn" onclick="moveLeft()">◀</div>
        <div class="ctrl-btn" onclick="jumpAction()">▲</div>
        <div class="ctrl-btn" onclick="crouchAction()">▼</div>
        <div class="ctrl-btn" onclick="moveRight()">▶</div>
    </div>
    <div id="canvas-container"></div>
    <script>
        const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
        function playSound(type) {
            if (audioCtx.state === 'suspended') audioCtx.resume();
            const osc = audioCtx.createOscillator(), gain = audioCtx.createGain();
            osc.connect(gain); gain.connect(audioCtx.destination);
            const now = audioCtx.currentTime;
            if (type === 'coin') {
                osc.type = 'sine'; osc.frequency.setValueAtTime(987, now); osc.frequency.exponentialRampToValueAtTime(1318, now + 0.08);
                gain.gain.setValueAtTime(0.15, now); gain.gain.linearRampToValueAtTime(0.01, now + 0.12);
                osc.start(now); osc.stop(now + 0.12);
            } else if (type === 'jump') {
                osc.type = 'triangle'; osc.frequency.setValueAtTime(150, now); osc.frequency.exponentialRampToValueAtTime(400, now + 0.15);
                gain.gain.setValueAtTime(0.2, now); gain.gain.linearRampToValueAtTime(0.01, now + 0.15);
                osc.start(now); osc.stop(now + 0.15);
            } else if (type === 'powerup') {
                osc.type = 'sine'; osc.frequency.setValueAtTime(300, now); osc.frequency.exponentialRampToValueAtTime(800, now + 0.25);
                gain.gain.setValueAtTime(0.2, now); gain.gain.linearRampToValueAtTime(0.01, now + 0.25);
                osc.start(now); osc.stop(now + 0.25);
            } else if (type === 'hit') {
                osc.type = 'sawtooth'; osc.frequency.setValueAtTime(120, now); osc.frequency.exponentialRampToValueAtTime(40, now + 0.3);
                gain.gain.setValueAtTime(0.3, now); gain.gain.linearRampToValueAtTime(0.01, now + 0.3);
                osc.start(now); osc.stop(now + 0.3);
            }
        }

        let scene, camera, renderer, player, torso, head, leftArm, rightArm, leftLeg, rightLeg, jacketMat;
        const lanes = [3.2, 0, -3.2]; let currentLane = 1, targetX = 0;
        let isJumping = false, isCrouching = false, jumpVelocity = 0; const gravity = -0.016;
        let speed = 0.38, score = 0, coins = 0, highScore = 0, isGameOver = false;
        let obstacles = [], coinObjects = [], powerupObjects = [], environmentObjects = [], particles = [], touchStartX = 0;
        let activePowerups = { magnet: 0, shield: false, multiplier: 0 };
        const characters = [
            { id: 0, name: "Klasik Koşucu", price: 0, color: 0x6c5ce7, bonus: "Standart", unlocked: true },
            { id: 1, name: "Kızıl Fırtına", price: 25, color: 0xff4757, bonus: "+Mıknatıs", unlocked: false },
            { id: 2, name: "Siber Ninja", price: 60, color: 0x2ed573, bonus: "Hızlı Geçiş", unlocked: false },
            { id: 3, name: "Altın Kral", price: 120, color: 0xffa502, bonus: "2x Para", unlocked: false }
        ];
        let selectedCharacterId = 0;

        function loadData() {
            coins = parseInt(localStorage.getItem('runner_coins') || 0);
            highScore = parseInt(localStorage.getItem('runner_highscore') || 0);
            const savedChars = localStorage.getItem('runner_unlocked_chars');
            if (savedChars) {
                const unlockedIds = JSON.parse(savedChars);
                characters.forEach(c => { if (unlockedIds.includes(c.id)) c.unlocked = true; });
            }
        }
        function saveData() {
            localStorage.setItem('runner_coins', coins);
            localStorage.setItem('runner_highscore', highScore);
            localStorage.setItem('runner_unlocked_chars', JSON.stringify(characters.filter(c => c.unlocked).map(c => c.id)));
        }

        function init() {
            loadData();
            scene = new THREE.Scene(); scene.background = new THREE.Color(0x0a0a1a); scene.fog = new THREE.FogExp2(0x0a0a1a, 0.015);
            camera = new THREE.PerspectiveCamera(55, window.innerWidth / window.innerHeight, 0.1, 1000);
            camera.position.set(0, 4.2, -8.5); camera.lookAt(0, 2, 8);
            renderer = new THREE.WebGLRenderer({ antialias: true });
            renderer.setSize(window.innerWidth, window.innerHeight);
            renderer.shadowMap.enabled = true;
            document.getElementById('canvas-container').appendChild(renderer.domElement);

            scene.add(new THREE.AmbientLight(0xffffff, 0.6));
            const sunLight = new THREE.DirectionalLight(0xff7675, 1.2); sunLight.position.set(15, 30, -10); sunLight.castShadow = true; scene.add(sunLight);

            const track = new THREE.Mesh(new THREE.PlaneGeometry(13, 600), new THREE.MeshStandardMaterial({ color: 0x0f172a, roughness: 0.4 }));
            track.rotation.x = -Math.PI / 2; track.position.z = 250; track.receiveShadow = true; scene.add(track);

            for(let i = -1.6; i <= 1.6; i += 3.2) {
                const line = new THREE.Mesh(new THREE.PlaneGeometry(0.18, 600), new THREE.MeshBasicMaterial({ color: 0x00d2d3 }));
                line.rotation.x = -Math.PI / 2; line.position.set(i, 0.02, 250); scene.add(line);
            }

            player = new THREE.Group();
            jacketMat = new THREE.MeshStandardMaterial({ color: characters[0].color });
            torso = new THREE.Mesh(new THREE.BoxGeometry(0.85, 1.1, 0.45), jacketMat); torso.position.y = 1.4; torso.castShadow = true; player.add(torso);
            head = new THREE.Mesh(new THREE.BoxGeometry(0.5, 0.52, 0.5), new THREE.MeshStandardMaterial({ color: 0xffd3b6 })); head.position.y = 2.22; head.castShadow = true; player.add(head);

            leftArm = new THREE.Mesh(new THREE.BoxGeometry(0.24, 0.95, 0.26), jacketMat); leftArm.position.set(-0.56, 1.4, 0); player.add(leftArm);
            rightArm = new THREE.Mesh(new THREE.BoxGeometry(0.24, 0.95, 0.26), jacketMat); rightArm.position.set(0.56, 1.4, 0); player.add(rightArm);

            const legGeo = new THREE.BoxGeometry(0.28, 0.95, 0.3), pantsMat = new THREE.MeshStandardMaterial({ color: 0x2d3436 });
            leftLeg = new THREE.Mesh(legGeo, pantsMat); leftLeg.position.set(-0.23, 0.48, 0); player.add(leftLeg);
            rightLeg = new THREE.Mesh(legGeo, pantsMat); rightLeg.position.set(0.23, 0.48, 0); player.add(rightLeg);
            scene.add(player);

            createEnvironment(); updateUI(); updateShopUI();
            window.addEventListener('keydown', handleKeyDown);
            window.addEventListener('touchstart', (e) => { touchStartX = e.touches[0].clientX; });
            window.addEventListener('touchend', (e) => {
                let diff = e.changedTouches[0].clientX - touchStartX;
                if (Math.abs(diff) > 40) { diff > 0 ? moveRight() : moveLeft(); }
            });
            window.addEventListener('resize', onWindowResize);
            animate();
        }

        function updateUI() { document.getElementById('coins').innerText = coins; document.getElementById('high-score').innerText = highScore; }
        function createEnvironment() {
            for (let z = 0; z < 400; z += 25) {
                const height = Math.random() * 18 + 10, bMat = new THREE.MeshStandardMaterial({ color: 0x0f172a });
                const bL = new THREE.Mesh(new THREE.BoxGeometry(9, height, 12), bMat); bL.position.set(-12, height / 2, z); scene.add(bL); environmentObjects.push(bL);
                const bR = bL.clone(); bR.position.x = 12; scene.add(bR); environmentObjects.push(bR);
            }
        }

        function createParticles(pos, color, count = 8) {
            for (let i = 0; i < count; i++) {
                const p = new THREE.Mesh(new THREE.BoxGeometry(0.15, 0.15, 0.15), new THREE.MeshBasicMaterial({ color: color }));
                p.position.copy(pos); p.vx = (Math.random() - 0.5) * 0.3; p.vy = Math.random() * 0.3 + 0.1; p.vz = (Math.random() - 0.5) * 0.3; p.life = 1.0;
                scene.add(p); particles.push(p);
            }
        }

        function spawnObstacleOrCoin() {
            const zPos = player.position.z + 120, laneX = lanes[Math.floor(Math.random() * 3)], rand = Math.random();
            if (rand < 0.3) {
                const obs = new THREE.Mesh(new THREE.BoxGeometry(2.6, 1.2, 0.9), new THREE.MeshStandardMaterial({ color: 0xff4757 }));
                obs.position.set(laneX, 0.6, zPos); scene.add(obs); obstacles.push(obs);
            } else if (rand < 0.55) {
                const obs = new THREE.Mesh(new THREE.BoxGeometry(2.6, 1.8, 0.9), new THREE.MeshStandardMaterial({ color: 0xff9f43 }));
                obs.position.set(laneX, 2.4, zPos); scene.add(obs); obstacles.push(obs);
            } else if (rand < 0.85) {
                for (let i = 0; i < 4; i++) {
                    const coin = new THREE.Mesh(new THREE.CylinderGeometry(0.45, 0.45, 0.1, 20), new THREE.MeshStandardMaterial({ color: 0xfeca57 }));
                    coin.rotation.x = Math.PI / 2; coin.position.set(laneX, 1.2, zPos + (i * 2.2)); scene.add(coin); coinObjects.push(coin);
                }
            } else {
                const pTypes = ['magnet', 'shield', 'multiplier'], pType = pTypes[Math.floor(Math.random() * pTypes.length)];
                let pColor = pType === 'shield' ? 0x2ed573 : (pType === 'multiplier' ? 0x8e44ad : 0x3742fa);
                const powerup = new THREE.Mesh(new THREE.OctahedronGeometry(0.6), new THREE.MeshStandardMaterial({ color: pColor }));
                powerup.position.set(laneX, 1.4, zPos); powerup.pType = pType; scene.add(powerup); powerupObjects.push(powerup);
            }
        }

        function moveLeft() { if (!isGameOver && currentLane > 0) currentLane--; }
        function moveRight() { if (!isGameOver && currentLane < 2) currentLane++; }
        function jumpAction() { if (!isGameOver && !isJumping) { isJumping = true; jumpVelocity = 0.42; playSound('jump'); } }
        function crouchAction() {
            if (isGameOver || isCrouching) return;
            isCrouching = true; torso.scale.set(1, 0.5, 1); torso.position.y = 0.85; head.position.y = 1.4;
            setTimeout(() => { torso.scale.set(1, 1, 1); torso.position.y = 1.4; head.position.y = 2.22; isCrouching = false; }, 850);
        }

        function handleKeyDown(e) {
            if (e.key === 'ArrowLeft' || e.key === 'a') moveLeft();
            if (e.key === 'ArrowRight' || e.key === 'd') moveRight();
            if (e.key === 'ArrowUp' || e.key === 'w') jumpAction();
            if (e.key === 'ArrowDown' || e.key === 's') crouchAction();
        }

        function animate() {
            if (isGameOver) return;
            requestAnimationFrame(animate);

            player.position.z += speed; speed += 0.00003;
            camera.position.z = player.position.z - 8.5; camera.position.x = player.position.x * 0.35;

            targetX = lanes[currentLane];
            let lerpSpeed = selectedCharacterId === 2 ? 0.32 : 0.22, diffX = targetX - player.position.x;
            player.position.x += diffX * lerpSpeed; player.rotation.z = -diffX * 0.12; 

            if (!isJumping) {
                let runCycle = player.position.z * 9;
                leftLeg.position.z = Math.sin(runCycle) * 0.38; rightLeg.position.z = -Math.sin(runCycle) * 0.38;
                leftArm.position.z = -Math.sin(runCycle) * 0.38; rightArm.position.z = Math.sin(runCycle) * 0.38;
            }

            if (isJumping) {
                player.position.y += jumpVelocity; jumpVelocity += gravity;
                if (player.position.y <= 0) { player.position.y = 0; isJumping = false; }
            }

            if (activePowerups.magnet > 0) {
                activePowerups.magnet -= 0.016;
                coinObjects.forEach(coin => { if (coin.position.distanceTo(player.position) < 18) coin.position.lerp(player.position, 0.2); });
            }
            if (activePowerups.multiplier > 0) activePowerups.multiplier -= 0.016;

            coinObjects.forEach(coin => coin.rotation.z += 0.07);
            powerupObjects.forEach(p => p.rotation.y += 0.05);

            for (let i = particles.length - 1; i >= 0; i--) {
                let p = particles[i]; p.position.x += p.vx; p.position.y += p.vy; p.position.z += p.vz; p.life -= 0.03; p.scale.multiplyScalar(0.95);
                if (p.life <= 0) { scene.remove(p); particles.splice(i, 1); }
            }

            frameCount++; if (frameCount % 30 === 0) spawnObstacleOrCoin();
            environmentObjects.forEach(obj => { if (obj.position.z < player.position.z - 25) obj.position.z += 400; });

            checkCollisions(); updatePowerupHUD();
            score = Math.floor(player.position.z * (activePowerups.multiplier > 0 ? 2 : 1));
            document.getElementById('score').innerText = score;
            renderer.render(scene, camera);
        }

        let frameCount = 0;
        function checkCollisions() {
            const playerBox = new THREE.Box3().setFromObject(player);

            for (let i = obstacles.length - 1; i >= 0; i--) {
                if (playerBox.intersectsBox(new THREE.Box3().setFromObject(obstacles[i]))) {
                    if (activePowerups.shield) {
                        activePowerups.shield = false; scene.remove(obstacles[i]); obstacles.splice(i, 1);
                        createParticles(player.position, 0x2ed573, 15); playSound('powerup');
                    } else { endGame(); return; }
                }
            }

            for (let i = coinObjects.length - 1; i >= 0; i--) {
                if (playerBox.intersectsBox(new THREE.Box3().setFromObject(coinObjects[i]))) {
                    createParticles(coinObjects[i].position, 0xfeca57); scene.remove(coinObjects[i]); coinObjects.splice(i, 1);
                    coins += (selectedCharacterId === 3 ? 2 : 1); document.getElementById('coins').innerText = coins; playSound('coin');
                }
            }

            for (let i = powerupObjects.length - 1; i >= 0; i--) {
                if (playerBox.intersectsBox(new THREE.Box3().setFromObject(powerupObjects[i]))) {
                    const p = powerupObjects[i]; playSound('powerup'); createParticles(p.position, 0xffffff, 12);
                    if (p.pType === 'magnet') activePowerups.magnet = selectedCharacterId === 1 ? 10 : 8;
                    if (p.pType === 'shield') activePowerups.shield = true;
                    if (p.pType === 'multiplier') activePowerups.multiplier = 8;
                    scene.remove(p); powerupObjects.splice(i, 1);
                }
            }
        }

        function updatePowerupHUD() {
            const container = document.getElementById('powerup-bar-container'); container.innerHTML = '';
            if (activePowerups.magnet > 0) container.innerHTML += `<div class="powerup-indicator">🧲 Mıknatıs <div class="p-bar" style="width:${(activePowerups.magnet/8)*100}%"></div></div>`;
            if (activePowerups.shield) container.innerHTML += `<div class="powerup-indicator" style="border-color:#2ed573">🛡️ Kalkan</div>`;
            if (activePowerups.multiplier > 0) container.innerHTML += `<div class="powerup-indicator" style="border-color:#8e44ad">⚡ 2x Skor <div class="p-bar" style="width:${(activePowerups.multiplier/8)*100}%; background:#8e44ad"></div></div>`;
        }

        function endGame() {
            isGameOver = true; playSound('hit');
            if (score > highScore) highScore = score;
            saveData();
            document.getElementById('final-score').innerText = score;
            document.getElementById('final-highscore').innerText = highScore;
            document.getElementById('game-over').style.display = 'block';
        }

        function resetGame() {
            obstacles.forEach(obs => scene.remove(obs)); coinObjects.forEach(coin => scene.remove(coin)); powerupObjects.forEach(p => scene.remove(p));
            obstacles = []; coinObjects = []; powerupObjects = [];
            player.position.set(0, 0, 0); currentLane = 1; speed = 0.38; score = 0; isGameOver = false;
            activePowerups = { magnet: 0, shield: false, multiplier: 0 };
            document.getElementById('game-over').style.display = 'none'; updateUI(); animate();
        }

        function toggleShop() {
            const modal = document.getElementById('shop-modal');
            modal.style.display = modal.style.display === 'block' ? 'none' : 'block'; updateShopUI();
        }

        function updateShopUI() {
            const listDiv = document.getElementById('character-list'); listDiv.innerHTML = '';
            characters.forEach(char => {
                let card = document.createElement('div'); card.className = `character-card`;
                let btnHTML = selectedCharacterId === char.id ? `<button class="char-action-btn btn-selected">Seçili</button>` :
                    (char.unlocked ? `<button class="char-action-btn btn-select" onclick="selectCharacter(${char.id})">Seç</button>` :
                    `<button class="char-action-btn btn-buy" onclick="buyCharacter(${char.id})">🪙 ${char.price}</button>`);
                card.innerHTML = `<div class="char-info"><b>${char.name}</b><span>${char.bonus}</span></div>${btnHTML}`;
                listDiv.appendChild(card);
            });
        }

        function buyCharacter(id) {
            let char = characters.find(c => c.id === id);
            if (coins >= char.price) { coins -= char.price; char.unlocked = true; saveData(); updateUI(); selectCharacter(id); }
            else alert("Yeterli altınınız yok!");
        }

        function selectCharacter(id) {
            selectedCharacterId = id; jacketMat.color.setHex(characters.find(c => c.id === id).color); updateShopUI();
        }

        function onWindowResize() { camera.aspect = window.innerWidth / window.innerHeight; camera.updateProjectionMatrix(); renderer.setSize(window.innerWidth, window.innerHeight); }
        init();
    </script>
</body>
</html>
