JustPaste.it

<!DOCTYPE html>
<html>
<head>
    <title>Naginata Trainer - Fast Death + Auto Respawn</title>
    <style>
        body { 
            background: #121212; 
            color: #eee; 
            sans-serif; 
            display: flex; 
            flex-direction: column; 
            align-items: center; 
            padding-top: 20px; 
        }
        #canvas-container { 
            position: relative; 
            width: 800px; 
            height: 500px; 
            background: #000; 
        }
        canvas { 
            background: #050505; 
            border: 2px solid #d40000; 
            display: block; 
        }
        #overlay {
            position: absolute; top: 0; left: 0; 
            width: 100%; height: 100%;
            background: rgba(0,0,0,0.85); 
            display: flex; 
            flex-direction: column;
            justify-content: center; 
            align-items: center; 
            color: #ff4444;
            font-weight: bold; 
            cursor: pointer; 
            z-index: 20;
            font-size: 28px;
        }
        .ui-main { 
            15px; 
            background: #1a1a1a; 
            padding: 20px; 
            border-radius: 8px; 
            width: 800px; 
            display: grid; 
            grid-template-columns: 1.5fr 1fr; 
            gap: 30px; 
            border: 1px solid #333; 
            box-sizing: border-box; 
        }
        .slider-group { 12px; }
        .slider-group label { 
            display: block; 
            font-size: 13px; 
            color: #ff4444; 
            4px; 
            font-weight: bold; 
        }
        button { 
            background: #d40000; 
            color: white; 
            border: none; 
            padding: 10px; 
            cursor: pointer; 
            border-radius: 4px; 
            font-weight: bold; 
            width: 100%; 
        }
        input[type=range] { width: 100%; accent-color: #d40000; }
    </style>
</head>
<body>
    <h2 style="color: #d40000;">NSX NAGINATA SIM - Fast Death</h2>
    
    <div id="canvas-container">
        <canvas id="simCanvas" width="800" height="500"></canvas>
        <div id="overlay">CLICK TO START</div>
    </div>

    <div class="ui-main">
        <div class="controls">
            <div class="slider-group">
                <label>Distance: <span id="distVal">35</span>m</label>
                <input type="range" id="distSlider" min="10" max="100" value="35">
            </div>
            <div class="slider-group">
                <label>Model Height (Stretching): <span id="hScaleVal">1.0</span>x</label>
                <input type="range" id="hScaleSlider" min="0.5" max="2.5" step="0.05" value="1.0">
            </div>
            <div class="slider-group">
                <label>Vertical Recoil: <span id="recoilVal">0.50</span>°</label>
                <input type="range" id="recoilSlider" min="0" max="1.5" step="0.01" value="0.50">
            </div>
            <div class="slider-group">
                <label>Strafe Speed: <span id="speedVal">2.6</span> m/s</label>
                <input type="range" id="speedSlider" min="0" max="6.5" step="0.1" value="2.6">
            </div>
            <button id="resetBtn">RESET SESSION</button>
        </div>
        <div class="stats-panel" style=" monospace; color: #00ff00;">
            KILLS: <span id="killCount" style="font-size: 24px;">0</span><br>
            TARGET HP: <span id="hpVal">1000</span><br>
            <div style="font-size: 11px; color: #666; 20px;">
                • Realistic strafe bob<br>
                • Crosshair + bullets + markers on top<br>
                • Fast death (0.65s) + instant respawn
            </div>
        </div>
    </div>

<script>
const canvas = document.getElementById('simCanvas');
const ctx = canvas.getContext('2d');
const overlay = document.getElementById('overlay');

// ============== AUDIO ==============
let audioCtx = null;
function initAudio() {
    if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
    if (audioCtx.state === 'suspended') audioCtx.resume();
}
function playShot() {
    if (!audioCtx) return;
    const n = audioCtx.createBufferSource(), g = audioCtx.createGain(), f = audioCtx.createBiquadFilter();
    const b = audioCtx.createBuffer(1, audioCtx.sampleRate * 0.06, audioCtx.sampleRate), d = b.getChannelData(0);
    for (let i = 0; i < b.length; i++) d[i] = Math.random() * 2 - 1;
    n.buffer = b; f.type = "lowpass"; f.frequency.value = 850;
    g.gain.setValueAtTime(0.12, audioCtx.currentTime);
    g.gain.exponentialRampToValueAtTime(0.008, audioCtx.currentTime + 0.06);
    n.connect(f); f.connect(g); g.connect(audioCtx.destination); n.start();
}
function playHit(isHead) {
    if (!audioCtx) return;
    const o = audioCtx.createOscillator(), g = audioCtx.createGain();
    o.frequency.setValueAtTime(isHead ? 1150 : 620, audioCtx.currentTime);
    g.gain.setValueAtTime(0.06, audioCtx.currentTime);
    g.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + 0.045);
    o.connect(g); g.connect(audioCtx.destination); o.start(); o.stop(audioCtx.currentTime + 0.045);
}

// ============== IMAGE ==============
const playerImg = new Image();
let imgReady = false;
playerImg.onload = () => { imgReady = true; };
playerImg.src = 'https://www-cdn.planetside2.com/images/players/player/profile/char-default-tr-13-female.png';

// ============== GAME VARIABLES ==============
const RPM = 659, FIRE_INT = 1000 / (RPM / 60);
const BLOOM = 0.05, MAX_COF = 0.6, FSRM = 1.8, H_RECOIL = 0.16;
const VELO = 490, RECOV = 13, SCALE_X = 80, SCALE_Y = 26.6;

let isFiring = false, lastFire = 0, currentCof = 0.1, shots = 0, kills = 0;
let aimX = 400, aimY = 250, recV = 0, recH = 0;
let bullets = [], markers = [];
let target = { x_deg: 0, hp: 1000, alive: true, dir: 1, lastFlip: 0 };

let animTime = 0;
let deathTimer = 0;   // countdown while dying
let spawnTimer = 0;

overlay.addEventListener('click', () => {
    initAudio();
    aimX = 400; aimY = 250; recV = 0; recH = 0;
    canvas.requestPointerLock();
});

document.addEventListener('pointerlockchange', () => {
    overlay.style.display = (document.pointerLockElement === canvas) ? 'none' : 'flex';
});

canvas.addEventListener('mousemove', (e) => {
    if (document.pointerLockElement === canvas) {
        aimX += e.movementX; aimY += e.movementY;
    }
});

canvas.addEventListener('mousedown', () => { if (document.pointerLockElement === canvas) isFiring = true; });
window.addEventListener('mouseup', () => { isFiring = false; shots = 0; });

document.getElementById('resetBtn').addEventListener('click', () => {
    kills = 0; document.getElementById('killCount').innerText = "0";
    spawnTarget();
});

function spawnTarget() {
    target.x_deg = (Math.random() - 0.5) * 4;
    target.hp = 1000; 
    target.alive = true;
    currentCof = 0.1; recV = 0; recH = 0; shots = 0;
    deathTimer = 0;
    spawnTimer = 0.7;           // short spawn pop-in
}

function update() {
    const now = performance.now();
    const dt = 1/60;
    animTime += dt;

    const dist = parseFloat(document.getElementById('distSlider').value);
    const hStretch = parseFloat(document.getElementById('hScaleSlider').value);
    const spd = parseFloat(document.getElementById('speedSlider').value);
    const rvb = parseFloat(document.getElementById('recoilSlider').value);

    document.getElementById('distVal').innerText = dist;
    document.getElementById('hScaleVal').innerText = hStretch.toFixed(2);
    document.getElementById('speedVal').innerText = spd.toFixed(1);
    document.getElementById('recoilVal').innerText = rvb.toFixed(2);

    // Strafe
    if (target.alive && deathTimer <= 0) {
        let angSpd = (Math.atan(spd / dist) * 180 / Math.PI);
        target.x_deg += target.dir * angSpd * dt;
        if (target.x_deg > 4.5) target.dir = -1;
        if (target.x_deg < -4.5) target.dir = 1;
    }

    // Firing (only when alive and not dying)
    if (!isFiring) {
        recV = Math.max(0, recV - RECOV * dt);
        recH *= 0.85; currentCof = 0.1;
    } else if (target.alive && deathTimer <= 0 && document.pointerLockElement === canvas) {
        if (now - lastFire > FIRE_INT) {
            playShot();
            let k = (shots === 0) ? (rvb * FSRM) : rvb;
            recV += k; recH += (Math.random() - 0.5) * H_RECOIL;
            currentCof = Math.min(currentCof + BLOOM, MAX_COF);
            let vX = aimX + (recH * SCALE_X), vY = aimY - (recV * SCALE_Y);
            let a = Math.random() * 6.28, d = Math.random() * (currentCof/2 * SCALE_X);
            bullets.push({ s: now, ox: vX, oy: vY + 20, dx: vX + Math.cos(a) * d, dy: vY + Math.sin(a) * d });
            shots++; lastFire = now;
        }
    }

    // Bullets & hits
    let tTime = (dist / VELO) * 1000;
    const sX = 35 / dist;
    const sY = (35 / dist) * hStretch;

    for (let i = bullets.length - 1; i >= 0; i--) {
        let b = bullets[i], p = (now - b.s) / tTime;
        if (p >= 1) { 
            const tx = 400 + (target.x_deg * SCALE_X), ty = 250;

            let hit = null;
            if (b.dx > tx-(15*sX) && b.dx < tx+(15*sX) && b.dy > ty-(110*sY) && b.dy < ty-(80*sY)) hit = 'H';
            else if (b.dx > tx-(30*sX) && b.dx < tx+(30*sX) && b.dy > ty-(80*sY) && b.dy < ty+(100*sY)) hit = 'B';
            
            if (hit && target.alive && deathTimer <= 0) {
                playHit(hit === 'H');
                let dmg = Math.max(125, Math.min(150, 150 - (25/55)*(dist-10)));
                target.hp -= (hit === 'H' ? dmg * 2 : dmg);
                markers.push({ x: b.dx, y: b.dy, t: hit, l: 0.15 });

                if (target.hp <= 0 && deathTimer <= 0) { 
                    target.alive = false; 
                    kills++; 
                    document.getElementById('killCount').innerText = kills; 
                    deathTimer = 0.65;   // FAST death (0.65 seconds)
                }
            }
            bullets.splice(i, 1); 
        } else { 
            b.cx = b.ox + (b.dx - b.ox) * p; 
            b.cy = b.oy + (b.dy - b.oy) * p; 
            b.sc = 1.0 - (p * 0.7); 
        }
    }
    markers.forEach((m, i) => { m.l -= dt; if(m.l <= 0) markers.splice(i,1); });

    // Timers
    if (deathTimer > 0) {
        deathTimer -= dt;
        if (deathTimer <= 0) {
            spawnTarget();          // instant new enemy after death animation
        }
    }
    if (spawnTimer > 0) spawnTimer -= dt;
}

function draw() {
    update();
    ctx.clearRect(0, 0, 800, 500);

    const dist = parseFloat(document.getElementById('distSlider').value);
    const hStretch = parseFloat(document.getElementById('hScaleSlider').value);
    const sX = 35 / dist;
    const sY = (35 / dist) * hStretch;
    let tx = 400 + (target.x_deg * SCALE_X);
    let ty = 250;

    // Draw target with animations
    if (target.alive || deathTimer > 0) {
        ctx.save();

        let drawX = tx - 100 * sX;
        let drawY = ty - 125 * sY;
        let w = 200 * sX;
        let h = 250 * sY;

        // Spawn pop-in
        if (spawnTimer > 0) {
            const p = 1 - spawnTimer / 0.7;
            const scale = 0.3 + p * 0.7;
            const jump = Math.sin(p * Math.PI) * 35;
            ctx.translate(drawX + w/2, drawY + h/2 + jump);
            ctx.scale(scale, scale);
            ctx.translate(-w/2, -h/2);
        }
        // Fast death flop
        else if (deathTimer > 0) {
            const p = (0.65 - deathTimer) / 0.65;
            const fall = p * 110;
            const rot = p * 42;
            ctx.translate(drawX + w/2, drawY + h/2 + fall);
            ctx.rotate(rot * Math.PI / 180);
            ctx.scale(1 - p * 0.3, 1 - p * 0.15);
            ctx.globalAlpha = 1 - p * 0.65;
            ctx.translate(-w/2, -h/2);
        }
        // Normal realistic bob
        else {
            const bobY = Math.sin(animTime * 9) * 6;
            const swayX = Math.sin(animTime * 4.5) * 7;
            const lean = Math.sin(animTime * 4.5) * 3;
            ctx.translate(drawX + w/2 + swayX, drawY + h/2 + bobY);
            ctx.rotate(lean * Math.PI / 180);
            ctx.translate(-w/2, -h/2);
        }

        if (imgReady) {
            ctx.drawImage(playerImg, 0, 0, w, h);
        } else {
            ctx.fillStyle = '#400';
            ctx.fillRect(0, 0, w, h);
        }
        ctx.restore();
    }

    // Bullets
    bullets.forEach(b => {
        ctx.fillStyle = `rgba(255, 255, 180, ${b.sc})`;
        ctx.beginPath(); ctx.arc(b.cx, b.cy, 3 * b.sc, 0, 7); ctx.fill();
    });

    // Hit markers
    markers.forEach(m => {
        ctx.strokeStyle = m.t === 'H' ? "#ff0" : "#f00"; 
        ctx.lineWidth = m.t === 'H' ? 3 : 1;
        let sz = m.t === 'H' ? 10 : 6;
        ctx.beginPath(); ctx.moveTo(m.x-sz, m.y-sz); ctx.lineTo(m.x+sz, m.y+sz);
        ctx.moveTo(m.x+sz, m.y-sz); ctx.lineTo(m.x-sz, m.y+sz); ctx.stroke();
    });

    // Crosshair (always on top)
    const vx = aimX + (recH * SCALE_X), vy = aimY - (recV * SCALE_Y);
    ctx.strokeStyle = 'rgba(0, 212, 255, 0.5)'; 
    ctx.beginPath(); ctx.arc(vx, vy, (currentCof/2 * SCALE_X), 0, 7); ctx.stroke();
    ctx.strokeStyle = '#0f0'; 
    ctx.beginPath(); ctx.moveTo(vx-8, vy); ctx.lineTo(vx+8, vy); 
    ctx.moveTo(vx, vy-8); ctx.lineTo(vx, vy+8); ctx.stroke();

    // HP bar (only when alive or dying)
    if (target.alive || deathTimer > 0) {
        ctx.fillStyle = '#555';
        ctx.fillRect(tx-40, ty - 115*sY - 22, 80, 5);
        ctx.fillStyle = '#f00';
        ctx.fillRect(tx-40, ty - 115*sY - 22, (target.hp/1000)*80, 5);
    }

    document.getElementById('hpVal').innerText = Math.max(0, Math.round(target.hp));
    requestAnimationFrame(draw);
}

spawnTarget();
draw();
</script>
</body>
</html>