lucky / index 16.html
Studytime171's picture
Duplicate from Studytime171/Lalitgangwani
89610f7
<!DOCTYPE html>
<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) !important;
}
.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 !important; }
.dead { opacity: 0.3; pointer-events: none; }
@media (orientation: landscape) and (min-width: 800px) {
.touch-controls { display: none !important; }
}
</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>