1911 lines
65 KiB
JavaScript
1911 lines
65 KiB
JavaScript
(window["webpackJsonpGUI"] = window["webpackJsonpGUI"] || []).push([["addon-entry-gamepad"],{
|
|
|
|
/***/ "./node_modules/css-loader/index.js!./src/addons/addons/gamepad/gamepadlib.css":
|
|
/*!****************************************************************************!*\
|
|
!*** ./node_modules/css-loader!./src/addons/addons/gamepad/gamepadlib.css ***!
|
|
\****************************************************************************/
|
|
/*! no static exports found */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
var escape = __webpack_require__(/*! ../../../../node_modules/css-loader/lib/url/escape.js */ "./node_modules/css-loader/lib/url/escape.js");
|
|
exports = module.exports = __webpack_require__(/*! ../../../../node_modules/css-loader/lib/css-base.js */ "./node_modules/css-loader/lib/css-base.js")(false);
|
|
// imports
|
|
|
|
|
|
// module
|
|
exports.push([module.i, ".gamepadlib-selector {\n width: 100%;\n margin-bottom: 3px;\n}\n\n.gamepadlib-content {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n}\n.gamepadlib-content-buttons {\n padding-right: 10px;\n}\n\n.gamepadlib-mapping {\n display: flex;\n align-items: center;\n margin-bottom: 3px;\n}\n.gamepadlib-mapping-label {\n width: 100px;\n text-align: center;\n}\n.gamepadlib-keyinput {\n text-align: center;\n width: 75px;\n height: 25px;\n border-radius: 0;\n border: 1px solid black;\n background: white;\n color: black;\n box-sizing: border-box;\n padding: 0;\n margin: 0;\n}\n[theme=\"dark\"] .gamepadlib-keyinput {\n background-color: var(--ui-tertiary);\n border-color: var(--ui-black-transparent);\n color: var(--text-primary);\n}\n.gamepadlib-mapping[data-value=\"1\"] .gamepadlib-keyinput {\n background: yellow;\n}\n[theme=\"dark\"] .gamepadlib-mapping[data-value=\"1\"] .gamepadlib-keyinput {\n background: hsl(60, 100%, 20%)\n}\n.gamepadlib-keyinput[data-accepting-input=\"true\"] {\n background: #d6fff9;\n}\n[theme=\"dark\"] .gamepadlib-keyinput[data-accepting-input=\"true\"] {\n background: hsl(171, 100%, 20%);\n}\n.gamepadlib-keyinput[data-empty=\"true\"]:not([data-accepting-input=\"true\"]) {\n color: #aaa;\n font-style: italic;\n}\n\n.gamepadlib-axis {\n margin-bottom: 8px;\n text-align: center;\n}\n.gamepadlib-axis-circle {\n position: relative;\n width: 150px;\n height: 150px;\n border: 1px solid black;\n overflow: hidden;\n}\n[theme=\"dark\"] .gamepadlib-axis-circle {\n border-color: var(--ui-black-transparent);\n}\n.gamepadlib-axis-dot {\n position: absolute;\n top: 50%;\n left: 50%;\n background-image: url(" + escape(__webpack_require__(/*! ./dot.svg */ "./src/addons/addons/gamepad/dot.svg")) + ");\n width: 8px;\n height: 8px;\n transform: translate(-50%, -50%);\n pointer-events: none;\n}\n.gamepadlib-axis-mapping {\n width: 100%;\n}\n\n.gamepadlib-axis-circle-overlay {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n}\n.gamepadlib-axis-circle-overlay > .gamepadlib-axis-mapper {\n position: absolute;\n}\n.gamepadlib-axis-circle-overlay > .gamepadlib-axis-mapper:nth-of-type(1) {\n left: 50%;\n top: 0;\n transform: translateX(-50%);\n}\n.gamepadlib-axis-circle-overlay > .gamepadlib-axis-mapper:nth-of-type(2) {\n left: 0;\n top: 50%;\n transform: translateY(-50%);\n}\n.gamepadlib-axis-circle-overlay > .gamepadlib-axis-mapper:nth-of-type(3) {\n right: 0;\n top: 50%;\n transform: translateY(-50%);\n}\n.gamepadlib-axis-circle-overlay > .gamepadlib-axis-mapper:nth-of-type(4) {\n left: 50%;\n bottom: 0;\n transform: translateX(-50%);\n}\n", ""]);
|
|
|
|
// exports
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ "./node_modules/css-loader/index.js!./src/addons/addons/gamepad/style.css":
|
|
/*!***********************************************************************!*\
|
|
!*** ./node_modules/css-loader!./src/addons/addons/gamepad/style.css ***!
|
|
\***********************************************************************/
|
|
/*! no static exports found */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
exports = module.exports = __webpack_require__(/*! ../../../../node_modules/css-loader/lib/css-base.js */ "./node_modules/css-loader/lib/css-base.js")(false);
|
|
// imports
|
|
|
|
|
|
// module
|
|
exports.push([module.i, "[dir=\"ltr\"] .sa-gamepad-container {\n margin-right: 0.2rem;\n}\n[dir=\"rtl\"] .sa-gamepad-container {\n margin-left: 0.2rem;\n}\n\n.sa-gamepad-popup-outer {\n /* above fullscreen */\n z-index: 99999;\n}\n.sa-gamepad-popup {\n box-sizing: border-box;\n width: 700px;\n max-height: min(800px, 85vh);\n height: 100%;\n max-width: 85%;\n margin: 50px auto;\n display: flex;\n flex-direction: column;\n}\n.sa-gamepad-popup-content {\n padding: 1.5rem 2.25rem;\n height: 100%;\n overflow-y: auto;\n}\n\n.sa-gamepad-popup [class*=\"modal_header-item-title\"] {\n margin: 0 -20rem 0 0;\n}\n\n.sa-gamepad-cursor {\n position: absolute;\n top: 0;\n left: 0;\n z-index: 9999;\n user-select: none;\n pointer-events: none;\n will-change: transform;\n image-rendering: optimizeSpeed;\n image-rendering: crisp-edges;\n image-rendering: pixelated;\n}\n.sa-gamepad-cursor-down {\n filter: invert(100%);\n}\n\n.sa-gamepad-small .sa-gamepad-container[data-editor-mode=\"editor\"] {\n display: none !important;\n}\n\n.sa-gamepad-hide-cursor [class^=\"stage_stage_\"] {\n cursor: none;\n}\n\n.sa-gamepad-browser-support-warning {\n font-weight: bold;\n margin-bottom: 10px;\n}\n\n.sa-gamepad-extra-options {\n display: none;\n}\n.sa-gamepad-has-controller .sa-gamepad-extra-options {\n display: block;\n}\n\n.sa-gamepad-store-settings {\n display: block;\n}\n.sa-gamepad-store-settings > input {\n margin-right: 4px;\n}\n\n.sa-gamepad-reset-button {\n margin: 8px 8px 8px 0;\n}\n", ""]);
|
|
|
|
// exports
|
|
|
|
|
|
/***/ }),
|
|
|
|
/***/ "./node_modules/url-loader/dist/cjs.js!./src/addons/addons/gamepad/active.png":
|
|
/*!************************************************************************************!*\
|
|
!*** ./node_modules/url-loader/dist/cjs.js!./src/addons/addons/gamepad/active.png ***!
|
|
\************************************************************************************/
|
|
/*! exports provided: default */
|
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony default export */ __webpack_exports__["default"] = ("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGAQMAAADaAn0LAAAABlBMVEX///8AAABVwtN+AAAADklEQVQI12MAAQMgBAIAAkwAYUis6mUAAAAASUVORK5CYII=");
|
|
|
|
/***/ }),
|
|
|
|
/***/ "./node_modules/url-loader/dist/cjs.js!./src/addons/addons/gamepad/close.svg":
|
|
/*!***********************************************************************************!*\
|
|
!*** ./node_modules/url-loader/dist/cjs.js!./src/addons/addons/gamepad/close.svg ***!
|
|
\***********************************************************************************/
|
|
/*! exports provided: default */
|
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony default export */ __webpack_exports__["default"] = ("data:image/svg+xml;base64,PHN2ZyBkYXRhLW5hbWU9IkxheWVyIDEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDcuNDggNy40OCI+PHBhdGggZD0iTTMuNzQgNi40OFYxTTEgMy43NGg1LjQ4IiBzdHlsZT0iZmlsbDpub25lO3N0cm9rZTojZmZmO3N0cm9rZS1saW5lY2FwOnJvdW5kO3N0cm9rZS1saW5lam9pbjpyb3VuZDtzdHJva2Utd2lkdGg6MnB4Ii8+PC9zdmc+");
|
|
|
|
/***/ }),
|
|
|
|
/***/ "./node_modules/url-loader/dist/cjs.js!./src/addons/addons/gamepad/cursor.png":
|
|
/*!************************************************************************************!*\
|
|
!*** ./node_modules/url-loader/dist/cjs.js!./src/addons/addons/gamepad/cursor.png ***!
|
|
\************************************************************************************/
|
|
/*! exports provided: default */
|
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony default export */ __webpack_exports__["default"] = ("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGAQMAAADaAn0LAAAABlBMVEUAAAD///+l2Z/dAAAADklEQVQI12MAAQMgBAIAAkwAYUis6mUAAAAASUVORK5CYII=");
|
|
|
|
/***/ }),
|
|
|
|
/***/ "./node_modules/url-loader/dist/cjs.js!./src/addons/addons/gamepad/dot.svg":
|
|
/*!*********************************************************************************!*\
|
|
!*** ./node_modules/url-loader/dist/cjs.js!./src/addons/addons/gamepad/dot.svg ***!
|
|
\*********************************************************************************/
|
|
/*! exports provided: default */
|
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony default export */ __webpack_exports__["default"] = ("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI4IiBoZWlnaHQ9IjgiIHZpZXdCb3g9IjAgMCAyLjExNyAyLjExNyI+PGNpcmNsZSBjeD0iMS4wNTgiIGN5PSIxLjA1OCIgcj0iMS4wNTgiIGZpbGw9InJlZCIvPjwvc3ZnPg==");
|
|
|
|
/***/ }),
|
|
|
|
/***/ "./node_modules/url-loader/dist/cjs.js!./src/addons/addons/gamepad/gamepad.svg":
|
|
/*!*************************************************************************************!*\
|
|
!*** ./node_modules/url-loader/dist/cjs.js!./src/addons/addons/gamepad/gamepad.svg ***!
|
|
\*************************************************************************************/
|
|
/*! exports provided: default */
|
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony default export */ __webpack_exports__["default"] = ("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGFyaWEtaGlkZGVuPSJ0cnVlIiB3aWR0aD0iMWVtIiBoZWlnaHQ9IjFlbSIgc3R5bGU9Ii1tcy10cmFuc2Zvcm06cm90YXRlKDM2MGRlZyk7LXdlYmtpdC10cmFuc2Zvcm06cm90YXRlKDM2MGRlZyk7dHJhbnNmb3JtOnJvdGF0ZSgzNjBkZWcpIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiI+PHBhdGggZmlsbD0iIzU3NWU3NSIgZD0iTTM2OCAxNjhoLTk2di02NGgxNzZhMjQuMDI3IDI0LjAyNyAwIDAgMCAyNC0yNFYxNmgtMzJ2NTZIMjY0YTI0LjAyNyAyNC4wMjcgMCAwIDAtMjQgMjR2NzJoLTk2QTEyOC4xNDUgMTI4LjE0NSAwIDAgMCAxNiAyOTZ2MTAwLjk1M0E5MS4xNSA5MS4xNSAwIDAgMCAxMDcuMDQ3IDQ4OGgxLjhhOTAuODA3IDkwLjgwNyAwIDAgMCA2OS45NTMtMzIuNzZMMjMxLjUgMzkyaDQ4LjYyOGw1Mi42NjYgNjguNDY1QTkxLjA0NiA5MS4wNDYgMCAwIDAgNDk2IDQwNC45NTNWMjk2YTEyOC4xNDUgMTI4LjE0NSAwIDAgMC0xMjgtMTI4em05NiAyMzYuOTUzYTU5LjA0NyA1OS4wNDcgMCAwIDEtMTA1Ljg0OSAzNkwyOTUuODc4IDM2MGgtNzkuMzcybC02Mi4yOTQgNzQuNzU0QTU4Ljg5MyA1OC44OTMgMCAwIDEgMTA4Ljg1IDQ1NmgtMS44QTU5LjExMyA1OS4xMTMgMCAwIDEgNDggMzk2Ljk1M1YyOTZhOTYuMTA4IDk2LjEwOCAwIDAgMSA5Ni05NmgyMjRhOTYuMTA4IDk2LjEwOCAwIDAgMSA5NiA5NnoiLz48cGF0aCBmaWxsPSIjNTc1ZTc1IiBkPSJNMzYwIDI0OGgzMnYzMmgtMzJ6bTAgODBoMzJ2MzJoLTMyem0tNDAtNDBoMzJ2MzJoLTMyem04MCAwaDMydjMyaC0zMnptLTI0OC00MGgtMzJ2NDBIODB2MzJoNDB2NDBoMzJ2LTQwaDQwdi0zMmgtNDB2LTQweiIvPjwvc3ZnPg==");
|
|
|
|
/***/ }),
|
|
|
|
/***/ "./src/addons/addons/gamepad/_runtime_entry.js":
|
|
/*!*****************************************************!*\
|
|
!*** ./src/addons/addons/gamepad/_runtime_entry.js ***!
|
|
\*****************************************************/
|
|
/*! exports provided: resources */
|
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "resources", function() { return resources; });
|
|
/* harmony import */ var _userscript_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./userscript.js */ "./src/addons/addons/gamepad/userscript.js");
|
|
/* harmony import */ var _css_loader_style_css__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! css-loader!./style.css */ "./node_modules/css-loader/index.js!./src/addons/addons/gamepad/style.css");
|
|
/* harmony import */ var _css_loader_style_css__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_style_css__WEBPACK_IMPORTED_MODULE_1__);
|
|
/* harmony import */ var _css_loader_gamepadlib_css__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! css-loader!./gamepadlib.css */ "./node_modules/css-loader/index.js!./src/addons/addons/gamepad/gamepadlib.css");
|
|
/* harmony import */ var _css_loader_gamepadlib_css__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(_css_loader_gamepadlib_css__WEBPACK_IMPORTED_MODULE_2__);
|
|
/* harmony import */ var _url_loader_active_png__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! url-loader!./active.png */ "./node_modules/url-loader/dist/cjs.js!./src/addons/addons/gamepad/active.png");
|
|
/* harmony import */ var _url_loader_close_svg__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! url-loader!./close.svg */ "./node_modules/url-loader/dist/cjs.js!./src/addons/addons/gamepad/close.svg");
|
|
/* harmony import */ var _url_loader_cursor_png__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! url-loader!./cursor.png */ "./node_modules/url-loader/dist/cjs.js!./src/addons/addons/gamepad/cursor.png");
|
|
/* harmony import */ var _url_loader_dot_svg__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! url-loader!./dot.svg */ "./node_modules/url-loader/dist/cjs.js!./src/addons/addons/gamepad/dot.svg");
|
|
/* harmony import */ var _url_loader_gamepad_svg__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(/*! url-loader!./gamepad.svg */ "./node_modules/url-loader/dist/cjs.js!./src/addons/addons/gamepad/gamepad.svg");
|
|
/* generated by pull.js */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const resources = {
|
|
"userscript.js": _userscript_js__WEBPACK_IMPORTED_MODULE_0__["default"],
|
|
"style.css": _css_loader_style_css__WEBPACK_IMPORTED_MODULE_1___default.a,
|
|
"gamepadlib.css": _css_loader_gamepadlib_css__WEBPACK_IMPORTED_MODULE_2___default.a,
|
|
"active.png": _url_loader_active_png__WEBPACK_IMPORTED_MODULE_3__["default"],
|
|
"close.svg": _url_loader_close_svg__WEBPACK_IMPORTED_MODULE_4__["default"],
|
|
"cursor.png": _url_loader_cursor_png__WEBPACK_IMPORTED_MODULE_5__["default"],
|
|
"dot.svg": _url_loader_dot_svg__WEBPACK_IMPORTED_MODULE_6__["default"],
|
|
"gamepad.svg": _url_loader_gamepad_svg__WEBPACK_IMPORTED_MODULE_7__["default"]
|
|
};
|
|
|
|
/***/ }),
|
|
|
|
/***/ "./src/addons/addons/gamepad/dot.svg":
|
|
/*!*******************************************!*\
|
|
!*** ./src/addons/addons/gamepad/dot.svg ***!
|
|
\*******************************************/
|
|
/*! no static exports found */
|
|
/***/ (function(module, exports, __webpack_require__) {
|
|
|
|
module.exports = __webpack_require__.p + "static/assets/88a77444f0bb453209bf1c62becbd37c.svg";
|
|
|
|
/***/ }),
|
|
|
|
/***/ "./src/addons/addons/gamepad/gamepadlib.js":
|
|
/*!*************************************************!*\
|
|
!*** ./src/addons/addons/gamepad/gamepadlib.js ***!
|
|
\*************************************************/
|
|
/*! exports provided: default */
|
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony import */ var _event_target_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../../event-target.js */ "./src/addons/event-target.js");
|
|
|
|
/* inserted by pull.js */
|
|
|
|
let console = window.console;
|
|
/*
|
|
Mapping types:
|
|
|
|
type: "key" maps a button to a keyboard key
|
|
All key names will be interpreted as a KeyboardEvent.key value (https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key/Key_Values)
|
|
high: "KeyName" is the name of the key to dispatch when a button reads a HIGH value
|
|
low: "KeyName" is the name of the key to dispatch when a button reads a LOW value
|
|
deadZone: 0.5 controls the minimum value necessary to be read in either + or - to trigger either high or low
|
|
The high/low distinction is necessary for axes. Buttons will only use high
|
|
|
|
type: "mousedown" maps a button to control whether the mouse is down or not
|
|
deadZone: 0.5 controls the minimum value to trigger a mousedown
|
|
button: 0, 1, 2, etc. controls which button to press
|
|
|
|
type: "virtual_cursor" maps a button to control the "virtual cursor"
|
|
deadZone: 0.5 again controls the minimum value to trigger a movement
|
|
sensitivity: 10 controls the speed
|
|
high: "+y"/"-y"/"+x"/"-x" defines what happens when an axis reads high
|
|
low: "+y"/"-y"/"+x"/"-x" defines what happens when an axis reads low
|
|
+y increases y, -y decreases y, +x increases x, -x decreases x.
|
|
*/
|
|
|
|
const defaultAxesMappings = {
|
|
arrows: [{
|
|
type: "key",
|
|
high: "ArrowRight",
|
|
low: "ArrowLeft",
|
|
deadZone: 0.5
|
|
}, {
|
|
type: "key",
|
|
high: "ArrowDown",
|
|
low: "ArrowUp",
|
|
deadZone: 0.5
|
|
}],
|
|
wasd: [{
|
|
type: "key",
|
|
high: "d",
|
|
low: "a",
|
|
deadZone: 0.5
|
|
}, {
|
|
type: "key",
|
|
high: "s",
|
|
low: "w",
|
|
deadZone: 0.5
|
|
}],
|
|
cursor: [{
|
|
type: "virtual_cursor",
|
|
high: "+x",
|
|
low: "-x",
|
|
sensitivity: 0.6,
|
|
deadZone: 0.2
|
|
}, {
|
|
type: "virtual_cursor",
|
|
high: "-y",
|
|
low: "+y",
|
|
sensitivity: 0.6,
|
|
deadZone: 0.2
|
|
}]
|
|
};
|
|
|
|
const emptyMapping = () => ({
|
|
type: "key",
|
|
high: null,
|
|
low: null
|
|
});
|
|
|
|
const transformAndCopyMapping = mapping => {
|
|
if (typeof mapping !== "object" || !mapping) {
|
|
console.warn("invalid mapping", mapping);
|
|
return emptyMapping();
|
|
}
|
|
|
|
const copy = Object.assign({}, mapping);
|
|
|
|
if (copy.type === "key") {
|
|
if (typeof copy.deadZone === "undefined") {
|
|
copy.deadZone = 0.5;
|
|
}
|
|
|
|
if (typeof copy.high === "undefined") {
|
|
copy.high = "";
|
|
}
|
|
|
|
if (typeof copy.low === "undefined") {
|
|
copy.low = "";
|
|
}
|
|
} else if (copy.type === "mousedown") {
|
|
if (typeof copy.deadZone === "undefined") {
|
|
copy.deadZone = 0.5;
|
|
}
|
|
|
|
if (typeof copy.button === "undefined") {
|
|
copy.button = 0;
|
|
}
|
|
} else if (copy.type === "virtual_cursor") {
|
|
if (typeof copy.high === "undefined") {
|
|
copy.high = "";
|
|
}
|
|
|
|
if (typeof copy.low === "undefined") {
|
|
copy.low = "";
|
|
}
|
|
|
|
if (typeof copy.sensitivity === "undefined") {
|
|
copy.sensitivity = 10;
|
|
}
|
|
|
|
if (typeof copy.deadZone === "undefined") {
|
|
copy.deadZone = 0.5;
|
|
}
|
|
} else {
|
|
console.warn("unknown mapping type", copy.type);
|
|
return emptyMapping();
|
|
}
|
|
|
|
return copy;
|
|
};
|
|
|
|
const prepareMappingForExport = mapping => Object.assign({}, mapping);
|
|
|
|
const prepareAxisMappingForExport = prepareMappingForExport;
|
|
|
|
const prepareButtonMappingForExport = mapping => {
|
|
const copy = prepareMappingForExport(mapping);
|
|
delete copy.deadZone;
|
|
delete copy.low;
|
|
return copy;
|
|
};
|
|
|
|
const padWithEmptyMappings = (array, length) => {
|
|
// Keep adding empty mappings until the list is full
|
|
while (array.length < length) {
|
|
array.push(emptyMapping());
|
|
} // In case the input array is longer than the desired length
|
|
|
|
|
|
array.length = length;
|
|
return array;
|
|
};
|
|
|
|
const createEmptyMappingList = length => padWithEmptyMappings([], length);
|
|
|
|
const getMovementConfiguration = usedKeys => ({
|
|
usesArrows: usedKeys.has("ArrowUp") || usedKeys.has("ArrowDown") || usedKeys.has("ArrowRight") || usedKeys.has("ArrowLeft"),
|
|
usesWASD: usedKeys.has("w") && usedKeys.has("s") || usedKeys.has("a") && usedKeys.has("d")
|
|
});
|
|
|
|
const getGamepadId = gamepad => "".concat(gamepad.id, " (").concat(gamepad.index, ")");
|
|
|
|
class GamepadData {
|
|
/**
|
|
* @param {Gamepad} gamepad Source Gamepad
|
|
* @param {GamepadLib} gamepadLib Parent GamepadLib
|
|
*/
|
|
constructor(gamepad, gamepadLib) {
|
|
this.gamepad = gamepad;
|
|
this.gamepadLib = gamepadLib;
|
|
this.resetMappings();
|
|
}
|
|
|
|
resetMappings() {
|
|
this.hints = this.gamepadLib.getHints();
|
|
this.buttonMappings = this.getDefaultButtonMappings().map(transformAndCopyMapping);
|
|
this.axesMappings = this.getDefaultAxisMappings().map(transformAndCopyMapping);
|
|
}
|
|
|
|
clearMappings() {
|
|
this.buttonMappings = createEmptyMappingList(this.gamepad.buttons.length);
|
|
this.axesMappings = createEmptyMappingList(this.gamepad.axes.length);
|
|
}
|
|
|
|
getDefaultButtonMappings() {
|
|
let buttons;
|
|
|
|
if (this.hints.importedSettings) {
|
|
buttons = this.hints.importedSettings.buttons;
|
|
} else {
|
|
const usedKeys = this.hints.usedKeys;
|
|
const alreadyUsedKeys = new Set();
|
|
const {
|
|
usesArrows,
|
|
usesWASD
|
|
} = getMovementConfiguration(usedKeys);
|
|
|
|
if (usesWASD) {
|
|
alreadyUsedKeys.add("w");
|
|
alreadyUsedKeys.add("a");
|
|
alreadyUsedKeys.add("s");
|
|
alreadyUsedKeys.add("d");
|
|
}
|
|
|
|
const possiblePauseKeys = [// Restart keys, pause keys, other potentially dangerous keys
|
|
"p", "q", "r"];
|
|
const possibleActionKeys = [" ", "Enter", "e", "f", "z", "x", "c", ...Array.from(usedKeys).filter(i => i.length === 1 && !possiblePauseKeys.includes(i))];
|
|
|
|
const findKey = keys => {
|
|
for (const key of keys) {
|
|
if (usedKeys.has(key) && !alreadyUsedKeys.has(key)) {
|
|
alreadyUsedKeys.add(key);
|
|
return key;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
const getPrimaryAction = () => {
|
|
if (usesArrows && usedKeys.has("ArrowUp")) {
|
|
return "ArrowUp";
|
|
}
|
|
|
|
if (usesWASD && usedKeys.has("w")) {
|
|
return "w";
|
|
}
|
|
|
|
return findKey(possibleActionKeys);
|
|
};
|
|
|
|
const getSecondaryAction = () => findKey(possibleActionKeys);
|
|
|
|
const getPauseKey = () => findKey(possiblePauseKeys);
|
|
|
|
const getUp = () => {
|
|
if (usesArrows || !usesWASD) return "ArrowUp";
|
|
return "w";
|
|
};
|
|
|
|
const getDown = () => {
|
|
if (usesArrows || !usesWASD) return "ArrowDown";
|
|
return "s";
|
|
};
|
|
|
|
const getRight = () => {
|
|
if (usesArrows || !usesWASD) return "ArrowRight";
|
|
return "d";
|
|
};
|
|
|
|
const getLeft = () => {
|
|
if (usesArrows || !usesWASD) return "ArrowLeft";
|
|
return "a";
|
|
};
|
|
|
|
const action1 = getPrimaryAction();
|
|
let action2 = getSecondaryAction();
|
|
let action3 = getSecondaryAction();
|
|
let action4 = getSecondaryAction(); // When only 1 or 2 action keys are detected, bind the other buttons to the same things.
|
|
|
|
if (action1 && !action2 && !action3 && !action4) {
|
|
action2 = action1;
|
|
action3 = action1;
|
|
action4 = action1;
|
|
}
|
|
|
|
if (action1 && action2 && !action3 && !action4) {
|
|
action3 = action1;
|
|
action4 = action2;
|
|
} // Set indices "manually" because we don't evaluate them in order.
|
|
|
|
|
|
buttons = [];
|
|
buttons[0] = {
|
|
/*
|
|
Xbox: A
|
|
SNES-like: B
|
|
*/
|
|
type: "key",
|
|
high: action1
|
|
};
|
|
buttons[1] = {
|
|
/*
|
|
Xbox: B
|
|
SNES-like: A
|
|
*/
|
|
type: "key",
|
|
high: action2
|
|
};
|
|
buttons[2] = {
|
|
/*
|
|
Xbox: X
|
|
SNES-like: Y
|
|
*/
|
|
type: "key",
|
|
high: action3
|
|
};
|
|
buttons[3] = {
|
|
/*
|
|
Xbox: Y
|
|
SNES-like: X
|
|
*/
|
|
type: "key",
|
|
high: action4
|
|
};
|
|
buttons[4] = {
|
|
/*
|
|
Xbox: LB
|
|
SNES-like: Left trigger
|
|
*/
|
|
type: "mousedown"
|
|
};
|
|
buttons[5] = {
|
|
/*
|
|
Xbox: RB
|
|
*/
|
|
type: "mousedown"
|
|
};
|
|
buttons[6] = {
|
|
/*
|
|
Xbox: LT
|
|
*/
|
|
type: "mousedown"
|
|
};
|
|
buttons[7] = {
|
|
/*
|
|
Xbox: RT
|
|
SNES-like: Right trigger
|
|
*/
|
|
type: "mousedown"
|
|
};
|
|
buttons[9] = {
|
|
/*
|
|
Xbox: Menu
|
|
SNES-like: Start
|
|
*/
|
|
type: "key",
|
|
high: getPauseKey()
|
|
};
|
|
buttons[8] = {
|
|
/*
|
|
Xbox: Change view
|
|
SNES-like: Select
|
|
*/
|
|
type: "key",
|
|
high: getPauseKey()
|
|
}; // Xbox: Left analog press
|
|
|
|
buttons[10] = emptyMapping(); // Xbox: Right analog press
|
|
|
|
buttons[11] = emptyMapping();
|
|
buttons[12] = {
|
|
/*
|
|
Xbox: D-pad up
|
|
*/
|
|
type: "key",
|
|
high: getUp()
|
|
};
|
|
buttons[13] = {
|
|
/*
|
|
Xbox: D-pad down
|
|
*/
|
|
type: "key",
|
|
high: getDown()
|
|
};
|
|
buttons[14] = {
|
|
/*
|
|
Xbox: D-pad left
|
|
*/
|
|
type: "key",
|
|
high: getLeft()
|
|
};
|
|
buttons[15] = {
|
|
/*
|
|
Xbox: D-pad right
|
|
*/
|
|
type: "key",
|
|
high: getRight()
|
|
};
|
|
}
|
|
|
|
return padWithEmptyMappings(buttons, this.gamepad.buttons.length);
|
|
}
|
|
|
|
getDefaultAxisMappings() {
|
|
let axes = [];
|
|
|
|
if (this.hints.importedSettings) {
|
|
axes = this.hints.importedSettings.axes;
|
|
} else {
|
|
// Only return default axis mappings when there are 4 axes, like an xbox controller
|
|
// If there isn't exactly 4, we can't really predict what the axes mean
|
|
// Some controllers map the dpad to *both* buttons and axes at the same time, which would cause conflicts.
|
|
if (this.gamepad.axes.length === 4) {
|
|
const usedKeys = this.hints.usedKeys;
|
|
const {
|
|
usesArrows,
|
|
usesWASD
|
|
} = getMovementConfiguration(usedKeys);
|
|
|
|
if (usesWASD) {
|
|
axes.push(defaultAxesMappings.wasd[0]);
|
|
axes.push(defaultAxesMappings.wasd[1]);
|
|
} else if (usesArrows) {
|
|
axes.push(defaultAxesMappings.arrows[0]);
|
|
axes.push(defaultAxesMappings.arrows[1]);
|
|
} else {
|
|
axes.push(defaultAxesMappings.cursor[0]);
|
|
axes.push(defaultAxesMappings.cursor[1]);
|
|
}
|
|
|
|
axes.push(defaultAxesMappings.cursor[0]);
|
|
axes.push(defaultAxesMappings.cursor[1]);
|
|
}
|
|
}
|
|
|
|
return padWithEmptyMappings(axes, this.gamepad.axes.length);
|
|
}
|
|
|
|
}
|
|
|
|
const defaultHints = () => ({
|
|
usedKeys: new Set(),
|
|
importedSettings: null,
|
|
generated: false
|
|
});
|
|
|
|
class GamepadLib extends _event_target_js__WEBPACK_IMPORTED_MODULE_0__["default"] {
|
|
constructor() {
|
|
super();
|
|
/** @type {Map<string, GamepadData>} */
|
|
|
|
this.gamepads = new Map();
|
|
this.handleConnect = this.handleConnect.bind(this);
|
|
this.handleDisconnect = this.handleDisconnect.bind(this);
|
|
this.update = this.update.bind(this);
|
|
this.animationFrame = null;
|
|
this.currentTime = null;
|
|
this.deltaTime = 0;
|
|
this.virtualCursor = {
|
|
x: 0,
|
|
y: 0,
|
|
maxX: Infinity,
|
|
minX: -Infinity,
|
|
maxY: Infinity,
|
|
minY: -Infinity,
|
|
modified: false
|
|
};
|
|
this._editor = null;
|
|
this.connectCallbacks = [];
|
|
this.keysPressedThisFrame = new Set();
|
|
this.oldKeysPressed = new Set();
|
|
this.mouseButtonsPressedThisFrame = new Set();
|
|
this.oldMouseDown = new Set();
|
|
this.addEventHandlers();
|
|
}
|
|
|
|
addEventHandlers() {
|
|
window.addEventListener("gamepadconnected", this.handleConnect);
|
|
window.addEventListener("gamepaddisconnected", this.handleDisconnect);
|
|
}
|
|
|
|
removeEventHandlers() {
|
|
window.removeEventListener("gamepadconnected", this.handleConnect);
|
|
window.removeEventListener("gamepaddisconnected", this.handleDisconnect);
|
|
}
|
|
|
|
gamepadConnected() {
|
|
if (this.gamepads.size > 0) {
|
|
return Promise.resolve();
|
|
}
|
|
|
|
return new Promise(resolve => {
|
|
this.connectCallbacks.push(resolve);
|
|
});
|
|
}
|
|
|
|
getHints() {
|
|
return Object.assign(defaultHints(), this.getUserHints());
|
|
}
|
|
|
|
getUserHints() {
|
|
// to be overridden by users
|
|
return {};
|
|
}
|
|
|
|
resetControls() {
|
|
for (const gamepad of this.gamepads.values()) {
|
|
gamepad.resetMappings();
|
|
}
|
|
}
|
|
|
|
clearControls() {
|
|
for (const gamepad of this.gamepads.values()) {
|
|
gamepad.clearMappings();
|
|
}
|
|
}
|
|
|
|
handleConnect(e) {
|
|
for (const callback of this.connectCallbacks) {
|
|
callback();
|
|
}
|
|
|
|
this.connectCallbacks = [];
|
|
const gamepad = e.gamepad;
|
|
const id = getGamepadId(gamepad);
|
|
console.log("connected", gamepad);
|
|
const gamepadData = new GamepadData(gamepad, this);
|
|
this.gamepads.set(id, gamepadData);
|
|
|
|
if (this.animationFrame === null) {
|
|
this.animationFrame = requestAnimationFrame(this.update);
|
|
}
|
|
|
|
this.dispatchEvent(new CustomEvent("gamepadconnected", {
|
|
detail: gamepadData
|
|
}));
|
|
}
|
|
|
|
handleDisconnect(e) {
|
|
const gamepad = e.gamepad;
|
|
const id = getGamepadId(gamepad);
|
|
console.log("disconnected", gamepad);
|
|
const gamepadData = this.gamepads.get(id);
|
|
this.gamepads.delete(id);
|
|
this.dispatchEvent(new CustomEvent("gamepaddisconnected", {
|
|
detail: gamepadData
|
|
}));
|
|
|
|
if (this.gamepads.size === 0) {
|
|
cancelAnimationFrame(this.animationFrame);
|
|
this.animationFrame = null;
|
|
this.currentTime = null;
|
|
}
|
|
}
|
|
|
|
dispatchKey(key, pressed) {
|
|
if (pressed) {
|
|
this.dispatchEvent(new CustomEvent("keydown", {
|
|
detail: key
|
|
}));
|
|
} else {
|
|
this.dispatchEvent(new CustomEvent("keyup", {
|
|
detail: key
|
|
}));
|
|
}
|
|
}
|
|
|
|
dispatchMouse(button, down) {
|
|
if (down) {
|
|
this.dispatchEvent(new CustomEvent("mousedown", {
|
|
detail: button
|
|
}));
|
|
} else {
|
|
this.dispatchEvent(new CustomEvent("mouseup", {
|
|
detail: button
|
|
}));
|
|
}
|
|
}
|
|
|
|
dispatchMouseMove(x, y) {
|
|
this.dispatchEvent(new CustomEvent("mousemove", {
|
|
detail: {
|
|
x,
|
|
y
|
|
}
|
|
}));
|
|
}
|
|
|
|
updateButton(value, mapping) {
|
|
if (mapping.type === "key") {
|
|
if (value >= mapping.deadZone) {
|
|
if (mapping.high) {
|
|
this.keysPressedThisFrame.add(mapping.high);
|
|
}
|
|
} else if (value <= -mapping.deadZone) {
|
|
if (mapping.low) {
|
|
this.keysPressedThisFrame.add(mapping.low);
|
|
}
|
|
}
|
|
} else if (mapping.type === "mousedown") {
|
|
const isDown = Math.abs(value) >= mapping.deadZone;
|
|
|
|
if (isDown) {
|
|
this.mouseButtonsPressedThisFrame.add(mapping.button);
|
|
}
|
|
} else if (mapping.type === "virtual_cursor") {
|
|
const deadZone = mapping.deadZone;
|
|
let action;
|
|
if (value >= deadZone) action = mapping.high;
|
|
if (value <= -deadZone) action = mapping.low;
|
|
|
|
if (action) {
|
|
// an axis value just beyond the deadzone should have a multiplier near 0, a high value should have a multiplier of 1
|
|
const multiplier = (Math.abs(value) - deadZone) / (1 - deadZone);
|
|
const speed = multiplier * multiplier * mapping.sensitivity * this.deltaTime;
|
|
|
|
if (action === "+x") {
|
|
this.virtualCursor.x += speed;
|
|
} else if (action === "-x") {
|
|
this.virtualCursor.x -= speed;
|
|
} else if (action === "+y") {
|
|
this.virtualCursor.y += speed;
|
|
} else if (action === "-y") {
|
|
this.virtualCursor.y -= speed;
|
|
}
|
|
|
|
this.virtualCursor.modified = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
update(time) {
|
|
this.oldKeysPressed = this.keysPressedThisFrame;
|
|
this.oldMouseButtonsPressed = this.mouseButtonsPressedThisFrame;
|
|
this.keysPressedThisFrame = new Set();
|
|
this.mouseButtonsPressedThisFrame = new Set();
|
|
|
|
if (this.currentTime === null) {
|
|
this.deltaTime = 0; // doesn't matter what this is, it's just the first frame
|
|
} else {
|
|
this.deltaTime = time - this.currentTime;
|
|
}
|
|
|
|
this.deltaTime = Math.max(Math.min(this.deltaTime, 1000), 0);
|
|
this.currentTime = time;
|
|
this.animationFrame = requestAnimationFrame(this.update);
|
|
const gamepads = navigator.getGamepads();
|
|
|
|
for (const gamepad of gamepads) {
|
|
if (gamepad === null) {
|
|
continue;
|
|
}
|
|
|
|
const id = getGamepadId(gamepad);
|
|
const data = this.gamepads.get(id);
|
|
|
|
for (let i = 0; i < gamepad.buttons.length; i++) {
|
|
const button = gamepad.buttons[i];
|
|
const value = button.value;
|
|
const mapping = data.buttonMappings[i];
|
|
this.updateButton(value, mapping);
|
|
}
|
|
|
|
for (let i = 0; i < gamepad.axes.length; i++) {
|
|
const axis = gamepad.axes[i];
|
|
const mapping = data.axesMappings[i];
|
|
this.updateButton(axis, mapping);
|
|
}
|
|
}
|
|
|
|
if (this._editor) {
|
|
this._editor.update(gamepads);
|
|
}
|
|
|
|
for (const key of this.keysPressedThisFrame) {
|
|
if (!this.oldKeysPressed.has(key)) {
|
|
this.dispatchKey(key, true);
|
|
}
|
|
}
|
|
|
|
for (const key of this.oldKeysPressed) {
|
|
if (!this.keysPressedThisFrame.has(key)) {
|
|
this.dispatchKey(key, false);
|
|
}
|
|
}
|
|
|
|
for (const button of this.mouseButtonsPressedThisFrame) {
|
|
if (!this.oldMouseButtonsPressed.has(button)) {
|
|
this.dispatchMouse(button, true);
|
|
}
|
|
}
|
|
|
|
for (const button of this.oldMouseButtonsPressed) {
|
|
if (!this.mouseButtonsPressedThisFrame.has(button)) {
|
|
this.dispatchMouse(button, false);
|
|
}
|
|
}
|
|
|
|
if (this.virtualCursor.modified) {
|
|
this.virtualCursor.modified = false;
|
|
|
|
if (this.virtualCursor.x > this.virtualCursor.maxX) {
|
|
this.virtualCursor.x = this.virtualCursor.maxX;
|
|
}
|
|
|
|
if (this.virtualCursor.x < this.virtualCursor.minX) {
|
|
this.virtualCursor.x = this.virtualCursor.minX;
|
|
}
|
|
|
|
if (this.virtualCursor.y > this.virtualCursor.maxY) {
|
|
this.virtualCursor.y = this.virtualCursor.maxY;
|
|
}
|
|
|
|
if (this.virtualCursor.y < this.virtualCursor.minY) {
|
|
this.virtualCursor.y = this.virtualCursor.minY;
|
|
}
|
|
|
|
this.dispatchMouseMove(this.virtualCursor.x, this.virtualCursor.y);
|
|
}
|
|
}
|
|
|
|
editor() {
|
|
if (!this._editor) {
|
|
this._editor = new GamepadEditor(this);
|
|
}
|
|
|
|
return this._editor;
|
|
}
|
|
|
|
}
|
|
|
|
GamepadLib.browserHasBrokenGamepadAPI = () => {
|
|
// Check that the gamepad API is supported at all
|
|
if (!navigator.getGamepads) {
|
|
return true;
|
|
} // Firefox on Linux has a broken gamepad API implementation that results in strange and sometimes unusable mappings
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1643358
|
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1643835
|
|
|
|
|
|
if (navigator.userAgent.includes("Firefox") && navigator.userAgent.includes("Linux")) {
|
|
return true;
|
|
} // Firefox on macOS has other bugs that result in strange and unusable mappings
|
|
// eg. https://bugzilla.mozilla.org/show_bug.cgi?id=1434408
|
|
|
|
|
|
if (navigator.userAgent.includes("Firefox") && navigator.userAgent.includes("Mac OS")) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
GamepadLib.setConsole = n => console = n;
|
|
|
|
const removeAllChildren = el => {
|
|
while (el.firstChild) {
|
|
el.removeChild(el.firstChild);
|
|
}
|
|
};
|
|
|
|
const buttonHtmlId = index => "gamepadlib-button-".concat(index);
|
|
|
|
const axisHtmlId = n => "gamepadlib-axis-".concat(n);
|
|
|
|
class GamepadEditor extends _event_target_js__WEBPACK_IMPORTED_MODULE_0__["default"] {
|
|
constructor(gamepadLib) {
|
|
super();
|
|
/** @type {GamepadLib} */
|
|
|
|
this.gamepadLib = gamepadLib;
|
|
this.root = Object.assign(document.createElement("div"), {
|
|
className: "gamepadlib-root"
|
|
});
|
|
this.selector = Object.assign(document.createElement("select"), {
|
|
className: "gamepadlib-selector"
|
|
});
|
|
this.content = Object.assign(document.createElement("div"), {
|
|
className: "gamepadlib-content"
|
|
});
|
|
this.root.appendChild(this.selector);
|
|
this.root.appendChild(this.content);
|
|
this.onSelectorChange = this.onSelectorChange.bind(this);
|
|
this.onGamepadsChange = this.onGamepadsChange.bind(this);
|
|
this.selector.addEventListener("change", this.onSelectorChange);
|
|
this.gamepadLib.addEventListener("gamepadconnected", this.onGamepadsChange);
|
|
this.gamepadLib.addEventListener("gamepaddisconnected", this.onGamepadsChange);
|
|
this.buttonIdToElement = new Map();
|
|
this.axisIdToElement = new Map();
|
|
this.hidden = false; // should be overridden later
|
|
|
|
this.msg = (id, opts) => id;
|
|
}
|
|
|
|
onSelectorChange() {
|
|
this.updateContent();
|
|
this.dispatchEvent(new CustomEvent("gamepad-changed"));
|
|
}
|
|
|
|
onGamepadsChange() {
|
|
this.updateAllContent();
|
|
this.dispatchEvent(new CustomEvent("gamepad-changed"));
|
|
}
|
|
|
|
updateAllContent() {
|
|
this.updateDropdown();
|
|
this.updateContent();
|
|
this.focus();
|
|
}
|
|
|
|
updateDropdown() {
|
|
removeAllChildren(this.selector);
|
|
const gamepads = Array.from(this.gamepadLib.gamepads.entries());
|
|
|
|
if (gamepads.length === 0) {
|
|
this.selector.hidden = true;
|
|
return;
|
|
}
|
|
|
|
this.selector.hidden = false;
|
|
|
|
for (const [id, _] of gamepads) {
|
|
const option = document.createElement("option");
|
|
option.textContent = id;
|
|
option.value = id;
|
|
this.selector.appendChild(option);
|
|
}
|
|
}
|
|
|
|
keyToString(key) {
|
|
if (key === " ") return this.msg("key-space");
|
|
if (key === "ArrowUp") return this.msg("key-up");
|
|
if (key === "ArrowDown") return this.msg("key-down");
|
|
if (key === "ArrowLeft") return this.msg("key-left");
|
|
if (key === "ArrowRight") return this.msg("key-right");
|
|
if (key === "Enter") return this.msg("key-enter");
|
|
|
|
if (key.length === 1) {
|
|
return key.toUpperCase();
|
|
} // Convert eg. "PageUp" -> "Page Up"
|
|
|
|
|
|
return key.replace(/[a-z]([A-Z])/, n => "".concat(n[0], " ").concat(n[1]));
|
|
}
|
|
|
|
createButtonMapping(mappingList, index, {
|
|
property = "high",
|
|
allowClick = true
|
|
} = {}) {
|
|
const input = document.createElement("input");
|
|
input.readOnly = true;
|
|
input.className = "gamepadlib-keyinput";
|
|
input.title = this.msg("keyinput-title");
|
|
input.dataset.index = index;
|
|
|
|
const update = () => {
|
|
const mapping = mappingList[index];
|
|
input.dataset.empty = false;
|
|
|
|
if (mapping.type === "key") {
|
|
if (mapping[property] === null) {
|
|
input.value = this.msg("key-none");
|
|
input.dataset.empty = true;
|
|
} else {
|
|
input.value = this.keyToString(mapping[property]);
|
|
}
|
|
} else if (mapping.type === "mousedown") {
|
|
let value = this.msg("key-click");
|
|
|
|
if (mapping.button !== 0) {
|
|
value += " (".concat(mapping.button, ")");
|
|
}
|
|
|
|
input.value = value;
|
|
} else {
|
|
// should never happen
|
|
input.value = "??? ".concat(mapping.type);
|
|
}
|
|
};
|
|
|
|
const changedMapping = () => {
|
|
mappingList[index] = transformAndCopyMapping(mappingList[index]);
|
|
isAcceptingInput = false;
|
|
input.blur();
|
|
update();
|
|
input.dispatchEvent(new CustomEvent("mapping-changed"));
|
|
this.changed();
|
|
};
|
|
|
|
let isAcceptingInput = false;
|
|
|
|
const handleClick = e => {
|
|
e.preventDefault();
|
|
|
|
if (isAcceptingInput) {
|
|
if (allowClick) {
|
|
const mapping = mappingList[index];
|
|
mapping.type = "mousedown";
|
|
mapping.button = e.button;
|
|
changedMapping();
|
|
} else {
|
|
handleBlur();
|
|
}
|
|
} else {
|
|
input.value = "...";
|
|
input.dataset.acceptingInput = true;
|
|
isAcceptingInput = true;
|
|
}
|
|
};
|
|
|
|
const handleKeyEvent = e => {
|
|
if (isAcceptingInput) {
|
|
e.preventDefault();
|
|
const key = e.key; // TW: We allow binding to control and shift
|
|
|
|
if (["Alt"].includes(key)) {
|
|
return;
|
|
}
|
|
|
|
const mapping = mappingList[index];
|
|
const KEYS = ["ArrowUp", "ArrowDown", "ArrowRight", "ArrowLeft", "Enter", // TW: We support more keys
|
|
// "Backspace",
|
|
// "Delete",
|
|
"Shift", "CapsLock", "ScrollLock", "Control", // "Escape",
|
|
"Insert", "Home", "End", "PageUp", "PageDown"];
|
|
|
|
if (key.length === 1 || KEYS.includes(key)) {
|
|
mapping.type = "key";
|
|
mapping[property] = key;
|
|
} else if (key !== "Escape") {
|
|
mapping.type = "key";
|
|
mapping[property] = null;
|
|
}
|
|
|
|
changedMapping();
|
|
} else if (e.key === "Enter") {
|
|
e.preventDefault();
|
|
e.target.click();
|
|
}
|
|
};
|
|
|
|
const MODIFIER_KEYS = ["Shift", "Control"];
|
|
|
|
const handleKeyDown = e => {
|
|
if (!MODIFIER_KEYS.includes(e.key)) handleKeyEvent(e);
|
|
};
|
|
|
|
const handleKeyUp = e => {
|
|
if (MODIFIER_KEYS.includes(e.key)) handleKeyEvent(e);
|
|
};
|
|
|
|
const handleBlur = () => {
|
|
input.dataset.acceptingInput = false;
|
|
|
|
if (isAcceptingInput) {
|
|
isAcceptingInput = false;
|
|
update();
|
|
}
|
|
};
|
|
|
|
input.addEventListener("contextmenu", e => {
|
|
e.preventDefault();
|
|
});
|
|
input.addEventListener("mouseup", handleClick);
|
|
input.addEventListener("keydown", handleKeyDown);
|
|
input.addEventListener("keyup", handleKeyUp);
|
|
input.addEventListener("blur", handleBlur);
|
|
update();
|
|
return input;
|
|
}
|
|
|
|
createAxisMapping(mappingList, index) {
|
|
const selector = document.createElement("select");
|
|
selector.className = "gamepadlib-axis-mapping";
|
|
selector.id = axisHtmlId(index);
|
|
selector.appendChild(Object.assign(document.createElement("option"), {
|
|
textContent: this.msg("axis-none"),
|
|
value: "none"
|
|
}));
|
|
selector.appendChild(Object.assign(document.createElement("option"), {
|
|
textContent: this.msg("axis-cursor"),
|
|
value: "cursor"
|
|
}));
|
|
selector.appendChild(Object.assign(document.createElement("option"), {
|
|
// doesn't really make sense to translate
|
|
textContent: "WASD",
|
|
value: "wasd"
|
|
}));
|
|
selector.appendChild(Object.assign(document.createElement("option"), {
|
|
textContent: this.msg("axis-arrows"),
|
|
value: "arrows"
|
|
}));
|
|
selector.appendChild(Object.assign(document.createElement("option"), {
|
|
textContent: this.msg("axis-custom"),
|
|
value: "custom"
|
|
}));
|
|
|
|
const updateDropdownValue = () => {
|
|
if (mappingList[index].type === "key" || mappingList[index].type === "mousedown") {
|
|
if (mappingList[index].high === null && mappingList[index].low === null && mappingList[index + 1].high === null && mappingList[index + 1].low === null) {
|
|
selector.value = "none";
|
|
} else if (mappingList[index].high === defaultAxesMappings.wasd[0].high && mappingList[index].low === defaultAxesMappings.wasd[0].low && mappingList[index + 1].high === defaultAxesMappings.wasd[1].high && mappingList[index + 1].low === defaultAxesMappings.wasd[1].low) {
|
|
selector.value = "wasd";
|
|
} else if (mappingList[index].high === defaultAxesMappings.arrows[0].high && mappingList[index].low === defaultAxesMappings.arrows[0].low && mappingList[index + 1].high === defaultAxesMappings.arrows[1].high && mappingList[index + 1].low === defaultAxesMappings.arrows[1].low) {
|
|
selector.value = "arrows";
|
|
} else {
|
|
selector.value = "custom";
|
|
}
|
|
} else if (mappingList[index].type === "virtual_cursor") {
|
|
selector.value = "cursor";
|
|
} else {
|
|
// should never happen
|
|
selector.value = "none";
|
|
}
|
|
};
|
|
|
|
updateDropdownValue();
|
|
const circleOverlay = document.createElement("div");
|
|
circleOverlay.className = "gamepadlib-axis-circle-overlay";
|
|
|
|
const updateOverlay = () => {
|
|
removeAllChildren(circleOverlay);
|
|
|
|
if (mappingList[index].type === "key") {
|
|
const buttons = [this.createButtonMapping(mappingList, index + 1, {
|
|
property: "low",
|
|
allowClick: false
|
|
}), this.createButtonMapping(mappingList, index, {
|
|
property: "low",
|
|
allowClick: false
|
|
}), this.createButtonMapping(mappingList, index, {
|
|
property: "high",
|
|
allowClick: false
|
|
}), this.createButtonMapping(mappingList, index + 1, {
|
|
property: "high",
|
|
allowClick: false
|
|
})];
|
|
|
|
for (const button of buttons) {
|
|
button.classList.add("gamepadlib-axis-mapper");
|
|
button.addEventListener("mapping-changed", updateDropdownValue);
|
|
circleOverlay.appendChild(button);
|
|
}
|
|
}
|
|
};
|
|
|
|
updateOverlay();
|
|
selector.addEventListener("change", () => {
|
|
if (selector.value === "custom") {
|
|
// If key mappings already exist, leave them as-is
|
|
if (mappingList[index].type !== "key") {
|
|
mappingList[index] = transformAndCopyMapping(defaultAxesMappings.arrows[0]);
|
|
mappingList[index + 1] = transformAndCopyMapping(defaultAxesMappings.arrows[1]);
|
|
}
|
|
} else if (selector.value === "arrows") {
|
|
mappingList[index] = transformAndCopyMapping(defaultAxesMappings.arrows[0]);
|
|
mappingList[index + 1] = transformAndCopyMapping(defaultAxesMappings.arrows[1]);
|
|
} else if (selector.value === "wasd") {
|
|
mappingList[index] = transformAndCopyMapping(defaultAxesMappings.wasd[0]);
|
|
mappingList[index + 1] = transformAndCopyMapping(defaultAxesMappings.wasd[1]);
|
|
} else if (selector.value === "cursor") {
|
|
mappingList[index] = transformAndCopyMapping(defaultAxesMappings.cursor[0]);
|
|
mappingList[index + 1] = transformAndCopyMapping(defaultAxesMappings.cursor[1]);
|
|
} else {
|
|
mappingList[index] = transformAndCopyMapping(emptyMapping());
|
|
mappingList[index + 1] = transformAndCopyMapping(emptyMapping());
|
|
}
|
|
|
|
updateOverlay();
|
|
this.changed();
|
|
});
|
|
return {
|
|
circleOverlay,
|
|
selector
|
|
};
|
|
}
|
|
|
|
hasControllerSelected() {
|
|
return !!this.selector.value;
|
|
}
|
|
|
|
updateContent() {
|
|
removeAllChildren(this.content);
|
|
|
|
if (this.hidden) {
|
|
return;
|
|
}
|
|
|
|
const selectedId = this.selector.value;
|
|
|
|
if (!selectedId) {
|
|
const message = document.createElement("div");
|
|
message.textContent = this.msg("no-controllers");
|
|
this.content.appendChild(message);
|
|
return;
|
|
}
|
|
|
|
const gamepadData = this.gamepadLib.gamepads.get(selectedId);
|
|
|
|
if (!gamepadData) {
|
|
// Users should never be able to see this
|
|
const message = document.createElement("div");
|
|
message.textContent = "Cannot find controller: ".concat(selectedId);
|
|
this.content.appendChild(message);
|
|
return;
|
|
}
|
|
|
|
this.buttonIdToElement.clear();
|
|
this.axisIdToElement.clear();
|
|
const mappingsContainer = document.createElement("div");
|
|
mappingsContainer.className = "gamepadlib-content-buttons";
|
|
const buttonMappings = gamepadData.buttonMappings;
|
|
|
|
for (let i = 0; i < buttonMappings.length; i++) {
|
|
const container = document.createElement("div");
|
|
container.className = "gamepadlib-mapping";
|
|
container.dataset.id = i;
|
|
const label = document.createElement("label");
|
|
label.className = "gamepadlib-mapping-label";
|
|
label.textContent = this.msg("button-n", {
|
|
n: i
|
|
});
|
|
const id = buttonHtmlId(i);
|
|
label.htmlFor = id;
|
|
const options = document.createElement("div");
|
|
options.className = "gamepadlib-mapping-options";
|
|
const mappingInput = this.createButtonMapping(buttonMappings, i);
|
|
mappingInput.id = id;
|
|
options.appendChild(mappingInput);
|
|
container.appendChild(label);
|
|
container.appendChild(options);
|
|
mappingsContainer.appendChild(container);
|
|
this.buttonIdToElement.set(i, container);
|
|
}
|
|
|
|
const axesContainer = document.createElement("div");
|
|
axesContainer.className = "gamepadlib-content-axes";
|
|
const axesMappings = gamepadData.axesMappings;
|
|
|
|
for (let i = 0; i < axesMappings.length; i += 2) {
|
|
const container = document.createElement("div");
|
|
container.className = "gamepadlib-axis";
|
|
const label = document.createElement("label");
|
|
label.textContent = this.msg("axes-a-b", {
|
|
a: i,
|
|
b: i + 1
|
|
});
|
|
label.htmlFor = axisHtmlId(i);
|
|
const circle = document.createElement("div");
|
|
circle.className = "gamepadlib-axis-circle";
|
|
const {
|
|
circleOverlay,
|
|
selector
|
|
} = this.createAxisMapping(axesMappings, i);
|
|
circle.appendChild(circleOverlay);
|
|
const dot = document.createElement("div");
|
|
dot.className = "gamepadlib-axis-dot";
|
|
circle.appendChild(dot);
|
|
container.appendChild(label);
|
|
container.appendChild(circle);
|
|
container.appendChild(selector);
|
|
axesContainer.appendChild(container);
|
|
this.axisIdToElement.set(i, dot);
|
|
}
|
|
|
|
this.content.appendChild(mappingsContainer);
|
|
this.content.appendChild(axesContainer);
|
|
}
|
|
|
|
update(gamepads) {
|
|
if (this.hidden) {
|
|
return;
|
|
}
|
|
|
|
const selectedId = this.selector.value;
|
|
|
|
if (!selectedId) {
|
|
return;
|
|
}
|
|
|
|
const gamepad = Array.from(gamepads).find(i => i && getGamepadId(i) === this.selector.value);
|
|
|
|
if (!gamepad) {
|
|
return;
|
|
}
|
|
|
|
for (let i = 0; i < gamepad.buttons.length; i++) {
|
|
const element = this.buttonIdToElement.get(i);
|
|
|
|
if (element) {
|
|
const button = gamepad.buttons[i];
|
|
const value = button.value.toString();
|
|
|
|
if (value !== element.dataset.value) {
|
|
element.dataset.value = value;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (let i = 0; i < gamepad.axes.length; i += 2) {
|
|
const element = this.axisIdToElement.get(i);
|
|
|
|
if (element) {
|
|
const x = gamepad.axes[i];
|
|
const y = gamepad.axes[i + 1] || 0;
|
|
const size = 150 / 2;
|
|
element.style.transform = "translate(-50%, -50%) translate(".concat(x * size, "px, ").concat(y * size, "px)");
|
|
}
|
|
}
|
|
}
|
|
|
|
export() {
|
|
const selectedId = this.selector.value;
|
|
|
|
if (!selectedId) {
|
|
return null;
|
|
}
|
|
|
|
const gamepadData = this.gamepadLib.gamepads.get(selectedId);
|
|
|
|
if (!gamepadData) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
axes: gamepadData.axesMappings.map(prepareAxisMappingForExport),
|
|
buttons: gamepadData.buttonMappings.map(prepareButtonMappingForExport)
|
|
};
|
|
}
|
|
|
|
changed() {
|
|
this.dispatchEvent(new CustomEvent("mapping-changed"));
|
|
}
|
|
|
|
hide() {
|
|
this.hidden = true;
|
|
this.updateContent();
|
|
}
|
|
|
|
focus() {
|
|
if (this.selector.value) {
|
|
this.selector.focus();
|
|
}
|
|
}
|
|
|
|
generateEditor() {
|
|
this.hidden = false;
|
|
this.updateAllContent();
|
|
return this.root;
|
|
}
|
|
|
|
}
|
|
|
|
/* harmony default export */ __webpack_exports__["default"] = (GamepadLib);
|
|
|
|
/***/ }),
|
|
|
|
/***/ "./src/addons/addons/gamepad/userscript.js":
|
|
/*!*************************************************!*\
|
|
!*** ./src/addons/addons/gamepad/userscript.js ***!
|
|
\*************************************************/
|
|
/*! exports provided: default */
|
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony import */ var _gamepadlib_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./gamepadlib.js */ "./src/addons/addons/gamepad/gamepadlib.js");
|
|
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) { symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); } keys.push.apply(keys, symbols); } return keys; }
|
|
|
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; }
|
|
|
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
|
|
|
|
/* harmony default export */ __webpack_exports__["default"] = (async function ({
|
|
addon,
|
|
console,
|
|
msg
|
|
}) {
|
|
const vm = addon.tab.traps.vm; // Wait for the project to finish loading. Renderer and scripts will not be fully available until this happens.
|
|
|
|
await new Promise(resolve => {
|
|
if (vm.editingTarget) return resolve();
|
|
vm.runtime.once("PROJECT_LOADED", resolve);
|
|
});
|
|
|
|
const vmStarted = () => vm.runtime._steppingInterval !== null;
|
|
|
|
const scratchKeyToKey = key => {
|
|
switch (key) {
|
|
case "right arrow":
|
|
return "ArrowRight";
|
|
|
|
case "up arrow":
|
|
return "ArrowUp";
|
|
|
|
case "left arrow":
|
|
return "ArrowLeft";
|
|
|
|
case "down arrow":
|
|
return "ArrowDown";
|
|
|
|
case "enter":
|
|
return "Enter";
|
|
|
|
case "space":
|
|
return " ";
|
|
}
|
|
|
|
return key.toLowerCase().charAt(0);
|
|
};
|
|
|
|
const getKeysUsedByProject = () => {
|
|
const allBlocks = [vm.runtime.getTargetForStage(), ...vm.runtime.targets].filter(i => i.isOriginal).map(i => i.blocks);
|
|
const result = new Set();
|
|
|
|
for (const blocks of allBlocks) {
|
|
for (const block of Object.values(blocks._blocks)) {
|
|
if (block.opcode === "event_whenkeypressed" || block.opcode === "sensing_keyoptions") {
|
|
// For blocks like "key (my variable) pressed?", the sensing_keyoptions still exists but has a null parent.
|
|
if (block.opcode === "sensing_keyoptions" && !block.parent) {
|
|
continue;
|
|
}
|
|
|
|
const key = block.fields.KEY_OPTION.value;
|
|
result.add(scratchKeyToKey(key));
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
const GAMEPAD_CONFIG_MAGIC = " // _gamepad_";
|
|
|
|
const findOptionsComment = () => {
|
|
const target = vm.runtime.getTargetForStage();
|
|
const comments = target.comments;
|
|
|
|
for (const comment of Object.values(comments)) {
|
|
if (comment.text.includes(GAMEPAD_CONFIG_MAGIC)) {
|
|
return comment;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
};
|
|
|
|
const parseOptionsComment = () => {
|
|
const comment = findOptionsComment();
|
|
|
|
if (!comment) {
|
|
return null;
|
|
}
|
|
|
|
const lineWithMagic = comment.text.split("\n").find(i => i.endsWith(GAMEPAD_CONFIG_MAGIC));
|
|
|
|
if (!lineWithMagic) {
|
|
console.warn("Gamepad comment does not contain valid line");
|
|
return null;
|
|
}
|
|
|
|
const jsonText = lineWithMagic.substr(0, lineWithMagic.length - GAMEPAD_CONFIG_MAGIC.length);
|
|
let parsed;
|
|
|
|
try {
|
|
parsed = JSON.parse(jsonText);
|
|
|
|
if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.buttons) || !Array.isArray(parsed.axes)) {
|
|
throw new Error("Invalid data");
|
|
}
|
|
} catch (e) {
|
|
console.warn("Gamepad comment has invalid JSON", e);
|
|
return null;
|
|
}
|
|
|
|
return parsed;
|
|
};
|
|
|
|
_gamepadlib_js__WEBPACK_IMPORTED_MODULE_0__["default"].setConsole(console);
|
|
const gamepad = new _gamepadlib_js__WEBPACK_IMPORTED_MODULE_0__["default"]();
|
|
|
|
gamepad.getUserHints = () => {
|
|
const parsedOptions = parseOptionsComment();
|
|
|
|
if (parsedOptions) {
|
|
return {
|
|
importedSettings: parsedOptions
|
|
};
|
|
}
|
|
|
|
return {
|
|
usedKeys: getKeysUsedByProject()
|
|
};
|
|
};
|
|
|
|
vm.runtime.on("PROJECT_LOADED", () => {
|
|
gamepad.resetControls();
|
|
});
|
|
|
|
if (addon.settings.get("hide")) {
|
|
await new Promise(resolve => {
|
|
const end = () => {
|
|
addon.settings.removeEventListener("change", listener);
|
|
resolve();
|
|
};
|
|
|
|
const listener = () => {
|
|
if (!addon.settings.get("hide")) {
|
|
end();
|
|
}
|
|
};
|
|
|
|
gamepad.gamepadConnected().then(end);
|
|
addon.settings.addEventListener("change", listener);
|
|
});
|
|
}
|
|
|
|
const renderer = vm.runtime.renderer;
|
|
const width = renderer._xRight - renderer._xLeft;
|
|
const height = renderer._yTop - renderer._yBottom;
|
|
const canvas = renderer.canvas;
|
|
const container = document.createElement("div");
|
|
container.className = "sa-gamepad-container";
|
|
addon.tab.displayNoneWhileDisabled(container, {
|
|
display: "flex"
|
|
});
|
|
const buttonContainer = document.createElement("span");
|
|
buttonContainer.className = addon.tab.scratchClass("button_outlined-button", "stage-header_stage-button");
|
|
const buttonContent = document.createElement("div");
|
|
buttonContent.className = addon.tab.scratchClass("button_content");
|
|
const buttonImage = document.createElement("img");
|
|
buttonImage.className = addon.tab.scratchClass("stage-header_stage-button-icon");
|
|
buttonImage.draggable = false;
|
|
buttonImage.src = addon.self.getResource("/gamepad.svg")
|
|
/* rewritten by pull.js */
|
|
;
|
|
buttonContent.appendChild(buttonImage);
|
|
buttonContainer.appendChild(buttonContent);
|
|
container.appendChild(buttonContainer);
|
|
let editor;
|
|
let shouldStoreSettingsInProject = false;
|
|
|
|
const didChangeProject = () => {
|
|
vm.runtime.emitProjectChanged();
|
|
|
|
if (vm.editingTarget === vm.runtime.getTargetForStage()) {
|
|
vm.emitWorkspaceUpdate();
|
|
}
|
|
};
|
|
|
|
const storeMappings = () => {
|
|
const exported = editor.export();
|
|
|
|
if (!exported) {
|
|
console.warn("Could not export gamepad settings");
|
|
return;
|
|
}
|
|
|
|
const text = "".concat(msg("config-header"), "\n").concat(JSON.stringify(exported)).concat(GAMEPAD_CONFIG_MAGIC);
|
|
const existingComment = findOptionsComment();
|
|
|
|
if (existingComment) {
|
|
existingComment.text = text;
|
|
} else {
|
|
const target = vm.runtime.getTargetForStage();
|
|
target.createComment( // comment ID, just has to be a random string
|
|
Math.random() + "", // block ID
|
|
null, // text
|
|
text, // x, y, width, height
|
|
50, 50, 350, 150, // minimized
|
|
false);
|
|
}
|
|
|
|
didChangeProject();
|
|
};
|
|
|
|
const removeStoredMappings = () => {
|
|
const comment = findOptionsComment();
|
|
|
|
if (comment) {
|
|
const target = vm.runtime.getTargetForStage();
|
|
delete target.comments[comment.id];
|
|
didChangeProject();
|
|
}
|
|
};
|
|
|
|
const handleGamepadMappingChanged = () => {
|
|
if (shouldStoreSettingsInProject) {
|
|
storeMappings();
|
|
}
|
|
};
|
|
|
|
const handleStoreSettingsCheckboxChanged = e => {
|
|
shouldStoreSettingsInProject = !!e.target.checked;
|
|
|
|
if (shouldStoreSettingsInProject) {
|
|
storeMappings();
|
|
} else {
|
|
removeStoredMappings();
|
|
}
|
|
};
|
|
|
|
const handleEditorControllerChanged = () => {
|
|
document.body.classList.toggle("sa-gamepad-has-controller", editor.hasControllerSelected());
|
|
handleGamepadMappingChanged();
|
|
};
|
|
|
|
buttonContainer.addEventListener("click", () => {
|
|
if (!editor) {
|
|
editor = gamepad.editor();
|
|
editor.msg = msg;
|
|
editor.addEventListener("mapping-changed", handleGamepadMappingChanged);
|
|
editor.addEventListener("gamepad-changed", handleEditorControllerChanged);
|
|
}
|
|
|
|
const editorEl = editor.generateEditor();
|
|
handleEditorControllerChanged();
|
|
const {
|
|
backdrop,
|
|
container,
|
|
content,
|
|
closeButton,
|
|
remove
|
|
} = addon.tab.createModal(msg("settings"), {
|
|
isOpen: true,
|
|
useEditorClasses: true
|
|
});
|
|
|
|
const handleKeyDown = e => {
|
|
if (e.key === "Escape" && !e.target.closest("[data-accepting-input]")) {
|
|
remove();
|
|
}
|
|
};
|
|
|
|
backdrop.addEventListener("click", remove);
|
|
window.addEventListener("keydown", handleKeyDown);
|
|
addon.self.addEventListener("disabled", remove);
|
|
backdrop.classList.add("sa-gamepad-popup-outer");
|
|
container.classList.add("sa-gamepad-popup");
|
|
closeButton.tabIndex = "0";
|
|
closeButton.setAttribute("role", "button");
|
|
closeButton.addEventListener("click", remove);
|
|
closeButton.addEventListener("keydown", e => {
|
|
if (e.key === "Enter" || e.key === " ") {
|
|
remove();
|
|
}
|
|
});
|
|
content.classList.add("sa-gamepad-popup-content");
|
|
|
|
if (_gamepadlib_js__WEBPACK_IMPORTED_MODULE_0__["default"].browserHasBrokenGamepadAPI()) {
|
|
const warning = document.createElement("div");
|
|
warning.textContent = msg("browser-support");
|
|
warning.className = "sa-gamepad-browser-support-warning";
|
|
content.appendChild(warning);
|
|
}
|
|
|
|
content.appendChild(editorEl);
|
|
const extraOptionsContainer = document.createElement("div");
|
|
extraOptionsContainer.className = "sa-gamepad-extra-options";
|
|
content.appendChild(extraOptionsContainer);
|
|
|
|
const mappingsWereResetOrCleared = () => {
|
|
editor.updateAllContent();
|
|
storeSettingsCheckbox.checked = false;
|
|
shouldStoreSettingsInProject = false;
|
|
};
|
|
|
|
const resetButton = document.createElement("button");
|
|
resetButton.className = "sa-gamepad-reset-button";
|
|
resetButton.textContent = msg("reset");
|
|
resetButton.addEventListener("click", () => {
|
|
gamepad.resetControls();
|
|
mappingsWereResetOrCleared();
|
|
});
|
|
extraOptionsContainer.appendChild(resetButton);
|
|
const clearButton = document.createElement("button");
|
|
clearButton.className = "sa-gamepad-reset-button";
|
|
clearButton.textContent = msg("clear");
|
|
clearButton.addEventListener("click", () => {
|
|
gamepad.clearControls();
|
|
mappingsWereResetOrCleared();
|
|
});
|
|
extraOptionsContainer.appendChild(clearButton);
|
|
const storeSettingsLabel = document.createElement("label");
|
|
storeSettingsLabel.className = "sa-gamepad-store-settings";
|
|
storeSettingsLabel.textContent = msg("store-in-project");
|
|
const storeSettingsCheckbox = document.createElement("input");
|
|
storeSettingsCheckbox.type = "checkbox";
|
|
storeSettingsCheckbox.checked = shouldStoreSettingsInProject;
|
|
storeSettingsCheckbox.addEventListener("change", handleStoreSettingsCheckboxChanged);
|
|
storeSettingsLabel.prepend(storeSettingsCheckbox);
|
|
extraOptionsContainer.appendChild(storeSettingsLabel);
|
|
editor.focus();
|
|
});
|
|
|
|
if (addon.tab.redux.state && addon.tab.redux.state.scratchGui.stageSize.stageSize === "small") {
|
|
document.body.classList.add("sa-gamepad-small");
|
|
}
|
|
|
|
document.addEventListener("click", e => {
|
|
if (e.target.closest("[class*='stage-header_stage-button-first']:not(.sa-hide-stage-button)")) {
|
|
document.body.classList.add("sa-gamepad-small");
|
|
} else if (e.target.closest("[class*='stage-header_stage-button-last']") || e.target.closest(".sa-hide-stage-button")) {
|
|
document.body.classList.remove("sa-gamepad-small");
|
|
}
|
|
}, {
|
|
capture: true
|
|
});
|
|
const virtualCursorElement = document.createElement("img");
|
|
virtualCursorElement.hidden = true;
|
|
virtualCursorElement.className = "sa-gamepad-cursor";
|
|
virtualCursorElement.src = addon.self.getResource("/cursor.png")
|
|
/* rewritten by pull.js */
|
|
;
|
|
addon.self.addEventListener("disabled", () => {
|
|
virtualCursorElement.hidden = true;
|
|
});
|
|
let hideCursorTimeout;
|
|
|
|
const hideRealCursor = () => {
|
|
document.body.classList.add("sa-gamepad-hide-cursor");
|
|
};
|
|
|
|
const showRealCursor = () => {
|
|
document.body.classList.remove("sa-gamepad-hide-cursor");
|
|
};
|
|
|
|
const virtualCursorSetVisible = visible => {
|
|
virtualCursorElement.hidden = !visible;
|
|
clearTimeout(hideCursorTimeout);
|
|
|
|
if (visible) {
|
|
hideRealCursor();
|
|
hideCursorTimeout = setTimeout(virtualCursorHide, 8000);
|
|
}
|
|
};
|
|
|
|
const virtualCursorHide = () => {
|
|
virtualCursorSetVisible(false);
|
|
};
|
|
|
|
const virtualCursorSetDown = down => {
|
|
virtualCursorSetVisible(true);
|
|
virtualCursorElement.classList.toggle("sa-gamepad-cursor-down", down);
|
|
};
|
|
|
|
const virtualCursorSetPosition = (x, y) => {
|
|
virtualCursorSetVisible(true);
|
|
const CURSOR_SIZE = 6;
|
|
const stageX = width / 2 + x - CURSOR_SIZE / 2;
|
|
const stageY = height / 2 - y - CURSOR_SIZE / 2;
|
|
virtualCursorElement.style.transform = "translate(".concat(stageX, "px, ").concat(stageY, "px)");
|
|
};
|
|
|
|
document.addEventListener("mousemove", () => {
|
|
virtualCursorSetVisible(false);
|
|
showRealCursor();
|
|
});
|
|
let getCanvasSize; // Support modern ResizeObserver and slow getBoundingClientRect version for improved browser support (matters for TurboWarp)
|
|
|
|
if (window.ResizeObserver) {
|
|
let canvasWidth = width;
|
|
let canvasHeight = height;
|
|
const resizeObserver = new ResizeObserver(entries => {
|
|
for (const entry of entries) {
|
|
canvasWidth = entry.contentRect.width;
|
|
canvasHeight = entry.contentRect.height;
|
|
}
|
|
});
|
|
resizeObserver.observe(canvas);
|
|
|
|
getCanvasSize = () => [canvasWidth, canvasHeight];
|
|
} else {
|
|
getCanvasSize = () => {
|
|
const rect = canvas.getBoundingClientRect();
|
|
return [rect.width, rect.height];
|
|
};
|
|
} // Both in Scratch space
|
|
|
|
|
|
let virtualX = 0;
|
|
let virtualY = 0;
|
|
|
|
const postMouseData = data => {
|
|
if (addon.self.disabled || !vmStarted()) return;
|
|
const [rectWidth, rectHeight] = getCanvasSize();
|
|
vm.postIOData("mouse", _objectSpread(_objectSpread({}, data), {}, {
|
|
canvasWidth: rectWidth,
|
|
canvasHeight: rectHeight,
|
|
x: (virtualX + width / 2) * (rectWidth / width),
|
|
y: (height / 2 - virtualY) * (rectHeight / height)
|
|
}));
|
|
};
|
|
|
|
const postKeyboardData = (key, isDown) => {
|
|
if (addon.self.disabled || !vmStarted()) return;
|
|
vm.postIOData("keyboard", {
|
|
key,
|
|
isDown
|
|
});
|
|
};
|
|
|
|
const handleGamepadButtonDown = e => postKeyboardData(e.detail, true);
|
|
|
|
const handleGamepadButtonUp = e => postKeyboardData(e.detail, false);
|
|
|
|
const handleGamepadMouseDown = e => {
|
|
virtualCursorSetDown(true);
|
|
postMouseData({
|
|
isDown: true,
|
|
button: e.detail
|
|
});
|
|
};
|
|
|
|
const handleGamepadMouseUp = e => {
|
|
virtualCursorSetDown(false);
|
|
postMouseData({
|
|
isDown: false,
|
|
button: e.detail
|
|
});
|
|
};
|
|
|
|
const handleGamepadMouseMove = e => {
|
|
virtualX = e.detail.x;
|
|
virtualY = e.detail.y;
|
|
virtualCursorSetPosition(virtualX, virtualY);
|
|
postMouseData({});
|
|
};
|
|
|
|
gamepad.virtualCursor.maxX = renderer._xRight;
|
|
gamepad.virtualCursor.minX = renderer._xLeft;
|
|
gamepad.virtualCursor.maxY = renderer._yTop;
|
|
gamepad.virtualCursor.minY = renderer._yBottom;
|
|
gamepad.addEventListener("keydown", handleGamepadButtonDown);
|
|
gamepad.addEventListener("keyup", handleGamepadButtonUp);
|
|
gamepad.addEventListener("mousedown", handleGamepadMouseDown);
|
|
gamepad.addEventListener("mouseup", handleGamepadMouseUp);
|
|
gamepad.addEventListener("mousemove", handleGamepadMouseMove);
|
|
|
|
while (true) {
|
|
const target = await addon.tab.waitForElement('[class^="stage-header_embed-buttons_"], [class^="stage-header_stage-size-row"], [class^="stage-header_stage-menu-wrapper"] > [class^="button_outlined-button"]', {
|
|
markAsSeen: true,
|
|
reduxEvents: ["scratch-gui/mode/SET_PLAYER", "scratch-gui/mode/SET_FULL_SCREEN", "fontsLoaded/SET_FONTS_LOADED", "scratch-gui/locales/SELECT_LOCALE"]
|
|
});
|
|
container.dataset.editorMode = addon.tab.editorMode;
|
|
|
|
if (target.className.includes("stage-size-row")) {
|
|
addon.tab.appendToSharedSpace({
|
|
space: "stageHeader",
|
|
element: container,
|
|
order: 1
|
|
});
|
|
} else {
|
|
addon.tab.appendToSharedSpace({
|
|
space: "fullscreenStageHeader",
|
|
element: container,
|
|
order: 0
|
|
});
|
|
}
|
|
|
|
const monitorListScaler = document.querySelector("[class^='monitor-list_monitor-list-scaler']");
|
|
monitorListScaler.appendChild(virtualCursorElement);
|
|
}
|
|
});
|
|
|
|
/***/ })
|
|
|
|
}]);
|
|
//# sourceMappingURL=addon-entry-gamepad.js.map
|