LBUtil

Made for LB

Du musst eine Erweiterung wie Tampermonkey, Greasemonkey oder Violentmonkey installieren, um dieses Skript zu installieren.

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

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

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

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

Sie müssten eine Skript Manager Erweiterung installieren damit sie dieses Skript installieren können

(Ich habe schon ein Skript Manager, Lass mich es installieren!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         LBUtil
// @version      1.2.1
// @description  Made for LB
// @author       FeiFei
// @match        https://bonk.io/gameframe-release.html
// @run-at       document-end
// @grant        none
// @namespace https://greatest.deepsurf.us/users/1366475
// ==/UserScript==

"use strict";

window.lbUtil = {};

lbUtil.windowConfigs = {
    windowName: "LBUtil",
    windowId: "lbUtil_window",
    modVersion: "1.2.1",
    bonkLIBVersion: "1.1.3",
    bonkVersion: "49",
    windowContent: null,
};

// Initialize default settings
lbUtil.defaultSettings = {
    smartCapZone: true,
    keepOutline: false,
    resetVTOLAngleOnDeath: false,
};

// Dynamic Vars
lbUtil.inGamePlayers = {};
lbUtil.bonkRenderer = null;

// Initialize settings
lbUtil.settings = JSON.parse(JSON.stringify(lbUtil.defaultSettings));

// Function to save settings to localStorage
lbUtil.saveSettings = function () {
    localStorage.setItem('lbUtilSettings', JSON.stringify(this.settings));
};

// Function to load settings from localStorage
lbUtil.loadSettings = function () {
    const savedSettings = localStorage.getItem('lbUtilSettings');
    if (savedSettings) {
        try {
            this.settings = JSON.parse(savedSettings);
        } catch (e) {
            console.error('Failed to parse saved settings:', e);
            this.settings = JSON.parse(JSON.stringify(this.defaultSettings));
        }
    }
};

lbUtil.capWontEnd = function () {
    let inGamePlayersArr = Object.values(lbUtil.inGamePlayers);
    // Only 1 player in game, game never ends
    if (inGamePlayersArr.length == 1) {
        return true;
    }
    
    // All player on same team
    let team = inGamePlayersArr[0];
    for (let i = 1; i < inGamePlayersArr.length; i++) {
        // If player not on same team or in FFA
        if (inGamePlayersArr[i] != team || inGamePlayersArr[i] == 1) {
            return false;
        }
    }
    
    return true;
};

lbUtil.resetCapzoneOutlines = function () {
    for(let body of lbUtil.bonkRenderer.roundGraphics.bodyGraphics) {
        if(!body) continue;
        for(let shape of body.shapes) {
            if(!shape || !shape.capZone) continue;
            if(shape.graphicTexture) {
                shape.graphicTexture.tint = 0xffffff;
            }
            else if(shape.graphic) {
                shape.graphic.tint = 0xffffff;
            }
        }
    }
}

// Create the mod window using BonkHUD
lbUtil.createWindow = function () {
    // Create the window using BonkHUD
    const modIndex = bonkHUD.createMod(this.windowConfigs.windowName, this.windowConfigs);

    // Load UI settings if available
    bonkHUD.loadUISetting(modIndex);
};

lbUtil.setWindowContent = function () {
    let windowHTML = document.createElement("div");
    windowHTML.id = lbUtil.windowConfigs.windowId; // Unique ID for scoping
    windowHTML.classList.add("bonkhud-background-color");
    
    // Helper function to create a setting toggle
    function createSettingToggle(settingKey, settingLabel) {
        let settingDiv = document.createElement("div");
        settingDiv.className = "setting-item bonkhud-border-color"; // Apply border color

        let settingName = document.createElement("span");
        settingName.className = "setting-name bonkhud-text-color"; // Apply text color
        settingName.textContent = settingLabel;

        // Create the checkbox input
        let checkbox = document.createElement("input");
        checkbox.type = "checkbox";
        checkbox.id = `${settingKey}Checkbox`;
        checkbox.checked = this.settings[settingKey];

        // Create the label for the checkbox (toggle switch)
        let label = document.createElement("label");
        label.htmlFor = `${settingKey}Checkbox`;
        label.className = "lbUtilToggle bonkhud-button-color"; // Apply button color

        // Event listener to update the setting
        checkbox.addEventListener("change", () => {
            this.settings[settingKey] = checkbox.checked;
            // Save settings
            this.saveSettings();
        });

        // Create a container for the toggle switch
        let toggleContainer = document.createElement("div");
        toggleContainer.style.display = "flex";
        toggleContainer.style.alignItems = "center";

        toggleContainer.appendChild(checkbox);
        toggleContainer.appendChild(label);

        settingDiv.appendChild(settingName);
        settingDiv.appendChild(toggleContainer);

        windowHTML.appendChild(settingDiv);
    }
    
    // Create the toggles for all settings
    createSettingToggle.call(this, "smartCapZone", "Smart Cap Zone");
    createSettingToggle.call(this, "keepOutline", "Keep Cap Zone Outline");
    createSettingToggle.call(this, "resetVTOLAngleOnDeath", "Reset VTOL Angle On Death(Desync warning)");

    // Set the windowContent to the container
    this.windowConfigs.windowContent = windowHTML;

    // Store a reference to the window content element if needed
    this.windowContentElement = windowHTML;
};

// Add styles for the toggle switch
lbUtil.addStyles = function() {
    const css = `
    /* Scoped Styles for LBUtil Mod */
    #lbUtil_window {
        font-family: Poppins;
        padding: 10px;
    }
    #lbUtil_window .setting-item {
        display: flex;
        justify-content: space-between;
        align-items: center;
        padding: 5px 0;
        border-bottom: 1px solid;
    }
    #lbUtil_window .setting-item:last-child {
        border-bottom: none;
    }
    #lbUtil_window .setting-name {
        flex-grow: 1;
        font-size: 16px;
    }
    #lbUtil_window .lbUtilToggle {
        position: relative;
        width: 40px;
        height: 20px;
        border-radius: 20px;
        cursor: pointer;
        transition: background-color 0.2s;
        background-color: #ccc; /* Default background color */
    }
    #lbUtil_window .lbUtilToggle::before {
        content: '';
        position: absolute;
        width: 18px;
        height: 18px;
        left: 1px;
        top: 1px;
        background-color: #fff;
        border-radius: 18px;
        transition: transform 0.2s;
    }
    #lbUtil_window input[type="checkbox"]:checked + .lbUtilToggle::before {
        transform: translateX(20px);
    }
    #lbUtil_window input[type="checkbox"]:checked + .lbUtilToggle {
        background-color: #4cd137; /* Background when checked */
    }
    #lbUtil_window input[type="checkbox"] {
        display: none;
    }
    `;
    let style = document.createElement('style');
    style.innerHTML = css;
    document.head.appendChild(style);
};

// Injector function to inject code into the game code
lbUtil.injector = function (src) {
    let newSrc = src;
    
    //! Inject capZoneEvent fire
    let orgCode = `K$h[9]=K$h[0][0][K$h[2][138]]()[K$h[2][115]];`;
    let newCode = `
        K$h[9]=K$h[0][0][K$h[2][138]]()[K$h[2][115]];
        
        try {
            // Initialize
            let inputState = z0M[0][0];
            let currentFrame = inputState.rl;
            let playerID = K$h[0][0].m_userData.arrayID;
            let capID = K$h[1];
            
            if (window.lbUtil.settings.smartCapZone && window.lbUtil.capWontEnd()) {
                if (playerID != window.bonkAPI.getMyID()) {
                    console.debug("Supress capZoneEvent");
                    return;
                }
            }
            
        } catch(err) {
            console.error(err);
        }
    `;

    newSrc = newSrc.replace(orgCode, newCode);
    
    //! Inject stepEvent fire
    orgCode = `return z0M[720];`;
    newCode = `
    
        let outputState = z0M[720];
        // console.info("outputState", outputState);
        
        try {
            if (window.lbUtil.settings.smartCapZone) {
                // For each death, check who dead this frame
                outputState.discDeaths.forEach((death) => {
                    if (death.f == 0 && death.i == window.bonkAPI.getMyID()) { // Dead this frame and me
                        console.debug("I dead this frame");
                        
                        // Unfill all cap zones
                        outputState.capZones.forEach((capZone) => {
                            capZone.p = 0;
                            capZone.f = -1;
                            capZone.o = -1;
                            capZone.ot = -1;
                        });
                        
                        // Reset capzone outlines
                        if (!window.lbUtil.settings.keepOutline) {
                            window.lbUtil.resetCapzoneOutlines();
                        }
                    }
                });
            }
                
            if (window.lbUtil.settings.resetVTOLAngleOnDeath) {
                if (outputState.discDeaths) {
                    outputState.discDeaths.forEach((death) => {
                        if (death.f == 0) { // Dead this frame
                            let disc = outputState.discs[death.i];
                            
                            if (disc) {
                                // Reset VTOL angle
                                disc.a = 0;
                                disc.av = 0;
                            }
                        }
                    });
                }
            }
        } catch(err) {
            console.error(err);
        }
        
        return z0M[720];`;

    newSrc = newSrc.replace(orgCode, newCode);
    
    //! Inject Renderer reference
    orgCode = `if(S6e.dontInterpolate == true){`;
    newCode = `
        window.lbUtil.bonkRenderer = this;
        
        if(S6e.dontInterpolate == true){`;

    newSrc = newSrc.replace(orgCode, newCode);
    
    return newSrc;
};

// Initialize the mod (run when document is ready)
lbUtil.initMod = function () {
    // Ensure BonkHUD is available
    if (!window.bonkHUD) {
        console.error("BonkHUD is not loaded. Please make sure BonkHUD is installed.");
        return;
    }

    // Load settings from localStorage
    this.loadSettings();
    
    this.setWindowContent();
    this.createWindow();
    
    // Add custom styles
    this.addStyles();
    
    bonkAPI.addEventListener("gameStart", (e) => {
        try {
            let players = e.mapData.discs;
            lbUtil.inGamePlayers = {};
            
            if (players != null) {
                for (let i = 0; i < players.length; i++) {
                    if (players[i] != null) {
                        // id : team
                        lbUtil.inGamePlayers[i] = players[i].team;
                    }
                }
            }
        } catch (error) {
            console.error(error);
        }
    });
    
    console.log(this.windowConfigs.windowName + " initialized");
};

// Function to handle document readiness and initialize the mod
lbUtil.onDocumentReady = function () {
    if (document.readyState === "complete" || document.readyState === "interactive") {
        this.initMod();
    } else {
        document.addEventListener("DOMContentLoaded", () => {
            this.initMod();
        });
    }
};

// Compatibility with Excigma's code injector userscript
if (!window.bonkCodeInjectors) window.bonkCodeInjectors = [];
window.bonkCodeInjectors.push((bonkCode) => {
    try {
        return lbUtil.injector(bonkCode);
    } catch (error) {
        throw error;
    }
});

console.log("lbUtil injector loaded");

// Call the function to check document readiness and initialize the mod
lbUtil.onDocumentReady();