diff --git a/src/GameManager.js b/src/GameManager.js index bf66107..dee742a 100644 --- a/src/GameManager.js +++ b/src/GameManager.js @@ -11,7 +11,9 @@ class EJS_GameManager { simulateInput: this.Module.cwrap('simulate_input', 'null', ['number', 'number', 'number']), toggleMainLoop: this.Module.cwrap('toggleMainLoop', 'null', ['number']), getCoreOptions: this.Module.cwrap('get_core_options', 'string', []), - setVariable: this.Module.cwrap('set_variable', 'null', ['string', 'string']) + setVariable: this.Module.cwrap('set_variable', 'null', ['string', 'string']), + setCheat: this.Module.cwrap('set_cheat', 'null', ['number', 'number', 'string']), + resetCheat: this.Module.cwrap('reset_cheat', 'null', []) } this.mkdir("/home"); this.mkdir("/home/web_user"); @@ -101,7 +103,12 @@ class EJS_GameManager { setVariable(option, value) { this.functions.setVariable(option, value); } - + setCheat(index, enabled, code) { + this.functions.setCheat(index, enabled, code); + } + resetCheat() { + this.functions.resetCheat(); + } } window.EJS_GameManager = EJS_GameManager; diff --git a/src/css/main.css b/src/css/main.css index 4cf6bc4..e95bc5e 100644 --- a/src/css/main.css +++ b/src/css/main.css @@ -247,6 +247,11 @@ .ejs_popup_container *, .ejs_popup_container *::after, .ejs_popup_container *::before { box-sizing: border-box; + + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } .ejs_popup_container h4 { @@ -694,3 +699,189 @@ background: rgba(var(--ejs-primary-color),1); color: #fff; } + +.ejs_cheat_heading { + margin-top: 0 !important; + margin-bottom: 0 !important; + font-weight: 600 !important; + font-size: 1.25rem; + line-height: 1.25 !important; + color: rgba(var(--ejs-primary-color),1) !important; +} +.ejs_cheat_close { + font: inherit; + line-height: inherit; + width: auto; + background: transparent; + border: 0; + color: #bcbcbc !important; + cursor: pointer; +} +.ejs_cheat_close::before { + content: "\2715"; + color: #bcbcbc !important; + font: inherit; + line-height: inherit; + width: auto; +} +.ejs_cheat_header { + display: flex; + justify-content: space-between; + align-items: center; +} +.ejs_cheat_main { + margin-top: 2rem; + margin-bottom: 2rem; + line-height: 1.5; + color: rgba(0,0,0,0.8); + text-align: left; + color: #bcbcbc !important; + border: unset; +} +.ejs_cheat_code { + color: #000 !important; + font-size: 1rem; + padding: .4rem; + max-width: 100%; +} +@keyframes ejs_cheat_animation{ + from{ + transform:translateY(15%) + } + to{ + transform:translateY(0) + } +} +.ejs_cheat_parent { + background-color: rgba(0,0,0,0.8); + border: 1px solid rgba(238,238,238,0.2); + padding: 30px; + min-width: 200px; + max-width: 500px; + max-height: 100vh; + border-radius: 4px; + overflow-y: auto; + box-sizing: border-box; + will-change: transform; + animation: ejs_cheat_animation .3s cubic-bezier(0,0,0.2,1); + font-size: 14px; +} +.ejs_popup_container_box { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0,0,0,0.6); + display: flex; + justify-content: center; + align-items: center; +} +.ejs_popup_submit { + touch-action: manipulation; + font: inherit; + line-height: inherit; + width: auto; +} +.ejs_button_button { + color: #fff !important; + padding-left: 1rem; + padding-right: 1rem; + padding-top: .5rem; + padding-bottom: .5rem; + background-color: #929292; + border-radius: .25rem; + border-style: none; + border-width: 0; + cursor: pointer; + -webkit-appearance: button; + text-transform: none; + overflow: visible; + margin: 0; + will-change: transform; + transition: transform .25s ease-out,-webkit-transform .25s ease-out; +} +.ejs_button_button:hover { + transform: scale(1.05); +} +.ejs_cheat_rows { + max-width: 320px; + margin: 0 auto; + text-align: left; + width: 100%; + float: none; + user-select: text !important; +} +.ejs_cheat_row { + padding-left: 2.25rem; + position: relative; + padding: .2em 0; + clear: both; +} +.ejs_cheat_row:hover { + background-color: rgba(0,0,0,0.8); +} +.ejs_cheat_row input[type=checkbox] { + position: absolute; + z-index: -1; + opacity: 0; + box-sizing: border-box; + width: auto; +} +.ejs_cheat_row label { + position: relative; + margin-bottom: 0; + vertical-align: top; + word-break: break-word; +} + +.ejs_cheat_row label::before { + position: absolute; + top: .325rem; + display: block; + height: 1rem; + content: ""; + background-color: #fff; + border: #adb5bd solid 1px; + left: -2.25rem; + width: 1.75rem; + pointer-events: all; + border-radius: .5rem; +} + +.ejs_cheat_row input:checked+label::before { + color: #fff; + border-color: rgba(var(--ejs-primary-color),1); + background-color: rgba(var(--ejs-primary-color),1); +} + +.ejs_cheat_row label::after { + position: absolute; + display: block; + content: ""; + background-repeat: no-repeat; + background-position: center center; + top: calc(.325rem + 2px); + left: calc(-2.25rem + 2px); + width: calc(1rem - 4px); + height: calc(1rem - 4px); + background-color: #adb5bd; + border-radius: .5rem; + transition: transform .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out,-webkit-transform .15s ease-in-out; +} +.ejs_cheat_row input:checked+label::after { + background-color: #fff; + -webkit-transform: translateX(0.75rem); + transform: translateX(0.75rem); +} + +.ejs_cheat_row_button { + position: absolute; + padding: .1rem .5rem; + background-color: rgba(var(--ejs-primary-color),1); + color: #fff !important; + border-radius: .25rem; + cursor: pointer; + right: .025rem; + border: 0; +} diff --git a/src/emulator.js b/src/emulator.js index c0de791..0d9935f 100644 --- a/src/emulator.js +++ b/src/emulator.js @@ -80,6 +80,7 @@ class EmulatorJS { window.EJS_TESTING = this; this.touch = false; this.debug = (window.EJS_DEBUG_XX === true); + this.cheats = []; this.setElements(element); this.started = false; this.paused = true; @@ -448,6 +449,7 @@ class EmulatorJS { startGame() { this.textElem.remove(); this.textElem = null; + this.game.classList.remove("ejs_game"); this.game.appendChild(this.canvas); const args = []; @@ -464,11 +466,13 @@ class EmulatorJS { this.setupSettingsMenu(); this.handleResize(); + this.updateCheatUI(); } bindListeners() { this.createContextMenu(); this.createBottomMenuBar(); this.createControlSettingMenu(); + this.createCheatsMenu() this.setVirtualGamepad(); this.addEventListener(document, "keydown keyup", this.keyChange.bind(this)); this.addEventListener(window, "resize", this.handleResize.bind(this)); @@ -701,6 +705,9 @@ class EmulatorJS { addButton("Control Settings", '', () => { this.controlMenu.style.display = ""; }); + addButton("Cheats", '', () => { + this.cheatMenu.style.display = ""; + }); const spacer = this.createElement("span"); spacer.style = "flex:1;"; @@ -957,7 +964,7 @@ class EmulatorJS { popup.classList.add("ejs_popup_container"); const popupMsg = this.createElement("div"); popupMsg.classList.add("ejs_popup_box"); - popupMsg.innerText = "yes"; + popupMsg.innerText = ""; popup.setAttribute("hidden", ""); this.controlPopup = popupMsg; popup.appendChild(popupMsg); @@ -1037,6 +1044,7 @@ class EmulatorJS { controls; keyChange(e) { if (!this.started) return; + if (this.cheatMenu.style.display !== "none" || this.settingsMenu.style.display !== "none") return; e.preventDefault(); if (this.controlPopup.parentElement.getAttribute("hidden") === null) { const num = this.controlPopup.getAttribute("button-num"); @@ -1594,15 +1602,149 @@ class EmulatorJS { this.settingsMenu.appendChild(nested); - this.settingParent.appendChild(this.settingsMenu); this.settingParent.style.position = "relative"; - const homeSize = this.getElementSize(home); nested.style.width = homeSize.width + "px"; nested.style.height = homeSize.height + "px"; this.settingsMenu.style.display = "none"; } + createSubPopup(hidden) { + const popup = this.createElement('div'); + popup.classList.add("ejs_popup_container"); + popup.classList.add("ejs_popup_container_box"); + const popupMsg = this.createElement("div"); + popupMsg.innerText = ""; + if (hidden) popup.setAttribute("hidden", ""); + popup.appendChild(popupMsg); + return [popup, popupMsg]; + } + createCheatsMenu() { + const body = this.createPopup("Cheats", { + "Add Cheat": () => { + const popups = this.createSubPopup(); + this.cheatMenu.appendChild(popups[0]); + popups[1].classList.add("ejs_cheat_parent"); + popups[1].style.width = "100%"; + const popup = popups[1]; + const header = this.createElement("div"); + header.classList.add("ejs_cheat_header"); + const title = this.createElement("h2"); + title.innerText = "Add Cheat Code"; + title.classList.add("ejs_cheat_heading"); + const close = this.createElement("button"); + close.classList.add("ejs_cheat_close"); + header.appendChild(title); + header.appendChild(close); + popup.appendChild(header); + this.addEventListener(close, "click", (e) => { + popups[0].remove(); + }) + + const main = this.createElement("div"); + main.classList.add("ejs_cheat_main"); + const header3 = this.createElement("strong"); + header3.innerText = "Code"; + main.appendChild(header3); + main.appendChild(this.createElement("br")); + const mainText = this.createElement("textarea"); + mainText.classList.add("ejs_cheat_code"); + mainText.style.width = "100%"; + mainText.style.height = "80px"; + main.appendChild(mainText); + main.appendChild(this.createElement("br")); + const header2 = this.createElement("strong"); + header2.innerText = "Description"; + main.appendChild(header2); + main.appendChild(this.createElement("br")); + const mainText2 = this.createElement("input"); + mainText2.type = "text"; + mainText2.classList.add("ejs_cheat_code"); + main.appendChild(mainText2); + main.appendChild(this.createElement("br")); + popup.appendChild(main); + + const footer = this.createElement("footer"); + const submit = this.createElement("button"); + const closeButton = this.createElement("button"); + submit.innerText = "Submit"; + closeButton.innerText = "Close"; + submit.classList.add("ejs_button_button"); + closeButton.classList.add("ejs_button_button"); + submit.classList.add("ejs_popup_submit"); + closeButton.classList.add("ejs_popup_submit"); + submit.style["background-color"] = "rgba(var(--ejs-primary-color),1)"; + footer.appendChild(submit); + const span = this.createElement("span"); + span.innerText = " "; + footer.appendChild(span); + footer.appendChild(closeButton); + popup.appendChild(footer); + + this.addEventListener(submit, "click", (e) => { + if (!mainText.value.trim() || !mainText2.value.trim()) return; + popups[0].remove(); + this.cheats.push({ + code: mainText.value, + desc: mainText2.value, + checked: false + }); + this.updateCheatUI(); + }) + this.addEventListener(closeButton, "click", (e) => { + popups[0].remove(); + }) + + }, + "Close": () => { + this.cheatMenu.style.display = "none"; + } + }, true); + this.cheatMenu = body.parentElement; + const rows = this.createElement("div"); + body.appendChild(rows); + rows.classList.add("ejs_cheat_rows"); + this.elements.cheatRows = rows; + } + updateCheatUI() { + if (!this.cheats) this.cheats = []; + this.elements.cheatRows.innerHTML = ""; + + const addToMenu = (desc, checked, code, i) => { + const row = this.createElement("div"); + row.classList.add("ejs_cheat_row"); + const input = this.createElement("input"); + input.type = "checkbox"; + input.checked = checked; + input.value = i; + input.id = "ejs_cheat_switch_"+i; + row.appendChild(input); + const label = this.createElement("label"); + label.for = "ejs_cheat_switch_"+i; + label.innerText = desc; + row.appendChild(label); + label.addEventListener("click", (e) => { + input.checked = !input.checked; + this.cheats[i].checked = input.checked; + this.cheatChanged(input.checked, code, i); + }) + const close = this.createElement("a"); + close.classList.add("ejs_cheat_row_button"); + close.innerText = "×"; + row.appendChild(close); + + this.elements.cheatRows.appendChild(row); + this.cheatChanged(checked, code, i); + + } + this.gameManager.resetCheat(); + for (let i=0; i