Files
rshtirmer f4b0812aa8 feat(rock-em-sock-em): scaffold 3D boxing game with punch/block/knockout mechanics
Two robots (Blue Bomber and Red Rocker) face off in a boxing ring.
Player controls the Blue Bomber with A/D/W keys or touch tap zones.
Red Rocker is AI-controlled with timing-based punch/block patterns.
Each hit reduces opponent head health; at zero, head pops up = knockout.
Score tracks rounds won. Game over when player gets knocked out.

Core systems: CombatSystem (damage, cooldowns, hit detection),
AISystem (timing patterns, reactive blocking), AnimationSystem
(punch/block/head-pop animations on primitive-built robots),
InputSystem (keyboard + mobile touch zones).

Includes spectacle events, combo tracking, and design-brief.md.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-02-26 14:59:58 -05:00

148 lines
4.5 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="data:,">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Rock 'Em Sock 'Em Robots</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: #000; overflow: hidden; font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; }
canvas { display: block; }
/* Score HUD omitted — Play.fun widget displays score in a deadzone at the top */
.overlay {
position: fixed;
inset: 0;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
background: rgba(0, 0, 0, 0.75);
z-index: 20;
color: #fff;
padding: 20px;
padding-top: max(20px, 8vh); /* Safe zone: Play.fun widget bar is ~75px at top */
}
.overlay.hidden { display: none; }
.overlay h1 {
font-size: clamp(28px, 7vmin, 56px);
margin-bottom: clamp(10px, 2vmin, 24px);
text-align: center;
color: #ff4444;
text-shadow: 0 0 20px rgba(255, 68, 68, 0.5);
}
.overlay p {
font-size: clamp(14px, 2.5vmin, 22px);
margin-bottom: 8px;
color: #aaa;
text-align: center;
}
.overlay .score-display {
font-size: clamp(18px, 4vmin, 32px);
margin: 8px 0;
}
.overlay .best-display {
font-size: clamp(14px, 2.5vmin, 22px);
color: #aaa;
}
.overlay button {
margin-top: clamp(16px, 3vmin, 32px);
padding: clamp(10px, 2vmin, 16px) clamp(28px, 6vmin, 48px);
font-size: clamp(16px, 2.5vmin, 22px);
font-family: inherit;
font-weight: bold;
color: #fff;
background: #cc2222;
border: none;
border-radius: 8px;
cursor: pointer;
min-height: 44px;
min-width: 120px;
transition: background 0.2s, transform 0.1s;
}
.overlay button:hover { background: #ee3333; transform: scale(1.05); }
.overlay button:active { background: #aa1111; transform: scale(0.95); }
/* Round won banner */
#round-banner {
position: fixed;
top: max(80px, 10vh); /* Below Play.fun widget safe zone */
left: 50%;
transform: translateX(-50%);
font-size: clamp(20px, 5vmin, 40px);
font-weight: bold;
color: #ffdd44;
text-shadow: 0 0 15px rgba(255, 221, 68, 0.6), 0 2px 4px rgba(0,0,0,0.8);
text-align: center;
z-index: 15;
pointer-events: none;
animation: bannerPulse 0.5s ease-out;
}
#round-banner.hidden { display: none; }
@keyframes bannerPulse {
0% { transform: translateX(-50%) scale(0.5); opacity: 0; }
50% { transform: translateX(-50%) scale(1.15); }
100% { transform: translateX(-50%) scale(1); opacity: 1; }
}
/* Touch controls hint (mobile only) */
#touch-hint {
position: fixed;
bottom: max(20px, 3vh);
left: 50%;
transform: translateX(-50%);
font-size: clamp(11px, 2vmin, 16px);
color: rgba(255, 255, 255, 0.4);
text-align: center;
z-index: 10;
pointer-events: none;
white-space: nowrap;
}
/* Desktop controls hint */
#desktop-hint {
position: fixed;
bottom: max(10px, 1.5vh);
left: 50%;
transform: translateX(-50%);
font-size: clamp(10px, 1.5vmin, 14px);
color: rgba(255, 255, 255, 0.3);
text-align: center;
z-index: 10;
pointer-events: none;
white-space: nowrap;
}
</style>
</head>
<body>
<div id="gameover-overlay" class="overlay hidden">
<h1>KNOCKED OUT!</h1>
<p>Your head popped up!</p>
<div class="score-display" id="final-score">Rounds Won: 0</div>
<div class="best-display" id="best-score">Best: 0</div>
<button id="restart-btn">FIGHT AGAIN</button>
</div>
<div id="round-banner" class="hidden"></div>
<div id="desktop-hint">A / Left = Left Punch &nbsp;&middot;&nbsp; D / Right = Right Punch &nbsp;&middot;&nbsp; W / Up = Block</div>
<script>
// Show appropriate hint based on device
const isMobile = /Android|iPhone|iPad|iPod/i.test(navigator.userAgent) || (navigator.maxTouchPoints > 1);
const hint = document.getElementById('desktop-hint');
if (isMobile) {
hint.textContent = 'Tap left = Left Punch \u00b7 Tap right = Right Punch \u00b7 Tap top = Block';
}
// Auto-hide hint after 5 seconds
setTimeout(() => { hint.style.opacity = '0'; hint.style.transition = 'opacity 1s'; }, 5000);
</script>
<script type="module" src="/src/main.js"></script>
</body>
</html>