This commit is contained in:
zees-dev 2026-02-03 13:20:53 +11:00 committed by GitHub
commit b87fb2a2fa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 190 additions and 29 deletions

View File

@ -297,5 +297,6 @@
"DPAD_UP": "DPAD_UP",
"DPAD_DOWN": "DPAD_DOWN",
"DPAD_LEFT": "DPAD_LEFT",
"DPAD_RIGHT": "DPAD_RIGHT"
"DPAD_RIGHT": "DPAD_RIGHT",
"Autofire": "Autofire"
}

View File

@ -297,5 +297,6 @@
"DPAD_UP": "DPAD_UP",
"DPAD_DOWN": "DPAD_DOWN",
"DPAD_LEFT": "DPAD_LEFT",
"DPAD_RIGHT": "DPAD_RIGHT"
"DPAD_RIGHT": "DPAD_RIGHT",
"Autofire": "Autofire"
}

View File

@ -297,5 +297,6 @@
"DPAD_UP": "DPAD_UP",
"DPAD_DOWN": "DPAD_DOWN",
"DPAD_LEFT": "DPAD_LEFT",
"DPAD_RIGHT": "DPAD_RIGHT"
"DPAD_RIGHT": "DPAD_RIGHT",
"Autofire": "Autofire"
}

View File

@ -297,5 +297,6 @@
"DPAD_UP": "DPAD_UP",
"DPAD_DOWN": "DPAD_DOWN",
"DPAD_LEFT": "DPAD_LEFT",
"DPAD_RIGHT": "DPAD_RIGHT"
"DPAD_RIGHT": "DPAD_RIGHT",
"Autofire": "Autofire"
}

View File

@ -335,5 +335,6 @@
"System Save interval": "System Save interval",
"Menu Bar Button": "Menu Bar Button",
"visible": "visible",
"hidden": "hidden"
"hidden": "hidden",
"Autofire": "Autofire"
}

View File

@ -306,5 +306,6 @@
"DPAD_UP": "DPAD ARRIBA",
"DPAD_DOWN": "DPAD_ABAJO",
"DPAD_LEFT": "DPAD_IZQUIERDA",
"DPAD_RIGHT": "DPAD_DERECHO"
"DPAD_RIGHT": "DPAD_DERECHO",
"Autofire": "Autofire"
}

View File

@ -306,5 +306,6 @@
"DPAD_UP": "دی پد بالا",
"DPAD_DOWN": "دی پد پایین",
"DPAD_LEFT": "دی پد چپ",
"DPAD_RIGHT": "دی پد راست"
"DPAD_RIGHT": "دی پد راست",
"Autofire": "Autofire"
}

View File

@ -335,5 +335,6 @@
"System Save interval": "Intervalle de sauvegarde du système",
"Menu Bar Button": "Bouton de la barre de menu",
"visible": "visible",
"hidden": "masqué"
"hidden": "masqué",
"Autofire": "Autofire"
}

View File

@ -297,5 +297,6 @@
"DPAD_UP": "डीपीएडी_यूपी",
"DPAD_DOWN": "डीपीएडी_डाउन",
"DPAD_LEFT": "DPAD_LEFT",
"DPAD_RIGHT": "डीपीएडी_दाएँ"
"DPAD_RIGHT": "डीपीएडी_दाएँ",
"Autofire": "Autofire"
}

View File

@ -298,5 +298,6 @@
"DPAD_UP": "DPAD_SU",
"DPAD_DOWN": "DPAD_GIU",
"DPAD_LEFT": "DPAD_SINISTRA",
"DPAD_RIGHT": "DPAD_DESTRA"
"DPAD_RIGHT": "DPAD_DESTRA",
"Autofire": "Autofire"
}

View File

@ -297,5 +297,6 @@
"DPAD_UP": "十字キー上",
"DPAD_DOWN": "十字キー下",
"DPAD_LEFT": "十字キー左",
"DPAD_RIGHT": "十字キー右"
"DPAD_RIGHT": "十字キー右",
"Autofire": "Autofire"
}

View File

@ -297,5 +297,6 @@
"DPAD_UP": "DPAD_UP",
"DPAD_DOWN": "DPAD_DOWN",
"DPAD_LEFT": "DPAD_LEFT",
"DPAD_RIGHT": "DPAD_RIGHT"
"DPAD_RIGHT": "DPAD_RIGHT",
"Autofire": "Autofire"
}

View File

@ -368,5 +368,6 @@
"Screen Recording Audio Bitrate": "화면 녹화 오디오 비트레이트",
"customDownload": "상태 파일 다운로드",
"customUpload": "상태 파일 업로드",
"customScreenshot": "스크린 샷 찍기"
"customScreenshot": "스크린 샷 찍기",
"Autofire": "Autofire"
}

View File

@ -357,5 +357,6 @@
"pcsx rearmed gunconadjustx": "pcsx rearmed - gunconadjustx",
"pcsx rearmed gunconadjusty": "pcsx rearmed - gunconadjusty",
"pcsx rearmed gunconadjustratiox": "pcsx rearmed - gunconadjustratiox",
"pcsx rearmed gunconadjustratioy": "pcsx rearmed - gunconadjustratioy"
"pcsx rearmed gunconadjustratioy": "pcsx rearmed - gunconadjustratioy",
"Autofire": "Autofire"
}

View File

@ -306,5 +306,6 @@
"DPAD_UP": "DPAD_UP",
"DPAD_DOWN": "DPAD_DOWN",
"DPAD_LEFT": "DPAD_LEFT",
"DPAD_RIGHT": "DPAD_RIGHT"
"DPAD_RIGHT": "DPAD_RIGHT",
"Autofire": "Autofire"
}

View File

@ -297,5 +297,6 @@
"DPAD_UP": "DPAD_ВВЕРХ",
"DPAD_DOWN": "DPAD_ВНИЗ",
"DPAD_LEFT": "DPAD_ВЛЕВО",
"DPAD_RIGHT": "DPAD_СПРАВО"
"DPAD_RIGHT": "DPAD_СПРАВО",
"Autofire": "Autofire"
}

View File

@ -306,5 +306,6 @@
"DPAD_UP": "DPAD_YUKARI",
"DPAD_DOWN": "DPAD_AŞAĞI",
"DPAD_LEFT": "DPAD_SOL",
"DPAD_RIGHT": "DPAD_SAĞ"
"DPAD_RIGHT": "DPAD_SAĞ",
"Autofire": "Autofire"
}

View File

@ -298,5 +298,6 @@
"DPAD_UP": "DPAD_UP",
"DPAD_DOWN": "DPAD_DOWN",
"DPAD_LEFT": "DPAD_LEFT",
"DPAD_RIGHT": "DPAD_RIGHT"
"DPAD_RIGHT": "DPAD_RIGHT",
"Autofire": "Autofire"
}

View File

@ -347,5 +347,6 @@
"Movement Anywhere": "Movement Anywhere",
"Direct Keyboard Input": "Direct Keyboard Input",
"Forward Alt key": "Forward Alt key",
"Lock Mouse": "Lock Mouse"
"Lock Mouse": "Lock Mouse",
"Autofire": "Autofire"
}

View File

@ -238,7 +238,21 @@ class EmulatorJS {
this.cheats = [];
this.started = false;
this.volume = (typeof this.config.volume === "number") ? this.config.volume : 0.5;
if (this.config.defaultControllers) this.defaultControllers = this.config.defaultControllers;
if (this.config.defaultControllers) {
// Merge user config with defaults instead of replacing
for (const [player, buttons] of Object.entries(this.config.defaultControllers)) {
this.defaultControllers[player] = this.defaultControllers[player] || {};
for (const [button, config] of Object.entries(buttons)) {
this.defaultControllers[player][button] = {
...(this.defaultControllers[player][button] || {}),
...config
};
}
}
}
this.defaultAutoFireInterval = 100;
this.autofireIntervals = {};
this.muted = false;
this.paused = true;
this.missingLang = [];
@ -1146,7 +1160,7 @@ class EmulatorJS {
if (document.activeElement !== this.elements.parent && this.config.noAutoFocus !== true) this.elements.parent.focus();
})
this.addEventListener(window, "resize", this.handleResize.bind(this));
//this.addEventListener(window, "blur", e => console.log(e), true); //TODO - add "click to make keyboard keys work" message?
this.addEventListener(window, "blur", () => this.stopAllAutofire());
let counter = 0;
this.elements.statePopupPanel = this.createPopup("", {}, true);
@ -2490,12 +2504,14 @@ class EmulatorJS {
this.controls = JSON.parse(JSON.stringify(this.defaultControllers));
const body = this.createPopup("Control Settings", {
"Reset": () => {
this.stopAllAutofire();
this.controls = JSON.parse(JSON.stringify(this.defaultControllers));
this.setupKeys();
this.checkGamepadInputs();
this.saveSettings();
},
"Clear": () => {
this.stopAllAutofire();
this.controls = { 0: {}, 1: {}, 2: {}, 3: {} };
this.setupKeys();
this.checkGamepadInputs();
@ -2991,7 +3007,7 @@ class EmulatorJS {
leftPadding.innerHTML = " ";
const aboutParent = this.createElement("div");
aboutParent.style = "font-size:12px;width:50%;float:left;";
aboutParent.style = "font-size:12px;width:40%;float:left;";
const gamepad = this.createElement("div");
gamepad.style = "text-align:center;width:50%;float:left;";
gamepad.innerText = this.localization("Gamepad");
@ -3001,12 +3017,22 @@ class EmulatorJS {
keyboard.innerText = this.localization("Keyboard");
aboutParent.appendChild(keyboard);
const setHeader = this.createElement("div");
setHeader.style = "font-size:12px;width:15%;float:left;text-align:center;";
setHeader.innerHTML = " ";
const autofireHeader = this.createElement("div");
autofireHeader.style = "font-size:12px;width:20%;float:left;text-align:center;";
autofireHeader.innerText = this.localization("Autofire");
const headingPadding = this.createElement("div");
headingPadding.style = "clear:both;";
playerTitle.appendChild(gamepadTitle);
playerTitle.appendChild(leftPadding);
playerTitle.appendChild(aboutParent);
playerTitle.appendChild(setHeader);
playerTitle.appendChild(autofireHeader);
if ((this.touch || this.hasTouchScreen) && i === 0) {
const vgp = this.createElement("div");
@ -3057,7 +3083,7 @@ class EmulatorJS {
title.appendChild(label);
const textBoxes = this.createElement("div");
textBoxes.style = "width:50%;float:left;";
textBoxes.style = "width:40%;float:left;";
const textBox1Parent = this.createElement("div");
textBox1Parent.style = "width:50%;float:left;padding: 0 5px;";
@ -3125,23 +3151,63 @@ class EmulatorJS {
textBoxes.appendChild(padding);
const setButton = this.createElement("div");
setButton.style = "width:25%;float:left;";
setButton.style = "width:15%;float:left;";
const button = this.createElement("a");
button.classList.add("ejs_control_set_button");
button.innerText = this.localization("Set");
setButton.appendChild(button);
// Autofire checkbox - not available for analog stick axes
const autofireColumn = this.createElement("div");
autofireColumn.style = "width:20%;float:left;text-align:center;";
if (!this.analogAxes.includes(k)) {
const autofireCheckbox = this.createElement("input");
autofireCheckbox.type = "checkbox";
autofireCheckbox.style = "cursor:pointer;";
autofireCheckbox.checked = this.controls[i][k] && this.controls[i][k].autofire === true;
autofireCheckbox.setAttribute("data-player", i);
autofireCheckbox.setAttribute("data-button", k);
// Update checkbox state when controls change
buttonListeners.push(() => {
autofireCheckbox.checked = this.controls[i][k] && this.controls[i][k].autofire === true;
});
this.addEventListener(autofireCheckbox, "change", (e) => {
e.stopPropagation();
const playerIdx = parseInt(e.target.getAttribute("data-player"));
const buttonIdx = parseInt(e.target.getAttribute("data-button"));
if (!this.controls[playerIdx][buttonIdx]) {
this.controls[playerIdx][buttonIdx] = {};
}
this.controls[playerIdx][buttonIdx].autofire = e.target.checked;
// Stop any active autofire if unchecked
if (!e.target.checked) {
this.stopAutofire(playerIdx, buttonIdx);
}
this.saveSettings();
});
autofireColumn.appendChild(autofireCheckbox);
}
const padding2 = this.createElement("div");
padding2.style = "clear:both;";
buttonText.appendChild(title);
buttonText.appendChild(textBoxes);
buttonText.appendChild(setButton);
buttonText.appendChild(autofireColumn);
buttonText.appendChild(padding2);
player.appendChild(buttonText);
this.addEventListener(buttonText, "mousedown", (e) => {
// Don't open popup when clicking on the autofire checkbox
if (e.target.tagName === "INPUT" && e.target.type === "checkbox") {
return;
}
e.preventDefault();
this.controlPopup.parentElement.parentElement.removeAttribute("hidden");
this.controlPopup.innerText = "[ " + controlLabel + " ]\n" + this.localization("Press Keyboard");
@ -3308,6 +3374,8 @@ class EmulatorJS {
2: {},
3: {}
}
// Analog stick axes - these use 0x7fff values and don't support autofire
this.analogAxes = [16, 17, 18, 19, 20, 21, 22, 23];
this.keyMap = {
0: "",
8: "backspace",
@ -3435,6 +3503,49 @@ class EmulatorJS {
}
return -1;
}
getAutofireInterval(playerIndex, buttonIndex) {
const control = this.controls[playerIndex] && this.controls[playerIndex][buttonIndex];
if (control && typeof control.autoFireInterval === "number") {
return control.autoFireInterval;
}
const settingValue = this.getSettingValue("autofireInterval");
return settingValue ? parseInt(settingValue) : this.defaultAutoFireInterval;
}
isAutofireEnabled(playerIndex, buttonIndex) {
const control = this.controls[playerIndex] && this.controls[playerIndex][buttonIndex];
return control && control.autofire === true;
}
startAutofire(playerIndex, buttonIndex, inputValue) {
const key = `${playerIndex}-${buttonIndex}`;
if (this.autofireIntervals[key]) {
return;
}
let pressed = true;
const interval = this.getAutofireInterval(playerIndex, buttonIndex);
this.autofireIntervals[key] = setInterval(() => {
if (this.paused || !this.gameManager) return;
pressed = !pressed;
this.gameManager.simulateInput(playerIndex, buttonIndex, pressed ? inputValue : 0);
}, interval);
}
stopAutofire(playerIndex, buttonIndex) {
const key = `${playerIndex}-${buttonIndex}`;
if (this.autofireIntervals[key]) {
clearInterval(this.autofireIntervals[key]);
delete this.autofireIntervals[key];
this.gameManager.simulateInput(playerIndex, buttonIndex, 0);
}
}
stopAllAutofire() {
for (const key in this.autofireIntervals) {
clearInterval(this.autofireIntervals[key]);
const [playerIndex, buttonIndex] = key.split("-").map(Number);
if (this.gameManager) {
this.gameManager.simulateInput(playerIndex, buttonIndex, 0);
}
}
this.autofireIntervals = {};
}
keyChange(e) {
if (e.repeat) return;
if (!this.started) return;
@ -3452,11 +3563,19 @@ class EmulatorJS {
}
if (this.settingsMenu.style.display !== "none" || this.isPopupOpen() || this.getSettingValue("keyboardInput") === "enabled") return;
e.preventDefault();
const special = [16, 17, 18, 19, 20, 21, 22, 23];
for (let i = 0; i < 4; i++) {
for (let j = 0; j < 30; j++) {
if (this.controls[i][j] && this.controls[i][j].value === e.keyCode) {
this.gameManager.simulateInput(i, j, (e.type === "keyup" ? 0 : (special.includes(j) ? 0x7fff : 1)));
const isAnalog = this.analogAxes.includes(j);
const inputValue = isAnalog ? 0x7fff : 1;
const isKeyUp = e.type === "keyup";
const value = isKeyUp ? 0 : inputValue;
if (this.isAutofireEnabled(i, j) && !isAnalog) {
isKeyUp ? this.stopAutofire(i, j) : this.startAutofire(i, j, inputValue);
} else {
this.gameManager.simulateInput(i, j, value);
}
}
}
}
@ -3489,7 +3608,6 @@ class EmulatorJS {
return;
}
if (this.settingsMenu.style.display !== "none" || this.isPopupOpen()) return;
const special = [16, 17, 18, 19, 20, 21, 22, 23];
for (let i = 0; i < 4; i++) {
if (gamepadIndex !== i) continue;
for (let j = 0; j < 30; j++) {
@ -3497,12 +3615,21 @@ class EmulatorJS {
continue;
}
const controlValue = this.controls[i][j].value2;
const isAnalog = this.analogAxes.includes(j);
if (["buttonup", "buttondown"].includes(e.type) && (controlValue === e.label || controlValue === e.index)) {
this.gameManager.simulateInput(i, j, (e.type === "buttonup" ? 0 : (special.includes(j) ? 0x7fff : 1)));
const inputValue = isAnalog ? 0x7fff : 1;
const isButtonUp = e.type === "buttonup";
const value = isButtonUp ? 0 : inputValue;
if (this.isAutofireEnabled(i, j) && !isAnalog) {
isButtonUp ? this.stopAutofire(i, j) : this.startAutofire(i, j, inputValue);
} else {
this.gameManager.simulateInput(i, j, value);
}
} else if (e.type === "axischanged") {
if (typeof controlValue === "string" && controlValue.split(":")[0] === e.axis) {
if (special.includes(j)) {
if (isAnalog) {
if (j === 16 || j === 17) {
if (e.value > 0) {
this.gameManager.simulateInput(i, 16, 0x7fff * e.value);
@ -3915,14 +4042,19 @@ class EmulatorJS {
let downValue = info[i].joystickInput === true ? 0x7fff : 1;
this.addEventListener(button, "touchstart touchend touchcancel", (e) => {
e.preventDefault();
const isAnalog = this.analogAxes.includes(value);
if (e.type === "touchend" || e.type === "touchcancel") {
e.target.classList.remove("ejs_virtualGamepad_button_down");
window.setTimeout(() => {
this.stopAutofire(0, value);
this.gameManager.simulateInput(0, value, 0);
})
} else {
e.target.classList.add("ejs_virtualGamepad_button_down");
this.gameManager.simulateInput(0, value, downValue);
if (this.isAutofireEnabled(0, value) && !isAnalog) {
this.startAutofire(0, value, downValue);
}
}
})
}
@ -4433,6 +4565,8 @@ class EmulatorJS {
this.gameManager.setAltKeyEnabled(value === "enabled");
} else if (option === "lockMouse") {
this.enableMouseLock = (value === "enabled");
} else if (option === "autofireInterval") {
this.defaultAutoFireInterval = parseInt(value);
}
}
menuOptionChanged(option, value) {
@ -5098,6 +5232,14 @@ class EmulatorJS {
"enabled": this.localization("Enabled"),
}, (this.enableMouseLock === true ? "enabled" : "disabled"), inputOptions, true);
addToMenu(this.localization("Autofire Interval"), "autofireInterval", {
"20": "20ms",
"50": "50ms",
"100": "100ms",
"200": "200ms",
"500": "500ms",
}, "100", inputOptions, true);
checkForEmptyMenu(inputOptions);
if (this.saveInBrowserSupported()) {