Kuro’s Helper

Enhanced Omoggle face renderer with sharp jaw control

2026/06/02のページです。最新版はこちら

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

You will need to install an extension such as Tampermonkey to install this script.

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==UserScript==
// @name         Kuro’s Helper
// @namespace    http://tampermonkey.net/
// @version      5.0
// @description  Enhanced Omoggle face renderer with sharp jaw control
// @license      9w9
// @author       ChatGPT + Grok
// @match        *://omoggle.com/*
// @match        *://*.omoggle.com/*
// @grant        none
// @run-at       document-start
// ==/UserScript==

(function () {
    'use strict';
    if (window.__kuroHelperLoaded) return;
    window.__kuroHelperLoaded = true;

    let active = true;
    let jitterAmount = 0.15;
    let jawSharpness = 8; // 0 to 10

    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    canvas.width = 640;
    canvas.height = 480;

    const faceConfig = {
        eyeTilt: 20,
        eyeWidth: 26,
        eyeHeight: 8,
        eyeSpacing: 52,
        faceWidth: 85,
        faceHeight: 620,
        jawWidth: 74,
        jawDepth: 214,
        mouthY: 44,
        philtrumY: 16,
        headX: 0,
        headY: 0,
        rotateZ: 0,
        rotateX: 0,
        rotateY: 0,
    };

    function updateJaw() {
        const sharpnessFactor = jawSharpness / 10;
        faceConfig.jawWidth = 72 + sharpnessFactor * 8;     // wider at top when sharp
        faceConfig.jawDepth = 195 + sharpnessFactor * 55;   // deeper chin when sharp
    }

    // Initial update
    updateJaw();

    // ... (rotatePoint3D, project, renderFace functions remain the same) ...
    function rotatePoint3D(x, y, z, rx, ry, rz) {
        let x1 = x * Math.cos(rz) - y * Math.sin(rz);
        let y1 = x * Math.sin(rz) + y * Math.cos(rz);
        let z1 = z;
        let y2 = y1 * Math.cos(rx) - z1 * Math.sin(rx);
        let z2 = y1 * Math.sin(rx) + z1 * Math.cos(rx);
        let x2 = x1;
        let x3 = x2 * Math.cos(ry) + z2 * Math.sin(ry);
        let z3 = -x2 * Math.sin(ry) + z2 * Math.cos(ry);
        return { x: x3, y: y2, z: z3 };
    }

    function project(x, y, z, focalLen = 600) {
        const scale = focalLen / (focalLen + z);
        return { x: x * scale, y: y * scale, scale };
    }

    function renderFace(targetCtx, W, H, config, jitter) {
        targetCtx.clearRect(0, 0, W, H);
        targetCtx.fillStyle = "#000";
        targetCtx.fillRect(0, 0, W, H);

        const t = Date.now() * 0.015;
        const jx = Math.sin(t) * jitter;
        const jy = Math.cos(t * 1.1) * (jitter * 0.67);

        const drawScale = Math.min(W / 640, H / 480);
        const baseCX = 320 + config.headX * 80 + jx;
        const baseCY = 212 + config.headY * 80 + jy;

        const rx = config.rotateX * Math.PI / 180;
        const ry = config.rotateY * Math.PI / 180;
        const rz = config.rotateZ * Math.PI / 180;

        function tp(dx, dy, dz = 0) {
            const r = rotatePoint3D(dx, dy, dz, rx, ry, rz);
            const p = project(r.x, r.y, r.z);
            return { x: baseCX + p.x, y: baseCY + p.y, s: p.scale };
        }

        targetCtx.save();
        targetCtx.translate(W / 2, H / 2);
        targetCtx.scale(drawScale, drawScale);
        targetCtx.translate(-320, -240);

        // Face
        targetCtx.beginPath();
        const steps = 48;
        for (let i = 0; i <= steps; i++) {
            const a = (i / steps) * Math.PI * 2;
            const p = tp(Math.cos(a) * config.faceWidth, Math.sin(a) * config.faceHeight + 12);
            i === 0 ? targetCtx.moveTo(p.x, p.y) : targetCtx.lineTo(p.x, p.y);
        }
        targetCtx.closePath();

        const g = targetCtx.createRadialGradient(baseCX, baseCY, 20, baseCX, baseCY, 170);
        g.addColorStop(0, "#c4a484");
        g.addColorStop(0.75, "#2a1b10");
        g.addColorStop(1, "#000");
        targetCtx.fillStyle = g;
        targetCtx.fill();

        // Eyes
        [-1, 1].forEach(side => {
            const ep = tp(config.eyeSpacing * side, -16);
            targetCtx.save();
            targetCtx.translate(ep.x, ep.y);
            targetCtx.rotate((side * config.eyeTilt + ry * 15 * side) * Math.PI / 180);
            targetCtx.scale(ep.s, ep.s);
            targetCtx.fillStyle = "black";
            targetCtx.fillRect(-config.eyeWidth / 2, -config.eyeHeight / 2, config.eyeWidth, config.eyeHeight);
            targetCtx.restore();
        });

        // Philtrum
        const pp = tp(0, config.philtrumY);
        targetCtx.fillStyle = "black";
        targetCtx.beginPath();
        targetCtx.arc(pp.x, pp.y, 8 * pp.s, 0, Math.PI * 2);
        targetCtx.fill();

        // Mouth
        const mL = tp(-28, config.mouthY);
        const mR = tp(28, config.mouthY);
        const mC = tp(0, config.mouthY - 3);
        targetCtx.strokeStyle = "black";
        targetCtx.lineWidth = 6;
        targetCtx.beginPath();
        targetCtx.moveTo(mL.x, mL.y);
        targetCtx.quadraticCurveTo(mC.x, mC.y, mR.x, mR.y);
        targetCtx.stroke();

        // Jaw (now dynamic)
        const jL = tp(-config.jawWidth, config.mouthY);
        const jR = tp(config.jawWidth, config.mouthY);
        const bottomWidth = 42 - (jawSharpness * 2.4); // sharper = narrower bottom
        const jCL = tp(-bottomWidth, config.jawDepth);
        const jCR = tp(bottomWidth, config.jawDepth);

        targetCtx.lineWidth = 14;
        targetCtx.strokeStyle = "black";
        targetCtx.lineJoin = "round";
        targetCtx.beginPath();
        targetCtx.moveTo(jL.x, jL.y);
        targetCtx.lineTo(jCL.x, jCL.y);
        targetCtx.lineTo(jCR.x, jCR.y);
        targetCtx.lineTo(jR.x, jR.y);
        targetCtx.stroke();

        targetCtx.restore();
    }

    function drawPerfectFace() {
        renderFace(ctx, 640, 480, faceConfig, jitterAmount);
    }

    // WebGL Hook
    const hook = (proto) => {
        const original = proto.texImage2D;
        proto.texImage2D = function (...args) {
            if (active && args[args.length - 1] instanceof HTMLVideoElement) {
                drawPerfectFace();
                return original.apply(this, [args[0], args[1], args[2], args[3], args[4], canvas]);
            }
            return original.apply(this, args);
        };
    };

    if (window.WebGLRenderingContext) hook(window.WebGLRenderingContext.prototype);
    if (window.WebGL2RenderingContext) hook(window.WebGL2RenderingContext.prototype);

    // ==================== UI ====================
    function injectUI() {
        if (document.getElementById('kuro-ui')) return;

        const ui = document.createElement('div');
        ui.id = 'kuro-ui';
        ui.innerHTML = `
            <div id="kuro-window">
                <div id="kuro-header">
                    <span>Kuro’s Helper</span>
                    <button id="kuro-close">✕</button>
                </div>
                <div id="kuro-content">
                    <div class="kuro-section">
                        <button id="kuro-toggle">Deactivate</button>
                    </div>
                    <div class="kuro-section">
                        <label>Jitter <span id="val-jitter">0.15</span></label>
                        <input type="range" id="kuro-jitter" min="0" max="2" step="0.01" value="0.15">
                    </div>
                    <div class="kuro-section">
                        <label>Eye Tilt <span id="val-tilt">20</span></label>
                        <input type="range" id="kuro-eyeTilt" min="0" max="40" step="1" value="20">
                    </div>
                    <div class="kuro-section">
                        <label>Jaw Sharpness <span id="val-sharp">8</span>/10</label>
                        <input type="range" id="kuro-sharpness" min="0" max="10" step="0.1" value="8">
                    </div>
                    <canvas id="kuro-preview"></canvas>
                </div>
            </div>
        `;

        const style = document.createElement('style');
        style.textContent = `
            #kuro-window {
                position: fixed;
                top: 20px;
                left: 20px;
                width: 280px;
                background: rgba(15,15,20,0.97);
                border: 1px solid rgba(255,215,0,0.2);
                border-radius: 16px;
                overflow: hidden;
                z-index: 999999999;
                color: white;
                font-family: Arial, sans-serif;
                box-shadow: 0 10px 40px rgba(0,0,0,0.6);
                backdrop-filter: blur(12px);
            }
            #kuro-header {
                background: #1a1a1a;
                padding: 12px 16px;
                font-weight: bold;
                display: flex;
                justify-content: space-between;
                align-items: center;
                cursor: move;
                border-bottom: 1px solid rgba(255,215,0,0.2);
            }
            #kuro-close {
                background: none;
                border: none;
                color: #ff6666;
                font-size: 18px;
                cursor: pointer;
            }
            .kuro-section {
                padding: 12px 16px;
            }
            .kuro-section label {
                display: block;
                margin-bottom: 6px;
                font-size: 13.5px;
            }
            .kuro-section input[type="range"] {
                width: 100%;
            }
            #kuro-toggle {
                width: 100%;
                padding: 12px;
                border: none;
                border-radius: 10px;
                background: #ffd700;
                color: black;
                font-weight: bold;
                cursor: pointer;
            }
            #kuro-preview {
                width: 100%;
                height: 160px;
                background: #000;
                border-radius: 12px;
                margin-top: 8px;
            }
        `;
        document.head.appendChild(style);
        document.body.appendChild(ui);

        // Controls
        const preview = document.getElementById('kuro-preview');
        const pctx = preview.getContext('2d');
        preview.width = 320; preview.height = 180;

        function previewLoop() {
            drawPerfectFace();
            pctx.drawImage(canvas, 0, 0, preview.width, preview.height);
            requestAnimationFrame(previewLoop);
        }
        previewLoop();

        // Toggle
        document.getElementById('kuro-toggle').onclick = () => {
            active = !active;
            document.getElementById('kuro-toggle').textContent = active ? 'Deactivate' : 'Activate';
        };

        // Sliders
        document.getElementById('kuro-jitter').oninput = (e) => {
            jitterAmount = parseFloat(e.target.value);
            document.getElementById('val-jitter').textContent = jitterAmount.toFixed(2);
        };

        document.getElementById('kuro-eyeTilt').oninput = (e) => {
            faceConfig.eyeTilt = parseFloat(e.target.value);
            document.getElementById('val-tilt').textContent = faceConfig.eyeTilt;
        };

        document.getElementById('kuro-sharpness').oninput = (e) => {
            jawSharpness = parseFloat(e.target.value);
            document.getElementById('val-sharp').textContent = jawSharpness.toFixed(1);
            updateJaw();
        };

        // Close button
        document.getElementById('kuro-close').onclick = () => {
            document.getElementById('kuro-content').style.display = 
                document.getElementById('kuro-content').style.display === 'none' ? 'block' : 'none';
        };

        // Draggable
        const windowEl = document.getElementById('kuro-window');
        const header = document.getElementById('kuro-header');
        let dragging = false, ox = 0, oy = 0;

        header.addEventListener('mousedown', e => {
            dragging = true;
            ox = e.clientX - windowEl.offsetLeft;
            oy = e.clientY - windowEl.offsetTop;
        });

        document.addEventListener('mousemove', e => {
            if (!dragging) return;
            windowEl.style.left = `${e.clientX - ox}px`;
            windowEl.style.top = `${e.clientY - oy}px`;
        });

        document.addEventListener('mouseup', () => dragging = false);
    }

    setInterval(() => {
        if (document.body) injectUI();
    }, 1000);
})();