11 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 01-foundation | 01 | execute | 1 |
|
true |
|
|
Purpose: Every subsequent phase builds on this foundation. The module structure established here is the architecture all later phases extend. Output: index.html — runnable directly in a browser with no build step.
<execution_context> @/home/dabit/.claude/get-shit-done/workflows/execute-plan.md @/home/dabit/.claude/get-shit-done/templates/summary.md </execution_context>
@.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @.planning/phases/01-foundation/01-CONTEXT.md @.planning/phases/01-foundation/01-RESEARCH.md Task 1: HTML scaffold, HiDPI canvas, and full-window Renderer index.html Create index.html from scratch. It must contain exactly:HTML structure:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Super Pong Next Gen</title>
<style>...</style>
</head>
<body>
<canvas id="gameCanvas"></canvas>
<script>...</script>
</body>
</html>
CSS (inside <style>):
* { margin: 0; padding: 0; box-sizing: border-box; }body { background: #000; overflow: hidden; width: 100vw; height: 100vh; }#gameCanvas { display: block; background: #000; }
Renderer object (inside <script>):
const Renderer = {
canvas: null,
ctx: null,
logicalWidth: 0,
logicalHeight: 0,
init() {
this.canvas = document.getElementById('gameCanvas');
this.resize();
window.addEventListener('resize', () => this.resize());
},
resize() {
const dpr = window.devicePixelRatio || 1;
// Enforce minimum 4:3 aspect ratio — canvas fills window
const w = window.innerWidth;
const h = window.innerHeight;
const minAspect = 4 / 3;
const currentAspect = w / h;
// Always fill entire window; use logical coords for game
this.logicalWidth = w;
this.logicalHeight = h;
// Enforce minimum 4:3: if narrower than 4:3, treat as 4:3 height
// (window fills screen; minimum 4:3 means we never let height dominate width)
if (currentAspect < minAspect) {
// Too tall — game uses 4:3 minimum: constrain logical height
this.logicalHeight = Math.floor(w / minAspect);
}
// Step 1: Set CSS size to logical dimensions
this.canvas.style.width = this.logicalWidth + 'px';
this.canvas.style.height = this.logicalHeight + 'px';
// Step 2: Scale bitmap to device pixel ratio
this.canvas.width = Math.floor(this.logicalWidth * dpr);
this.canvas.height = Math.floor(this.logicalHeight * dpr);
// Step 3: Scale context so all draw calls use logical pixels
this.ctx = this.canvas.getContext('2d');
this.ctx.scale(dpr, dpr);
// Notify Physics of new dimensions (Physics may not exist yet)
if (typeof Physics !== 'undefined' && Physics.onResize) {
Physics.onResize(this.logicalWidth, this.logicalHeight);
}
},
clear() {
this.ctx.clearRect(0, 0, this.logicalWidth, this.logicalHeight);
this.ctx.fillStyle = '#000';
this.ctx.fillRect(0, 0, this.logicalWidth, this.logicalHeight);
},
drawRect(x, y, w, h, color) {
this.ctx.fillStyle = color || '#fff';
this.ctx.fillRect(Math.round(x), Math.round(y), Math.round(w), Math.round(h));
},
drawCircle(x, y, radius, color) {
this.ctx.beginPath();
this.ctx.arc(Math.round(x), Math.round(y), radius, 0, Math.PI * 2);
this.ctx.fillStyle = color || '#fff';
this.ctx.fill();
}
};
Place the full Renderer object in the script tag. Do not initialize yet (initialization happens at bottom of script after all objects are defined). Open index.html in browser — canvas fills window, background is black, no scrollbars. Resize window — canvas adjusts. No JS console errors. index.html exists with valid HTML5 structure, black full-window canvas, Renderer object with HiDPI-aware resize() method using devicePixelRatio. Canvas fills browser window on load and on resize.
Task 2: GameLoop, Input module, and Player 1 paddle rendering index.html Add to the existing `<script>` tag in index.html (after Renderer, before initialization code):GameState object — holds all mutable game state:
const GameState = {
paddle1: {
x: 0, y: 0, // Set on Physics.init()
width: 12, height: 80,
speed: 400, // Logical pixels per second
color: '#fff'
},
ball: {
x: 0, y: 0, // Set on Physics.init()
radius: 8,
vx: 0, vy: 0, // Set on Physics.init()
speed: 0, // Current scalar speed
color: '#fff'
}
};
Input object:
const Input = {
keys: { w: false, s: false },
init() {
document.addEventListener('keydown', (e) => {
if (e.code === 'KeyW') { this.keys.w = true; e.preventDefault(); }
if (e.code === 'KeyS') { this.keys.s = true; e.preventDefault(); }
});
document.addEventListener('keyup', (e) => {
if (e.code === 'KeyW') this.keys.w = false;
if (e.code === 'KeyS') this.keys.s = false;
});
},
getVerticalInput() {
if (this.keys.w) return -1;
if (this.keys.s) return 1;
return 0;
}
};
Physics object (skeleton — full physics in Plan 02):
const Physics = {
width: 0,
height: 0,
init(width, height) {
this.width = width;
this.height = height;
// Position paddle1 on left side, vertically centered
GameState.paddle1.x = 30;
GameState.paddle1.y = height / 2 - GameState.paddle1.height / 2;
// Ball positioned center — physics will drive it in Plan 02
GameState.ball.x = width / 2;
GameState.ball.y = height / 2;
},
onResize(width, height) {
this.width = width;
this.height = height;
},
update(deltaTime) {
// Move paddle1 based on input
const dir = Input.getVerticalInput();
const paddle = GameState.paddle1;
paddle.y += dir * paddle.speed * deltaTime;
// Clamp to canvas bounds
paddle.y = Math.max(0, Math.min(this.height - paddle.height, paddle.y));
}
};
GameLoop object:
const GameLoop = {
stopHandle: null,
lastTime: 0,
start() {
this.lastTime = performance.now();
this.stopHandle = window.requestAnimationFrame(this.main.bind(this));
},
stop() {
if (this.stopHandle) cancelAnimationFrame(this.stopHandle);
},
main(currentTime) {
this.stopHandle = window.requestAnimationFrame(this.main.bind(this));
const deltaTime = Math.min((currentTime - this.lastTime) / 1000, 0.05); // Cap at 50ms
this.lastTime = currentTime;
Physics.update(deltaTime);
Renderer.clear();
// Draw paddle1
const p1 = GameState.paddle1;
Renderer.drawRect(p1.x, p1.y, p1.width, p1.height, p1.color);
// Draw ball placeholder (will move in Plan 02)
const b = GameState.ball;
Renderer.drawCircle(b.x, b.y, b.radius, b.color);
}
};
Initialization block (at bottom of script):
// Initialize all modules
Renderer.init();
Physics.init(Renderer.logicalWidth, Renderer.logicalHeight);
Input.init();
GameLoop.start();
The deltaTime cap of 50ms prevents the physics from exploding if the tab loses focus and resumes (a single large delta would fling the ball off screen). Open index.html in browser — white paddle visible on left side, white ball dot visible at center. Press W: paddle moves up smoothly. Press S: paddle moves down smoothly. Paddle stops at top/bottom canvas edges. No console errors. Holding W while resizing window should not break anything. index.html renders a visible paddle on the left side that responds immediately to W/S keyboard input with no OS key-repeat lag. Ball dot is visible at center. Game loop running at device refresh rate. All four objects (GameLoop, Physics, Renderer, Input) exist and are initialized. Canvas remains HiDPI-correct on resize.
Open index.html in a browser: 1. Canvas fills entire window, black background, no scrollbars 2. White paddle visible on left side (x~30), white ball dot at center 3. Press W — paddle moves up immediately (no lag) 4. Press S — paddle moves down immediately (no lag) 5. Paddle stops at top and bottom edges (clamped) 6. Resize window — canvas resizes, paddle stays visible 7. On Retina/HiDPI display — paddle and ball edges are crisp, not blurry 8. Browser console shows zero errors<success_criteria>
- index.html exists and opens without errors in Chrome/Firefox/Safari
- Canvas fills window, renders sharp on HiDPI (VFX-05)
- Player 1 W/S input moves paddle responsively (CORE-07)
- GameLoop, Renderer, Physics, Input, GameState objects all exist in script scope
- Resize does not break layout or throw errors </success_criteria>