DiepShadow (Fixed)

Press CTRL + I to activate. Create cool glow or 3D effects using this tool. Fixed for modern diep.io

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         DiepShadow (Fixed)
// @namespace    *://diep.io/*
// @version      1.4
// @description  Press CTRL + I to activate. Create cool glow or 3D effects using this tool. Fixed for modern diep.io
// @author       Binary (maintained and fixed by razor)
// @match        *://diep.io/*
// @match        *://localhost:8080/*
// @run-at       document-start
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    var hotkey_activate_sequence = function(event) { // CTRL + I
        if (event.ctrlKey && !event.altKey && !event.shiftKey && event.code === 'KeyI' && !event.repeat) {
            event.preventDefault();
            return true;
        }
    };

    var localStorage_key = 'diepshadow_preferences';
    var version = '1.4';

    var presets = [{
        name: 'funny 1',
        description: 'yeah',
        shadowBlur: 0,
        shadowOffsetX: 8,
        shadowOffsetY: 8,
        shadowStrength: 1,
        shadowColor: 'rgb(35,14,53)'
    },{
        name: 'funny 2',
        description: 'yeah',
        shadowBlur: 0,
        shadowOffsetX: 8,
        shadowOffsetY: 8,
        shadowStrength: 1,
        shadowColor: 'rgb(24,1,55)'
    }];

    var storageSettings;
    try{
        storageSettings = JSON.parse(window.localStorage.getItem(localStorage_key));
    }catch(e){storageSettings = {}}
    if(!(storageSettings instanceof Object)) storageSettings = {};
    var booleanOrDefault = function(key, defaultValue){
        if(typeof storageSettings[key] === 'boolean') return storageSettings[key];
        return defaultValue;
    };
    var numberOrDefault = function(key, defaultValue){
        if(typeof storageSettings[key] === 'number' && !isNaN(storageSettings[key])) return storageSettings[key];
        return defaultValue;
    };
    var settings = {
        enableShadow: booleanOrDefault('enableShadow', true),
        enableSaving: booleanOrDefault('enableSaving', true),
        shadowBlur: numberOrDefault('shadowBlur', 0),
        shadowOffsetX: numberOrDefault('shadowOffsetX', 8),
        shadowOffsetY: numberOrDefault('shadowOffsetY', 8),
        shadowStrength: numberOrDefault('shadowStrength', 1),
        shadowColor: typeof storageSettings.shadowColor === 'string' ? storageSettings.shadowColor : 'rgb(24,1,55)'
    };

    // ==== INTERCEPT CANVAS CONTEXT BEFORE PAGE LOADS ====

    var OriginalGetContext = HTMLCanvasElement.prototype.getContext;
    var OriginalFill = CanvasRenderingContext2D.prototype.fill;
    var OriginalStroke = CanvasRenderingContext2D.prototype.stroke;
    var OriginalFillRect = CanvasRenderingContext2D.prototype.fillRect;
    var OriginalFillText = CanvasRenderingContext2D.prototype.fillText;

    var trackedContexts = new WeakMap();

    function getShadowSettings() {
        if (!settings.enableShadow) {
            return {
                shadowBlur: 0,
                shadowColor: 'rgba(0,0,0,0)',
                shadowOffsetX: 0,
                shadowOffsetY: 0
            };
        }
        return {
            shadowBlur: settings.shadowBlur,
            shadowColor: settings.shadowColor
                .replace('rgb(', 'rgba(')
                .replace(')', ',' + settings.shadowStrength + ')'),
            shadowOffsetX: settings.shadowOffsetX,
            shadowOffsetY: settings.shadowOffsetY
        };
    }

    function applyShadow(ctx) {
        var shadow = getShadowSettings();
        ctx.shadowBlur = shadow.shadowBlur;
        ctx.shadowColor = shadow.shadowColor;
        ctx.shadowOffsetX = shadow.shadowOffsetX;
        ctx.shadowOffsetY = shadow.shadowOffsetY;
    }

    function removeShadow(ctx) {
        ctx.shadowBlur = 0;
        ctx.shadowColor = 'rgba(0,0,0,0)';
        ctx.shadowOffsetX = 0;
        ctx.shadowOffsetY = 0;
    }

    // Intercept getContext
    HTMLCanvasElement.prototype.getContext = function() {
        var ctx = OriginalGetContext.apply(this, arguments);

        if (ctx && arguments[0] === '2d' && !trackedContexts.has(ctx)) {
            trackedContexts.set(ctx, true);
            console.log('DiepShadow: New 2D context detected');
        }

        return ctx;
    };

    // Only apply shadow to fill operations (not strokes)
    CanvasRenderingContext2D.prototype.fill = function() {
        applyShadow(this);
        var result = OriginalFill.apply(this, arguments);
        removeShadow(this);
        return result;
    };

    CanvasRenderingContext2D.prototype.fillRect = function() {
        applyShadow(this);
        var result = OriginalFillRect.apply(this, arguments);
        removeShadow(this);
        return result;
    };

    CanvasRenderingContext2D.prototype.fillText = function() {
        applyShadow(this);
        var result = OriginalFillText.apply(this, arguments);
        removeShadow(this);
        return result;
    };

    // Make sure stroke operations DON'T have shadow
    CanvasRenderingContext2D.prototype.stroke = function() {
        removeShadow(this);
        return OriginalStroke.apply(this, arguments);
    };

    console.log('DiepShadow: Canvas hooks installed at document-start');

    // ==== UI SETUP (runs when DOM is ready) ====

    function initUI() {
        var wrapper = document.createElement('div');
        wrapper.style.position = 'fixed';
        wrapper.style.backgroundColor = '#a3bfce';
        wrapper.style.padding = '10px';
        wrapper.style.top = '0px';
        wrapper.style.right = '0px';
        wrapper.style.bottom = '0px';
        wrapper.style.overflowY = 'auto';
        wrapper.style.overflowX = 'hidden';
        wrapper.style.fontFamily = 'Ubuntu';
        wrapper.style.display = 'none';
        wrapper.style.zIndex = '999999';
        wrapper.style.width = '460px';

        var checkbox_inputs = {};
        var addCheckboxInput = function(displayText, name) {
            checkbox_inputs[name] = document.createElement('input');
            var enableShadowLabel = document.createElement('label');
            var enableShadowText = document.createTextNode(displayText);
            checkbox_inputs[name].type = 'checkbox';
            checkbox_inputs[name].checked = settings[name];
            enableShadowLabel.style.display = 'block';
            enableShadowLabel.style.width = 'fit-content';
            enableShadowLabel.appendChild(checkbox_inputs[name]);
            enableShadowLabel.appendChild(enableShadowText);
            wrapper.appendChild(enableShadowLabel);

            checkbox_inputs[name].addEventListener('change', function() {
                settings[name] = checkbox_inputs[name].checked;
                saveSettings(true);
                console.log('DiepShadow: Setting changed -', name, '=', settings[name]);
            });
        };
        var sliders = {};
        var addSliderInput = function(displayText, name, min, max, step) {
            var slider = document.createElement('input');
            slider.type = 'range';
            slider.style.verticalAlign = 'middle';
            slider.style.width = '250px';
            slider.style.transform = 'none';
            slider.min = min;
            slider.max = max;
            slider.step = step;
            var label = document.createElement('label');
            var displayTextSpan = document.createElement('span');
            var displayValueSpan = document.createElement('span');
            label.style.display = 'block';
            label.style.width = 'fit-content';

            displayTextSpan.style.width = '140px';
            displayTextSpan.style.display = 'inline-block';
            displayTextSpan.textContent = displayText;

            displayValueSpan.style.width = '30px';
            displayValueSpan.style.display = 'inline-block';
            displayValueSpan.style.textAlign = 'center';

            label.appendChild(displayTextSpan);
            label.appendChild(displayValueSpan);
            label.appendChild(slider);
            wrapper.appendChild(label);

            sliders[name] = {
                addonchange: function(callback) {
                    var listener = function() {
                        displayValueSpan.textContent = slider.value;
                        callback(parseFloat(slider.value));
                    };
                    slider.addEventListener('change', listener);
                    slider.addEventListener('input', listener);
                },
                setValue: function(newValue) {
                    slider.value = newValue;
                    displayValueSpan.textContent = newValue;
                    slider.dispatchEvent(new window.Event('input', { bubbles: true }));
                }
            };
            sliders[name].setValue(settings[name]);
            sliders[name].addonchange(function(newValue) {
                settings[name] = newValue;
                saveSettings();
                console.log('DiepShadow: Setting changed -', name, '=', newValue);
            });
        };
        var colors = {};
        var addColorInput = function(displayText, name) {
            var colorInput = document.createElement('input');
            colorInput.type = 'color';
            colorInput.style.verticalAlign = 'middle';
            var label = document.createElement('label');
            var displayTextSpan = document.createElement('span');
            label.style.display = 'block';
            label.style.width = 'fit-content';

            displayTextSpan.style.width = '170px';
            displayTextSpan.style.display = 'inline-block';
            displayTextSpan.textContent = displayText;

            label.appendChild(displayTextSpan);
            label.appendChild(colorInput);
            wrapper.appendChild(label);

            colors[name] = {
                addonchange: function(callback) {
                    var listener = function() {
                        callback(hexToRgb(colorInput.value));
                    };
                    colorInput.addEventListener('change', listener);
                    colorInput.addEventListener('input', listener);
                },
                setValue: function(newValue) {
                    colorInput.value = newValue;
                    colorInput.dispatchEvent(new window.Event('input', { bubbles: true }));
                }
            };
            colors[name].setValue(rgbToHex(settings[name]));
            colors[name].addonchange(function(newValue) {
                settings[name] = newValue;
                saveSettings();
                console.log('DiepShadow: Color changed -', name, '=', newValue);
            });
        };
        var addPreset = function(eachPreset){
            var presetWrap = document.createElement('div');
            var name = document.createElement('p');
            var description = document.createElement('p');
            var demo_lighttheme = document.createElement('p');
            var demo_darktheme = document.createElement('p');
            var btn = document.createElement('p');

            presetWrap.style.marginTop = '20px';
            presetWrap.style.borderTop = '2px solid black';

            name.textContent = 'Preset name: ' + eachPreset.name;
            name.style.width = '440px';
            name.style.margin = '5px 0px';

            description.textContent = 'Preset description: ' + eachPreset.description;
            description.style.width = '440px';
            description.style.margin = '5px 0px';

            var applyThemeToDemo = function(element){
                element.style.textShadow =
                    eachPreset.shadowOffsetX + 'px ' +
                    eachPreset.shadowOffsetY + 'px ' +
                    eachPreset.shadowBlur + 'px ' +
                    eachPreset.shadowColor.replace('rgb(', 'rgba(').replace(')', ',' + eachPreset.shadowStrength + ')');
                element.style.padding = '0px 20px 5px 7px';
                element.style.display = 'inline-block';
                element.style.borderRadius = '8px';
                element.style.margin = '0px';
                element.style.fontSize = '40px';
                element.style.webkitTextStrokeWidth = '3px';
            };

            applyThemeToDemo(demo_lighttheme);
            demo_lighttheme.textContent = '\u2B24';
            demo_lighttheme.style.backgroundColor = '#cdcdcd';
            demo_lighttheme.style.webkitTextStrokeColor = '#0084a6';
            demo_lighttheme.style.color = '#00b1de';

            applyThemeToDemo(demo_darktheme);
            demo_darktheme.textContent = '\u2B24';
            demo_darktheme.style.backgroundColor = 'black';
            demo_darktheme.style.webkitTextStrokeColor = '#0084a6';
            demo_darktheme.style.color = 'black';
            demo_darktheme.style.marginLeft = '5px';

            btn.style.marginTop = '5px';
            btn.style.width = 'fit-content';
            btn.style.cursor = 'pointer';
            btn.style.color = '#004981';
            btn.textContent = 'Load preset';

            presetWrap.appendChild(name);
            presetWrap.appendChild(description);
            presetWrap.appendChild(demo_lighttheme);
            presetWrap.appendChild(demo_darktheme);
            presetWrap.appendChild(btn);

            wrapper.appendChild(presetWrap);

            btn.addEventListener('click', function() {
                sliders['shadowBlur'].setValue(eachPreset.shadowBlur);
                sliders['shadowOffsetX'].setValue(eachPreset.shadowOffsetX);
                sliders['shadowOffsetY'].setValue(eachPreset.shadowOffsetY);
                sliders['shadowStrength'].setValue(eachPreset.shadowStrength);
                colors['shadowColor'].setValue(rgbToHex(eachPreset.shadowColor));
                saveSettings();
            });
        };
        var addSeparator = function(height, parentElement = wrapper) {
            var separator = document.createElement('div');
            separator.style.height = height + 'px';
            parentElement.appendChild(separator);
        };

        var versionHeader = document.createElement('p');
        versionHeader.style.margin = '0px';
        versionHeader.style.fontSize = '12px';
        versionHeader.style.position = 'absolute';
        versionHeader.style.right = '10px';
        versionHeader.textContent = 'Version: ' + version;
        wrapper.appendChild(versionHeader);

        var hotkeyTip = document.createElement('p');
        hotkeyTip.style.margin = '0px';
        hotkeyTip.style.fontSize = '12px';
        hotkeyTip.style.position = 'absolute';
        hotkeyTip.textContent = 'Press CTRL + i to activate';
        wrapper.appendChild(hotkeyTip);

        var heading = document.createElement('h1');
        heading.textContent = 'DiepShadow';
        heading.style.filter = 'drop-shadow(5px 0px 2px black)';
        heading.style.color = 'white';
        wrapper.appendChild(heading);

        var warning = document.createElement('p');
        warning.style.color = '#bb1e1e';
        warning.appendChild(document.createTextNode('Warning: canvas\'s shadowBlur function is very CPU heavy.'));
        addSeparator(0, warning);
        warning.appendChild(document.createTextNode('Do not use if your computer is a turtle'));
        wrapper.appendChild(warning);

        var statusText = document.createElement('p');
        statusText.style.color = '#00aa00';
        statusText.style.fontWeight = 'bold';
        statusText.textContent = '✓ Shadow on fills only (v4)';
        wrapper.appendChild(statusText);

        addCheckboxInput('Enable DiepShadow', 'enableShadow');
        addCheckboxInput('Enable saving', 'enableSaving');

        addSeparator(16);

        addSliderInput('Shadow Radius: ', 'shadowBlur', 0, 200, 1);
        addSliderInput('Shadow X Offset: ', 'shadowOffsetX', -50, 50, 1);
        addSliderInput('Shadow Y Offset: ', 'shadowOffsetY', -50, 50, 1);
        addSliderInput('Shadow Strength: ', 'shadowStrength', 0, 1, 0.01);
        addColorInput('Shadow Color: ', 'shadowColor');

        var darkDemoNotice = document.createElement('p');
        darkDemoNotice.textContent = 'Note: Dark mode colors are taken from Diep.Style';
        wrapper.appendChild(darkDemoNotice);

        presets.forEach(addPreset);

        document.body.appendChild(wrapper);

        var isDisplaying = false;
        document.addEventListener('keydown', function(event) {
            if (!hotkey_activate_sequence(event)) return;
            if (isDisplaying) {
                isDisplaying = false;
                wrapper.style.display = 'none';
            }
            else {
                isDisplaying = true;
                wrapper.style.display = 'block';
            }
        });

        console.log('DiepShadow: UI initialized');
    }

    function saveSettings(bypass) {
        if (!settings.enableSaving && !bypass) return;
        window.localStorage.setItem(localStorage_key, JSON.stringify(settings));
    }

    // Helper functions
    function hexToRgb(hex) {
        var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
        return `rgb(${parseInt(result[1], 16)},${parseInt(result[2], 16)},${parseInt(result[3], 16)})`;
    }

    function componentToHex(c) {
        var hex = parseInt(c).toString(16);
        return hex.length == 1 ? "0" + hex : hex;
    }

    function rgbToHex(rgb) {
        var rgbsplit = rgb.split('rgb(')[1].replace(')', '').split(',');
        return "#" + componentToHex(rgbsplit[0]) + componentToHex(rgbsplit[1]) + componentToHex(rgbsplit[2]);
    }

    // Initialize UI when DOM is ready
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', initUI);
    } else {
        initUI();
    }

    console.log('DiepShadow v' + version + ' loaded successfully!');
    console.log('Current settings:', settings);
})();