function simulateGamepad() { const app = window.app; app.chrome.showCursor = true; let cursorPos = [0, 0]; let intTabIndex = 0; const cursorSpeedPvt = 8; const cursorSize = 16; let scrollSpeed = 8; let buttonPressDelay = 500; let stickDeadZone = 0.2; let scrollGroup = null; let scrollGroupY = null; let elementFocusEnabled = true; let start; let cursorSpeed = cursorSpeedPvt; let lastButtonPress = {}; var sounds = { Confirm: new Audio("./sounds/confirm.ogg"), Menu: new Audio("./sounds/btn1.ogg"), Hover: new Audio("./sounds/hover.ogg"), }; let element = document.elementFromPoint(0, 0); let elementType = 0; function appLoop() { var gamepads = navigator.getGamepads ? navigator.getGamepads() : navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []; if (!gamepads) { return; } var gp = gamepads[0]; // LEFT STICK if (gp.axes[0] > stickDeadZone) { cursorPos[0] += gp.axes[0] * cursorSpeed; } else if (gp.axes[0] < -stickDeadZone) { cursorPos[0] += gp.axes[0] * cursorSpeed; } if (gp.axes[1] > stickDeadZone) { cursorPos[1] += gp.axes[1] * cursorSpeed; } else if (gp.axes[1] < -stickDeadZone) { cursorPos[1] += gp.axes[1] * cursorSpeed; } if (cursorPos[0] < cursorSize) { cursorPos[0] = cursorSize; } if (cursorPos[1] < cursorSize) { cursorPos[1] = cursorSize; } if (cursorPos[0] > window.innerWidth - cursorSize) { cursorPos[0] = window.innerWidth - cursorSize; } if (cursorPos[1] > window.innerHeight - cursorSize) { cursorPos[1] = window.innerHeight - cursorSize; } // RIGHT STICK. if (scrollGroupY) { if (gp.axes[3] > stickDeadZone) { $(scrollGroupY).scrollTop($(scrollGroupY).scrollTop() + gp.axes[3] * scrollSpeed); elementFocusEnabled = false; } else if (gp.axes[3] < -stickDeadZone) { $(scrollGroupY).scrollTop($(scrollGroupY).scrollTop() + gp.axes[3] * scrollSpeed); elementFocusEnabled = false; } else { elementFocusEnabled = true; } } if (scrollGroup) { if (gp.axes[2] > stickDeadZone) { $(scrollGroup).scrollLeft($(scrollGroup).scrollLeft() + gp.axes[2] * scrollSpeed); elementFocusEnabled = false; } else if (gp.axes[2] < -stickDeadZone) { $(scrollGroup).scrollLeft($(scrollGroup).scrollLeft() + gp.axes[2] * scrollSpeed); elementFocusEnabled = false; } else { elementFocusEnabled = true; } } $(".cursor").css({ top: cursorPos[1] + "px", left: cursorPos[0] + "px", display: "block", }); // A BUTTON if (gp.buttons[0].pressed) { if (!lastButtonPress["A"]) { lastButtonPress["A"] = 0; } if (Date.now() - lastButtonPress["A"] > buttonPressDelay) { lastButtonPress["A"] = Date.now(); sounds.Confirm.play(); if (elementType == 0) { document.activeElement.dispatchEvent(new Event("click")); document.activeElement.dispatchEvent(new Event("controller-click")); } else { element.dispatchEvent(new Event("click")); element.dispatchEvent(new Event("controller-click")); } } } // B BUTTON if (gp.buttons[1].pressed) { if (!lastButtonPress["B"]) { lastButtonPress["B"] = 0; } if (Date.now() - lastButtonPress["B"] > buttonPressDelay) { lastButtonPress["B"] = Date.now(); if (elementType == 0) { document.activeElement.dispatchEvent(new Event("contextmenu")); setTimeout(() => { if ($(".menu-option").length > 0) { let bounds = $(".menu-option")[0].getBoundingClientRect(); cursorPos[0] = bounds.left + bounds.width / 2; cursorPos[1] = bounds.top + bounds.height / 2; } }, 100); } else { element.dispatchEvent(new Event("contextmenu")); } } } // right bumper if (gp.buttons[5].pressed) { if (!lastButtonPress["RB"]) { lastButtonPress["RB"] = 0; } if (Date.now() - lastButtonPress["RB"] > buttonPressDelay) { lastButtonPress["RB"] = Date.now(); app.navigateForward(); } } // left bumper if (gp.buttons[4].pressed) { if (!lastButtonPress["LB"]) { lastButtonPress["LB"] = 0; } if (Date.now() - lastButtonPress["LB"] > buttonPressDelay) { lastButtonPress["LB"] = Date.now(); app.navigateBack(); } } // cursor hover if (elementFocusEnabled) { element = document.elementFromPoint(cursorPos[0], cursorPos[1]); } if (element) { let closest = element.closest("[tabindex], input, button, a"); // VERT SCROLL let scrollGroupCloY = element.closest(`[scrollaxis="y"]`); if (scrollGroupCloY) { scrollGroupY = scrollGroupCloY; } // HOZ SCROLL let scrollGroupClo = element.closest(".v-hl-container"); if (scrollGroupClo) { if (scrollGroupClo.classList.contains("v-hl-container")) { scrollGroup = scrollGroupClo; scrollGroup.style["scroll-snap-type"] = "unset"; } else { scrollGroup.style["scroll-snap-type"] = ""; scrollGroup = null; } } if (closest) { elementType = 0; closest.focus(); } else { if (closest) { closest.blur(); } elementType = 1; element.focus(); } cursorSpeed = cursorSpeedPvt; if (!element.classList.contains("app-chrome") && !element.classList.contains("app-content")) { cursorSpeed = cursorSpeedPvt; } // console.log($._data($(element), "events")) } else { cursorSpeed = 12; } // console.log(gp.axes[0], gp.axes[1]) start = requestAnimationFrame(appLoop); } // controller pairing notyf.error("Press the button on your controller to pair it to Cider."); window.addEventListener( "gamepadconnected", function (e) { console.log("Gamepad connected at index %d: %s. %d buttons, %d axes.", e.gamepad.index, e.gamepad.id, e.gamepad.buttons.length, e.gamepad.axes.length); notyf.success("Pairing successful!"); appLoop(); }, { once: true } ); document.addEventListener("keydown", (e) => { sounds.Confirm.currentTime = 0; sounds.Menu.currentTime = 0; sounds.Hover.currentTime = 0; let tabbable = $("[tabindex]"); console.log(e.key); switch (e.key) { default: break; case "ArrowLeft": e.preventDefault(); cursorPos[0] -= cursorSpeed; break; case "ArrowRight": e.preventDefault(); cursorPos[0] += cursorSpeed; break; case "ArrowUp": e.preventDefault(); cursorPos[1] -= cursorSpeed; // sounds.Hover.play() // if (intTabIndex <= 0) { // intTabIndex = 0 // } else { // intTabIndex-- // } // $(tabbable[intTabIndex]).focus() // $("#app-content").scrollTop($(document.activeElement).offset().top) break; case "ArrowDown": e.preventDefault(); cursorPos[1] += cursorSpeed; // if (intTabIndex < tabbable.length) { // intTabIndex++ // } else { // intTabIndex = tabbable.length // } // $(tabbable[intTabIndex]).focus() // $("#app-content").scrollTop($(document.activeElement).offset().top) break; case "c": app.resetState(); break; case "x": // set cursorPos to the top right of the screen // sounds.Menu.play() if (elementType == 0) { document.activeElement.dispatchEvent(new Event("contextmenu")); } else { element.dispatchEvent(new Event("contextmenu")); } e.preventDefault(); break; case "z": sounds.Confirm.play(); if (elementType == 0) { document.activeElement.dispatchEvent(new Event("click")); document.activeElement.dispatchEvent(new Event("controller-click")); } else { element.dispatchEvent(new Event("click")); element.dispatchEvent(new Event("controller-click")); } e.preventDefault(); break; } $(".cursor").css({ top: cursorPos[1] + "px", left: cursorPos[0] + "px", }); function lerp(a, b, n) { return (1 - n) * a + n * b; } element = document.elementFromPoint(cursorPos[0], cursorPos[1]); if (element) { let closest = element.closest("[tabindex], input, button, a"); if (closest) { elementType = 0; closest.focus(); } else { elementType = 1; element.focus(); } } console.log(element); }); } export { simulateGamepad };