mirror of
https://github.com/PlayableIntelligence/game-creator.git
synced 2026-09-19 07:34:10 +08:00
f4b0812aa8
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>
148 lines
4.5 KiB
HTML
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 · D / Right = Right Punch · 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>
|