Download index 16.html from Studytime171/lucky: direct link, hf CLI and curl.
- Browser
- Download file 14.2 kB
-
https://huggingface.co/Studytime171/lucky/resolve/main/index%2016.html
- Command line
-
hf download 'hf://Studytime171/lucky/index 16.html'
-
curl -L -o 'index 16.html' https://huggingface.co/Studytime171/lucky/resolve/main/index%2016.html
14.2 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/> | |
| <title>GBA.js - Touch Controls</title> | |
| <style> | |
| body { | |
| margin: 0; | |
| padding: 0; | |
| background: #000; | |
| font-family: Arial, Helvetica, sans-serif; | |
| overflow: hidden; | |
| height: 100vh; | |
| touch-action: none; | |
| } | |
| #screen { | |
| image-rendering: pixelated; | |
| image-rendering: -moz-crisp-edges; | |
| image-rendering: crisp-edges; | |
| width: 100%; | |
| height: auto; | |
| max-height: 100vh; | |
| display: block; | |
| margin: 0 auto; | |
| } | |
| .touch-controls { | |
| position: absolute; | |
| inset: 0; | |
| pointer-events: none; | |
| user-select: none; | |
| -webkit-user-select: none; | |
| touch-action: none; | |
| z-index: 100; | |
| display: none; /* shown after game loads */ | |
| } | |
| .dpad, .face-buttons { | |
| position: absolute; | |
| bottom: 5%; | |
| pointer-events: auto; | |
| } | |
| .dpad { | |
| left: 4%; | |
| width: 140px; | |
| height: 140px; | |
| } | |
| .face-buttons { | |
| right: 4%; | |
| display: grid; | |
| grid-template-columns: repeat(3, 1fr); | |
| gap: 10px; | |
| width: 180px; | |
| } | |
| .dpad div, .face-buttons div { | |
| background: rgba(120, 120, 120, 0.45); | |
| border: 3px solid rgba(220, 220, 220, 0.7); | |
| border-radius: 50%; | |
| color: white; | |
| font-weight: bold; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 1.1rem; | |
| box-shadow: 0 4px 10px rgba(0,0,0,0.5); | |
| transition: all 0.12s ease; | |
| } | |
| .dpad div:active, .face-buttons div:active, | |
| .dpad div.pressed, .face-buttons div.pressed { | |
| transform: scale(0.88); | |
| opacity: 0.65; | |
| background: rgba(220, 220, 220, 0.75) ; | |
| } | |
| .dpad-up { width: 60px; height: 60px; position: absolute; left: 40px; top: 0; border-radius: 12px 12px 50% 50%; } | |
| .dpad-down { width: 60px; height: 60px; position: absolute; left: 40px; bottom: 0; border-radius: 50% 50% 12px 12px; } | |
| .dpad-left { width: 60px; height: 60px; position: absolute; top: 40px; left: 0; border-radius: 12px 50% 50% 12px; } | |
| .dpad-right { width: 60px; height: 60px; position: absolute; top: 40px; right: 0; border-radius: 50% 12px 12px 50%; } | |
| .dpad-center{ width: 60px; height: 60px; position: absolute; left: 40px; top: 40px; background: transparent; border: none; pointer-events: none; } | |
| .btn-a { background: rgba(0, 180, 0, 0.55); grid-column: 3; grid-row: 2; border-radius: 50%; width: 70px; height: 70px; font-size: 1.4rem; } | |
| .btn-b { background: rgba(220, 0, 0, 0.55); grid-column: 2; grid-row: 3; border-radius: 50%; width: 70px; height: 70px; font-size: 1.4rem; } | |
| .btn-l { background: rgba(100, 100, 255, 0.5); grid-column: 1; grid-row: 1; width: 60px; height: 50px; border-radius: 10px; font-size: 1rem; } | |
| .btn-r { background: rgba(100, 100, 255, 0.5); grid-column: 3; grid-row: 1; width: 60px; height: 50px; border-radius: 10px; font-size: 1rem; } | |
| .btn-start { background: rgba(200, 200, 50, 0.6); grid-column: 2 / 4; grid-row: 1; width: auto; height: 44px; border-radius: 10px; font-size: 0.95rem; } | |
| .btn-select { background: rgba(200, 200, 50, 0.6); grid-column: 1 / 3; grid-row: 1; width: auto; height: 44px; border-radius: 10px; font-size: 0.95rem; } | |
| #controls { | |
| position: absolute; | |
| bottom: 0; | |
| left: 0; | |
| right: 0; | |
| z-index: 200; | |
| padding: 10px; | |
| background: rgba(0,0,0,0.4); | |
| color: white; | |
| text-align: center; | |
| } | |
| .bigbutton { | |
| padding: 12px 24px; | |
| font-size: 1.1rem; | |
| margin: 6px; | |
| background: #444; | |
| color: white; | |
| border: none; | |
| border-radius: 8px; | |
| cursor: pointer; | |
| } | |
| .hidden { display: none ; } | |
| .dead { opacity: 0.3; pointer-events: none; } | |
| @media (orientation: landscape) and (min-width: 800px) { | |
| .touch-controls { display: none ; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <canvas id="screen" width="480" height="320"></canvas> | |
| <div id="touch-controls" class="touch-controls"> | |
| <!-- D-Pad --> | |
| <div class="dpad"> | |
| <div class="dpad-up" data-keycode="38"></div> | |
| <div class="dpad-down" data-keycode="40"></div> | |
| <div class="dpad-left" data-keycode="37"></div> | |
| <div class="dpad-right" data-keycode="39"></div> | |
| <div class="dpad-center"></div> | |
| </div> | |
| <!-- Face buttons --> | |
| <div class="face-buttons"> | |
| <div class="btn btn-l" data-keycode="65">L</div> | |
| <div class="btn btn-r" data-keycode="83">R</div> | |
| <div class="btn btn-select"data-keycode="220">Select</div> | |
| <div class="btn btn-start" data-keycode="13">Start</div> | |
| <div class="btn btn-b" data-keycode="88">B</div> | |
| <div class="btn btn-a" data-keycode="90">A</div> | |
| </div> | |
| </div> | |
| <section id="controls"> | |
| <div id="preload"> | |
| <button class="bigbutton" id="select" onclick="document.getElementById('loader').click()">SELECT ROM</button> | |
| <input id="loader" type="file" accept=".gba" onchange="run(this.files[0]);" style="display:none;"> | |
| <button class="bigbutton" onclick="document.getElementById('saveloader').click()">Upload Save</button> | |
| <input id="saveloader" type="file" onchange="uploadSavedataPending(this.files[0]);" style="display:none;"> | |
| </div> | |
| <div id="ingame" class="hidden"> | |
| <button id="pause" class="bigbutton" onclick="togglePause()">PAUSE</button> | |
| <button class="bigbutton" onclick="reset()">RESET</button> | |
| <button class="bigbutton" onclick="gba?.downloadSavedata?.()">Download Save</button> | |
| <button class="bigbutton" onclick="screenshot()">Screenshot</button> | |
| <label style="color:white; margin:0 12px;"> | |
| <input type="checkbox" onchange="setPixelated(this.checked)"> Pixelated | |
| </label> | |
| <div id="sound" style="display:inline-block; color:white;"> | |
| <label> | |
| <input type="checkbox" checked onchange="gba.audio.masterEnable = this.checked"> Sound | |
| </label> | |
| <input type="range" min="0" max="1" value="1" step="any" onchange="setVolume(this.value)" oninput="setVolume(this.value)"> | |
| </div> | |
| <p id="openDebug" onclick="enableDebug()" style="cursor:pointer; color:#88f; margin:8px 0;">Open Debugger</p> | |
| </div> | |
| </section> | |
| <!-- GBA.js scripts - keep these files in the js/ and resources/ folders --> | |
| <script src="js/util.js"></script> | |
| <script src="js/core.js"></script> | |
| <script src="js/arm.js"></script> | |
| <script src="js/thumb.js"></script> | |
| <script src="js/mmu.js"></script> | |
| <script src="js/io.js"></script> | |
| <script src="js/audio.js"></script> | |
| <script src="js/video.js"></script> | |
| <script src="js/video/proxy.js"></script> | |
| <script src="js/video/software.js"></script> | |
| <script src="js/irq.js"></script> | |
| <script src="js/keypad.js"></script> | |
| <script src="js/sio.js"></script> | |
| <script src="js/savedata.js"></script> | |
| <script src="js/gpio.js"></script> | |
| <script src="js/gba.js"></script> | |
| <script src="resources/xhr.js"></script> | |
| <script> | |
| // ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // Main GBA logic | |
| // ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| var gba = null; | |
| var runCommands = []; | |
| var debug = null; | |
| try { | |
| gba = new GameBoyAdvance(); | |
| gba.keypad.eatInput = true; | |
| gba.setLogger(function(level, error) { | |
| console.error("Emulator error:", error); | |
| gba.pause(); | |
| const screen = document.getElementById('screen'); | |
| if (screen.className === 'dead') return; | |
| const crash = document.createElement('img'); | |
| crash.id = 'crash'; | |
| crash.src = 'resources/crash.png'; | |
| screen.parentElement.insertBefore(crash, screen); | |
| screen.className = 'dead'; | |
| }); | |
| } catch (e) { | |
| console.error("Failed to initialize GBA emulator:", e); | |
| gba = null; | |
| } | |
| window.onload = function() { | |
| if (gba && FileReader) { | |
| gba.setCanvas(document.getElementById('screen')); | |
| gba.logLevel = gba.LOG_ERROR || 1; | |
| // Optional BIOS loading - uncomment and adjust path if you have bios.bin | |
| // loadRom('resources/bios.bin', bios => gba.setBios(bios)); | |
| if (!gba.audio?.context) { | |
| const soundbox = document.getElementById('sound'); | |
| if (soundbox) soundbox.style.display = 'none'; | |
| } | |
| } else { | |
| const controls = document.getElementById('controls'); | |
| if (controls) controls.remove(); | |
| } | |
| }; | |
| // ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // Core functions | |
| // ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function fadeOut(id, nextId, kill) { | |
| const e = document.getElementById(id); | |
| if (!e) return; | |
| const e2 = document.getElementById(nextId); | |
| const removeSelf = () => { | |
| if (kill) e.remove(); | |
| else { | |
| e.className = 'dead'; | |
| e.removeEventListener('transitionend', removeSelf); | |
| } | |
| if (e2) { | |
| e2.classList.add('hidden'); | |
| setTimeout(() => e2.classList.remove('hidden'), 20); | |
| } | |
| }; | |
| e.addEventListener('transitionend', removeSelf); | |
| e.classList.add('hidden'); | |
| } | |
| function run(file) { | |
| const selectBtn = document.getElementById('select'); | |
| selectBtn.textContent = 'Loading...'; | |
| selectBtn.removeAttribute('onclick'); | |
| const pauseBtn = document.getElementById('pause'); | |
| if (pauseBtn) pauseBtn.textContent = "PAUSE"; | |
| gba.loadRomFromFile(file, result => { | |
| if (result) { | |
| runCommands.forEach(cmd => cmd()); | |
| runCommands = []; | |
| fadeOut('preload', 'ingame'); | |
| gba.runStable(); | |
| // Show touch controls after successful load | |
| document.getElementById('touch-controls').style.display = 'block'; | |
| } else { | |
| selectBtn.textContent = 'FAILED'; | |
| setTimeout(() => { | |
| selectBtn.textContent = 'SELECT ROM'; | |
| selectBtn.onclick = () => document.getElementById('loader').click(); | |
| }, 2000); | |
| } | |
| }); | |
| } | |
| function reset() { | |
| if (!gba) return; | |
| gba.pause(); | |
| gba.reset(); | |
| document.getElementById('select').textContent = 'SELECT ROM'; | |
| const crash = document.getElementById('crash'); | |
| if (crash) { | |
| const ctx = gba.targetCanvas?.getContext('2d'); | |
| if (ctx) ctx.clearRect(0,0,480,320); | |
| gba.video?.drawCallback?.(); | |
| crash.remove(); | |
| document.getElementById('screen').className = ''; | |
| } | |
| document.getElementById('select').onclick = () => document.getElementById('loader').click(); | |
| fadeOut('ingame', 'preload'); | |
| } | |
| function uploadSavedataPending(file) { | |
| runCommands.push(() => gba?.loadSavedataFromFile?.(file)); | |
| } | |
| function togglePause() { | |
| if (!gba) return; | |
| const e = document.getElementById('pause'); | |
| if (gba.paused) { | |
| gba.runStable(); | |
| e.textContent = "PAUSE"; | |
| } else { | |
| gba.pause(); | |
| e.textContent = "UNPAUSE"; | |
| } | |
| } | |
| function screenshot() { | |
| if (!gba?.indirectCanvas) return; | |
| window.open(gba.indirectCanvas.toDataURL('image/png'), 'gba-screenshot'); | |
| } | |
| function setVolume(value) { | |
| if (gba?.audio) gba.audio.masterVolume = Math.pow(2, value) - 1; | |
| } | |
| function setPixelated(pixelated) { | |
| const screen = document.getElementById('screen'); | |
| const ctx = screen.getContext('2d'); | |
| if (ctx) ctx.imageSmoothingEnabled = !pixelated; | |
| } | |
| // ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| // TOUCH CONTROLS - DIRECT KEYPAD MANIPULATION (works on iOS Safari) | |
| // ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| if ('ontouchstart' in window || navigator.maxTouchPoints > 0) { | |
| const keypad = gba?.keypad; | |
| if (!keypad) { | |
| console.warn("GBA keypad not available β touch controls disabled"); | |
| } else { | |
| const keyMap = { | |
| "38": keypad.KEYCODE_UP || 6, // UP | |
| "40": keypad.KEYCODE_DOWN || 7, // DOWN | |
| "37": keypad.KEYCODE_LEFT || 5, // LEFT | |
| "39": keypad.KEYCODE_RIGHT || 4, // RIGHT | |
| "13": keypad.KEYCODE_START || 3, // START | |
| "220": keypad.KEYCODE_SELECT || 2, // SELECT | |
| "90": keypad.KEYCODE_A || 0, // A | |
| "88": keypad.KEYCODE_B || 1, // B | |
| "65": keypad.KEYCODE_L || 9, // L | |
| "83": keypad.KEYCODE_R || 8 // R | |
| }; | |
| const pressed = new Set(); | |
| document.querySelectorAll('[data-keycode]').forEach(el => { | |
| const dataCode = el.dataset.keycode; | |
| const bitIndex = keyMap[dataCode]; | |
| if (bitIndex === undefined) return; | |
| const toggle = 1 << bitIndex; | |
| el.addEventListener('touchstart', e => { | |
| e.preventDefault(); | |
| if (!pressed.has(dataCode)) { | |
| pressed.add(dataCode); | |
| keypad.currentDown &= ~toggle; // button pressed (clear bit) | |
| } | |
| el.classList.add('pressed'); | |
| }, { passive: false }); | |
| el.addEventListener('touchend', e => { | |
| e.preventDefault(); | |
| if (pressed.has(dataCode)) { | |
| pressed.delete(dataCode); | |
| keypad.currentDown |= toggle; // button released (set bit) | |
| } | |
| el.classList.remove('pressed'); | |
| }, { passive: false }); | |
| el.addEventListener('touchcancel', e => { | |
| e.preventDefault(); | |
| if (pressed.has(dataCode)) { | |
| pressed.delete(dataCode); | |
| keypad.currentDown |= toggle; | |
| } | |
| el.classList.remove('pressed'); | |
| }, { passive: false }); | |
| }); | |
| console.log("Touch controls enabled via direct keypad manipulation"); | |
| } | |
| } | |
| // Optional: debug opener | |
| function enableDebug() { | |
| console.log("Debugger requested (implement if needed)"); | |
| } | |
| </script> | |
| </body> | |
| </html> |