WhatBeatsRock Save Loader

Load saves for whatbeatsrock.com with pause functionality, adjustable wait time, file-based save selection, improved UI with padding, and edge-only dragging with scrollable middle

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         WhatBeatsRock Save Loader
// @namespace    http://violentmonkey.net/
// @version      3.0
// @description  Load saves for whatbeatsrock.com with pause functionality, adjustable wait time, file-based save selection, improved UI with padding, and edge-only dragging with scrollable middle
// @author       JoTheStupid
// @match        https://www.whatbeatsrock.com/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    let paused = false;
    let saveEntries = [];
    let index = 0;
    let isDragging = false;
    let dragStartX, dragStartY, startX, startY;

    // UI Styles
    const buttonStyle = `
        padding: 8px 16px;
        margin-top: 10px;
        background-color: #4CAF50;
        border: 2px solid #333;
        color: white;
        cursor: pointer;
        font-size: 14px;
        border-radius: 5px;
        text-align: center;
    `;
    const pauseButtonStyle = `
        padding: 8px 16px;
        margin-top: 10px;
        background-color: #f44336;
        border: 2px solid #333;
        color: white;
        cursor: pointer;
        font-size: 14px;
        border-radius: 5px;
        text-align: center;
    `;
    const containerStyle = `
        padding: 10px;
        background-color: white;
        border: 2px solid black;
        border-radius: 10px;
        position: fixed;
        top: 10px;
        right: 10px;
        width: 320px;
        height: 500px;
        z-index: 1000;
        resize: both;
        overflow: auto;
        min-width: 200px;
        min-height: 200px;
    `;
    const edgeThreshold = 10; // The distance from the edge in pixels to allow dragging

    // Create a container for input elements
    let container = document.createElement('div');
    container.style.cssText = containerStyle;
    document.body.appendChild(container);

    // Handle dragging the container (only from edges)
    container.addEventListener('mousedown', function (e) {
        const rect = container.getBoundingClientRect();
        const isEdge = e.clientX - rect.left < edgeThreshold ||
                       rect.right - e.clientX < edgeThreshold ||
                       e.clientY - rect.top < edgeThreshold ||
                       rect.bottom - e.clientY < edgeThreshold;

        if (isEdge) {
            isDragging = true;
            dragStartX = e.clientX;
            dragStartY = e.clientY;
            startX = container.offsetLeft;
            startY = container.offsetTop;
            e.preventDefault();
        }
    });

    document.addEventListener('mousemove', function (e) {
        if (isDragging) {
            let offsetX = e.clientX - dragStartX;
            let offsetY = e.clientY - dragStartY;
            container.style.left = startX + offsetX + 'px';
            container.style.top = startY + offsetY + 'px';
        }
    });

    document.addEventListener('mouseup', function () {
        isDragging = false;
    });

    // Create a file input for save file upload
    let fileInput = document.createElement('input');
    fileInput.type = 'file';
    fileInput.accept = '.txt';
    fileInput.style.width = '100%';
    fileInput.style.marginBottom = '10px';
    container.appendChild(fileInput);

    // Create a textbox for input
    let inputBox = document.createElement('textarea');
    inputBox.style.width = '100%';
    inputBox.style.height = '100px';
    inputBox.style.padding = '5px';
    inputBox.style.border = '1px solid #ccc';
    inputBox.style.borderRadius = '5px';
    inputBox.style.marginBottom = '10px';
    container.appendChild(inputBox);

    // Create a button to apply the save
    let enterSaveButton = document.createElement('button');
    enterSaveButton.innerHTML = 'Enter Save';
    enterSaveButton.style.cssText += buttonStyle;
    container.appendChild(enterSaveButton);

    // Create a pause/resume button
    let pauseButton = document.createElement('button');
    pauseButton.innerHTML = 'Pause';
    pauseButton.style.cssText += pauseButtonStyle;
    container.appendChild(pauseButton);

    pauseButton.addEventListener('click', () => {
        paused = !paused;
        pauseButton.innerHTML = paused ? 'Resume' : 'Pause';
        pauseButton.style.backgroundColor = paused ? '#f44336' : '#4CAF50';
        if (!paused) {
            processNextEntry();
        }
    });

    // Create a label for the slider
    let sliderLabel = document.createElement('label');
    sliderLabel.innerHTML = 'Wait Time (ms):';
    container.appendChild(sliderLabel);

    // Create a slider for wait time
    let waitTimeSlider = document.createElement('input');
    waitTimeSlider.type = 'range';
    waitTimeSlider.min = '100';
    waitTimeSlider.max = '3000';
    waitTimeSlider.value = '1000';
    waitTimeSlider.style.width = '100%';
    waitTimeSlider.style.marginBottom = '10px';
    container.appendChild(waitTimeSlider);

    // Display the current wait time
    let waitTimeDisplay = document.createElement('div');
    waitTimeDisplay.innerHTML = `Current wait time: ${waitTimeSlider.value} ms`;
    waitTimeDisplay.style.marginBottom = '10px';
    container.appendChild(waitTimeDisplay);

    // Create a div to show color-coded save options
    let saveListContainer = document.createElement('div');
    saveListContainer.style.width = '100%';
    saveListContainer.style.height = '150px';  // Set height and make it scrollable
    saveListContainer.style.overflowY = 'auto';  // Enable vertical scrolling for the save list
    saveListContainer.style.border = '1px solid #ccc';
    saveListContainer.style.borderRadius = '5px';
    container.appendChild(saveListContainer);

    // Color cycle for the saves
    const colors = ['red', 'orange', 'yellow', 'blue', 'purple'];

    // Handle file reading and populate the saveList with color-coded saves
    fileInput.addEventListener('change', (event) => {
        const file = event.target.files[0];
        if (file) {
            const reader = new FileReader();
            reader.onload = function(e) {
                const contents = e.target.result;
                const saves = contents.split('\n').map(save => save.trim()).filter(save => save !== '');

                // Clear previous saves
                saveListContainer.innerHTML = '';

                // Populate the save list with color-coded clickable divs
                saves.forEach((save, idx) => {
                    let saveItem = document.createElement('div');
                    saveItem.textContent = save;
                    saveItem.style.cursor = 'pointer';
                    saveItem.style.padding = '5px';
                    saveItem.style.border = '1px solid black';
                    saveItem.style.marginBottom = '5px';
                    saveItem.style.backgroundColor = colors[idx % colors.length];  // Alternate colors

                    // Click to insert the save into the input box
                    saveItem.addEventListener('click', () => {
                        inputBox.value = save;
                    });

                    saveListContainer.appendChild(saveItem);
                });

                console.log('Saves loaded:', saves);
            };
            reader.readAsText(file);
        }
    });

    // Update the wait time display as the slider is moved
    waitTimeSlider.addEventListener('input', () => {
        waitTimeDisplay.innerHTML = `Current wait time: ${waitTimeSlider.value} ms`;
    });

    // Function to find the Next button
    function findNextButton() {
        return document.querySelector('button.py-4.px-8.border.border-1-black.text-lg');
    }

    // Function to simulate typing and clicking
    function simulateInput(input, callback) {
        if (paused) return;

        console.log(`Simulating input: ${input}`);
        let inputField = document.querySelector('.pl-4.py-4.text-lg.border.border-1-black');
        let submitButton = document.querySelector('.p-4.border.border-1-black.text-lg.bg-green-200, .p-4.border.border-1-black.text-lg.text-gray-400');

        if (inputField && submitButton) {
            inputField.focus();
            Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set.call(inputField, input);

            let inputEvent = new Event('input', { bubbles: true });
            let changeEvent = new Event('change', { bubbles: true });
            inputField.dispatchEvent(inputEvent);
            inputField.dispatchEvent(changeEvent);

            setTimeout(() => {
                submitButton.click();
                setTimeout(() => {
                    let nextButton = findNextButton();
                    if (nextButton) {
                        nextButton.click();  // Click the next button
                        setTimeout(callback, parseInt(waitTimeSlider.value));
                    } else {
                        console.log('Next button not found, retrying...');
                        setTimeout(() => simulateInput(input, callback), parseInt(waitTimeSlider.value));  // Retry if the next button is not found
                    }
                }, parseInt(waitTimeSlider.value));
            }, parseInt(waitTimeSlider.value));
        } else {
            console.log('Input field or submit button not found, retrying...');
            setTimeout(() => simulateInput(input, callback), parseInt(waitTimeSlider.value));
        }
    }

    // Function to enter the save from the input box and start processing it
    function enterSave() {
        let saveText = inputBox.value.trim();
        if (!saveText) {
            alert('Please enter a save');
            console.log('No save text entered.');
            return;
        }

        saveEntries = saveText.split('🤜').map(entry => entry.trim()).reverse();
        index = 0;
        processNextEntry();
    }

    function processNextEntry() {
        if (paused) return;

        if (index < saveEntries.length) {
            let currentInput = checkTextBoxForCurrentInput();
            let currentIndex = saveEntries.findIndex(entry => entry.toLowerCase() === currentInput.toLowerCase());

            if (currentIndex !== -1 && currentIndex + 1 < saveEntries.length) {
                console.log(`Current input is "${currentInput}". Inputting next entry: ${saveEntries[currentIndex + 1]}`);
                simulateInput(saveEntries[currentIndex + 1], () => {
                    index = currentIndex + 2;
                    processNextEntry();
                });
            } else {
                console.log(`Current input "${currentInput}" does not match any expected input. Retrying...`);
                setTimeout(processNextEntry, parseInt(waitTimeSlider.value));
            }
        } else {
            console.log('All entries processed.');
        }
    }

    function checkTextBoxForCurrentInput() {
        let currentInputElement = document.querySelector('.text-2xl.text-center');
        if (currentInputElement) {
            let currentInput = currentInputElement.textContent.trim();
            if (currentInput.endsWith('?')) {
                currentInput = currentInput.slice(0, -1);
            }
            return currentInput;
        }
        return null;
    }

    // Add the Enter Save button click event
    enterSaveButton.addEventListener('click', () => {
        console.log('Enter Save button clicked.');
        enterSave();
    });
})();