Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /**
- * @name KBM AA (Desktop UI)
- * @description Emulates an Xbox controller with a modern, draggable UI.
- * Press PageUp to open/close the settings menu.
- * v3 Improvements: Complete UI overhaul to a draggable, resizable, desktop-style application window.
- */
- (function() {
- 'use strict';
- // --- Primary Toggle ---
- const KBM_AA_ENABLED = true;
- // --- Default Configuration ---
- const DEFAULT_KEY_BINDS = {
- FIRE: 'mouse0', ADS: 't', JUMP: ' ', SPRINT: 'shift', CROUCH: 'control',
- RELOAD: 'r', USE: 'e', EDIT: 'g', WALL: 'z', RAMP: 'x', FLOOR: 'c', ROOF: 'v',
- };
- const DEFAULT_SENSITIVITY = { X: 8.5, Y: 8.5, ADS_MULTIPLIER: 85 };
- const DEFAULT_VIRTUAL_ZEN_SETTINGS = {
- ENABLED: true, ACTIVATE_ON_ADS_ONLY: false, ENABLE_STRAFE_AIM_ASSIST: true,
- STRAFE_AIM_ASSIST_STRENGTH: 0.05, STRAFE_AIM_ASSIST_ADS_MULTIPLIER: 1.3,
- ENABLE_MOVEMENT_VERTICAL_AIM_ASSIST: true, FORWARD_AIM_DOWN_STRENGTH: 0.09,
- BACKWARD_AIM_UP_STRENGTH: 0.09, STRAFE_LOOK_STRENGTH: 0.10,
- };
- // --- Backend ---
- const ADVANCED = { POLLING_RATE_MS: 16, CONNECTION_DISPATCH_RATE_MS: 100, SENS_BASE_MULTIPLIER: 0.0018 };
- const CONTROLLER_BUTTON_MAP = {
- A: 0, B: 1, X: 2, Y: 3, LB: 4, RB: 5, LT: 6, RT: 7, L_STICK: 10, R_STICK: 11,
- };
- const BIND_ACTION_TO_BUTTON = {
- JUMP: CONTROLLER_BUTTON_MAP.A, EDIT: CONTROLLER_BUTTON_MAP.B, RELOAD: CONTROLLER_BUTTON_MAP.X,
- USE: CONTROLLER_BUTTON_MAP.X, WALL: CONTROLLER_BUTTON_MAP.RT, ADS: CONTROLLER_BUTTON_MAP.LT,
- RAMP: CONTROLLER_BUTTON_MAP.LT, ROOF: CONTROLLER_BUTTON_MAP.LB, FLOOR: CONTROLLER_BUTTON_MAP.RB,
- SPRINT: CONTROLLER_BUTTON_MAP.L_STICK, CROUCH: CONTROLLER_BUTTON_MAP.R_STICK,
- FIRE: CONTROLLER_BUTTON_MAP.RT,
- };
- // --- Script State ---
- let keyMap = {}, mouseMap = {}, currentBinds = {}, currentSens = {}, virtualZenSettings = {};
- const controllerState = {
- axes: [0.0, 0.0, 0.0, 0.0],
- buttons: Array(17).fill(false).map((_, i) => ({ pressed: false, touched: false, value: 0.0, index: i })),
- axisKeyPresses: { 0: { neg: false, pos: false }, 1: { neg: false, pos: false } },
- mouseDeltaX: 0, mouseDeltaY: 0, isConnected: true, timestamp: performance.now(),
- };
- let pointerLocked = false, stateIntervalId = null, connectionIntervalId = null, gameAreaElement = null;
- const originalGetGamepads = navigator.getGamepads?.bind(navigator);
- const eventListeners = [];
- // --- Core Logic (Unchanged) ---
- function generateMappings() {
- keyMap = {
- 'w': { t: 'a', a: 1, v: -1 }, 'a': { t: 'a', a: 0, v: -1 },
- 's': { t: 'a', a: 1, v: 1 }, 'd': { t: 'a', a: 0, v: 1 },
- };
- mouseMap = {};
- for (const action in currentBinds) {
- const key = currentBinds[action].toLowerCase();
- const buttonIndex = BIND_ACTION_TO_BUTTON[action];
- if (buttonIndex !== undefined) {
- if (key.startsWith('mouse')) mouseMap[parseInt(key.replace('mouse', ''), 10)] = { t: 'b', i: buttonIndex };
- else keyMap[key] = { t: 'b', i: buttonIndex };
- }
- }
- }
- function createGamepadObject() {
- return {
- axes: controllerState.axes, buttons: controllerState.buttons, connected: controllerState.isConnected,
- id: "Xbox Controller (XInput STANDARD GAMEPAD)", index: 0, mapping: "standard", timestamp: controllerState.timestamp,
- };
- }
- function updateAndSimulateGamepad() {
- let scriptSensX = currentSens.X * ADVANCED.SENS_BASE_MULTIPLIER, scriptSensY = currentSens.Y * ADVANCED.SENS_BASE_MULTIPLIER;
- const isADS = controllerState.buttons[BIND_ACTION_TO_BUTTON.ADS]?.pressed || false;
- if (isADS) { const m = currentSens.ADS_MULTIPLIER / 100; scriptSensX *= m; scriptSensY *= m; }
- let finalTargetRX = 0, finalTargetRY = 0;
- if (pointerLocked) {
- finalTargetRX = controllerState.mouseDeltaX * scriptSensX;
- finalTargetRY = controllerState.mouseDeltaY * scriptSensY;
- const isZenActive = virtualZenSettings.ENABLED && (!virtualZenSettings.ACTIVATE_ON_ADS_ONLY || isADS);
- if (isZenActive) {
- if (virtualZenSettings.ENABLE_STRAFE_AIM_ASSIST) {
- const s = virtualZenSettings.STRAFE_AIM_ASSIST_STRENGTH * (isADS ? virtualZenSettings.STRAFE_AIM_ASSIST_ADS_MULTIPLIER : 1);
- if (controllerState.axisKeyPresses[0].neg) finalTargetRX += s;
- else if (controllerState.axisKeyPresses[0].pos) finalTargetRX -= s;
- }
- if (virtualZenSettings.ENABLE_MOVEMENT_VERTICAL_AIM_ASSIST) {
- if (controllerState.axisKeyPresses[1].neg) finalTargetRY += virtualZenSettings.FORWARD_AIM_DOWN_STRENGTH;
- else if (controllerState.axisKeyPresses[1].pos) finalTargetRY -= virtualZenSettings.BACKWARD_AIM_UP_STRENGTH;
- }
- if (controllerState.axisKeyPresses[0].neg) finalTargetRX += virtualZenSettings.STRAFE_LOOK_STRENGTH;
- else if (controllerState.axisKeyPresses[0].pos) finalTargetRX -= virtualZenSettings.STRAFE_LOOK_STRENGTH;
- }
- controllerState.mouseDeltaX = 0; controllerState.mouseDeltaY = 0;
- }
- controllerState.axes[2] = Math.max(-1, Math.min(1, finalTargetRX));
- controllerState.axes[3] = Math.max(-1, Math.min(1, finalTargetRY));
- controllerState.timestamp = performance.now();
- }
- // --- Event Handlers (Unchanged) ---
- function handleKeyEvent(event, isDown) {
- if (event.key === 'PageUp' && isDown) { toggleUI(); return; }
- const gui = document.getElementById('kbm-aa-gui');
- if (gui && gui.style.display !== 'none') { if (event.key === 'Escape') { toggleUI(); } return; }
- const mappedKey = (event.key.toLowerCase().startsWith('control') ? 'control' : event.key.toLowerCase());
- const mapping = keyMap[mappedKey];
- if (mapping) {
- event.preventDefault(); event.stopPropagation();
- if (mapping.t === 'b') {
- const button = controllerState.buttons[mapping.i];
- if (button.pressed !== isDown) { button.pressed = isDown; button.touched = isDown; button.value = isDown ? 1.0 : 0.0; }
- } else if (mapping.t === 'a') {
- const axisState = controllerState.axisKeyPresses[mapping.a];
- if (mapping.v < 0) axisState.neg = isDown; else axisState.pos = isDown;
- controllerState.axes[mapping.a] = axisState.neg ? -1 : (axisState.pos ? 1 : 0);
- }
- }
- }
- function handleMouseEvent(event, isDown) {
- const gui = document.getElementById('kbm-aa-gui');
- if (gui && gui.style.display !== 'none') return;
- const mapping = mouseMap[event.button];
- if (mapping) {
- event.preventDefault(); event.stopPropagation();
- if (!pointerLocked && isDown) return;
- const button = controllerState.buttons[mapping.i];
- if (button.pressed !== isDown) { button.pressed = isDown; button.touched = isDown; button.value = isDown ? 1.0 : 0.0; }
- }
- }
- function handleMouseMove(event) { if (pointerLocked) { controllerState.mouseDeltaX += event.movementX || 0; controllerState.mouseDeltaY += event.movementY || 0; } }
- function handlePointerLockChange() {
- pointerLocked = (document.pointerLockElement === gameAreaElement);
- if (!pointerLocked) {
- Object.assign(controllerState, { mouseDeltaX: 0, mouseDeltaY: 0 });
- Object.values(mouseMap).forEach(m => { const b = controllerState.buttons[m.i]; b.pressed = b.touched = false; b.value = 0.0; });
- }
- }
- function requestPointerLock() { if (!pointerLocked) gameAreaElement.requestPointerLock().catch(e => {}); }
- // --- New UI Functions ---
- function toggleUI() { const gui = document.getElementById('kbm-aa-gui'); if (gui) { gui.style.display = gui.style.display === 'block' ? 'none' : 'block'; } }
- function saveSettings() {
- for (const action in DEFAULT_KEY_BINDS) { currentBinds[action] = document.getElementById(`bind-${action}`).value; }
- currentSens.X = parseFloat(document.getElementById('sens-x').value);
- currentSens.Y = parseFloat(document.getElementById('sens-y').value);
- currentSens.ADS_MULTIPLIER = parseFloat(document.getElementById('sens-ads').value);
- virtualZenSettings.ENABLED = document.getElementById('zen-enabled').checked;
- virtualZenSettings.ACTIVATE_ON_ADS_ONLY = document.getElementById('zen-ads-only').checked;
- localStorage.setItem('kbm-aa-binds', JSON.stringify(currentBinds));
- localStorage.setItem('kbm-aa-sens', JSON.stringify(currentSens));
- localStorage.setItem('kbm-aa-zen', JSON.stringify(virtualZenSettings));
- generateMappings();
- toggleUI();
- }
- function loadSettings() {
- currentBinds = JSON.parse(localStorage.getItem('kbm-aa-binds')) || { ...DEFAULT_KEY_BINDS };
- currentSens = JSON.parse(localStorage.getItem('kbm-aa-sens')) || { ...DEFAULT_SENSITIVITY };
- virtualZenSettings = JSON.parse(localStorage.getItem('kbm-aa-zen')) || { ...DEFAULT_VIRTUAL_ZEN_SETTINGS };
- }
- function createConfigUI() {
- if (document.getElementById('kbm-aa-gui')) return;
- let bindsHTML = '';
- for (const action in DEFAULT_KEY_BINDS) { bindsHTML += `<div class="kbm-aa-row"><label>${action}</label><input type="text" id="bind-${action}" value="${currentBinds[action] || ''}"></div>`; }
- const uiHTML = `
- <div id="kbm-aa-title-bar">
- <div id="kbm-aa-logo">K</div>
- <span class="kbm-title-text">KBM AA Settings</span>
- <button id="kbm-aa-close-btn">×</button>
- </div>
- <div id="kbm-aa-content">
- <div class="kbm-aa-panel">
- <h3 class="kbm-aa-panel-title">Sensitivity</h3>
- <div class="kbm-aa-row"><label>X Sensitivity</label><input type="range" id="sens-x" min="1" max="30" step="0.1" value="${currentSens.X}"><span class="kbm-aa-value" id="sens-x-val">${currentSens.X}</span></div>
- <div class="kbm-aa-row"><label>Y Sensitivity</label><input type="range" id="sens-y" min="1" max="30" step="0.1" value="${currentSens.Y}"><span class="kbm-aa-value" id="sens-y-val">${currentSens.Y}</span></div>
- <div class="kbm-aa-row"><label>ADS Multiplier</label><input type="range" id="sens-ads" min="10" max="100" step="1" value="${currentSens.ADS_MULTIPLIER}"><span class="kbm-aa-value" id="sens-ads-val">${currentSens.ADS_MULTIPLIER}%</span></div>
- </div>
- <div class="kbm-aa-panel">
- <h3 class="kbm-aa-panel-title">Virtual Aim Assist</h3>
- <div class="kbm-aa-row"><label for="zen-enabled">Enable Aim Assist</label><input type="checkbox" id="zen-enabled" ${virtualZenSettings.ENABLED ? 'checked' : ''}></div>
- <div class="kbm-aa-row"><label for="zen-ads-only">Activate on ADS Only</label><input type="checkbox" id="zen-ads-only" ${virtualZenSettings.ACTIVATE_ON_ADS_ONLY ? 'checked' : ''}></div>
- </div>
- <div class="kbm-aa-panel" id="kbm-aa-binds-panel">
- <h3 class="kbm-aa-panel-title">Key Binds</h3>
- ${bindsHTML}
- </div>
- </div>
- <button id="kbm-aa-save-btn">Save & Close</button>
- <div id="kbm-aa-resize-handle"></div>`;
- const style = document.createElement('style');
- style.id = 'kbm-aa-style';
- style.textContent = `
- #kbm-aa-gui { display: none; position: fixed; top: 10%; left: 15%; width: 600px; min-width: 450px; min-height: 400px; background: #1a1a1a; border-radius: 12px; z-index: 99999; color: #e0e0e0; font-family: 'Segoe UI', sans-serif; box-shadow: 0 10px 30px rgba(0,0,0,0.5); border: 1px solid #333; resize: both; overflow: hidden; }
- #kbm-aa-title-bar { height: 40px; background: #222; display: flex; align-items: center; padding: 0 10px; cursor: move; border-top-left-radius: 12px; border-top-right-radius: 12px; user-select: none; }
- #kbm-aa-logo { font-weight: bold; font-size: 20px; color: #ffb700; margin-right: 10px; }
- .kbm-title-text { flex-grow: 1; }
- #kbm-aa-close-btn { background: none; border: none; color: #ccc; font-size: 24px; cursor: pointer; }
- #kbm-aa-content { padding: 20px; display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; height: calc(100% - 100px); overflow-y: auto; }
- .kbm-aa-panel { background: #252525; padding: 15px; border-radius: 8px; border: 1px solid #383838; }
- .kbm-aa-panel-title { margin: 0 0 15px 0; color: #ffb700; border-bottom: 1px solid #444; padding-bottom: 8px; }
- #kbm-aa-binds-panel { grid-column: span 2; }
- .kbm-aa-row { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; font-size: 14px; }
- .kbm-aa-row label { flex-basis: 50%; }
- .kbm-aa-row input[type="text"] { width: 80px; padding: 5px; background: #333; border: 1px solid #555; color: #fff; border-radius: 4px; }
- .kbm-aa-row input[type="range"] { flex-grow: 1; margin: 0 10px; accent-color: #ffb700; }
- .kbm-aa-row input[type="checkbox"] { width: 18px; height: 18px; accent-color: #ffb700; }
- .kbm-aa-value { min-width: 50px; text-align: right; }
- #kbm-aa-save-btn { position: absolute; bottom: 10px; right: 10px; padding: 8px 15px; background: #ffb700; border: none; color: #111; border-radius: 4px; cursor: pointer; font-size: 14px; font-weight: bold; }
- #kbm-aa-resize-handle { position: absolute; bottom: 0; right: 0; width: 10px; height: 10px; cursor: nwse-resize; }
- `;
- document.head.appendChild(style);
- const guiContainer = document.createElement('div');
- guiContainer.id = 'kbm-aa-gui';
- guiContainer.innerHTML = uiHTML;
- document.body.appendChild(guiContainer);
- document.getElementById('kbm-aa-close-btn').addEventListener('click', toggleUI);
- document.getElementById('kbm-aa-save-btn').addEventListener('click', saveSettings);
- document.getElementById('sens-x').addEventListener('input', (e) => { document.getElementById('sens-x-val').textContent = e.target.value; });
- document.getElementById('sens-y').addEventListener('input', (e) => { document.getElementById('sens-y-val').textContent = e.target.value; });
- document.getElementById('sens-ads').addEventListener('input', (e) => { document.getElementById('sens-ads-val').textContent = e.target.value + '%'; });
- makeDraggable(guiContainer, document.getElementById('kbm-aa-title-bar'));
- }
- function makeDraggable(element, handle) {
- let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
- handle.onmousedown = dragMouseDown;
- function dragMouseDown(e) {
- e.preventDefault();
- pos3 = e.clientX; pos4 = e.clientY;
- document.onmouseup = closeDragElement;
- document.onmousemove = elementDrag;
- }
- function elementDrag(e) {
- e.preventDefault();
- pos1 = pos3 - e.clientX; pos2 = pos4 - e.clientY;
- pos3 = e.clientX; pos4 = e.clientY;
- element.style.top = (element.offsetTop - pos2) + "px";
- element.style.left = (element.offsetLeft - pos1) + "px";
- }
- function closeDragElement() {
- document.onmouseup = null; document.onmousemove = null;
- }
- }
- // --- Initialization & Shutdown ---
- function addListener(element, event, handler, options = false) {
- element.addEventListener(event, handler, options);
- eventListeners.push({ element, event, handler, options });
- }
- function removeAllListeners() {
- eventListeners.forEach(({ element, event, handler, options }) => element.removeEventListener(event, handler, options));
- eventListeners.length = 0;
- }
- function initialize() {
- gameAreaElement = document.getElementById('game-stream') || document.querySelector('video') || document.body;
- if (!gameAreaElement) { console.error("KBM AA Script: Could not find a suitable game element."); return; }
- loadSettings(); createConfigUI(); generateMappings();
- navigator.getGamepads = () => [createGamepadObject(), null, null, null];
- addListener(window, 'keydown', (e) => handleKeyEvent(e, true), true);
- addListener(window, 'keyup', (e) => handleKeyEvent(e, false), true);
- addListener(gameAreaElement, 'mousemove', handleMouseMove, true);
- addListener(gameAreaElement, 'mousedown', (e) => handleMouseEvent(e, true), true);
- addListener(gameAreaElement, 'mouseup', (e) => handleMouseEvent(e, false), true);
- addListener(document, 'pointerlockchange', handlePointerLockChange, false);
- addListener(gameAreaElement, 'click', requestPointerLock, false);
- stateIntervalId = setInterval(updateAndSimulateGamepad, ADVANCED.POLLING_RATE_MS);
- connectionIntervalId = setInterval(() => { window.dispatchEvent(new CustomEvent('gamepadconnected', { detail: { gamepad: createGamepadObject() } })) }, ADVANCED.CONNECTION_DISPATCH_RATE_MS);
- }
- window.stopKbmAa = function() {
- if (!stateIntervalId) return;
- clearInterval(stateIntervalId); clearInterval(connectionIntervalId);
- stateIntervalId = null; connectionIntervalId = null;
- removeAllListeners();
- if (originalGetGamepads) navigator.getGamepads = originalGetGamepads; else delete navigator.getGamepads;
- const gui = document.getElementById('kbm-aa-gui'); if (gui) gui.remove();
- const style = document.getElementById('kbm-aa-style'); if (style) style.remove();
- if (document.pointerLockElement) document.exitPointerLock();
- console.log("KBM AA Script stopped and cleaned up.");
- };
- if (KBM_AA_ENABLED) {
- if (document.readyState === 'complete' || document.readyState === 'interactive') { setTimeout(initialize, 500); }
- else { document.addEventListener('DOMContentLoaded', () => setTimeout(initialize, 500)); }
- }
- })();
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement