README
Cannon Drop
A premium 2D physics puzzle game built with HTML5 Canvas, TypeScript, and Matter.js. Aim a cannon, fire a glowing ball upward, and watch it arc and fall through pegs, platforms, pipes, bumpers, and funnels before landing in scoring buckets.
Quick Start
bash
npm install
npm run build
npx http-server dist -p 8080
# Open http://localhost:8080/cannon-drop.html
Installation
Prerequisites: Node.js 18+
bash
cd projects/017-cannon-drop/3-development
npm install
npm run buildVerify:
bash
npm test
# Expected: 155 tests, all passing
Usage
Build
bash
npm run buildCompiles src/cannon-drop-engine.ts to dist/cannon-drop-engine.js and copies src/cannon-drop.html to dist/cannon-drop.html.
Test
bash
npm testRuns all engine tests via Node.js built-in test runner.
Play
Serve thedist/ directory via any HTTP server:bash
npx http-server dist -p 8080Open http://localhost:8080/cannon-drop.html in a modern browser (Chrome 100+, Firefox 100+, Safari 16+, Edge 100+).
How to Play
Controls
| Input | Action |
|---|---|
| Click + drag | Aim cannon (angle + power) |
| Release | Fire ball |
| R key | Reset active ball (no refund) |
| N key | Next level (when current complete) |
| Volume icon | Toggle mute |
Architecture
Two files:
cannon-drop-engine.ts— Pure TypeScript module exporting game logic, level data, scoring, and constants. No DOM/Canvas dependencies. Fully testable with Node.js.cannon-drop.html— Browser file with inline<script>that imports the compiled engine, manages Matter.js physics, renders via Canvas 2D, handles audio via Web Audio API, and processes input.
Exported Engine API
| Function | Returns | Description |
|---|---|---|
getLevelData(n) | LevelData | Complete layout for level 1-5 |
getLevelCount() | 5 | Total number of levels |
getPegBaseValue(type) | number | Point value for peg type |
getComboMultiplier(hitCount) | number | Tiered combo multiplier |
calculateShotScore(pegsHit, bucketValue) | ShotScore | Full score breakdown |
getStarRating(level, score, orangeCleared, allCleared) | 0-3 | Star rating |
clampAngle(deg) | number | Clamp to [10, 85] |
clampPower(power) | number | Clamp to [15, 30] |
calculateLaunchVelocity(angle, power) | {vx, vy} | Launch velocity |
createGameState(level) | GameState | Initial game state |
isLevelComplete(state) | boolean | Orange pegs cleared? |
isGameOver(state) | boolean | No balls, no active ball? |
shouldTriggerSlowMotion(orangeRemaining) | boolean | Last orange peg hit? |
Examples
Scoring a shot
javascript
import { calculateShotScore } from './cannon-drop-engine.js';
const result = calculateShotScore(
[{ type: 'orange' }, { type: 'orange' }, { type: 'orange' }],
250
);
// result = { pegScore: 300, comboMultiplier: 2, bucketScore: 500, totalShotScore: 800 }
Getting level data
javascript
import { getLevelData } from './cannon-drop-engine.js';
const level1 = getLevelData(1);
// level1.name === 'First Drop'
// level1.pegs.length === 16 (10 orange + 5 blue + 1 green)
Star rating
javascript
import { getStarRating } from './cannon-drop-engine.js';
getStarRating(1, 5000, true, false); // 2 stars
getStarRating(1, 100, true, true); // 3 stars (all pegs cleared)
Levels
| Level | Name | Orange | Blue | Green | Obstacles | Buckets |
|---|---|---|---|---|---|---|
| 1 | First Drop | 10 | 5 | 1 | None | 5 |
| 2 | The Tilt | 10 | 8 | 1 | 2 platforms | 6 |
| 3 | Pipeline | 12 | 10 | 1 | 1 platform, 2 pipes | 6 |
| 4 | Bounce House | 12 | 12 | 2 | 1 platform, 1 pipe, 3 bumpers | 7 |
| 5 | The Gauntlet | 15 | 15 | 2 | 2 platforms, 2 pipes, 3 bumpers, 2 funnels, moving jackpot | 7 |