DiepShadow

Press CTRL + I to activate. Create cool glow or 3D effects using this tool.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         DiepShadow
// @namespace    https://diep.io
// @version      1.0
// @description  Press CTRL + I to activate. Create cool glow or 3D effects using this tool.
// @author       Binary
// @match        https://diep.io/*
// @run-at       document-end
// ==/UserScript==

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 = window.GM_info ? window.GM_info.script.version : 'error, update tampermonkey';

var presets = [{
    name: 'Default',
    description: 'Resets everything to 0',
    shadowBlur: 0,
    shadowOffsetX: 0,
    shadowOffsetY: 0,
    shadowStrength: 0,
    shadowColor: 'rgb(0,0,0)'
},{
    name: 'Diep.io 3D',
    description: '"Realistic" shadows that make Diep look 3D (not recommended for dark themes).',
    shadowBlur: 10,
    shadowOffsetX: 10,
    shadowOffsetY: 8,
    shadowStrength: 0.8,
    shadowColor: 'rgb(0,0,0)'
},{
    name: 'Underglow theme',
    description: 'Vroom! Vroom! Look at my new custom installed underglow! Increase Shadow Strength to "turn up the brightness."',
    shadowBlur: 10,
    shadowOffsetX: 10,
    shadowOffsetY: 8,
    shadowStrength: 0.8,
    shadowColor: 'rgb(255,255,255)'
},{
    name: 'Pop art theme',
    description: 'Black shadows that imitate the style of pop art.',
    shadowBlur: 0,
    shadowOffsetX: 10,
    shadowOffsetY: 8,
    shadowStrength: 1,
    shadowColor: 'rgb(0,0,0)'
},{
    name: 'Retro vibes',
    description: 'Purple + pop art = retro vibe. Demo of custom shadow colors. ' + 
    'Even more pog would be combining this with Diep.Style\'s 80s theme.',
    shadowBlur: 0,
    shadowOffsetX: 10,
    shadowOffsetY: 8,
    shadowStrength: 1,
    shadowColor: 'rgb(101,33,186)'
},{
    name: 'Pop art underglow theme',
    description: 'Super cool when combined with Diep.Style\'s dark theme.',
    shadowBlur: 0,
    shadowOffsetX: 10,
    shadowOffsetY: 8,
    shadowStrength: 1,
    shadowColor: 'rgb(255,255,255)'
},{
    name: 'Haze theme',
    description: 'Hazy shadows that look like fog. ' + 
    'Won\'t have much effect on Diep.Style dark themes.',
    shadowBlur: 25,
    shadowOffsetX: 0,
    shadowOffsetY: 0,
    shadowStrength: 0.75,
    shadowColor: 'rgb(0,0,0)'
},{
    name: 'White glow theme',
    description: 'This is best used with Diep.Style\'s dark themes.',
    shadowBlur: 25,
    shadowOffsetX: 0,
    shadowOffsetY: 0,
    shadowStrength: 0.75,
    shadowColor: 'rgb(255,255,255)'
}];

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', 0),
    shadowOffsetY: numberOrDefault('shadowOffsetY', 0),
    shadowStrength: numberOrDefault('shadowStrength', 0),
    shadowColor: typeof storageSettings.shadowColor === 'string' ? storageSettings.shadowColor : 'rgb(0,0,0)'
};

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';

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;
        updateContext();
        saveSettings(true);
    });
};
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'; // reset Diep.Style's global style
    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);

    // addonchange useless, don't know why I implemented it. I guess this is for
    // ease of use in case future updates
    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;
        updateContext();
        saveSettings();
    });
};
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);

    // addonchange useless, don't know why I implemented it. I guess this is for
    // ease of use in case future updates
    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;
        updateContext();
        saveSettings();
    });
};
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'; // color of default diep map
    demo_lighttheme.style.webkitTextStrokeColor = '#0084a6'; // color of diep tank outline
    demo_lighttheme.style.color = '#00b1de';  // color of diep tank body
    
    applyThemeToDemo(demo_darktheme);
    demo_darktheme.textContent = '\u2B24';//#00bbfd
    demo_darktheme.style.backgroundColor = 'black';
    demo_darktheme.style.webkitTextStrokeColor = '#0084a6'; // color of Diep.Style dark theme's diep tank outline
    demo_darktheme.style.color = 'black';  // color of Diep.Style dark theme's diep tank body
    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));
        updateContext();
        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)'; // drop shadow on the title just because. :^)
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);

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



var ctx = document.getElementById('canvas').getContext('2d');
// windowresize erases these settings, so make sure to apply them on every
// window resize event
function updateContext() {
    if (!settings.enableShadow) {
        ctx.shadowBlur = 0;
        ctx.shadowColor = 'rgba(0,0,0,0)';
        return;
    }
    ctx.shadowBlur = settings.shadowBlur;
    ctx.shadowColor = settings.shadowColor
        .replace('rgb(', 'rgba(')
        .replace(')', ',' + settings.shadowStrength + ')');
    ctx.shadowOffsetX = settings.shadowOffsetX;
    ctx.shadowOffsetY = settings.shadowOffsetY;
}

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

window.addEventListener('resize', updateContext);
updateContext();



// misc functions

// credit to https://stackoverflow.com/a/5624139/6850723
function hexToRgb(hex) {
    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    // return result ? {
    //     r: parseInt(result[1], 16),
    //     g: parseInt(result[2], 16),
    //     b: parseInt(result[3], 16)
    // } : null;
    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) {
    // return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
    var rgbsplit = rgb.split('rgb(')[1].replace(')', '').split(',');
    return "#" + componentToHex(rgbsplit[0]) + componentToHex(rgbsplit[1]) + componentToHex(rgbsplit[2]);
}