mirror of
https://github.com/EmulatorJS/EmulatorJS.git
synced 2026-02-06 11:17:36 +00:00
- added support for autofire for all controls
- global autofire delay configurable via EJS_defaultAutoFireInterval - each button can have custom autofire delay and be toggled - configurable upon startup - can be updated in control settings for each button - joystick not supported
This commit is contained in:
parent
5680535946
commit
60baa7269d
@ -129,6 +129,7 @@
|
||||
config.fixedSaveInterval = window.EJS_fixedSaveInterval;
|
||||
config.disableAutoUnload = window.EJS_disableAutoUnload;
|
||||
config.disableBatchBootup = window.EJS_disableBatchBootup;
|
||||
config.defaultAutoFireInterval = window.EJS_defaultAutoFireInterval;
|
||||
|
||||
let systemLang;
|
||||
try {
|
||||
|
||||
@ -237,7 +237,23 @@ 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 in this.config.defaultControllers) {
|
||||
if (!this.defaultControllers[player]) {
|
||||
this.defaultControllers[player] = {};
|
||||
}
|
||||
for (const button in this.config.defaultControllers[player]) {
|
||||
this.defaultControllers[player][button] = Object.assign(
|
||||
{},
|
||||
this.defaultControllers[player][button] || {},
|
||||
this.config.defaultControllers[player][button]
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.defaultAutoFireInterval = this.config.defaultAutoFireInterval || 100;
|
||||
this.autofireIntervals = {};
|
||||
this.muted = false;
|
||||
this.paused = true;
|
||||
this.missingLang = [];
|
||||
@ -1145,7 +1161,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);
|
||||
@ -1961,6 +1977,9 @@ class EmulatorJS {
|
||||
}
|
||||
}
|
||||
this.gameManager.toggleMainLoop(this.paused ? 0 : 1);
|
||||
if (this.paused) {
|
||||
this.stopAllAutofire();
|
||||
}
|
||||
|
||||
//I now realize its not easy to pause it while the cursor is locked, just in case I guess
|
||||
if (this.enableMouseLock) {
|
||||
@ -2489,12 +2508,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();
|
||||
@ -2990,7 +3011,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");
|
||||
@ -3000,12 +3021,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");
|
||||
@ -3056,7 +3087,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;";
|
||||
@ -3124,23 +3155,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");
|
||||
@ -3307,6 +3378,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",
|
||||
@ -3434,6 +3507,48 @@ 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;
|
||||
}
|
||||
return 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;
|
||||
this.gameManager.simulateInput(playerIndex, buttonIndex, inputValue);
|
||||
const interval = this.getAutofireInterval(playerIndex, buttonIndex);
|
||||
this.autofireIntervals[key] = setInterval(() => {
|
||||
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;
|
||||
@ -3451,11 +3566,24 @@ 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;
|
||||
if (e.type === "keyup") {
|
||||
if (this.isAutofireEnabled(i, j) && !isAnalog) {
|
||||
this.stopAutofire(i, j);
|
||||
} else {
|
||||
this.gameManager.simulateInput(i, j, 0);
|
||||
}
|
||||
} else {
|
||||
if (this.isAutofireEnabled(i, j) && !isAnalog) {
|
||||
this.startAutofire(i, j, inputValue);
|
||||
} else {
|
||||
this.gameManager.simulateInput(i, j, inputValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3488,7 +3616,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++) {
|
||||
@ -3496,12 +3623,26 @@ 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;
|
||||
if (e.type === "buttonup") {
|
||||
if (this.isAutofireEnabled(i, j) && !isAnalog) {
|
||||
this.stopAutofire(i, j);
|
||||
} else {
|
||||
this.gameManager.simulateInput(i, j, 0);
|
||||
}
|
||||
} else {
|
||||
if (this.isAutofireEnabled(i, j) && !isAnalog) {
|
||||
this.startAutofire(i, j, inputValue);
|
||||
} else {
|
||||
this.gameManager.simulateInput(i, j, inputValue);
|
||||
}
|
||||
}
|
||||
} 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);
|
||||
@ -3914,14 +4055,23 @@ 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.gameManager.simulateInput(0, value, 0);
|
||||
if (this.isAutofireEnabled(0, value) && !isAnalog) {
|
||||
this.stopAutofire(0, value);
|
||||
} else {
|
||||
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);
|
||||
} else {
|
||||
this.gameManager.simulateInput(0, value, downValue);
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user