Chrome Dino Mod Menu V15.2 with Air Jump + God Mode + More

Mod Menu with God Mode, Speed Hack, Super Jump, Fly Hack, Remove Obstacles, Bigger Dino, Rainbow Glow, Low Gravity, Rainbow Background, Night Mode, Obstacle ESP, Obstacle Tracers, Remove Clouds, Rain Clouds, Auto Play & Auto Restart, Teleport Past Obstacles, Air Jump

// ==UserScript==
// @name         Chrome Dino Mod Menu V15.2 with Air Jump + God Mode + More
// @namespace    http://tampermonkey.net/
// @version      15.2
// @description  Mod Menu with God Mode, Speed Hack, Super Jump, Fly Hack, Remove Obstacles, Bigger Dino, Rainbow Glow, Low Gravity, Rainbow Background, Night Mode, Obstacle ESP, Obstacle Tracers, Remove Clouds, Rain Clouds, Auto Play & Auto Restart, Teleport Past Obstacles, Air Jump
// @author       Luckyday999 Jadob Lane
// @match        https://chromedino.com/
// @match        https://dino-chrome.com/
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    // --- Shared state ---
    let flyInterval = null;
    let obstacleInterval = null;
    let rainbowGlowEnabled = false;
    let glowElement = null;
    let glowAnimation = null;
    let originalGravity = null;
    let rainbowStyleElement = null;
    let originalBgColor = null;
    let obsESPOverlay = null;
    let obsESPContext = null;
    let obsESPDrawHook = null;
    let removeCloudsInterval = null;
    let rainCloudsEnabled = false;
    let rainCloudsAnimationId = null;
    let rainCloudCanvas = null;
    let rainCloudContext = null;

    let autoPlayEnabled = false;
    let autoPlayInterval = null;

    // Obstacle Tracers variables
    let obsTracerOverlay = null;
    let obsTracerContext = null;
    let obsTracerInterval = null;

    // Teleport Past Obstacles interval
    let teleportInterval = null;

    // Air Jump variables
    let airJumping = false;
    let airJumpInterval = null;

    // --- Mod Menu UI ---
    function createModMenu() {
        const menu = document.createElement('div');
        menu.id = 'modMenu';
        menu.style = `
            position: fixed;
            top: 20px;
            left: 20px;
            padding: 10px;
            background-color: rgba(0, 0, 0, 0.7);
            color: #fff;
            font-family: Arial, sans-serif;
            font-size: 14px;
            border-radius: 8px;
            z-index: 9999;
            user-select: none;
            pointer-events: auto;
            max-width: 200px;
        `;

        const title = document.createElement('div');
        title.textContent = '🦖Dino Game Mod Menu';
        title.style.fontWeight = 'bold';
        title.style.marginBottom = '8px';
        menu.appendChild(title);

        function addToggle(labelText, onToggle) {
            const label = document.createElement('label');
            label.style.display = 'block';
            label.style.marginBottom = '6px';

            const checkbox = document.createElement('input');
            checkbox.type = 'checkbox';
            checkbox.style.marginRight = '6px';
            checkbox.addEventListener('click', e => e.stopPropagation());
            checkbox.addEventListener('change', function () {
                onToggle(this.checked);
            });

            label.appendChild(checkbox);
            label.appendChild(document.createTextNode(labelText));
            menu.appendChild(label);
        }

        addToggle('God Mode', toggleGodMode);
        addToggle('Speed Hack', applySpeedHack);
        addToggle('Super Jump', toggleSuperJump);
        addToggle('Fly Hack', toggleFlyHack);
        addToggle('Remove Obstacles', toggleRemoveObstacles);
        addToggle('Remove Clouds', toggleRemoveClouds);
        addToggle('Rain Clouds', toggleRainClouds);
        addToggle('Clone Game', toggleBigDino);
        addToggle('Rainbow Glow', toggleRainbowGlow);
        addToggle('Low Gravity', toggleLowGravity);
        addToggle('Rainbow Background', toggleRainbowBackground);
        addToggle('Night Mode', toggleNightMode);
        addToggle('Obstacle ESP', toggleObstacleESP);
        addToggle('Obstacle Tracers', toggleObsTracers);
        addToggle('Auto Play', toggleAutoPlay);
        addToggle('Teleport Past Obstacles', toggleTeleportPastObstacles);

        // New Air Jump toggle added here
        addToggle('Air Jump', toggleAirJump);

        document.body.appendChild(menu);
        // --- Feature implementations ---
    function toggleGodMode(enabled) {
        const proto = Runner.prototype;
        if (enabled) {
            if (!proto._origGameOver) proto._origGameOver = proto.gameOver;
            proto.gameOver = () => {};
        } else if (proto._origGameOver) {
            proto.gameOver = proto._origGameOver;
        }
    }

    function applySpeedHack(enabled) {
        const inst = Runner.instance_;
        if (inst?.setSpeed) inst.setSpeed(enabled ? 1000 : 6);
    }

    function toggleSuperJump(enabled) {
        const trex = Runner.instance_?.tRex;
        if (trex?.setJumpVelocity) trex.setJumpVelocity(enabled ? 100 : -10.5);
    }

    function toggleFlyHack(enabled) {
        const runner = Runner.instance_;
        if (!runner?.tRex) return;
        if (enabled) {
            flyInterval = setInterval(() => {
                const d = runner.tRex;
                d.groundYPos = d.yPos = 20;
                d.jumpVelocity = 0;
                d.status = 'JUMPING';
            }, 20);
        } else {
            clearInterval(flyInterval);
            const d = runner.tRex;
            d.groundYPos = d.config.GROUND_Y_POS;
            d.status = 'RUNNING';
        }
    }

    function toggleRemoveObstacles(enabled) {
        const r = Runner.instance_;
        if (!r) return;
        if (enabled) {
            obstacleInterval = setInterval(() => { r.horizon.obstacles = []; }, 30);
        } else {
            clearInterval(obstacleInterval);
        }
    }

    function toggleRemoveClouds(enabled) {
        const r = Runner.instance_;
        if (!r) return;
        if (enabled) {
            removeCloudsInterval = setInterval(() => { r.horizon.clouds = []; }, 30);
        } else {
            clearInterval(removeCloudsInterval);
        }
    }

    function toggleRainClouds(enabled) {
        const runner = Runner.instance_;
        if (!runner) return;

        const cloudColor = 'rgba(200,200,200,0.7)';
        const cloudCount = 8;
        const clouds = [];

        if (enabled) {
            rainCloudsEnabled = true;
            rainCloudCanvas = document.createElement('canvas');
            rainCloudCanvas.style.cssText = 'position:absolute;pointer-events:none;z-index:10;';
            document.body.appendChild(rainCloudCanvas);
            rainCloudContext = rainCloudCanvas.getContext('2d');

            for (let i = 0; i < cloudCount; i++) {
                clouds.push({ x: Math.random()*window.innerWidth, y: Math.random()*40+10, r: Math.random()*10+10, s: 0.15+Math.random()*0.1 });
            }

            function animate() {
                if (!rainCloudsEnabled) return;
                const rect = runner.canvas.getBoundingClientRect();
                rainCloudCanvas.width = rect.width;
                rainCloudCanvas.height = rect.height;
                rainCloudCanvas.style.top = rect.top+'px';
                rainCloudCanvas.style.left = rect.left+'px';
                rainCloudContext.clearRect(0,0,rect.width,rect.height);

                for (const c of clouds) {
                    c.x += c.s;
                    if (c.x - c.r*2 > rect.width) { c.x = -c.r*2; c.y = Math.random()*40+10; }
                    rainCloudContext.beginPath();
                    rainCloudContext.fillStyle = cloudColor;
                    rainCloudContext.arc(c.x,c.y,c.r,0,2*Math.PI);
                    rainCloudContext.arc(c.x+c.r*0.6,c.y-c.r*0.3,c.r*0.7,0,2*Math.PI);
                    rainCloudContext.arc(c.x+c.r*1.2,c.y,c.r,0,2*Math.PI);
                    rainCloudContext.fill();
                }

                rainCloudsAnimationId = requestAnimationFrame(animate);
            }
            animate();
        } else {
            rainCloudsEnabled = false;
            cancelAnimationFrame(rainCloudsAnimationId);
            rainCloudCanvas.remove();
        }
    }

    function toggleBigDino(enabled) {
        const interval = setInterval(() => {
            const gameCanvas = document.querySelector('canvas');
            const runner = window.Runner?.instance_;

            if (!gameCanvas || !runner) return;

            clearInterval(interval);

            // Create cloned canvas
            const cloneCanvas = document.createElement('canvas');
            cloneCanvas.width = gameCanvas.width;
            cloneCanvas.height = gameCanvas.height;
            cloneCanvas.style.position = 'absolute';
            cloneCanvas.style.left = gameCanvas.offsetLeft + gameCanvas.width + 20 + 'px';
            cloneCanvas.style.top = gameCanvas.offsetTop + 'px';
            cloneCanvas.style.zIndex = 1000;
            document.body.appendChild(cloneCanvas);

            const cloneCtx = cloneCanvas.getContext('2d');
            const originalCtx = gameCanvas.getContext('2d');

            // Copy the game canvas to the clone every frame
            function updateClone() {
                try {
                    cloneCtx.clearRect(0, 0, cloneCanvas.width, cloneCanvas.height);
                    cloneCtx.drawImage(gameCanvas, 0, 0);
                } catch (e) {
                    console.warn('Clone draw failed:', e);
                }
                requestAnimationFrame(updateClone);
            }

            updateClone();
        }, 100);
    }

    function toggleRainbowGlow(enabled) {
        const r = Runner.instance_;
        const canvas = document.querySelector('.runner-canvas');
        if (!r || !canvas) return;

        const container = canvas.parentElement;
        container.style.position = 'relative';

        if (enabled) {
            if (!glowElement) {
                glowElement = document.createElement('div');
                glowElement.style.cssText = 'position:absolute;pointer-events:none;z-index:9999;';
                container.appendChild(glowElement);
            }
            rainbowGlowEnabled = true;
            let hue = 0;
            glowAnimation = setInterval(() => {
                hue = (hue + 3) % 360;
                glowElement.style.boxShadow = `0 0 8px 4px hsl(${hue},100%,60%)`;
            }, 50);

            (function update() {
                if (!rainbowGlowEnabled) return;
                const d = r.tRex;
                glowElement.style.left  = d.xPos + 'px';
                glowElement.style.top   = d.yPos + 'px';
                glowElement.style.width = d.config.WIDTH + 'px';
                glowElement.style.height= d.config.HEIGHT + 'px';
                requestAnimationFrame(update);
            })();
        } else {
            rainbowGlowEnabled = false;
            clearInterval(glowAnimation);
            glowElement?.remove();
            glowElement = null;
        }
    }

    function toggleLowGravity(enabled) {
        const t = Runner.instance_?.tRex;
        if (!t) return;
        if (enabled) {
            if (originalGravity === null) originalGravity = t.config.GRAVITY;
            t.config.GRAVITY = 0.1;
        } else if (originalGravity !== null) {
            t.config.GRAVITY = originalGravity;
        }
    }

    function toggleRainbowBackground(enabled) {
        if (enabled) {
            rainbowStyleElement = document.createElement('style');
            rainbowStyleElement.textContent = `
                @keyframes rainbowBackground {
                    0% { background-color: red; }
                    16% { background-color: orange; }
                    33% { background-color: yellow; }
                    50% { background-color: green; }
                    66% { background-color: blue; }
                    83% { background-color: indigo; }
                    100% { background-color: violet; }
                }
                body { animation: rainbowBackground 5s linear infinite !important; }
            `;
            document.head.appendChild(rainbowStyleElement);
        } else {
            rainbowStyleElement?.remove();
            document.body.style.animation = 'none';
        }
    }

    function toggleNightMode(enabled) {
        if (enabled) {
            originalBgColor = document.body.style.backgroundColor;
            document.body.style.backgroundColor = 'black';
        } else {
            document.body.style.backgroundColor = originalBgColor || '';
        }
    }

    function toggleObstacleESP(enabled) {
        const r = Runner.instance_;
        const base = r?.canvas;
        if (!base) return;

        if (enabled) {
            if (!obsESPOverlay) {
                obsESPOverlay = document.createElement('canvas');
                obsESPOverlay.width  = base.width;
                obsESPOverlay.height = base.height;
                obsESPOverlay.style.cssText = `
                    position:absolute;
                    left:${base.offsetLeft}px;
                    top:${base.offsetTop}px;
                    pointer-events:none;
                    z-index:9998;
                `;
                base.parentNode.appendChild(obsESPOverlay);
                obsESPContext = obsESPOverlay.getContext('2d');
            }
            obsESPDrawHook = setInterval(() => {
                obsESPContext.clearRect(0,0,obsESPOverlay.width,obsESPOverlay.height);
                for (const o of r.horizon.obstacles) {
                    obsESPContext.strokeStyle = 'blue';
                    obsESPContext.lineWidth = 2;
                    obsESPContext.strokeRect(o.xPos, o.yPos, o.width, o.typeConfig.height);
                }
            }, 16);
        } else {
            clearInterval(obsESPDrawHook);
            obsESPOverlay?.remove();
            obsESPOverlay = null;
        }
    }

    // --- Obstacle Tracers: red lines from dino center to obstacles ---
    function toggleObsTracers(enabled) {
        const r = Runner.instance_;
        const base = r?.canvas;
        if (!base) return;

        if (enabled) {
            if (!obsTracerOverlay) {
                obsTracerOverlay = document.createElement('canvas');
                obsTracerOverlay.width = base.width;
                obsTracerOverlay.height = base.height;
                obsTracerOverlay.style.cssText = `
                    position:absolute;
                    left:${base.offsetLeft}px;
                    top:${base.offsetTop}px;
                    pointer-events:none;
                    z-index:9998;
                `;
                base.parentNode.appendChild(obsTracerOverlay);
                obsTracerContext = obsTracerOverlay.getContext('2d');
            }

            obsTracerInterval = setInterval(() => {
                obsTracerContext.clearRect(0, 0, obsTracerOverlay.width, obsTracerOverlay.height);

                const dino = r.tRex;
                if (!dino) return;

                const dinoCenterX = dino.xPos + dino.config.WIDTH / 2;
                const dinoCenterY = dino.yPos + dino.config.HEIGHT / 2;

                for (const o of r.horizon.obstacles) {
                    const obstacleCenterX = o.xPos + o.width / 2;
                    const obstacleCenterY = o.yPos + o.typeConfig.height / 2;

                    obsTracerContext.beginPath();
                    obsTracerContext.moveTo(dinoCenterX, dinoCenterY);
                    obsTracerContext.lineTo(obstacleCenterX, obstacleCenterY);
                    obsTracerContext.strokeStyle = 'red';
                    obsTracerContext.lineWidth = 2;
                    obsTracerContext.stroke();
                }
            }, 16);
        } else {
            clearInterval(obsTracerInterval);
            if (obsTracerOverlay) {
                obsTracerOverlay.remove();
                obsTracerOverlay = null;
                obsTracerContext = null;
            }
        }
    }
}    // --- Auto Play logic with duck lock and early cactus jump ---
    function toggleAutoPlay(enabled) {
        const r = Runner.instance_;
        if (!r) return;
        autoPlayEnabled = enabled;

        let duckLockUntil = 0; // Timestamp until which the dino should stay ducked

        if (enabled) {
            autoPlayInterval = setInterval(() => {
                const t = r.tRex;
                const obstacles = r.horizon.obstacles;
                if (!t || !obstacles) return;

                const now = Date.now();
                const upcomingObstacles = obstacles.filter(o => o.xPos > t.xPos);
                let shouldDuck = false;

                for (const obs of upcomingObstacles) {
                    const dist = obs.xPos - t.xPos;

                    // Handle pterodactyls
                    if (obs.typeConfig.type === 'PTERODACTYL') {
                        const isLow = obs.yPos > t.yPos;

                        if (dist < 100) {
                            if (isLow && !t.jumping && !t.ducking) {
                                // Jump over low pterodactyl
                                document.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 38 }));
                                document.dispatchEvent(new KeyboardEvent('keyup', { keyCode: 38 }));
                                break;
                            } else if (!isLow) {
                                // Duck under high pterodactyl
                                shouldDuck = true;
                                duckLockUntil = now + 400; // Stay ducked for 400ms
                                break;
                            }
                        }
                    } else {
                        // Handle cactus or ground obstacle
                        if (dist < 100 && !t.jumping && !t.ducking) {
                            document.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 38 })); // jump
                            document.dispatchEvent(new KeyboardEvent('keyup', { keyCode: 38 }));
                            break;
                        }
                    }
                }

                // Apply duck or un-duck with duck lock
                if ((shouldDuck || now < duckLockUntil) && !t.ducking) {
                    document.dispatchEvent(new KeyboardEvent('keydown', { keyCode: 40 })); // duck
                } else if (!shouldDuck && now >= duckLockUntil && t.ducking) {
                    document.dispatchEvent(new KeyboardEvent('keyup', { keyCode: 40 })); // un-duck
                }

            }, 20);
        } else {
            clearInterval(autoPlayInterval);
            document.dispatchEvent(new KeyboardEvent('keyup', { keyCode: 40 })); // stop ducking
        }
    }

    // --- Teleport Past Obstacles feature ---
    function toggleTeleportPastObstacles(enabled) {
        const runner = Runner.instance_;
        if (!runner) return;

        const dino = runner.tRex;
        const triggerDistance = 80; // pixels ahead of dino

        function teleportPastClosestObstacle() {
            if (!runner.horizon || !runner.horizon.obstacles.length) return false;

            const obstacles = runner.horizon.obstacles;
            const dinoX = dino.xPos;

            let closestObstacle = null;
            let minDistance = Infinity;

            for (const obs of obstacles) {
                const dist = obs.xPos - dinoX;
                if (dist > 0 && dist < triggerDistance && dist < minDistance) {
                    closestObstacle = obs;
                    minDistance = dist;
                }
            }

            if (!closestObstacle) return false;

            // Teleport so dino is just past obstacle + buffer
            const shiftAmount = minDistance + closestObstacle.width + 10;

            obstacles.forEach(o => {
                o.xPos -= shiftAmount;
            });

            if (runner.horizon.clouds && runner.horizon.clouds.length) {
                runner.horizon.clouds.forEach(cloud => {
                    cloud.xPos -= shiftAmount;
                });
            }

            // Reset dino state to running, cancel jump/duck if any
            if (dino.jumping || dino.ducking) {
                dino.jumping = false;
                dino.ducking = false;
                dino.update(0, 'RUNNING');
            }

            return true;
        }

        function teleportMultipleObstacles() {
            let teleported;
            do {
                teleported = teleportPastClosestObstacle();
            } while (teleported);
        }

        if (enabled) {
            teleportInterval = setInterval(() => {
                teleportMultipleObstacles();
            }, 20);
        } else {
            clearInterval(teleportInterval);
        }
    }

    // --- Air Jump (Auto Jump on T toggle) ---
    function toggleAirJump(enabled) {
        const runner = Runner.instance_;
        if (!runner || !runner.tRex) return;

        airJumping = enabled;

        if (airJumping) {
            airJumpInterval = setInterval(() => {
                // Reset jumping state to allow continuous jumping
                runner.tRex.jumping = false;
                runner.tRex.startJump(runner.currentSpeed);
            }, 300); // Adjust interval for speed of auto jump
        } else {
            clearInterval(airJumpInterval);
            airJumpInterval = null;
        }
    }

    // Also listen for 'T' key to toggle Air Jump (independent of mod menu checkbox)
    document.addEventListener('keydown', function (e) {
        if (e.key.toLowerCase() === 't') {
            toggleAirJump(!airJumping);
            // Update checkbox state in menu if visible
            const menu = document.getElementById('modMenu');
            if (menu) {
                const labels = menu.getElementsByTagName('label');
                for (const label of labels) {
                    if (label.textContent.trim() === 'Air Jump (Auto Jump on T)') {
                        const cb = label.querySelector('input[type="checkbox"]');
                        if (cb) cb.checked = airJumping;
                        break;
                    }
                }
            }
        }
    });

    // --- Auto Restart & Hats initialization ---
    const waitForRunner = setInterval(() => {
        if (Runner.instance_) {
            clearInterval(waitForRunner);
            const r = Runner.instance_;
            const canvas = document.querySelector('canvas');

            // Auto-restart
            setInterval(() => {
                if (r.crashed) {
                    canvas.focus();
                    ['keydown','keyup'].forEach(type => {
                        document.dispatchEvent(new KeyboardEvent(type,{
                            key: ' ',
                            code: 'Space',
                            keyCode: 32,
                            which: 32,
                            bubbles: true
                        }));
                    });
                }
            }, 100);

            // Hats
            const hats = ['🎩','👑','⛑️','🧢','🎓'];
            let idx = 0, hatEl, t=0;
            function createHat() {
                hatEl = document.createElement('div');
                hatEl.style.cssText = 'position:absolute;font-size:22px;z-index:1000;pointer-events:none;';
                document.body.appendChild(hatEl);
            }
            function updateHat() {
                const d = r.tRex;
                if (!d) return;
                const rect = r.canvas.getBoundingClientRect();
                const x = rect.left + d.xPos + 22;
                const y = rect.top + d.yPos - 10;
                const angle = Math.sin(t)*20;
                t += 0.1;
                hatEl.style.left = `${x}px`;
                hatEl.style.top  = `${y}px`;
                hatEl.style.transform = `translate(-50%,-50%) rotate(${angle}deg)`;
            }
            function toggleHat() {
                idx = (idx+1) % hats.length;
                hatEl.textContent = hats[idx];
            }
            createHat();
            document.addEventListener('keydown', e => {
                if (e.key.toLowerCase()==='h') toggleHat();
            });
            setInterval(updateHat, 30);
        }
    }, 100);

    // --- Canvas Visibility Check & Redirect ---
    const CHECK_INTERVAL = 1000;
    const HIDDEN_THRESHOLD = 3; // Number of checks canvas must be missing/hidden before redirect

    let hiddenCount = 0;

    function isCanvasVisible(canvas) {
        if (!canvas) return false;
        const style = window.getComputedStyle(canvas);
        if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') return false;
        if (canvas.width === 0 || canvas.height === 0) return false;
        return true;
    }

    const interval = setInterval(() => {
        const canvas = document.querySelector('canvas');

        if (!isCanvasVisible(canvas)) {
            hiddenCount++;
            if (hiddenCount >= HIDDEN_THRESHOLD) {
                clearInterval(interval);
                console.log('[Dino Redirect] Canvas missing or hidden for multiple checks. Redirecting...');
                window.location.href = 'https://dino-chrome.com/';
            }
        } else {
            hiddenCount = 0; // reset if canvas visible again
        }
    }, CHECK_INTERVAL);

    // --- Show Mod Menu ---
    const checker = setInterval(() => {
        if (Runner.instance_?.tRex) {
            clearInterval(checker);
            createModMenu();
        }
    }, 500);

})();