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);
})();