Made for LB
// ==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();