airship-phaser/index.html
2026-09-17 20:44:58 +00:00

114 lines
4.8 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<meta name="theme-color" content="#0f1720" />
<title>Arrowstack Airship</title>
<style>
body {
touch-action: none;
overscroll-behavior: none;
}
#mobile-controls {
position: fixed;
inset: 0;
z-index: 100;
pointer-events: none;
user-select: none;
-webkit-user-select: none;
}
.mobile-control {
position: absolute;
min-width: 62px;
height: 62px;
padding: 0 14px;
border: 1px solid rgba(255, 255, 255, 0.35);
border-radius: 18px;
background: rgba(7, 16, 24, 0.68);
color: #e8f2f7;
font: 700 18px/1 system-ui, sans-serif;
backdrop-filter: blur(6px);
-webkit-backdrop-filter: blur(6px);
pointer-events: auto;
touch-action: none;
}
.mobile-control:active,
.mobile-control.active {
background: rgba(140, 211, 255, 0.34);
transform: scale(0.96);
}
.mobile-control.left { left: max(14px, env(safe-area-inset-left)); bottom: max(18px, env(safe-area-inset-bottom)); }
.mobile-control.right { left: max(88px, calc(env(safe-area-inset-left) + 74px)); bottom: max(18px, env(safe-area-inset-bottom)); }
.mobile-control.throttle { right: max(14px, env(safe-area-inset-right)); bottom: max(92px, calc(env(safe-area-inset-bottom) + 74px)); }
.mobile-control.reverse { right: max(14px, env(safe-area-inset-right)); bottom: max(18px, env(safe-area-inset-bottom)); }
.mobile-control.port { left: max(14px, env(safe-area-inset-left)); bottom: max(100px, calc(env(safe-area-inset-bottom) + 82px)); font-size: 13px; }
.mobile-control.starboard { right: max(88px, calc(env(safe-area-inset-right) + 74px)); bottom: max(18px, env(safe-area-inset-bottom)); font-size: 13px; }
.mobile-control.restart { top: max(14px, env(safe-area-inset-top)); right: max(14px, env(safe-area-inset-right)); width: 48px; min-width: 48px; height: 48px; border-radius: 50%; }
@media (hover: hover) and (pointer: fine) {
#mobile-controls { display: none; }
}
</style>
</head>
<body>
<div id="app">
<div id="game"></div>
</div>
<div id="mobile-controls" aria-label="Touch controls">
<button class="mobile-control left" data-code="ArrowLeft" data-key="ArrowLeft" data-keycode="37" aria-label="Steer left">←</button>
<button class="mobile-control right" data-code="ArrowRight" data-key="ArrowRight" data-keycode="39" aria-label="Steer right">→</button>
<button class="mobile-control throttle" data-code="ArrowUp" data-key="ArrowUp" data-keycode="38" aria-label="Throttle">▲</button>
<button class="mobile-control reverse" data-code="ArrowDown" data-key="ArrowDown" data-keycode="40" aria-label="Reverse">▼</button>
<button class="mobile-control port" data-code="KeyQ" data-key="q" data-keycode="81" aria-label="Fire port broadside">PORT</button>
<button class="mobile-control starboard" data-code="KeyE" data-key="e" data-keycode="69" aria-label="Fire starboard broadside">STARBOARD</button>
<button class="mobile-control restart" data-code="KeyR" data-key="r" data-keycode="82" aria-label="Restart">↻</button>
</div>
<script type="module" src="/src/game-v2.js"></script>
<script type="module">
const dispatchKey = (type, button) => {
const keyCode = Number(button.dataset.keycode);
const event = new KeyboardEvent(type, {
key: button.dataset.key,
code: button.dataset.code,
bubbles: true,
cancelable: true,
});
Object.defineProperty(event, 'keyCode', { get: () => keyCode });
Object.defineProperty(event, 'which', { get: () => keyCode });
window.dispatchEvent(event);
};
for (const button of document.querySelectorAll('.mobile-control')) {
const release = (event) => {
event.preventDefault();
dispatchKey('keyup', button);
button.classList.remove('active');
};
button.addEventListener('pointerdown', (event) => {
event.preventDefault();
button.setPointerCapture?.(event.pointerId);
dispatchKey('keydown', button);
button.classList.add('active');
});
button.addEventListener('pointerup', release);
button.addEventListener('pointercancel', release);
button.addEventListener('lostpointercapture', () => {
dispatchKey('keyup', button);
button.classList.remove('active');
});
button.addEventListener('contextmenu', (event) => event.preventDefault());
}
</script>
</body>
</html>