├── static ├── favicon.ico ├── phase00 │ ├── favicon.ico │ ├── img │ │ ├── alien.png │ │ ├── bullet.png │ │ ├── earth.png │ │ ├── sentry.png │ │ ├── explosion.png │ │ └── alien-explosion.png │ ├── audio │ │ ├── bullet.mp3 │ │ ├── alien_boom.mp3 │ │ └── bullet_boom.mp3 │ ├── swf │ │ ├── soundmanager2_flash9.swf │ │ └── soundmanager2_flash9_debug.swf │ ├── manifest.appcache │ ├── index.html │ └── scripts │ │ └── stats.js ├── phase01 │ ├── favicon.ico │ └── index.html ├── phase02 │ ├── favicon.ico │ ├── img │ │ ├── alien.png │ │ ├── bullet.png │ │ ├── earth.png │ │ ├── sentry.png │ │ ├── explosion.png │ │ └── alien-explosion.png │ ├── index.html │ └── scripts │ │ └── app.js ├── phase03 │ ├── favicon.ico │ ├── img │ │ ├── alien.png │ │ └── earth.png │ ├── index.html │ └── scripts │ │ └── app.js ├── phase04 │ ├── favicon.ico │ ├── img │ │ ├── alien.png │ │ └── earth.png │ ├── index.html │ └── scripts │ │ └── app.js ├── phase05 │ ├── favicon.ico │ ├── img │ │ ├── alien.png │ │ └── earth.png │ ├── index.html │ └── scripts │ │ └── app.js ├── phase06 │ ├── favicon.ico │ ├── img │ │ ├── alien.png │ │ └── earth.png │ ├── index.html │ └── scripts │ │ └── app.js ├── phase07 │ ├── favicon.ico │ ├── img │ │ ├── alien.png │ │ ├── bullet.png │ │ ├── earth.png │ │ ├── sentry.png │ │ ├── explosion.png │ │ └── alien-explosion.png │ ├── index.html │ └── scripts │ │ └── app.js ├── phase08 │ ├── favicon.ico │ ├── img │ │ ├── alien.png │ │ ├── bullet.png │ │ ├── earth.png │ │ ├── sentry.png │ │ └── bullet-single.png │ ├── index.html │ └── scripts │ │ └── app.js ├── phase09 │ ├── favicon.ico │ ├── img │ │ ├── alien.png │ │ ├── bullet.png │ │ ├── earth.png │ │ ├── sentry.png │ │ └── explosion.png │ ├── index.html │ └── scripts │ │ └── app.js ├── phase10 │ ├── favicon.ico │ ├── img │ │ ├── alien.png │ │ ├── bullet.png │ │ ├── earth.png │ │ ├── sentry.png │ │ └── explosion.png │ ├── audio │ │ ├── bullet.mp3 │ │ ├── alien_boom.mp3 │ │ └── bullet_boom.mp3 │ ├── swf │ │ ├── soundmanager2_flash9.swf │ │ └── soundmanager2_flash9_debug.swf │ ├── index.html │ └── scripts │ │ ├── stats.js │ │ └── app.js ├── phase11 │ ├── favicon.ico │ ├── img │ │ ├── alien.png │ │ ├── bullet.png │ │ ├── earth.png │ │ ├── sentry.png │ │ ├── explosion.png │ │ └── alien-explosion.png │ ├── audio │ │ ├── bullet.mp3 │ │ ├── alien_boom.mp3 │ │ └── bullet_boom.mp3 │ ├── swf │ │ ├── soundmanager2_flash9.swf │ │ └── soundmanager2_flash9_debug.swf │ ├── index.html │ └── scripts │ │ └── app.js ├── phase12 │ ├── favicon.ico │ ├── img │ │ ├── alien.png │ │ ├── bullet.png │ │ ├── earth.png │ │ ├── sentry.png │ │ ├── explosion.png │ │ └── alien-explosion.png │ ├── audio │ │ ├── bullet.mp3 │ │ ├── alien_boom.mp3 │ │ └── bullet_boom.mp3 │ ├── swf │ │ ├── soundmanager2_flash9.swf │ │ └── soundmanager2_flash9_debug.swf │ ├── index.html │ └── scripts │ │ └── app.js ├── phase13 │ ├── favicon.ico │ ├── img │ │ ├── alien.png │ │ ├── bullet.png │ │ ├── earth.png │ │ ├── sentry.png │ │ ├── explosion.png │ │ └── alien-explosion.png │ ├── audio │ │ ├── bullet.mp3 │ │ ├── alien_boom.mp3 │ │ └── bullet_boom.mp3 │ ├── swf │ │ ├── soundmanager2_flash9.swf │ │ └── soundmanager2_flash9_debug.swf │ ├── index.html │ └── scripts │ │ ├── stats.js │ │ └── app.js ├── phase14 │ ├── favicon.ico │ ├── img │ │ ├── alien.png │ │ ├── bullet.png │ │ ├── earth.png │ │ ├── sentry.png │ │ ├── explosion.png │ │ └── alien-explosion.png │ ├── audio │ │ ├── bullet.mp3 │ │ ├── alien_boom.mp3 │ │ └── bullet_boom.mp3 │ ├── swf │ │ ├── soundmanager2_flash9.swf │ │ └── soundmanager2_flash9_debug.swf │ ├── manifest.appcache │ ├── index.html │ └── scripts │ │ └── stats.js ├── .gitignore ├── img │ ├── alien.png │ ├── bullet.png │ ├── earth.png │ ├── sentry.png │ ├── explosion.png │ └── alien-explosion.png ├── audio │ ├── bullet.mp3 │ ├── alien_boom.mp3 │ └── bullet_boom.mp3 ├── swf │ ├── soundmanager2_flash9.swf │ └── soundmanager2_flash9_debug.swf ├── manifest.appcache ├── index.html └── scripts │ └── stats.js ├── .gitignore ├── TODO ├── app.yaml ├── index.yaml ├── README └── LICENSE /static/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /static/phase00/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/phase01/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/phase02/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/phase03/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/phase04/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/phase05/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/phase06/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/phase07/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/phase08/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/phase09/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/phase10/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/phase11/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/phase12/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/phase13/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/phase14/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /static/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /static/img/alien.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/img/alien.png -------------------------------------------------------------------------------- /static/img/bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/img/bullet.png -------------------------------------------------------------------------------- /static/img/earth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/img/earth.png -------------------------------------------------------------------------------- /static/img/sentry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/img/sentry.png -------------------------------------------------------------------------------- /static/audio/bullet.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/audio/bullet.mp3 -------------------------------------------------------------------------------- /static/img/explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/img/explosion.png -------------------------------------------------------------------------------- /static/audio/alien_boom.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/audio/alien_boom.mp3 -------------------------------------------------------------------------------- /static/audio/bullet_boom.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/audio/bullet_boom.mp3 -------------------------------------------------------------------------------- /static/phase00/img/alien.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase00/img/alien.png -------------------------------------------------------------------------------- /static/phase00/img/bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase00/img/bullet.png -------------------------------------------------------------------------------- /static/phase00/img/earth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase00/img/earth.png -------------------------------------------------------------------------------- /static/phase00/img/sentry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase00/img/sentry.png -------------------------------------------------------------------------------- /static/phase02/img/alien.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase02/img/alien.png -------------------------------------------------------------------------------- /static/phase02/img/bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase02/img/bullet.png -------------------------------------------------------------------------------- /static/phase02/img/earth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase02/img/earth.png -------------------------------------------------------------------------------- /static/phase02/img/sentry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase02/img/sentry.png -------------------------------------------------------------------------------- /static/phase03/img/alien.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase03/img/alien.png -------------------------------------------------------------------------------- /static/phase03/img/earth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase03/img/earth.png -------------------------------------------------------------------------------- /static/phase04/img/alien.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase04/img/alien.png -------------------------------------------------------------------------------- /static/phase04/img/earth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase04/img/earth.png -------------------------------------------------------------------------------- /static/phase05/img/alien.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase05/img/alien.png -------------------------------------------------------------------------------- /static/phase05/img/earth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase05/img/earth.png -------------------------------------------------------------------------------- /static/phase06/img/alien.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase06/img/alien.png -------------------------------------------------------------------------------- /static/phase06/img/earth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase06/img/earth.png -------------------------------------------------------------------------------- /static/phase07/img/alien.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase07/img/alien.png -------------------------------------------------------------------------------- /static/phase07/img/bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase07/img/bullet.png -------------------------------------------------------------------------------- /static/phase07/img/earth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase07/img/earth.png -------------------------------------------------------------------------------- /static/phase07/img/sentry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase07/img/sentry.png -------------------------------------------------------------------------------- /static/phase08/img/alien.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase08/img/alien.png -------------------------------------------------------------------------------- /static/phase08/img/bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase08/img/bullet.png -------------------------------------------------------------------------------- /static/phase08/img/earth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase08/img/earth.png -------------------------------------------------------------------------------- /static/phase08/img/sentry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase08/img/sentry.png -------------------------------------------------------------------------------- /static/phase09/img/alien.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase09/img/alien.png -------------------------------------------------------------------------------- /static/phase09/img/bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase09/img/bullet.png -------------------------------------------------------------------------------- /static/phase09/img/earth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase09/img/earth.png -------------------------------------------------------------------------------- /static/phase09/img/sentry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase09/img/sentry.png -------------------------------------------------------------------------------- /static/phase10/img/alien.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase10/img/alien.png -------------------------------------------------------------------------------- /static/phase10/img/bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase10/img/bullet.png -------------------------------------------------------------------------------- /static/phase10/img/earth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase10/img/earth.png -------------------------------------------------------------------------------- /static/phase10/img/sentry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase10/img/sentry.png -------------------------------------------------------------------------------- /static/phase11/img/alien.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase11/img/alien.png -------------------------------------------------------------------------------- /static/phase11/img/bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase11/img/bullet.png -------------------------------------------------------------------------------- /static/phase11/img/earth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase11/img/earth.png -------------------------------------------------------------------------------- /static/phase11/img/sentry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase11/img/sentry.png -------------------------------------------------------------------------------- /static/phase12/img/alien.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase12/img/alien.png -------------------------------------------------------------------------------- /static/phase12/img/bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase12/img/bullet.png -------------------------------------------------------------------------------- /static/phase12/img/earth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase12/img/earth.png -------------------------------------------------------------------------------- /static/phase12/img/sentry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase12/img/sentry.png -------------------------------------------------------------------------------- /static/phase13/img/alien.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase13/img/alien.png -------------------------------------------------------------------------------- /static/phase13/img/bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase13/img/bullet.png -------------------------------------------------------------------------------- /static/phase13/img/earth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase13/img/earth.png -------------------------------------------------------------------------------- /static/phase13/img/sentry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase13/img/sentry.png -------------------------------------------------------------------------------- /static/phase14/img/alien.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase14/img/alien.png -------------------------------------------------------------------------------- /static/phase14/img/bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase14/img/bullet.png -------------------------------------------------------------------------------- /static/phase14/img/earth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase14/img/earth.png -------------------------------------------------------------------------------- /static/phase14/img/sentry.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase14/img/sentry.png -------------------------------------------------------------------------------- /static/img/alien-explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/img/alien-explosion.png -------------------------------------------------------------------------------- /static/phase00/audio/bullet.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase00/audio/bullet.mp3 -------------------------------------------------------------------------------- /static/phase10/audio/bullet.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase10/audio/bullet.mp3 -------------------------------------------------------------------------------- /static/phase11/audio/bullet.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase11/audio/bullet.mp3 -------------------------------------------------------------------------------- /static/phase12/audio/bullet.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase12/audio/bullet.mp3 -------------------------------------------------------------------------------- /static/phase13/audio/bullet.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase13/audio/bullet.mp3 -------------------------------------------------------------------------------- /static/phase14/audio/bullet.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase14/audio/bullet.mp3 -------------------------------------------------------------------------------- /static/phase00/img/explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase00/img/explosion.png -------------------------------------------------------------------------------- /static/phase02/img/explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase02/img/explosion.png -------------------------------------------------------------------------------- /static/phase07/img/explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase07/img/explosion.png -------------------------------------------------------------------------------- /static/phase09/img/explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase09/img/explosion.png -------------------------------------------------------------------------------- /static/phase10/img/explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase10/img/explosion.png -------------------------------------------------------------------------------- /static/phase11/img/explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase11/img/explosion.png -------------------------------------------------------------------------------- /static/phase12/img/explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase12/img/explosion.png -------------------------------------------------------------------------------- /static/phase13/img/explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase13/img/explosion.png -------------------------------------------------------------------------------- /static/phase14/img/explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase14/img/explosion.png -------------------------------------------------------------------------------- /static/phase00/audio/alien_boom.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase00/audio/alien_boom.mp3 -------------------------------------------------------------------------------- /static/phase00/audio/bullet_boom.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase00/audio/bullet_boom.mp3 -------------------------------------------------------------------------------- /static/phase08/img/bullet-single.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase08/img/bullet-single.png -------------------------------------------------------------------------------- /static/phase10/audio/alien_boom.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase10/audio/alien_boom.mp3 -------------------------------------------------------------------------------- /static/phase10/audio/bullet_boom.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase10/audio/bullet_boom.mp3 -------------------------------------------------------------------------------- /static/phase11/audio/alien_boom.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase11/audio/alien_boom.mp3 -------------------------------------------------------------------------------- /static/phase11/audio/bullet_boom.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase11/audio/bullet_boom.mp3 -------------------------------------------------------------------------------- /static/phase12/audio/alien_boom.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase12/audio/alien_boom.mp3 -------------------------------------------------------------------------------- /static/phase12/audio/bullet_boom.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase12/audio/bullet_boom.mp3 -------------------------------------------------------------------------------- /static/phase13/audio/alien_boom.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase13/audio/alien_boom.mp3 -------------------------------------------------------------------------------- /static/phase13/audio/bullet_boom.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase13/audio/bullet_boom.mp3 -------------------------------------------------------------------------------- /static/phase14/audio/alien_boom.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase14/audio/alien_boom.mp3 -------------------------------------------------------------------------------- /static/phase14/audio/bullet_boom.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase14/audio/bullet_boom.mp3 -------------------------------------------------------------------------------- /static/swf/soundmanager2_flash9.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/swf/soundmanager2_flash9.swf -------------------------------------------------------------------------------- /static/phase00/img/alien-explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase00/img/alien-explosion.png -------------------------------------------------------------------------------- /static/phase02/img/alien-explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase02/img/alien-explosion.png -------------------------------------------------------------------------------- /static/phase07/img/alien-explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase07/img/alien-explosion.png -------------------------------------------------------------------------------- /static/phase11/img/alien-explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase11/img/alien-explosion.png -------------------------------------------------------------------------------- /static/phase12/img/alien-explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase12/img/alien-explosion.png -------------------------------------------------------------------------------- /static/phase13/img/alien-explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase13/img/alien-explosion.png -------------------------------------------------------------------------------- /static/phase14/img/alien-explosion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase14/img/alien-explosion.png -------------------------------------------------------------------------------- /static/swf/soundmanager2_flash9_debug.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/swf/soundmanager2_flash9_debug.swf -------------------------------------------------------------------------------- /static/phase00/swf/soundmanager2_flash9.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase00/swf/soundmanager2_flash9.swf -------------------------------------------------------------------------------- /static/phase10/swf/soundmanager2_flash9.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase10/swf/soundmanager2_flash9.swf -------------------------------------------------------------------------------- /static/phase11/swf/soundmanager2_flash9.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase11/swf/soundmanager2_flash9.swf -------------------------------------------------------------------------------- /static/phase12/swf/soundmanager2_flash9.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase12/swf/soundmanager2_flash9.swf -------------------------------------------------------------------------------- /static/phase13/swf/soundmanager2_flash9.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase13/swf/soundmanager2_flash9.swf -------------------------------------------------------------------------------- /static/phase14/swf/soundmanager2_flash9.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase14/swf/soundmanager2_flash9.swf -------------------------------------------------------------------------------- /static/phase00/swf/soundmanager2_flash9_debug.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase00/swf/soundmanager2_flash9_debug.swf -------------------------------------------------------------------------------- /static/phase10/swf/soundmanager2_flash9_debug.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase10/swf/soundmanager2_flash9_debug.swf -------------------------------------------------------------------------------- /static/phase11/swf/soundmanager2_flash9_debug.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase11/swf/soundmanager2_flash9_debug.swf -------------------------------------------------------------------------------- /static/phase12/swf/soundmanager2_flash9_debug.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase12/swf/soundmanager2_flash9_debug.swf -------------------------------------------------------------------------------- /static/phase13/swf/soundmanager2_flash9_debug.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase13/swf/soundmanager2_flash9_debug.swf -------------------------------------------------------------------------------- /static/phase14/swf/soundmanager2_flash9_debug.swf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sethladd/Bad-Aliens/HEAD/static/phase14/swf/soundmanager2_flash9_debug.swf -------------------------------------------------------------------------------- /static/phase01/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bad Aliens 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /static/phase02/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bad Aliens 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /static/phase03/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bad Aliens 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /static/phase04/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bad Aliens 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /static/phase05/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bad Aliens 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /static/phase06/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bad Aliens 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /static/phase07/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bad Aliens 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /static/phase08/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bad Aliens 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /static/phase09/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bad Aliens 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | * Finish refactoring into screens 2 | * Game Over Screen 3 | * Refactor entities from Game Engine over to EntitiesScreen 4 | * Record high scores 5 | * Display high scores on Game Over screen 6 | * Display "Play Again?" on Game Over screen, handle button clicks for Yes or No 7 | * Create New Game screen 8 | * Display "start" on New Game screen 9 | * Display high scores on New Game screen 10 | * Refactor into multiple files -------------------------------------------------------------------------------- /app.yaml: -------------------------------------------------------------------------------- 1 | application: bad-aliens 2 | version: 1 3 | runtime: python 4 | api_version: 1 5 | 6 | #default_expiration: "30d" 7 | 8 | handlers: 9 | 10 | - url: /(.*\.appcache) 11 | static_files: static/\1 12 | mime_type: text/cache-manifest 13 | upload: static/(.*\.appcache) 14 | 15 | - url: (.*)/ 16 | static_files: static\1/index.html 17 | upload: static/index.html 18 | 19 | - url: / 20 | static_dir: static 21 | 22 | -------------------------------------------------------------------------------- /static/manifest.appcache: -------------------------------------------------------------------------------- 1 | CACHE MANIFEST 2 | 3 | # version 16 4 | 5 | CACHE: 6 | scripts/app.js 7 | scripts/soundmanager2-nodebug-jsmin.js 8 | scripts/stats.js 9 | 10 | swf/soundmanager2_flash9.swf 11 | 12 | audio/alien_boom.mp3 13 | audio/bullet.mp3 14 | audio/bullet_boom.mp3 15 | 16 | img/alien-explosion.png 17 | img/alien.png 18 | img/bullet.png 19 | img/earth.png 20 | img/explosion.png 21 | img/sentry.png 22 | 23 | favicon.ico 24 | index.html -------------------------------------------------------------------------------- /static/phase00/manifest.appcache: -------------------------------------------------------------------------------- 1 | CACHE MANIFEST 2 | 3 | # version 5 4 | 5 | CACHE: 6 | scripts/app.js 7 | scripts/soundmanager2-nodebug-jsmin.js 8 | scripts/stats.js 9 | 10 | swf/soundmanager2_flash9.swf 11 | 12 | audio/alien_boom.mp3 13 | audio/bullet.mp3 14 | audio/bullet_boom.mp3 15 | 16 | img/alien-explosion.png 17 | img/alien.png 18 | img/bullet.png 19 | img/earth.png 20 | img/explosion.png 21 | img/sentry.png 22 | 23 | favicon.ico 24 | index.html -------------------------------------------------------------------------------- /static/phase14/manifest.appcache: -------------------------------------------------------------------------------- 1 | CACHE MANIFEST 2 | 3 | # version 5 4 | 5 | CACHE: 6 | scripts/app.js 7 | scripts/soundmanager2-nodebug-jsmin.js 8 | scripts/stats.js 9 | 10 | swf/soundmanager2_flash9.swf 11 | 12 | audio/alien_boom.mp3 13 | audio/bullet.mp3 14 | audio/bullet_boom.mp3 15 | 16 | img/alien-explosion.png 17 | img/alien.png 18 | img/bullet.png 19 | img/earth.png 20 | img/explosion.png 21 | img/sentry.png 22 | 23 | favicon.ico 24 | index.html -------------------------------------------------------------------------------- /static/phase10/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bad Aliens 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /static/phase11/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bad Aliens 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /static/phase12/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bad Aliens 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /index.yaml: -------------------------------------------------------------------------------- 1 | indexes: 2 | 3 | # AUTOGENERATED 4 | 5 | # This index.yaml is automatically updated whenever the dev_appserver 6 | # detects that a new type of query is run. If you want to manage the 7 | # index.yaml file manually, remove the above marker line (the line 8 | # saying "# AUTOGENERATED"). If you want to manage some indexes 9 | # manually, move them above the marker line. The index.yaml file is 10 | # automatically uploaded to the admin console when you next deploy 11 | # your application using appcfg.py. 12 | -------------------------------------------------------------------------------- /static/phase13/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bad Aliens 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bad Aliens 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | WARNING: THIS GAME IS DEPRECATED. 2 | 3 | This code is no longer maintained. Certain aspects of it (like, using SoundManager2) are no longer 4 | the right way to do it. This code is left here for posterity only. 5 | 6 | Example HTML5 Game for Google IO Presentation. 7 | 8 | # Credits 9 | 10 | Images: 11 | * http://www.lostgarden.com/2005/03/download-complete-set-of-sweet-8-bit.html 12 | 13 | Sounds: 14 | * http://www.freesound.org/samplesViewSingle.php?id=18397 15 | * http://www.freesound.org/samplesViewSingle.php?id=47252 16 | * http://www.freesound.org/samplesViewSingle.php?id=35643 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2011 Seth Ladd 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /static/phase00/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bad Aliens 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /static/phase14/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Bad Aliens 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /static/phase02/scripts/app.js: -------------------------------------------------------------------------------- 1 | function AssetManager() { 2 | this.successCount = 0; 3 | this.errorCount = 0; 4 | this.cache = {}; 5 | this.downloadQueue = []; 6 | } 7 | 8 | AssetManager.prototype.queueDownload = function(path) { 9 | this.downloadQueue.push(path); 10 | } 11 | 12 | AssetManager.prototype.downloadAll = function(callback) { 13 | if (this.downloadQueue.length === 0 && this.soundsQueue.length === 0) { 14 | callback(); 15 | } 16 | 17 | for (var i = 0; i < this.downloadQueue.length; i++) { 18 | var path = this.downloadQueue[i]; 19 | var img = new Image(); 20 | var that = this; 21 | img.addEventListener("load", function() { 22 | console.log(this.src + ' is loaded'); 23 | that.successCount += 1; 24 | if (that.isDone()) { 25 | callback(); 26 | } 27 | }, false); 28 | img.addEventListener("error", function() { 29 | that.errorCount += 1; 30 | if (that.isDone()) { 31 | callback(); 32 | } 33 | }, false); 34 | img.src = path; 35 | this.cache[path] = img; 36 | } 37 | } 38 | 39 | AssetManager.prototype.getAsset = function(path) { 40 | return this.cache[path]; 41 | } 42 | 43 | AssetManager.prototype.isDone = function() { 44 | return (this.downloadQueue.length == this.successCount + this.errorCount); 45 | } 46 | 47 | var canvas = document.getElementById('surface'); 48 | var ctx = canvas.getContext('2d'); 49 | var ASSET_MANAGER = new AssetManager(); 50 | 51 | ASSET_MANAGER.queueDownload('img/earth.png'); 52 | 53 | ASSET_MANAGER.downloadAll(function() { 54 | var x = 0, y = 0; 55 | var sprite = ASSET_MANAGER.getAsset('img/earth.png'); 56 | ctx.save(); 57 | ctx.translate(canvas.width/2, canvas.height/2); 58 | ctx.drawImage(sprite, x - sprite.width/2, y - sprite.height/2); 59 | ctx.restore(); 60 | }); -------------------------------------------------------------------------------- /static/scripts/stats.js: -------------------------------------------------------------------------------- 1 | // stats.js r5 - http://github.com/mrdoob/stats.js 2 | var Stats=function(){function w(d,K,n){var u,f,c;for(f=0;f<30;f++)for(u=0;u<73;u++){c=(u+f*74)*4;d[c]=d[c+4];d[c+1]=d[c+5];d[c+2]=d[c+6]}for(f=0;f<30;f++){c=(73+f*74)*4;if(f'+q+" MS ("+D+"-"+E+")";r.putImageData(F,0,0);J=l;if(l> 9 | z+1E3){o=Math.round(y*1E3/(l-z));A=Math.min(A,o);B=Math.max(B,o);w(C.data,Math.min(30,30-o/100*30),"fps");g.innerHTML=''+o+" FPS ("+A+"-"+B+")";p.putImageData(C,0,0);if(x==3){s=webkitPerformance.memory.usedJSHeapSize*9.54E-7;G=Math.min(G,s);H=Math.max(H,s);w(I.data,Math.min(30,30-s/2),"mem");k.innerHTML=''+Math.round(s)+" MEM ("+Math.round(G)+"-"+Math.round(H)+")";t.putImageData(I,0,0)}z=l;y=0}}}}; 10 | -------------------------------------------------------------------------------- /static/phase00/scripts/stats.js: -------------------------------------------------------------------------------- 1 | // stats.js r5 - http://github.com/mrdoob/stats.js 2 | var Stats=function(){function w(d,K,n){var u,f,c;for(f=0;f<30;f++)for(u=0;u<73;u++){c=(u+f*74)*4;d[c]=d[c+4];d[c+1]=d[c+5];d[c+2]=d[c+6]}for(f=0;f<30;f++){c=(73+f*74)*4;if(f'+q+" MS ("+D+"-"+E+")";r.putImageData(F,0,0);J=l;if(l> 9 | z+1E3){o=Math.round(y*1E3/(l-z));A=Math.min(A,o);B=Math.max(B,o);w(C.data,Math.min(30,30-o/100*30),"fps");g.innerHTML=''+o+" FPS ("+A+"-"+B+")";p.putImageData(C,0,0);if(x==3){s=webkitPerformance.memory.usedJSHeapSize*9.54E-7;G=Math.min(G,s);H=Math.max(H,s);w(I.data,Math.min(30,30-s/2),"mem");k.innerHTML=''+Math.round(s)+" MEM ("+Math.round(G)+"-"+Math.round(H)+")";t.putImageData(I,0,0)}z=l;y=0}}}}; 10 | -------------------------------------------------------------------------------- /static/phase10/scripts/stats.js: -------------------------------------------------------------------------------- 1 | // stats.js r5 - http://github.com/mrdoob/stats.js 2 | var Stats=function(){function w(d,K,n){var u,f,c;for(f=0;f<30;f++)for(u=0;u<73;u++){c=(u+f*74)*4;d[c]=d[c+4];d[c+1]=d[c+5];d[c+2]=d[c+6]}for(f=0;f<30;f++){c=(73+f*74)*4;if(f'+q+" MS ("+D+"-"+E+")";r.putImageData(F,0,0);J=l;if(l> 9 | z+1E3){o=Math.round(y*1E3/(l-z));A=Math.min(A,o);B=Math.max(B,o);w(C.data,Math.min(30,30-o/100*30),"fps");g.innerHTML=''+o+" FPS ("+A+"-"+B+")";p.putImageData(C,0,0);if(x==3){s=webkitPerformance.memory.usedJSHeapSize*9.54E-7;G=Math.min(G,s);H=Math.max(H,s);w(I.data,Math.min(30,30-s/2),"mem");k.innerHTML=''+Math.round(s)+" MEM ("+Math.round(G)+"-"+Math.round(H)+")";t.putImageData(I,0,0)}z=l;y=0}}}}; 10 | -------------------------------------------------------------------------------- /static/phase13/scripts/stats.js: -------------------------------------------------------------------------------- 1 | // stats.js r5 - http://github.com/mrdoob/stats.js 2 | var Stats=function(){function w(d,K,n){var u,f,c;for(f=0;f<30;f++)for(u=0;u<73;u++){c=(u+f*74)*4;d[c]=d[c+4];d[c+1]=d[c+5];d[c+2]=d[c+6]}for(f=0;f<30;f++){c=(73+f*74)*4;if(f'+q+" MS ("+D+"-"+E+")";r.putImageData(F,0,0);J=l;if(l> 9 | z+1E3){o=Math.round(y*1E3/(l-z));A=Math.min(A,o);B=Math.max(B,o);w(C.data,Math.min(30,30-o/100*30),"fps");g.innerHTML=''+o+" FPS ("+A+"-"+B+")";p.putImageData(C,0,0);if(x==3){s=webkitPerformance.memory.usedJSHeapSize*9.54E-7;G=Math.min(G,s);H=Math.max(H,s);w(I.data,Math.min(30,30-s/2),"mem");k.innerHTML=''+Math.round(s)+" MEM ("+Math.round(G)+"-"+Math.round(H)+")";t.putImageData(I,0,0)}z=l;y=0}}}}; 10 | -------------------------------------------------------------------------------- /static/phase14/scripts/stats.js: -------------------------------------------------------------------------------- 1 | // stats.js r5 - http://github.com/mrdoob/stats.js 2 | var Stats=function(){function w(d,K,n){var u,f,c;for(f=0;f<30;f++)for(u=0;u<73;u++){c=(u+f*74)*4;d[c]=d[c+4];d[c+1]=d[c+5];d[c+2]=d[c+6]}for(f=0;f<30;f++){c=(73+f*74)*4;if(f'+q+" MS ("+D+"-"+E+")";r.putImageData(F,0,0);J=l;if(l> 9 | z+1E3){o=Math.round(y*1E3/(l-z));A=Math.min(A,o);B=Math.max(B,o);w(C.data,Math.min(30,30-o/100*30),"fps");g.innerHTML=''+o+" FPS ("+A+"-"+B+")";p.putImageData(C,0,0);if(x==3){s=webkitPerformance.memory.usedJSHeapSize*9.54E-7;G=Math.min(G,s);H=Math.max(H,s);w(I.data,Math.min(30,30-s/2),"mem");k.innerHTML=''+Math.round(s)+" MEM ("+Math.round(G)+"-"+Math.round(H)+")";t.putImageData(I,0,0)}z=l;y=0}}}}; 10 | -------------------------------------------------------------------------------- /static/phase03/scripts/app.js: -------------------------------------------------------------------------------- 1 | window.requestAnimFrame = (function(){ 2 | return window.requestAnimationFrame || 3 | window.webkitRequestAnimationFrame || 4 | window.mozRequestAnimationFrame || 5 | window.oRequestAnimationFrame || 6 | window.msRequestAnimationFrame || 7 | function(/* function */ callback, /* DOMElement */ element){ 8 | window.setTimeout(callback, 1000 / 60); 9 | }; 10 | })(); 11 | 12 | function AssetManager() { 13 | this.successCount = 0; 14 | this.errorCount = 0; 15 | this.cache = {}; 16 | this.downloadQueue = []; 17 | } 18 | 19 | AssetManager.prototype.queueDownload = function(path) { 20 | this.downloadQueue.push(path); 21 | } 22 | 23 | AssetManager.prototype.downloadAll = function(callback) { 24 | if (this.downloadQueue.length === 0 && this.soundsQueue.length === 0) { 25 | callback(); 26 | } 27 | 28 | for (var i = 0; i < this.downloadQueue.length; i++) { 29 | var path = this.downloadQueue[i]; 30 | var img = new Image(); 31 | var that = this; 32 | img.addEventListener("load", function() { 33 | console.log(this.src + ' is loaded'); 34 | that.successCount += 1; 35 | if (that.isDone()) { 36 | callback(); 37 | } 38 | }, false); 39 | img.addEventListener("error", function() { 40 | that.errorCount += 1; 41 | if (that.isDone()) { 42 | callback(); 43 | } 44 | }, false); 45 | img.src = path; 46 | this.cache[path] = img; 47 | } 48 | } 49 | 50 | AssetManager.prototype.getAsset = function(path) { 51 | return this.cache[path]; 52 | } 53 | 54 | AssetManager.prototype.isDone = function() { 55 | return (this.downloadQueue.length == this.successCount + this.errorCount); 56 | } 57 | 58 | function GameEngine() { 59 | this.entities = []; 60 | this.ctx = null; 61 | 62 | this.lastUpdateTimestamp = null; 63 | this.deltaTime = null; 64 | 65 | this.surfaceWidth = null; 66 | this.surfaceHeight = null; 67 | this.halfSurfaceWidth = null; 68 | this.halfSurfaceHeight = null; 69 | } 70 | 71 | GameEngine.prototype.init = function(ctx) { 72 | console.log('game initialized'); 73 | this.ctx = ctx; 74 | this.surfaceWidth = this.ctx.canvas.width; 75 | this.surfaceHeight = this.ctx.canvas.height; 76 | this.halfSurfaceWidth = this.surfaceWidth/2; 77 | this.halfSurfaceHeight = this.surfaceHeight/2; 78 | } 79 | 80 | GameEngine.prototype.start = function() { 81 | console.log("starting game"); 82 | this.lastUpdateTimestamp = Date.now(); 83 | var that = this; 84 | (function gameLoop() { 85 | that.loop(); 86 | requestAnimFrame(gameLoop, that.ctx.canvas); 87 | })(); 88 | } 89 | 90 | GameEngine.prototype.addEntity = function(entity) { 91 | this.entities.push(entity); 92 | } 93 | 94 | GameEngine.prototype.draw = function(callback) { 95 | this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); 96 | this.ctx.save(); 97 | this.ctx.translate(this.ctx.canvas.width/2, this.ctx.canvas.height/2); 98 | for (var i = 0; i < this.entities.length; i++) { 99 | this.entities[i].draw(this.ctx); 100 | } 101 | if (callback) { 102 | callback(this); 103 | } 104 | this.ctx.restore(); 105 | } 106 | 107 | GameEngine.prototype.update = function() { 108 | var entitiesCount = this.entities.length; 109 | 110 | for (var i = 0; i < entitiesCount; i++) { 111 | var entity = this.entities[i]; 112 | 113 | if (!entity.removeFromWorld) { 114 | entity.update(); 115 | } 116 | } 117 | 118 | for (var i = this.entities.length-1; i >= 0; --i) { 119 | if (this.entities[i].removeFromWorld) { 120 | this.entities.splice(i, 1); 121 | } 122 | } 123 | } 124 | 125 | GameEngine.prototype.loop = function() { 126 | var now = Date.now(); 127 | this.deltaTime = now - this.lastUpdateTimestamp; 128 | this.update(); 129 | this.draw(); 130 | this.lastUpdateTimestamp = now; 131 | } 132 | 133 | function Entity(game, x, y) { 134 | this.game = game; 135 | this.x = x; 136 | this.y = y; 137 | this.removeFromWorld = false; 138 | } 139 | 140 | Entity.prototype.update = function() { 141 | } 142 | 143 | Entity.prototype.draw = function(ctx) { 144 | if (this.game.showOutlines && this.radius) { 145 | ctx.beginPath(); 146 | ctx.strokeStyle = "green"; 147 | ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, false); 148 | ctx.stroke(); 149 | ctx.closePath(); 150 | } 151 | } 152 | 153 | Entity.prototype.drawSpriteCentered = function(ctx) { 154 | if (this.sprite && this.x && this.y) { 155 | var x = this.x - this.sprite.width/2; 156 | var y = this.y - this.sprite.height/2; 157 | ctx.drawImage(this.sprite, x, y); 158 | } 159 | } 160 | 161 | Entity.prototype.outsideScreen = function() { 162 | return (this.x > this.game.halfSurfaceWidth || this.x < -(this.game.halfSurfaceWidth) || 163 | this.y > this.game.halfSurfaceHeight || this.y < -(this.game.halfSurfaceHeight)); 164 | } 165 | 166 | function Alien(game, radial_distance, angle) { 167 | Entity.call(this, game); 168 | this.radial_distance = radial_distance; 169 | this.angle = angle; 170 | this.speed = 0.2; 171 | this.sprite = ASSET_MANAGER.getAsset('img/alien.png'); 172 | this.radius = this.sprite.height/2; 173 | this.setCoords(); 174 | } 175 | Alien.prototype = new Entity(); 176 | Alien.prototype.constructor = Alien; 177 | 178 | Alien.prototype.setCoords = function() { 179 | this.x = this.radial_distance * Math.cos(this.angle); 180 | this.y = this.radial_distance * Math.sin(this.angle); 181 | } 182 | 183 | Alien.prototype.update = function() { 184 | this.setCoords(); 185 | this.radial_distance -= this.speed * this.game.deltaTime; 186 | 187 | Entity.prototype.update.call(this); 188 | } 189 | 190 | Alien.prototype.draw = function(ctx) { 191 | this.drawSpriteCentered(ctx); 192 | Entity.prototype.draw.call(this, ctx); 193 | } 194 | 195 | function Earth(game) { 196 | Entity.call(this, game, 0, 0); 197 | this.sprite = ASSET_MANAGER.getAsset('img/earth.png'); 198 | } 199 | Earth.prototype = new Entity(); 200 | Earth.prototype.constructor = Earth; 201 | 202 | Earth.RADIUS = 67; 203 | 204 | Earth.prototype.draw = function(ctx) { 205 | ctx.drawImage(this.sprite, this.x - this.sprite.width/2, this.y - this.sprite.height/2); 206 | } 207 | 208 | function EvilAliens() { 209 | GameEngine.call(this); 210 | } 211 | EvilAliens.prototype = new GameEngine(); 212 | EvilAliens.prototype.constructor = EvilAliens; 213 | 214 | EvilAliens.prototype.start = function() { 215 | this.earth = new Earth(this); 216 | this.addEntity(this.earth); 217 | GameEngine.prototype.start.call(this); 218 | } 219 | 220 | EvilAliens.prototype.update = function() { 221 | if (this.lastAlienAddedAt == null || (this.lastUpdateTimestamp - this.lastAlienAddedAt) > 1000) { 222 | this.addEntity(new Alien(this, this.ctx.canvas.width, Math.random() * Math.PI * 180)); 223 | this.lastAlienAddedAt = this.lastUpdateTimestamp; 224 | } 225 | 226 | GameEngine.prototype.update.call(this); 227 | } 228 | 229 | EvilAliens.prototype.draw = function() { 230 | GameEngine.prototype.draw.call(this); 231 | } 232 | 233 | var canvas = document.getElementById('surface'); 234 | var ctx = canvas.getContext('2d'); 235 | var game = new EvilAliens(); 236 | var ASSET_MANAGER = new AssetManager(); 237 | 238 | ASSET_MANAGER.queueDownload('img/alien.png'); 239 | ASSET_MANAGER.queueDownload('img/earth.png'); 240 | 241 | ASSET_MANAGER.downloadAll(function() { 242 | game.init(ctx); 243 | game.start(); 244 | }); -------------------------------------------------------------------------------- /static/phase04/scripts/app.js: -------------------------------------------------------------------------------- 1 | window.requestAnimFrame = (function(){ 2 | return window.requestAnimationFrame || 3 | window.webkitRequestAnimationFrame || 4 | window.mozRequestAnimationFrame || 5 | window.oRequestAnimationFrame || 6 | window.msRequestAnimationFrame || 7 | function(/* function */ callback, /* DOMElement */ element){ 8 | window.setTimeout(callback, 1000 / 60); 9 | }; 10 | })(); 11 | 12 | function AssetManager() { 13 | this.successCount = 0; 14 | this.errorCount = 0; 15 | this.cache = {}; 16 | this.downloadQueue = []; 17 | } 18 | 19 | AssetManager.prototype.queueDownload = function(path) { 20 | this.downloadQueue.push(path); 21 | } 22 | 23 | AssetManager.prototype.downloadAll = function(callback) { 24 | if (this.downloadQueue.length === 0 && this.soundsQueue.length === 0) { 25 | callback(); 26 | } 27 | 28 | for (var i = 0; i < this.downloadQueue.length; i++) { 29 | var path = this.downloadQueue[i]; 30 | var img = new Image(); 31 | var that = this; 32 | img.addEventListener("load", function() { 33 | console.log(this.src + ' is loaded'); 34 | that.successCount += 1; 35 | if (that.isDone()) { 36 | callback(); 37 | } 38 | }, false); 39 | img.addEventListener("error", function() { 40 | that.errorCount += 1; 41 | if (that.isDone()) { 42 | callback(); 43 | } 44 | }, false); 45 | img.src = path; 46 | this.cache[path] = img; 47 | } 48 | } 49 | 50 | AssetManager.prototype.getAsset = function(path) { 51 | return this.cache[path]; 52 | } 53 | 54 | AssetManager.prototype.isDone = function() { 55 | return (this.downloadQueue.length == this.successCount + this.errorCount); 56 | } 57 | 58 | function Timer() { 59 | this.gameTime = 0; 60 | this.maxStep = 0.05; 61 | this.wallLastTimestamp = 0; 62 | } 63 | 64 | Timer.prototype.tick = function() { 65 | var wallCurrent = Date.now(); 66 | var wallDelta = (wallCurrent - this.wallLastTimestamp) / 1000; 67 | this.wallLastTimestamp = wallCurrent; 68 | 69 | var gameDelta = Math.min(wallDelta, this.maxStep); 70 | this.gameTime += gameDelta; 71 | return gameDelta; 72 | } 73 | 74 | function GameEngine() { 75 | this.entities = []; 76 | this.ctx = null; 77 | this.timer = new Timer(); 78 | this.surfaceWidth = null; 79 | this.surfaceHeight = null; 80 | this.halfSurfaceWidth = null; 81 | this.halfSurfaceHeight = null; 82 | } 83 | 84 | GameEngine.prototype.init = function(ctx) { 85 | console.log('game initialized'); 86 | this.ctx = ctx; 87 | this.surfaceWidth = this.ctx.canvas.width; 88 | this.surfaceHeight = this.ctx.canvas.height; 89 | this.halfSurfaceWidth = this.surfaceWidth/2; 90 | this.halfSurfaceHeight = this.surfaceHeight/2; 91 | } 92 | 93 | GameEngine.prototype.start = function() { 94 | console.log("starting game"); 95 | var that = this; 96 | (function gameLoop() { 97 | that.loop(); 98 | requestAnimFrame(gameLoop, that.ctx.canvas); 99 | })(); 100 | } 101 | 102 | GameEngine.prototype.addEntity = function(entity) { 103 | this.entities.push(entity); 104 | } 105 | 106 | GameEngine.prototype.draw = function(callback) { 107 | this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); 108 | this.ctx.save(); 109 | this.ctx.translate(this.ctx.canvas.width/2, this.ctx.canvas.height/2); 110 | for (var i = 0; i < this.entities.length; i++) { 111 | this.entities[i].draw(this.ctx); 112 | } 113 | if (callback) { 114 | callback(this); 115 | } 116 | this.ctx.restore(); 117 | } 118 | 119 | GameEngine.prototype.update = function() { 120 | var entitiesCount = this.entities.length; 121 | 122 | for (var i = 0; i < entitiesCount; i++) { 123 | var entity = this.entities[i]; 124 | 125 | if (!entity.removeFromWorld) { 126 | entity.update(); 127 | } 128 | } 129 | 130 | for (var i = this.entities.length-1; i >= 0; --i) { 131 | if (this.entities[i].removeFromWorld) { 132 | this.entities.splice(i, 1); 133 | } 134 | } 135 | } 136 | 137 | GameEngine.prototype.loop = function() { 138 | this.clockTick = this.timer.tick(); 139 | this.update(); 140 | this.draw(); 141 | } 142 | 143 | function Entity(game, x, y) { 144 | this.game = game; 145 | this.x = x; 146 | this.y = y; 147 | this.removeFromWorld = false; 148 | } 149 | 150 | Entity.prototype.update = function() { 151 | } 152 | 153 | Entity.prototype.draw = function(ctx) { 154 | if (this.game.showOutlines && this.radius) { 155 | ctx.beginPath(); 156 | ctx.strokeStyle = "green"; 157 | ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, false); 158 | ctx.stroke(); 159 | ctx.closePath(); 160 | } 161 | } 162 | 163 | Entity.prototype.drawSpriteCentered = function(ctx) { 164 | if (this.sprite && this.x && this.y) { 165 | var x = this.x - this.sprite.width/2; 166 | var y = this.y - this.sprite.height/2; 167 | ctx.drawImage(this.sprite, x, y); 168 | } 169 | } 170 | 171 | Entity.prototype.outsideScreen = function() { 172 | return (this.x > this.game.halfSurfaceWidth || this.x < -(this.game.halfSurfaceWidth) || 173 | this.y > this.game.halfSurfaceHeight || this.y < -(this.game.halfSurfaceHeight)); 174 | } 175 | 176 | function Alien(game, radial_distance, angle) { 177 | Entity.call(this, game); 178 | this.radial_distance = radial_distance; 179 | this.angle = angle; 180 | this.speed = 100; 181 | this.sprite = ASSET_MANAGER.getAsset('img/alien.png'); 182 | this.radius = this.sprite.height/2; 183 | this.setCoords(); 184 | } 185 | Alien.prototype = new Entity(); 186 | Alien.prototype.constructor = Alien; 187 | 188 | Alien.prototype.setCoords = function() { 189 | this.x = this.radial_distance * Math.cos(this.angle); 190 | this.y = this.radial_distance * Math.sin(this.angle); 191 | } 192 | 193 | Alien.prototype.update = function() { 194 | this.setCoords(); 195 | this.radial_distance -= this.speed * this.game.clockTick; 196 | 197 | Entity.prototype.update.call(this); 198 | } 199 | 200 | Alien.prototype.draw = function(ctx) { 201 | this.drawSpriteCentered(ctx); 202 | Entity.prototype.draw.call(this, ctx); 203 | } 204 | 205 | function Earth(game) { 206 | Entity.call(this, game, 0, 0); 207 | this.sprite = ASSET_MANAGER.getAsset('img/earth.png'); 208 | } 209 | Earth.prototype = new Entity(); 210 | Earth.prototype.constructor = Earth; 211 | 212 | Earth.RADIUS = 67; 213 | 214 | Earth.prototype.draw = function(ctx) { 215 | ctx.drawImage(this.sprite, this.x - this.sprite.width/2, this.y - this.sprite.height/2); 216 | } 217 | 218 | function EvilAliens() { 219 | GameEngine.call(this); 220 | } 221 | EvilAliens.prototype = new GameEngine(); 222 | EvilAliens.prototype.constructor = EvilAliens; 223 | 224 | EvilAliens.prototype.start = function() { 225 | this.earth = new Earth(this); 226 | this.addEntity(this.earth); 227 | GameEngine.prototype.start.call(this); 228 | } 229 | 230 | EvilAliens.prototype.update = function() { 231 | if (this.lastAlienAddedAt == null || (this.timer.gameTime - this.lastAlienAddedAt) > 1) { 232 | this.addEntity(new Alien(this, this.ctx.canvas.width, Math.random() * Math.PI * 180)); 233 | this.lastAlienAddedAt = this.timer.gameTime; 234 | } 235 | 236 | GameEngine.prototype.update.call(this); 237 | } 238 | 239 | EvilAliens.prototype.draw = function() { 240 | GameEngine.prototype.draw.call(this); 241 | } 242 | 243 | var canvas = document.getElementById('surface'); 244 | var ctx = canvas.getContext('2d'); 245 | var game = new EvilAliens(); 246 | var ASSET_MANAGER = new AssetManager(); 247 | 248 | ASSET_MANAGER.queueDownload('img/alien.png'); 249 | ASSET_MANAGER.queueDownload('img/earth.png'); 250 | 251 | ASSET_MANAGER.downloadAll(function() { 252 | game.init(ctx); 253 | game.start(); 254 | }); -------------------------------------------------------------------------------- /static/phase05/scripts/app.js: -------------------------------------------------------------------------------- 1 | window.requestAnimFrame = (function(){ 2 | return window.requestAnimationFrame || 3 | window.webkitRequestAnimationFrame || 4 | window.mozRequestAnimationFrame || 5 | window.oRequestAnimationFrame || 6 | window.msRequestAnimationFrame || 7 | function(/* function */ callback, /* DOMElement */ element){ 8 | window.setTimeout(callback, 1000 / 60); 9 | }; 10 | })(); 11 | 12 | function AssetManager() { 13 | this.successCount = 0; 14 | this.errorCount = 0; 15 | this.cache = {}; 16 | this.downloadQueue = []; 17 | } 18 | 19 | AssetManager.prototype.queueDownload = function(path) { 20 | this.downloadQueue.push(path); 21 | } 22 | 23 | AssetManager.prototype.downloadAll = function(callback) { 24 | if (this.downloadQueue.length === 0 && this.soundsQueue.length === 0) { 25 | callback(); 26 | } 27 | 28 | for (var i = 0; i < this.downloadQueue.length; i++) { 29 | var path = this.downloadQueue[i]; 30 | var img = new Image(); 31 | var that = this; 32 | img.addEventListener("load", function() { 33 | console.log(this.src + ' is loaded'); 34 | that.successCount += 1; 35 | if (that.isDone()) { 36 | callback(); 37 | } 38 | }, false); 39 | img.addEventListener("error", function() { 40 | that.errorCount += 1; 41 | if (that.isDone()) { 42 | callback(); 43 | } 44 | }, false); 45 | img.src = path; 46 | this.cache[path] = img; 47 | } 48 | } 49 | 50 | AssetManager.prototype.getAsset = function(path) { 51 | return this.cache[path]; 52 | } 53 | 54 | AssetManager.prototype.isDone = function() { 55 | return (this.downloadQueue.length == this.successCount + this.errorCount); 56 | } 57 | 58 | function Timer() { 59 | this.gameTime = 0; 60 | this.maxStep = 0.05; 61 | this.wallLastTimestamp = 0; 62 | } 63 | 64 | Timer.prototype.tick = function() { 65 | var wallCurrent = Date.now(); 66 | var wallDelta = (wallCurrent - this.wallLastTimestamp) / 1000; 67 | this.wallLastTimestamp = wallCurrent; 68 | 69 | var gameDelta = Math.min(wallDelta, this.maxStep); 70 | this.gameTime += gameDelta; 71 | return gameDelta; 72 | } 73 | 74 | function GameEngine() { 75 | this.entities = []; 76 | this.ctx = null; 77 | this.timer = new Timer(); 78 | this.surfaceWidth = null; 79 | this.surfaceHeight = null; 80 | this.halfSurfaceWidth = null; 81 | this.halfSurfaceHeight = null; 82 | } 83 | 84 | GameEngine.prototype.init = function(ctx) { 85 | console.log('game initialized'); 86 | this.ctx = ctx; 87 | this.surfaceWidth = this.ctx.canvas.width; 88 | this.surfaceHeight = this.ctx.canvas.height; 89 | this.halfSurfaceWidth = this.surfaceWidth/2; 90 | this.halfSurfaceHeight = this.surfaceHeight/2; 91 | } 92 | 93 | GameEngine.prototype.start = function() { 94 | console.log("starting game"); 95 | var that = this; 96 | (function gameLoop() { 97 | that.loop(); 98 | requestAnimFrame(gameLoop, that.ctx.canvas); 99 | })(); 100 | } 101 | 102 | GameEngine.prototype.addEntity = function(entity) { 103 | this.entities.push(entity); 104 | } 105 | 106 | GameEngine.prototype.draw = function(callback) { 107 | this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); 108 | this.ctx.save(); 109 | this.ctx.translate(this.ctx.canvas.width/2, this.ctx.canvas.height/2); 110 | for (var i = 0; i < this.entities.length; i++) { 111 | this.entities[i].draw(this.ctx); 112 | } 113 | if (callback) { 114 | callback(this); 115 | } 116 | this.ctx.restore(); 117 | } 118 | 119 | GameEngine.prototype.update = function() { 120 | var entitiesCount = this.entities.length; 121 | 122 | for (var i = 0; i < entitiesCount; i++) { 123 | var entity = this.entities[i]; 124 | 125 | if (!entity.removeFromWorld) { 126 | entity.update(); 127 | } 128 | } 129 | 130 | for (var i = this.entities.length-1; i >= 0; --i) { 131 | if (this.entities[i].removeFromWorld) { 132 | this.entities.splice(i, 1); 133 | } 134 | } 135 | } 136 | 137 | GameEngine.prototype.loop = function() { 138 | this.clockTick = this.timer.tick(); 139 | this.update(); 140 | this.draw(); 141 | this.click = null; 142 | } 143 | 144 | function Entity(game, x, y) { 145 | this.game = game; 146 | this.x = x; 147 | this.y = y; 148 | this.removeFromWorld = false; 149 | } 150 | 151 | Entity.prototype.update = function() { 152 | } 153 | 154 | Entity.prototype.draw = function(ctx) { 155 | if (this.game.showOutlines && this.radius) { 156 | ctx.beginPath(); 157 | ctx.strokeStyle = "green"; 158 | ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, false); 159 | ctx.stroke(); 160 | ctx.closePath(); 161 | } 162 | } 163 | 164 | Entity.prototype.drawSpriteCentered = function(ctx) { 165 | if (this.sprite && this.x && this.y) { 166 | var x = this.x - this.sprite.width/2; 167 | var y = this.y - this.sprite.height/2; 168 | ctx.drawImage(this.sprite, x, y); 169 | } 170 | } 171 | 172 | Entity.prototype.outsideScreen = function() { 173 | return (this.x > this.game.halfSurfaceWidth || this.x < -(this.game.halfSurfaceWidth) || 174 | this.y > this.game.halfSurfaceHeight || this.y < -(this.game.halfSurfaceHeight)); 175 | } 176 | 177 | function Alien(game, radial_distance, angle) { 178 | Entity.call(this, game); 179 | this.radial_distance = radial_distance; 180 | this.angle = angle; 181 | this.speed = 100; 182 | this.sprite = ASSET_MANAGER.getAsset('img/alien.png'); 183 | this.radius = this.sprite.height/2; 184 | this.setCoords(); 185 | } 186 | Alien.prototype = new Entity(); 187 | Alien.prototype.constructor = Alien; 188 | 189 | Alien.prototype.setCoords = function() { 190 | this.x = this.radial_distance * Math.cos(this.angle); 191 | this.y = this.radial_distance * Math.sin(this.angle); 192 | } 193 | 194 | Alien.prototype.update = function() { 195 | this.setCoords(); 196 | this.radial_distance -= this.speed * this.game.clockTick; 197 | 198 | Entity.prototype.update.call(this); 199 | } 200 | 201 | Alien.prototype.draw = function(ctx) { 202 | ctx.save(); 203 | ctx.translate(this.x, this.y); 204 | ctx.rotate(this.angle + Math.PI/2); 205 | ctx.drawImage(this.sprite, -this.sprite.width/2, -this.sprite.height/2); 206 | ctx.restore(); 207 | 208 | Entity.prototype.draw.call(this, ctx); 209 | } 210 | 211 | function Earth(game) { 212 | Entity.call(this, game, 0, 0); 213 | this.sprite = ASSET_MANAGER.getAsset('img/earth.png'); 214 | } 215 | Earth.prototype = new Entity(); 216 | Earth.prototype.constructor = Earth; 217 | 218 | Earth.RADIUS = 67; 219 | 220 | Earth.prototype.draw = function(ctx) { 221 | ctx.drawImage(this.sprite, this.x - this.sprite.width/2, this.y - this.sprite.height/2); 222 | } 223 | 224 | function EvilAliens() { 225 | GameEngine.call(this); 226 | } 227 | EvilAliens.prototype = new GameEngine(); 228 | EvilAliens.prototype.constructor = EvilAliens; 229 | 230 | EvilAliens.prototype.start = function() { 231 | this.earth = new Earth(this); 232 | this.addEntity(this.earth); 233 | GameEngine.prototype.start.call(this); 234 | } 235 | 236 | EvilAliens.prototype.update = function() { 237 | if (this.lastAlienAddedAt == null || (this.timer.gameTime - this.lastAlienAddedAt) > 1) { 238 | this.addEntity(new Alien(this, this.ctx.canvas.width, Math.floor(Math.random() * Math.PI * 2))); 239 | this.lastAlienAddedAt = this.timer.gameTime; 240 | } 241 | 242 | GameEngine.prototype.update.call(this); 243 | } 244 | 245 | EvilAliens.prototype.draw = function() { 246 | GameEngine.prototype.draw.call(this); 247 | } 248 | 249 | var canvas = document.getElementById('surface'); 250 | var ctx = canvas.getContext('2d'); 251 | var game = new EvilAliens(); 252 | var ASSET_MANAGER = new AssetManager(); 253 | 254 | ASSET_MANAGER.queueDownload('img/alien.png'); 255 | ASSET_MANAGER.queueDownload('img/earth.png'); 256 | 257 | ASSET_MANAGER.downloadAll(function() { 258 | game.init(ctx); 259 | game.start(); 260 | }); -------------------------------------------------------------------------------- /static/phase06/scripts/app.js: -------------------------------------------------------------------------------- 1 | window.requestAnimFrame = (function(){ 2 | return window.requestAnimationFrame || 3 | window.webkitRequestAnimationFrame || 4 | window.mozRequestAnimationFrame || 5 | window.oRequestAnimationFrame || 6 | window.msRequestAnimationFrame || 7 | function(/* function */ callback, /* DOMElement */ element){ 8 | window.setTimeout(callback, 1000 / 60); 9 | }; 10 | })(); 11 | 12 | function AssetManager() { 13 | this.successCount = 0; 14 | this.errorCount = 0; 15 | this.cache = {}; 16 | this.downloadQueue = []; 17 | } 18 | 19 | AssetManager.prototype.queueDownload = function(path) { 20 | this.downloadQueue.push(path); 21 | } 22 | 23 | AssetManager.prototype.downloadAll = function(callback) { 24 | if (this.downloadQueue.length === 0 && this.soundsQueue.length === 0) { 25 | callback(); 26 | } 27 | 28 | for (var i = 0; i < this.downloadQueue.length; i++) { 29 | var path = this.downloadQueue[i]; 30 | var img = new Image(); 31 | var that = this; 32 | img.addEventListener("load", function() { 33 | console.log(this.src + ' is loaded'); 34 | that.successCount += 1; 35 | if (that.isDone()) { 36 | callback(); 37 | } 38 | }, false); 39 | img.addEventListener("error", function() { 40 | that.errorCount += 1; 41 | if (that.isDone()) { 42 | callback(); 43 | } 44 | }, false); 45 | img.src = path; 46 | this.cache[path] = img; 47 | } 48 | } 49 | 50 | AssetManager.prototype.getAsset = function(path) { 51 | return this.cache[path]; 52 | } 53 | 54 | AssetManager.prototype.isDone = function() { 55 | return (this.downloadQueue.length == this.successCount + this.errorCount); 56 | } 57 | 58 | function Timer() { 59 | this.gameTime = 0; 60 | this.maxStep = 0.05; 61 | this.wallLastTimestamp = 0; 62 | } 63 | 64 | Timer.prototype.tick = function() { 65 | var wallCurrent = Date.now(); 66 | var wallDelta = (wallCurrent - this.wallLastTimestamp) / 1000; 67 | this.wallLastTimestamp = wallCurrent; 68 | 69 | var gameDelta = Math.min(wallDelta, this.maxStep); 70 | this.gameTime += gameDelta; 71 | return gameDelta; 72 | } 73 | 74 | function GameEngine() { 75 | this.entities = []; 76 | this.ctx = null; 77 | this.timer = new Timer(); 78 | this.surfaceWidth = null; 79 | this.surfaceHeight = null; 80 | this.halfSurfaceWidth = null; 81 | this.halfSurfaceHeight = null; 82 | 83 | //this.showOutlines = true; 84 | } 85 | 86 | GameEngine.prototype.init = function(ctx) { 87 | console.log('game initialized'); 88 | this.ctx = ctx; 89 | this.surfaceWidth = this.ctx.canvas.width; 90 | this.surfaceHeight = this.ctx.canvas.height; 91 | this.halfSurfaceWidth = this.surfaceWidth/2; 92 | this.halfSurfaceHeight = this.surfaceHeight/2; 93 | } 94 | 95 | GameEngine.prototype.start = function() { 96 | console.log("starting game"); 97 | var that = this; 98 | (function gameLoop() { 99 | that.loop(); 100 | requestAnimFrame(gameLoop, that.ctx.canvas); 101 | })(); 102 | } 103 | 104 | GameEngine.prototype.addEntity = function(entity) { 105 | this.entities.push(entity); 106 | } 107 | 108 | GameEngine.prototype.draw = function(callback) { 109 | this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); 110 | this.ctx.save(); 111 | this.ctx.translate(this.ctx.canvas.width/2, this.ctx.canvas.height/2); 112 | for (var i = 0; i < this.entities.length; i++) { 113 | this.entities[i].draw(this.ctx); 114 | } 115 | if (callback) { 116 | callback(this); 117 | } 118 | this.ctx.restore(); 119 | } 120 | 121 | GameEngine.prototype.update = function() { 122 | var entitiesCount = this.entities.length; 123 | 124 | for (var i = 0; i < entitiesCount; i++) { 125 | var entity = this.entities[i]; 126 | 127 | if (!entity.removeFromWorld) { 128 | entity.update(); 129 | } 130 | } 131 | 132 | for (var i = this.entities.length-1; i >= 0; --i) { 133 | if (this.entities[i].removeFromWorld) { 134 | this.entities.splice(i, 1); 135 | } 136 | } 137 | } 138 | 139 | GameEngine.prototype.loop = function() { 140 | this.clockTick = this.timer.tick(); 141 | this.update(); 142 | this.draw(); 143 | this.click = null; 144 | } 145 | 146 | function Entity(game, x, y) { 147 | this.game = game; 148 | this.x = x; 149 | this.y = y; 150 | this.removeFromWorld = false; 151 | } 152 | 153 | Entity.prototype.update = function() { 154 | } 155 | 156 | Entity.prototype.draw = function(ctx) { 157 | if (this.game.showOutlines && this.radius) { 158 | ctx.beginPath(); 159 | ctx.strokeStyle = "green"; 160 | ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, false); 161 | ctx.stroke(); 162 | ctx.closePath(); 163 | } 164 | } 165 | 166 | Entity.prototype.drawSpriteCentered = function(ctx) { 167 | if (this.sprite && this.x && this.y) { 168 | var x = this.x - this.sprite.width/2; 169 | var y = this.y - this.sprite.height/2; 170 | ctx.drawImage(this.sprite, x, y); 171 | } 172 | } 173 | 174 | Entity.prototype.outsideScreen = function() { 175 | return (this.x > this.game.halfSurfaceWidth || this.x < -(this.game.halfSurfaceWidth) || 176 | this.y > this.game.halfSurfaceHeight || this.y < -(this.game.halfSurfaceHeight)); 177 | } 178 | 179 | Entity.prototype.rotateAndCache = function(image) { 180 | var offscreenCanvas = document.createElement('canvas'); 181 | var size = Math.max(image.width, image.height); 182 | offscreenCanvas.width = size; 183 | offscreenCanvas.height = size; 184 | var offscreenCtx = offscreenCanvas.getContext('2d'); 185 | offscreenCtx.translate(size/2, size/2); 186 | offscreenCtx.rotate(this.angle + Math.PI/2); 187 | offscreenCtx.drawImage(image, -(image.width/2), -(image.height/2)); 188 | return offscreenCanvas; 189 | } 190 | 191 | function Alien(game, radial_distance, angle) { 192 | Entity.call(this, game); 193 | this.radial_distance = radial_distance; 194 | this.angle = angle; 195 | this.speed = 100; 196 | this.sprite = this.rotateAndCache(ASSET_MANAGER.getAsset('img/alien.png')); 197 | this.radius = this.sprite.height/2; 198 | this.setCoords(); 199 | } 200 | Alien.prototype = new Entity(); 201 | Alien.prototype.constructor = Alien; 202 | 203 | Alien.prototype.setCoords = function() { 204 | this.x = this.radial_distance * Math.cos(this.angle); 205 | this.y = this.radial_distance * Math.sin(this.angle); 206 | } 207 | 208 | Alien.prototype.update = function() { 209 | this.setCoords(); 210 | this.radial_distance -= this.speed * this.game.clockTick; 211 | 212 | Entity.prototype.update.call(this); 213 | } 214 | 215 | Alien.prototype.draw = function(ctx) { 216 | this.drawSpriteCentered(ctx); 217 | 218 | Entity.prototype.draw.call(this, ctx); 219 | } 220 | 221 | function Earth(game) { 222 | Entity.call(this, game, 0, 0); 223 | this.sprite = ASSET_MANAGER.getAsset('img/earth.png'); 224 | } 225 | Earth.prototype = new Entity(); 226 | Earth.prototype.constructor = Earth; 227 | 228 | Earth.RADIUS = 67; 229 | 230 | Earth.prototype.draw = function(ctx) { 231 | ctx.drawImage(this.sprite, this.x - this.sprite.width/2, this.y - this.sprite.height/2); 232 | } 233 | 234 | function EvilAliens() { 235 | GameEngine.call(this); 236 | } 237 | EvilAliens.prototype = new GameEngine(); 238 | EvilAliens.prototype.constructor = EvilAliens; 239 | 240 | EvilAliens.prototype.start = function() { 241 | this.earth = new Earth(this); 242 | this.addEntity(this.earth); 243 | GameEngine.prototype.start.call(this); 244 | } 245 | 246 | EvilAliens.prototype.update = function() { 247 | if (this.lastAlienAddedAt == null || (this.timer.gameTime - this.lastAlienAddedAt) > 1) { 248 | this.addEntity(new Alien(this, this.ctx.canvas.width, Math.floor(Math.random() * Math.PI * 2))); 249 | this.lastAlienAddedAt = this.timer.gameTime; 250 | } 251 | 252 | GameEngine.prototype.update.call(this); 253 | } 254 | 255 | EvilAliens.prototype.draw = function() { 256 | GameEngine.prototype.draw.call(this); 257 | } 258 | 259 | var canvas = document.getElementById('surface'); 260 | var ctx = canvas.getContext('2d'); 261 | var game = new EvilAliens(); 262 | var ASSET_MANAGER = new AssetManager(); 263 | 264 | ASSET_MANAGER.queueDownload('img/alien.png'); 265 | ASSET_MANAGER.queueDownload('img/earth.png'); 266 | 267 | ASSET_MANAGER.downloadAll(function() { 268 | game.init(ctx); 269 | game.start(); 270 | }); -------------------------------------------------------------------------------- /static/phase07/scripts/app.js: -------------------------------------------------------------------------------- 1 | window.requestAnimFrame = (function(){ 2 | return window.requestAnimationFrame || 3 | window.webkitRequestAnimationFrame || 4 | window.mozRequestAnimationFrame || 5 | window.oRequestAnimationFrame || 6 | window.msRequestAnimationFrame || 7 | function(/* function */ callback, /* DOMElement */ element){ 8 | window.setTimeout(callback, 1000 / 60); 9 | }; 10 | })(); 11 | 12 | function AssetManager() { 13 | this.successCount = 0; 14 | this.errorCount = 0; 15 | this.cache = {}; 16 | this.downloadQueue = []; 17 | } 18 | 19 | AssetManager.prototype.queueDownload = function(path) { 20 | this.downloadQueue.push(path); 21 | } 22 | 23 | AssetManager.prototype.downloadAll = function(callback) { 24 | if (this.downloadQueue.length === 0 && this.soundsQueue.length === 0) { 25 | callback(); 26 | } 27 | 28 | for (var i = 0; i < this.downloadQueue.length; i++) { 29 | var path = this.downloadQueue[i]; 30 | var img = new Image(); 31 | var that = this; 32 | img.addEventListener("load", function() { 33 | console.log(this.src + ' is loaded'); 34 | that.successCount += 1; 35 | if (that.isDone()) { 36 | callback(); 37 | } 38 | }, false); 39 | img.addEventListener("error", function() { 40 | that.errorCount += 1; 41 | if (that.isDone()) { 42 | callback(); 43 | } 44 | }, false); 45 | img.src = path; 46 | this.cache[path] = img; 47 | } 48 | } 49 | 50 | AssetManager.prototype.getAsset = function(path) { 51 | return this.cache[path]; 52 | } 53 | 54 | AssetManager.prototype.isDone = function() { 55 | return (this.downloadQueue.length == this.successCount + this.errorCount); 56 | } 57 | 58 | function Timer() { 59 | this.gameTime = 0; 60 | this.maxStep = 0.05; 61 | this.wallLastTimestamp = 0; 62 | } 63 | 64 | Timer.prototype.tick = function() { 65 | var wallCurrent = Date.now(); 66 | var wallDelta = (wallCurrent - this.wallLastTimestamp) / 1000; 67 | this.wallLastTimestamp = wallCurrent; 68 | 69 | var gameDelta = Math.min(wallDelta, this.maxStep); 70 | this.gameTime += gameDelta; 71 | return gameDelta; 72 | } 73 | 74 | function GameEngine() { 75 | this.entities = []; 76 | this.ctx = null; 77 | this.click = null; 78 | this.mouse = null; 79 | this.timer = new Timer(); 80 | this.surfaceWidth = null; 81 | this.surfaceHeight = null; 82 | this.halfSurfaceWidth = null; 83 | this.halfSurfaceHeight = null; 84 | } 85 | 86 | GameEngine.prototype.init = function(ctx) { 87 | console.log('game initialized'); 88 | this.ctx = ctx; 89 | this.surfaceWidth = this.ctx.canvas.width; 90 | this.surfaceHeight = this.ctx.canvas.height; 91 | this.halfSurfaceWidth = this.surfaceWidth/2; 92 | this.halfSurfaceHeight = this.surfaceHeight/2; 93 | this.startInput(); 94 | } 95 | 96 | GameEngine.prototype.start = function() { 97 | console.log("starting game"); 98 | var that = this; 99 | (function gameLoop() { 100 | that.loop(); 101 | requestAnimFrame(gameLoop, that.ctx.canvas); 102 | })(); 103 | } 104 | 105 | GameEngine.prototype.startInput = function() { 106 | var getXandY = function(e) { 107 | var x = e.clientX - that.ctx.canvas.getBoundingClientRect().left - (that.ctx.canvas.width/2); 108 | var y = e.clientY - that.ctx.canvas.getBoundingClientRect().top - (that.ctx.canvas.height/2); 109 | return {x: x, y: y}; 110 | } 111 | 112 | var that = this; 113 | 114 | this.ctx.canvas.addEventListener("click", function(e) { 115 | that.click = getXandY(e); 116 | e.stopPropagation(); 117 | e.preventDefault(); 118 | }, false); 119 | 120 | this.ctx.canvas.addEventListener("mousemove", function(e) { 121 | that.mouse = getXandY(e); 122 | }, false); 123 | } 124 | 125 | GameEngine.prototype.addEntity = function(entity) { 126 | this.entities.push(entity); 127 | } 128 | 129 | GameEngine.prototype.draw = function(callback) { 130 | this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); 131 | this.ctx.save(); 132 | this.ctx.translate(this.ctx.canvas.width/2, this.ctx.canvas.height/2); 133 | for (var i = 0; i < this.entities.length; i++) { 134 | this.entities[i].draw(this.ctx); 135 | } 136 | if (callback) { 137 | callback(this); 138 | } 139 | this.ctx.restore(); 140 | } 141 | 142 | GameEngine.prototype.update = function() { 143 | var entitiesCount = this.entities.length; 144 | 145 | for (var i = 0; i < entitiesCount; i++) { 146 | var entity = this.entities[i]; 147 | 148 | if (!entity.removeFromWorld) { 149 | entity.update(); 150 | } 151 | } 152 | 153 | for (var i = this.entities.length-1; i >= 0; --i) { 154 | if (this.entities[i].removeFromWorld) { 155 | this.entities.splice(i, 1); 156 | } 157 | } 158 | } 159 | 160 | GameEngine.prototype.loop = function() { 161 | this.clockTick = this.timer.tick(); 162 | this.update(); 163 | this.draw(); 164 | this.click = null; 165 | } 166 | 167 | function Entity(game, x, y) { 168 | this.game = game; 169 | this.x = x; 170 | this.y = y; 171 | this.removeFromWorld = false; 172 | } 173 | 174 | Entity.prototype.update = function() { 175 | } 176 | 177 | Entity.prototype.draw = function(ctx) { 178 | if (this.game.showOutlines && this.radius) { 179 | ctx.beginPath(); 180 | ctx.strokeStyle = "green"; 181 | ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, false); 182 | ctx.stroke(); 183 | ctx.closePath(); 184 | } 185 | } 186 | 187 | Entity.prototype.drawSpriteCentered = function(ctx) { 188 | if (this.sprite && this.x && this.y) { 189 | var x = this.x - this.sprite.width/2; 190 | var y = this.y - this.sprite.height/2; 191 | ctx.drawImage(this.sprite, x, y); 192 | } 193 | } 194 | 195 | Entity.prototype.outsideScreen = function() { 196 | return (this.x > this.game.halfSurfaceWidth || this.x < -(this.game.halfSurfaceWidth) || 197 | this.y > this.game.halfSurfaceHeight || this.y < -(this.game.halfSurfaceHeight)); 198 | } 199 | 200 | Entity.prototype.rotateAndCache = function(image) { 201 | var offscreenCanvas = document.createElement('canvas'); 202 | var size = Math.max(image.width, image.height); 203 | offscreenCanvas.width = size; 204 | offscreenCanvas.height = size; 205 | var offscreenCtx = offscreenCanvas.getContext('2d'); 206 | offscreenCtx.save(); 207 | offscreenCtx.translate(size/2, size/2); 208 | offscreenCtx.rotate(this.angle + Math.PI/2); 209 | offscreenCtx.drawImage(image, -(image.width/2), -(image.height/2)); 210 | offscreenCtx.restore(); 211 | //offscreenCtx.strokeStyle = "red"; 212 | //offscreenCtx.strokeRect(0,0,size,size); 213 | return offscreenCanvas; 214 | } 215 | 216 | function Alien(game, radial_distance, angle) { 217 | Entity.call(this, game); 218 | this.radial_distance = radial_distance; 219 | this.angle = angle; 220 | this.speed = 100; 221 | this.sprite = this.rotateAndCache(ASSET_MANAGER.getAsset('img/alien.png')); 222 | this.radius = this.sprite.height/2; 223 | this.setCoords(); 224 | } 225 | Alien.prototype = new Entity(); 226 | Alien.prototype.constructor = Alien; 227 | 228 | Alien.prototype.setCoords = function() { 229 | this.x = this.radial_distance * Math.cos(this.angle); 230 | this.y = this.radial_distance * Math.sin(this.angle); 231 | } 232 | 233 | Alien.prototype.update = function() { 234 | this.setCoords(); 235 | this.radial_distance -= this.speed * this.game.clockTick; 236 | 237 | Entity.prototype.update.call(this); 238 | } 239 | 240 | Alien.prototype.draw = function(ctx) { 241 | this.drawSpriteCentered(ctx); 242 | 243 | Entity.prototype.draw.call(this, ctx); 244 | } 245 | 246 | function Sentry(game) { 247 | this.distanceFromEarthCenter = 85; 248 | Entity.call(this, game, 0, this.distanceFromEarthCenter); 249 | this.sprite = ASSET_MANAGER.getAsset('img/sentry.png'); 250 | this.radius = this.sprite.width / 2; 251 | this.angle = 0; 252 | } 253 | Sentry.prototype = new Entity(); 254 | Sentry.prototype.constructor = Sentry; 255 | 256 | Sentry.prototype.update = function() { 257 | if (this.game.mouse) { 258 | this.angle = Math.atan2(this.game.mouse.y, this.game.mouse.x); 259 | if (this.angle < 0) { 260 | this.angle += Math.PI * 2; 261 | } 262 | this.x = (Math.cos(this.angle) * this.distanceFromEarthCenter); 263 | this.y = (Math.sin(this.angle) * this.distanceFromEarthCenter); 264 | } 265 | Entity.prototype.update.call(this); 266 | } 267 | 268 | Sentry.prototype.draw = function(ctx) { 269 | ctx.save(); 270 | ctx.translate(this.x, this.y); 271 | ctx.rotate(this.angle + Math.PI/2); 272 | ctx.drawImage(this.sprite, -this.sprite.width/2, -this.sprite.height/2); 273 | ctx.restore(); 274 | 275 | Entity.prototype.draw.call(this, ctx); 276 | } 277 | 278 | function Earth(game) { 279 | Entity.call(this, game, 0, 0); 280 | this.sprite = ASSET_MANAGER.getAsset('img/earth.png'); 281 | } 282 | Earth.prototype = new Entity(); 283 | Earth.prototype.constructor = Earth; 284 | 285 | Earth.RADIUS = 67; 286 | 287 | Earth.prototype.draw = function(ctx) { 288 | ctx.drawImage(this.sprite, this.x - this.sprite.width/2, this.y - this.sprite.height/2); 289 | } 290 | 291 | function EvilAliens() { 292 | GameEngine.call(this); 293 | //this.showOutlines = true; 294 | } 295 | EvilAliens.prototype = new GameEngine(); 296 | EvilAliens.prototype.constructor = EvilAliens; 297 | 298 | EvilAliens.prototype.start = function() { 299 | this.sentry = new Sentry(this); 300 | this.earth = new Earth(this); 301 | this.addEntity(this.earth); 302 | this.addEntity(this.sentry); 303 | GameEngine.prototype.start.call(this); 304 | } 305 | 306 | EvilAliens.prototype.update = function() { 307 | if (this.lastAlienAddedAt == null || (this.timer.gameTime - this.lastAlienAddedAt) > 1) { 308 | this.addEntity(new Alien(this, this.ctx.canvas.width, Math.floor(Math.random() * Math.PI * 2))); 309 | this.lastAlienAddedAt = this.timer.gameTime; 310 | } 311 | 312 | GameEngine.prototype.update.call(this); 313 | } 314 | 315 | EvilAliens.prototype.draw = function() { 316 | GameEngine.prototype.draw.call(this); 317 | } 318 | 319 | var canvas = document.getElementById('surface'); 320 | var ctx = canvas.getContext('2d'); 321 | var game = new EvilAliens(); 322 | var ASSET_MANAGER = new AssetManager(); 323 | 324 | ASSET_MANAGER.queueDownload('img/alien.png'); 325 | ASSET_MANAGER.queueDownload('img/earth.png'); 326 | ASSET_MANAGER.queueDownload('img/sentry.png'); 327 | 328 | ASSET_MANAGER.downloadAll(function() { 329 | game.init(ctx); 330 | game.start(); 331 | }); -------------------------------------------------------------------------------- /static/phase08/scripts/app.js: -------------------------------------------------------------------------------- 1 | window.requestAnimFrame = (function(){ 2 | return window.requestAnimationFrame || 3 | window.webkitRequestAnimationFrame || 4 | window.mozRequestAnimationFrame || 5 | window.oRequestAnimationFrame || 6 | window.msRequestAnimationFrame || 7 | function(/* function */ callback, /* DOMElement */ element){ 8 | window.setTimeout(callback, 1000 / 60); 9 | }; 10 | })(); 11 | 12 | function AssetManager() { 13 | this.successCount = 0; 14 | this.errorCount = 0; 15 | this.cache = {}; 16 | this.downloadQueue = []; 17 | } 18 | 19 | AssetManager.prototype.queueDownload = function(path) { 20 | this.downloadQueue.push(path); 21 | } 22 | AssetManager.prototype.downloadAll = function(callback) { 23 | if (this.downloadQueue.length === 0 && this.soundsQueue.length === 0) { 24 | callback(); 25 | } 26 | 27 | for (var i = 0; i < this.downloadQueue.length; i++) { 28 | var path = this.downloadQueue[i]; 29 | var img = new Image(); 30 | var that = this; 31 | img.addEventListener("load", function() { 32 | console.log(this.src + ' is loaded'); 33 | that.successCount += 1; 34 | if (that.isDone()) { 35 | callback(); 36 | } 37 | }, false); 38 | img.addEventListener("error", function() { 39 | that.errorCount += 1; 40 | if (that.isDone()) { 41 | callback(); 42 | } 43 | }, false); 44 | img.src = path; 45 | this.cache[path] = img; 46 | } 47 | } 48 | 49 | AssetManager.prototype.getAsset = function(path) { 50 | return this.cache[path]; 51 | } 52 | 53 | AssetManager.prototype.isDone = function() { 54 | return (this.downloadQueue.length == this.successCount + this.errorCount); 55 | } 56 | 57 | function Timer() { 58 | this.gameTime = 0; 59 | this.maxStep = 0.05; 60 | this.wallLastTimestamp = 0; 61 | } 62 | 63 | Timer.prototype.tick = function() { 64 | var wallCurrent = Date.now(); 65 | var wallDelta = (wallCurrent - this.wallLastTimestamp) / 1000; 66 | this.wallLastTimestamp = wallCurrent; 67 | 68 | var gameDelta = Math.min(wallDelta, this.maxStep); 69 | this.gameTime += gameDelta; 70 | return gameDelta; 71 | } 72 | 73 | function GameEngine() { 74 | this.entities = []; 75 | this.ctx = null; 76 | this.click = null; 77 | this.mouse = null; 78 | this.timer = new Timer(); 79 | this.surfaceWidth = null; 80 | this.surfaceHeight = null; 81 | this.halfSurfaceWidth = null; 82 | this.halfSurfaceHeight = null; 83 | } 84 | 85 | GameEngine.prototype.init = function(ctx) { 86 | console.log('game initialized'); 87 | this.ctx = ctx; 88 | this.surfaceWidth = this.ctx.canvas.width; 89 | this.surfaceHeight = this.ctx.canvas.height; 90 | this.halfSurfaceWidth = this.surfaceWidth/2; 91 | this.halfSurfaceHeight = this.surfaceHeight/2; 92 | this.startInput(); 93 | } 94 | 95 | GameEngine.prototype.start = function() { 96 | console.log("starting game"); 97 | var that = this; 98 | (function gameLoop() { 99 | that.loop(); 100 | requestAnimFrame(gameLoop, that.ctx.canvas); 101 | })(); 102 | } 103 | 104 | GameEngine.prototype.startInput = function() { 105 | var getXandY = function(e) { 106 | var x = e.clientX - that.ctx.canvas.getBoundingClientRect().left - (that.ctx.canvas.width/2); 107 | var y = e.clientY - that.ctx.canvas.getBoundingClientRect().top - (that.ctx.canvas.height/2); 108 | return {x: x, y: y}; 109 | } 110 | 111 | var that = this; 112 | 113 | this.ctx.canvas.addEventListener("click", function(e) { 114 | that.click = getXandY(e); 115 | e.stopPropagation(); 116 | e.preventDefault(); 117 | }, false); 118 | 119 | this.ctx.canvas.addEventListener("mousemove", function(e) { 120 | that.mouse = getXandY(e); 121 | }, false); 122 | } 123 | 124 | GameEngine.prototype.addEntity = function(entity) { 125 | this.entities.push(entity); 126 | } 127 | 128 | GameEngine.prototype.draw = function(callback) { 129 | this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); 130 | this.ctx.save(); 131 | this.ctx.translate(this.ctx.canvas.width/2, this.ctx.canvas.height/2); 132 | for (var i = 0; i < this.entities.length; i++) { 133 | this.entities[i].draw(this.ctx); 134 | } 135 | if (callback) { 136 | callback(this); 137 | } 138 | this.ctx.restore(); 139 | } 140 | 141 | GameEngine.prototype.update = function() { 142 | var entitiesCount = this.entities.length; 143 | 144 | for (var i = 0; i < entitiesCount; i++) { 145 | var entity = this.entities[i]; 146 | 147 | if (!entity.removeFromWorld) { 148 | entity.update(); 149 | } 150 | } 151 | 152 | for (var i = this.entities.length-1; i >= 0; --i) { 153 | if (this.entities[i].removeFromWorld) { 154 | this.entities.splice(i, 1); 155 | } 156 | } 157 | } 158 | 159 | GameEngine.prototype.loop = function() { 160 | this.clockTick = this.timer.tick(); 161 | this.update(); 162 | this.draw(); 163 | this.click = null; 164 | } 165 | 166 | function Entity(game, x, y) { 167 | this.game = game; 168 | this.x = x; 169 | this.y = y; 170 | this.removeFromWorld = false; 171 | } 172 | 173 | Entity.prototype.update = function() { 174 | } 175 | 176 | Entity.prototype.draw = function(ctx) { 177 | if (this.game.showOutlines && this.radius) { 178 | ctx.beginPath(); 179 | ctx.strokeStyle = "green"; 180 | ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, false); 181 | ctx.stroke(); 182 | ctx.closePath(); 183 | } 184 | } 185 | 186 | Entity.prototype.drawSpriteCentered = function(ctx) { 187 | if (this.sprite && this.x && this.y) { 188 | var x = this.x - this.sprite.width/2; 189 | var y = this.y - this.sprite.height/2; 190 | ctx.drawImage(this.sprite, x, y); 191 | } 192 | } 193 | 194 | Entity.prototype.outsideScreen = function() { 195 | return (this.x > this.game.halfSurfaceWidth || this.x < -(this.game.halfSurfaceWidth) || 196 | this.y > this.game.halfSurfaceHeight || this.y < -(this.game.halfSurfaceHeight)); 197 | } 198 | 199 | Entity.prototype.rotateAndCache = function(image, angle) { 200 | var offscreenCanvas = document.createElement('canvas'); 201 | var size = Math.max(image.width, image.height); 202 | offscreenCanvas.width = size; 203 | offscreenCanvas.height = size; 204 | var offscreenCtx = offscreenCanvas.getContext('2d'); 205 | offscreenCtx.save(); 206 | offscreenCtx.translate(size/2, size/2); 207 | offscreenCtx.rotate(angle + Math.PI/2); 208 | offscreenCtx.drawImage(image, -(image.width/2), -(image.height/2)); 209 | offscreenCtx.restore(); 210 | //offscreenCtx.strokeStyle = "red"; 211 | //offscreenCtx.strokeRect(0,0,size,size); 212 | return offscreenCanvas; 213 | } 214 | 215 | function Alien(game, radial_distance, angle) { 216 | Entity.call(this, game); 217 | this.radial_distance = radial_distance; 218 | this.angle = angle; 219 | this.speed = 100; 220 | this.sprite = this.rotateAndCache(ASSET_MANAGER.getAsset('img/alien.png'), this.angle); 221 | this.radius = this.sprite.height/2; 222 | this.setCoords(); 223 | } 224 | Alien.prototype = new Entity(); 225 | Alien.prototype.constructor = Alien; 226 | 227 | Alien.prototype.setCoords = function() { 228 | this.x = this.radial_distance * Math.cos(this.angle); 229 | this.y = this.radial_distance * Math.sin(this.angle); 230 | } 231 | 232 | Alien.prototype.update = function() { 233 | this.setCoords(); 234 | this.radial_distance -= this.speed * this.game.clockTick; 235 | 236 | Entity.prototype.update.call(this); 237 | } 238 | 239 | Alien.prototype.draw = function(ctx) { 240 | this.drawSpriteCentered(ctx); 241 | 242 | Entity.prototype.draw.call(this, ctx); 243 | } 244 | 245 | function Sentry(game) { 246 | this.distanceFromEarthCenter = 85; 247 | Entity.call(this, game, 0, this.distanceFromEarthCenter); 248 | this.sprite = ASSET_MANAGER.getAsset('img/sentry.png'); 249 | this.radius = this.sprite.width / 2; 250 | this.angle = 0; 251 | } 252 | Sentry.prototype = new Entity(); 253 | Sentry.prototype.constructor = Sentry; 254 | 255 | Sentry.prototype.update = function() { 256 | if (this.game.mouse) { 257 | this.angle = Math.atan2(this.game.mouse.y, this.game.mouse.x); 258 | if (this.angle < 0) { 259 | this.angle += Math.PI * 2; 260 | } 261 | this.x = (Math.cos(this.angle) * this.distanceFromEarthCenter); 262 | this.y = (Math.sin(this.angle) * this.distanceFromEarthCenter); 263 | } 264 | if (this.game.click) { 265 | this.shoot(); 266 | } 267 | Entity.prototype.update.call(this); 268 | } 269 | 270 | Sentry.prototype.draw = function(ctx) { 271 | ctx.save(); 272 | ctx.translate(this.x, this.y); 273 | ctx.rotate(this.angle + Math.PI/2); 274 | ctx.drawImage(this.sprite, -this.sprite.width/2, -this.sprite.height/2); 275 | ctx.restore(); 276 | 277 | Entity.prototype.draw.call(this, ctx); 278 | } 279 | 280 | Sentry.prototype.shoot = function() { 281 | var bullet = new Bullet(this.game, this.x, this.y, this.angle, this.game.click); 282 | this.game.addEntity(bullet); 283 | } 284 | 285 | function Bullet(game, x, y, angle, explodesAt) { 286 | Entity.call(this, game, x, y); 287 | this.angle = angle; 288 | this.explodesAt = explodesAt; 289 | this.speed = 250; 290 | this.radial_distance = 95; 291 | this.sprite = this.rotateAndCache(ASSET_MANAGER.getAsset('img/bullet-single.png'), this.angle); 292 | } 293 | Bullet.prototype = new Entity(); 294 | Bullet.prototype.constructor = Bullet; 295 | 296 | Bullet.prototype.update = function() { 297 | if (this.outsideScreen()) { 298 | this.removeFromWorld = true; 299 | } else { 300 | this.x = this.radial_distance * Math.cos(this.angle); 301 | this.y = this.radial_distance * Math.sin(this.angle); 302 | this.radial_distance += this.speed * this.game.clockTick; 303 | } 304 | } 305 | 306 | Bullet.prototype.draw = function(ctx) { 307 | this.drawSpriteCentered(ctx); 308 | 309 | Entity.prototype.draw.call(this, ctx); 310 | } 311 | 312 | function Earth(game) { 313 | Entity.call(this, game, 0, 0); 314 | this.sprite = ASSET_MANAGER.getAsset('img/earth.png'); 315 | } 316 | Earth.prototype = new Entity(); 317 | Earth.prototype.constructor = Earth; 318 | 319 | Earth.RADIUS = 67; 320 | 321 | Earth.prototype.draw = function(ctx) { 322 | ctx.drawImage(this.sprite, this.x - this.sprite.width/2, this.y - this.sprite.height/2); 323 | } 324 | 325 | function EvilAliens() { 326 | GameEngine.call(this); 327 | //this.showOutlines = true; 328 | } 329 | EvilAliens.prototype = new GameEngine(); 330 | EvilAliens.prototype.constructor = EvilAliens; 331 | 332 | EvilAliens.prototype.start = function() { 333 | this.sentry = new Sentry(this); 334 | this.earth = new Earth(this); 335 | this.addEntity(this.earth); 336 | this.addEntity(this.sentry); 337 | GameEngine.prototype.start.call(this); 338 | } 339 | 340 | EvilAliens.prototype.update = function() { 341 | if (this.lastAlienAddedAt == null || (this.timer.gameTime - this.lastAlienAddedAt) > 1) { 342 | this.addEntity(new Alien(this, this.ctx.canvas.width, Math.floor(Math.random() * Math.PI * 2))); 343 | this.lastAlienAddedAt = this.timer.gameTime; 344 | } 345 | 346 | GameEngine.prototype.update.call(this); 347 | } 348 | 349 | EvilAliens.prototype.draw = function() { 350 | GameEngine.prototype.draw.call(this); 351 | } 352 | 353 | var canvas = document.getElementById('surface'); 354 | var ctx = canvas.getContext('2d'); 355 | var game = new EvilAliens(); 356 | var ASSET_MANAGER = new AssetManager(); 357 | 358 | ASSET_MANAGER.queueDownload('img/alien.png'); 359 | ASSET_MANAGER.queueDownload('img/bullet-single.png'); 360 | ASSET_MANAGER.queueDownload('img/earth.png'); 361 | ASSET_MANAGER.queueDownload('img/sentry.png'); 362 | 363 | ASSET_MANAGER.downloadAll(function() { 364 | game.init(ctx); 365 | game.start(); 366 | }); -------------------------------------------------------------------------------- /static/phase09/scripts/app.js: -------------------------------------------------------------------------------- 1 | window.requestAnimFrame = (function(){ 2 | return window.requestAnimationFrame || 3 | window.webkitRequestAnimationFrame || 4 | window.mozRequestAnimationFrame || 5 | window.oRequestAnimationFrame || 6 | window.msRequestAnimationFrame || 7 | function(/* function */ callback, /* DOMElement */ element){ 8 | window.setTimeout(callback, 1000 / 60); 9 | }; 10 | })(); 11 | 12 | function AssetManager() { 13 | this.successCount = 0; 14 | this.errorCount = 0; 15 | this.cache = {}; 16 | this.downloadQueue = []; 17 | } 18 | 19 | AssetManager.prototype.queueDownload = function(path) { 20 | this.downloadQueue.push(path); 21 | } 22 | 23 | AssetManager.prototype.downloadAll = function(callback) { 24 | if (this.downloadQueue.length === 0 && this.soundsQueue.length === 0) { 25 | callback(); 26 | } 27 | 28 | for (var i = 0; i < this.downloadQueue.length; i++) { 29 | var path = this.downloadQueue[i]; 30 | var img = new Image(); 31 | var that = this; 32 | img.addEventListener("load", function() { 33 | console.log(this.src + ' is loaded'); 34 | that.successCount += 1; 35 | if (that.isDone()) { 36 | callback(); 37 | } 38 | }, false); 39 | img.addEventListener("error", function() { 40 | that.errorCount += 1; 41 | if (that.isDone()) { 42 | callback(); 43 | } 44 | }, false); 45 | img.src = path; 46 | this.cache[path] = img; 47 | } 48 | } 49 | 50 | AssetManager.prototype.getAsset = function(path) { 51 | return this.cache[path]; 52 | } 53 | 54 | AssetManager.prototype.isDone = function() { 55 | return (this.downloadQueue.length == this.successCount + this.errorCount); 56 | } 57 | 58 | function Animation(spriteSheet, frameWidth, frameDuration, loop) { 59 | this.spriteSheet = spriteSheet; 60 | this.frameWidth = frameWidth; 61 | this.frameDuration = frameDuration; 62 | this.frameHeight= this.spriteSheet.height; 63 | this.totalTime = (this.spriteSheet.width / this.frameWidth) * this.frameDuration; 64 | this.elapsedTime = 0; 65 | this.loop = loop; 66 | } 67 | 68 | Animation.prototype.drawFrame = function(tick, ctx, x, y, scaleBy) { 69 | var scaleBy = scaleBy || 1; 70 | this.elapsedTime += tick; 71 | if (this.loop) { 72 | if (this.isDone()) { 73 | this.elapsedTime = 0; 74 | } 75 | } else if (this.isDone()) { 76 | return; 77 | } 78 | var index = this.currentFrame(); 79 | var locX = x - (this.frameWidth/2) * scaleBy; 80 | var locY = y - (this.frameHeight/2) * scaleBy; 81 | ctx.drawImage(this.spriteSheet, 82 | index*this.frameWidth, 0, // source from sheet 83 | this.frameWidth, this.frameHeight, 84 | locX, locY, 85 | this.frameWidth*scaleBy, 86 | this.frameHeight*scaleBy); 87 | } 88 | 89 | Animation.prototype.currentFrame = function() { 90 | return Math.floor(this.elapsedTime / this.frameDuration); 91 | } 92 | 93 | Animation.prototype.isDone = function() { 94 | return (this.elapsedTime >= this.totalTime); 95 | } 96 | 97 | function Timer() { 98 | this.gameTime = 0; 99 | this.maxStep = 0.05; 100 | this.wallLastTimestamp = 0; 101 | } 102 | 103 | Timer.prototype.tick = function() { 104 | var wallCurrent = Date.now(); 105 | var wallDelta = (wallCurrent - this.wallLastTimestamp) / 1000; 106 | this.wallLastTimestamp = wallCurrent; 107 | 108 | var gameDelta = Math.min(wallDelta, this.maxStep); 109 | this.gameTime += gameDelta; 110 | return gameDelta; 111 | } 112 | 113 | function GameEngine() { 114 | this.entities = []; 115 | this.ctx = null; 116 | this.click = null; 117 | this.mouse = null; 118 | this.timer = new Timer(); 119 | this.surfaceWidth = null; 120 | this.surfaceHeight = null; 121 | this.halfSurfaceWidth = null; 122 | this.halfSurfaceHeight = null; 123 | } 124 | 125 | GameEngine.prototype.init = function(ctx) { 126 | console.log('game initialized'); 127 | this.ctx = ctx; 128 | this.surfaceWidth = this.ctx.canvas.width; 129 | this.surfaceHeight = this.ctx.canvas.height; 130 | this.halfSurfaceWidth = this.surfaceWidth/2; 131 | this.halfSurfaceHeight = this.surfaceHeight/2; 132 | this.startInput(); 133 | } 134 | 135 | GameEngine.prototype.start = function() { 136 | console.log("starting game"); 137 | var that = this; 138 | (function gameLoop() { 139 | that.loop(); 140 | requestAnimFrame(gameLoop, that.ctx.canvas); 141 | })(); 142 | } 143 | 144 | GameEngine.prototype.startInput = function() { 145 | var getXandY = function(e) { 146 | var x = e.clientX - that.ctx.canvas.getBoundingClientRect().left - (that.ctx.canvas.width/2); 147 | var y = e.clientY - that.ctx.canvas.getBoundingClientRect().top - (that.ctx.canvas.height/2); 148 | return {x: x, y: y}; 149 | } 150 | 151 | var that = this; 152 | 153 | this.ctx.canvas.addEventListener("click", function(e) { 154 | that.click = getXandY(e); 155 | e.stopPropagation(); 156 | e.preventDefault(); 157 | }, false); 158 | 159 | this.ctx.canvas.addEventListener("mousemove", function(e) { 160 | that.mouse = getXandY(e); 161 | }, false); 162 | } 163 | 164 | GameEngine.prototype.addEntity = function(entity) { 165 | this.entities.push(entity); 166 | } 167 | 168 | GameEngine.prototype.draw = function(callback) { 169 | this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); 170 | this.ctx.save(); 171 | this.ctx.translate(this.ctx.canvas.width/2, this.ctx.canvas.height/2); 172 | for (var i = 0; i < this.entities.length; i++) { 173 | this.entities[i].draw(this.ctx); 174 | } 175 | if (callback) { 176 | callback(this); 177 | } 178 | this.ctx.restore(); 179 | } 180 | 181 | GameEngine.prototype.update = function() { 182 | var entitiesCount = this.entities.length; 183 | 184 | for (var i = 0; i < entitiesCount; i++) { 185 | var entity = this.entities[i]; 186 | 187 | if (!entity.removeFromWorld) { 188 | entity.update(); 189 | } 190 | } 191 | 192 | for (var i = this.entities.length-1; i >= 0; --i) { 193 | if (this.entities[i].removeFromWorld) { 194 | this.entities.splice(i, 1); 195 | } 196 | } 197 | } 198 | 199 | GameEngine.prototype.loop = function() { 200 | this.clockTick = this.timer.tick(); 201 | this.update(); 202 | this.draw(); 203 | this.click = null; 204 | } 205 | 206 | function Entity(game, x, y) { 207 | this.game = game; 208 | this.x = x; 209 | this.y = y; 210 | this.removeFromWorld = false; 211 | } 212 | 213 | Entity.prototype.update = function() { 214 | } 215 | 216 | Entity.prototype.draw = function(ctx) { 217 | if (this.game.showOutlines && this.radius) { 218 | ctx.beginPath(); 219 | ctx.strokeStyle = "green"; 220 | ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, false); 221 | ctx.stroke(); 222 | ctx.closePath(); 223 | } 224 | } 225 | 226 | Entity.prototype.drawSpriteCentered = function(ctx) { 227 | if (this.sprite && this.x && this.y) { 228 | var x = this.x - this.sprite.width/2; 229 | var y = this.y - this.sprite.height/2; 230 | ctx.drawImage(this.sprite, x, y); 231 | } 232 | } 233 | 234 | Entity.prototype.outsideScreen = function() { 235 | return (this.x > this.game.halfSurfaceWidth || this.x < -(this.game.halfSurfaceWidth) || 236 | this.y > this.game.halfSurfaceHeight || this.y < -(this.game.halfSurfaceHeight)); 237 | } 238 | 239 | Entity.prototype.rotateAndCache = function(image, angle) { 240 | var offscreenCanvas = document.createElement('canvas'); 241 | var size = Math.max(image.width, image.height); 242 | offscreenCanvas.width = size; 243 | offscreenCanvas.height = size; 244 | var offscreenCtx = offscreenCanvas.getContext('2d'); 245 | offscreenCtx.save(); 246 | offscreenCtx.translate(size/2, size/2); 247 | offscreenCtx.rotate(angle + Math.PI/2); 248 | offscreenCtx.drawImage(image, -(image.width/2), -(image.height/2)); 249 | offscreenCtx.restore(); 250 | //offscreenCtx.strokeStyle = "red"; 251 | //offscreenCtx.strokeRect(0,0,size,size); 252 | return offscreenCanvas; 253 | } 254 | 255 | function Alien(game, radial_distance, angle) { 256 | Entity.call(this, game); 257 | this.radial_distance = radial_distance; 258 | this.angle = angle; 259 | this.speed = 100; 260 | this.sprite = this.rotateAndCache(ASSET_MANAGER.getAsset('img/alien.png'), this.angle); 261 | this.radius = this.sprite.height/2; 262 | this.setCoords(); 263 | } 264 | Alien.prototype = new Entity(); 265 | Alien.prototype.constructor = Alien; 266 | 267 | Alien.prototype.setCoords = function() { 268 | this.x = this.radial_distance * Math.cos(this.angle); 269 | this.y = this.radial_distance * Math.sin(this.angle); 270 | } 271 | 272 | Alien.prototype.update = function() { 273 | this.setCoords(); 274 | this.radial_distance -= this.speed * this.game.clockTick; 275 | 276 | Entity.prototype.update.call(this); 277 | } 278 | 279 | Alien.prototype.draw = function(ctx) { 280 | this.drawSpriteCentered(ctx); 281 | 282 | Entity.prototype.draw.call(this, ctx); 283 | } 284 | 285 | function Sentry(game) { 286 | this.distanceFromEarthCenter = 85; 287 | Entity.call(this, game, 0, this.distanceFromEarthCenter); 288 | this.sprite = ASSET_MANAGER.getAsset('img/sentry.png'); 289 | this.radius = this.sprite.width / 2; 290 | this.angle = 0; 291 | } 292 | Sentry.prototype = new Entity(); 293 | Sentry.prototype.constructor = Sentry; 294 | 295 | Sentry.prototype.update = function() { 296 | if (this.game.mouse) { 297 | this.angle = Math.atan2(this.game.mouse.y, this.game.mouse.x); 298 | if (this.angle < 0) { 299 | this.angle += Math.PI * 2; 300 | } 301 | this.x = (Math.cos(this.angle) * this.distanceFromEarthCenter); 302 | this.y = (Math.sin(this.angle) * this.distanceFromEarthCenter); 303 | } 304 | if (this.game.click) { 305 | this.shoot(); 306 | } 307 | Entity.prototype.update.call(this); 308 | } 309 | 310 | Sentry.prototype.draw = function(ctx) { 311 | ctx.save(); 312 | ctx.translate(this.x, this.y); 313 | ctx.rotate(this.angle + Math.PI/2); 314 | ctx.drawImage(this.sprite, -this.sprite.width/2, -this.sprite.height/2); 315 | ctx.restore(); 316 | 317 | Entity.prototype.draw.call(this, ctx); 318 | } 319 | 320 | Sentry.prototype.shoot = function() { 321 | var bullet = new Bullet(this.game, this.x, this.y, this.angle, this.game.click); 322 | this.game.addEntity(bullet); 323 | } 324 | 325 | function Bullet(game, x, y, angle, explodesAt) { 326 | Entity.call(this, game, x, y); 327 | this.angle = angle; 328 | this.explodesAt = explodesAt; 329 | this.speed = 250; 330 | this.radial_distance = 95; 331 | this.sprite = ASSET_MANAGER.getAsset('img/bullet.png'); 332 | this.animation = new Animation(this.sprite, 7, 0.05, true); 333 | } 334 | Bullet.prototype = new Entity(); 335 | Bullet.prototype.constructor = Bullet; 336 | 337 | Bullet.prototype.update = function() { 338 | if (this.outsideScreen()) { 339 | this.removeFromWorld = true; 340 | } else if (Math.abs(this.x) >= Math.abs(this.explodesAt.x) || Math.abs(this.y) >= Math.abs(this.explodesAt.y)) { 341 | this.game.addEntity(new BulletExplosion(this.game, this.explodesAt.x, this.explodesAt.y)); 342 | this.removeFromWorld = true; 343 | } else { 344 | this.x = this.radial_distance * Math.cos(this.angle); 345 | this.y = this.radial_distance * Math.sin(this.angle); 346 | this.radial_distance += this.speed * this.game.clockTick; 347 | } 348 | } 349 | 350 | Bullet.prototype.draw = function(ctx) { 351 | ctx.save(); 352 | ctx.translate(this.x, this.y); 353 | ctx.rotate(this.angle + Math.PI/2); 354 | ctx.translate(-this.x, -this.y); 355 | this.animation.drawFrame(this.game.clockTick, ctx, this.x, this.y); 356 | ctx.restore(); 357 | 358 | Entity.prototype.draw.call(this, ctx); 359 | } 360 | 361 | function BulletExplosion(game, x, y) { 362 | Entity.call(this, game, x, y); 363 | this.sprite = ASSET_MANAGER.getAsset('img/explosion.png'); 364 | this.animation = new Animation(this.sprite, 34, 0.05); 365 | this.radius = this.animation.frameWidth / 2; 366 | } 367 | BulletExplosion.prototype = new Entity(); 368 | BulletExplosion.prototype.constructor = BulletExplosion; 369 | 370 | BulletExplosion.prototype.update = function() { 371 | Entity.prototype.update.call(this); 372 | 373 | if (this.animation.isDone()) { 374 | this.removeFromWorld = true; 375 | return; 376 | } 377 | 378 | this.radius = (this.animation.frameWidth/2) * this.scaleFactor(); 379 | } 380 | 381 | BulletExplosion.prototype.scaleFactor = function() { 382 | return 1 + (this.animation.currentFrame() / 3); 383 | } 384 | 385 | BulletExplosion.prototype.draw = function(ctx) { 386 | this.animation.drawFrame(this.game.clockTick, ctx, this.x, this.y, this.scaleFactor()); 387 | 388 | Entity.prototype.draw.call(this, ctx); 389 | } 390 | 391 | function Earth(game) { 392 | Entity.call(this, game, 0, 0); 393 | this.sprite = ASSET_MANAGER.getAsset('img/earth.png'); 394 | } 395 | Earth.prototype = new Entity(); 396 | Earth.prototype.constructor = Earth; 397 | 398 | Earth.RADIUS = 67; 399 | 400 | Earth.prototype.draw = function(ctx) { 401 | ctx.drawImage(this.sprite, this.x - this.sprite.width/2, this.y - this.sprite.height/2); 402 | } 403 | 404 | function EvilAliens() { 405 | GameEngine.call(this); 406 | //this.showOutlines = true; 407 | } 408 | EvilAliens.prototype = new GameEngine(); 409 | EvilAliens.prototype.constructor = EvilAliens; 410 | 411 | EvilAliens.prototype.start = function() { 412 | this.sentry = new Sentry(this); 413 | this.earth = new Earth(this); 414 | this.addEntity(this.earth); 415 | this.addEntity(this.sentry); 416 | GameEngine.prototype.start.call(this); 417 | } 418 | 419 | EvilAliens.prototype.update = function() { 420 | if (this.lastAlienAddedAt == null || (this.timer.gameTime - this.lastAlienAddedAt) > 1) { 421 | this.addEntity(new Alien(this, this.ctx.canvas.width, Math.floor(Math.random() * Math.PI * 2))); 422 | this.lastAlienAddedAt = this.timer.gameTime; 423 | } 424 | 425 | GameEngine.prototype.update.call(this); 426 | } 427 | 428 | EvilAliens.prototype.draw = function() { 429 | GameEngine.prototype.draw.call(this); 430 | } 431 | 432 | 433 | var canvas = document.getElementById('surface'); 434 | var ctx = canvas.getContext('2d'); 435 | var game = new EvilAliens(); 436 | var ASSET_MANAGER = new AssetManager(); 437 | 438 | ASSET_MANAGER.queueDownload('img/alien.png'); 439 | ASSET_MANAGER.queueDownload('img/bullet.png'); 440 | ASSET_MANAGER.queueDownload('img/earth.png'); 441 | ASSET_MANAGER.queueDownload('img/sentry.png'); 442 | ASSET_MANAGER.queueDownload('img/explosion.png'); 443 | 444 | ASSET_MANAGER.downloadAll(function() { 445 | game.init(ctx); 446 | game.start(); 447 | }); -------------------------------------------------------------------------------- /static/phase10/scripts/app.js: -------------------------------------------------------------------------------- 1 | soundManager.url = 'swf/'; 2 | soundManager.flashVersion = 9; 3 | soundManager.debugFlash = false; 4 | soundManager.debugMode = false; 5 | 6 | window.requestAnimFrame = (function(){ 7 | return window.requestAnimationFrame || 8 | window.webkitRequestAnimationFrame || 9 | window.mozRequestAnimationFrame || 10 | window.oRequestAnimationFrame || 11 | window.msRequestAnimationFrame || 12 | function(/* function */ callback, /* DOMElement */ element){ 13 | window.setTimeout(callback, 1000 / 60); 14 | }; 15 | })(); 16 | 17 | function AssetManager() { 18 | this.successCount = 0; 19 | this.errorCount = 0; 20 | this.cache = {}; 21 | this.downloadQueue = []; 22 | this.soundsQueue = []; 23 | } 24 | 25 | AssetManager.prototype.queueDownload = function(path) { 26 | this.downloadQueue.push(path); 27 | } 28 | 29 | AssetManager.prototype.queueSound = function(id, path) { 30 | this.soundsQueue.push({id: id, path: path}); 31 | } 32 | 33 | AssetManager.prototype.downloadAll = function(callback) { 34 | if (this.downloadQueue.length === 0 && this.soundsQueue.length === 0) { 35 | callback(); 36 | } 37 | 38 | this.downloadSounds(callback); 39 | 40 | for (var i = 0; i < this.downloadQueue.length; i++) { 41 | var path = this.downloadQueue[i]; 42 | var img = new Image(); 43 | var that = this; 44 | img.addEventListener("load", function() { 45 | console.log(this.src + ' is loaded'); 46 | that.successCount += 1; 47 | if (that.isDone()) { 48 | callback(); 49 | } 50 | }, false); 51 | img.addEventListener("error", function() { 52 | that.errorCount += 1; 53 | if (that.isDone()) { 54 | callback(); 55 | } 56 | }, false); 57 | img.src = path; 58 | this.cache[path] = img; 59 | } 60 | } 61 | 62 | AssetManager.prototype.downloadSounds = function(callback) { 63 | var that = this; 64 | soundManager.onready(function() { 65 | console.log('soundManager ready'); 66 | for (var i = 0; i < that.soundsQueue.length; i++) { 67 | that.downloadSound(that.soundsQueue[i].id, that.soundsQueue[i].path, callback); 68 | } 69 | }); 70 | soundManager.ontimeout(function() { 71 | console.log('SM2 did not start'); 72 | }); 73 | } 74 | 75 | AssetManager.prototype.downloadSound = function(id, path, callback) { 76 | var that = this; 77 | this.cache[path] = soundManager.createSound({ 78 | id: id, 79 | autoLoad: true, 80 | url: path, 81 | onload: function() { 82 | console.log(this.url + ' is loaded'); 83 | that.successCount += 1; 84 | if (that.isDone()) { 85 | callback(); 86 | } 87 | } 88 | }); 89 | } 90 | 91 | AssetManager.prototype.getSound = function(path) { 92 | return this.cache[path]; 93 | } 94 | 95 | AssetManager.prototype.getAsset = function(path) { 96 | return this.cache[path]; 97 | } 98 | 99 | AssetManager.prototype.isDone = function() { 100 | return ((this.downloadQueue.length + this.soundsQueue.length) == this.successCount + this.errorCount); 101 | } 102 | 103 | function Animation(spriteSheet, frameWidth, frameDuration, loop) { 104 | this.spriteSheet = spriteSheet; 105 | this.frameWidth = frameWidth; 106 | this.frameDuration = frameDuration; 107 | this.frameHeight= this.spriteSheet.height; 108 | this.totalTime = (this.spriteSheet.width / this.frameWidth) * this.frameDuration; 109 | this.elapsedTime = 0; 110 | this.loop = loop; 111 | } 112 | 113 | Animation.prototype.drawFrame = function(tick, ctx, x, y, scaleBy) { 114 | var scaleBy = scaleBy || 1; 115 | this.elapsedTime += tick; 116 | if (this.loop) { 117 | if (this.isDone()) { 118 | this.elapsedTime = 0; 119 | } 120 | } else if (this.isDone()) { 121 | return; 122 | } 123 | var index = this.currentFrame(); 124 | var locX = x - (this.frameWidth/2) * scaleBy; 125 | var locY = y - (this.frameHeight/2) * scaleBy; 126 | ctx.drawImage(this.spriteSheet, 127 | index*this.frameWidth, 0, // source from sheet 128 | this.frameWidth, this.frameHeight, 129 | locX, locY, 130 | this.frameWidth*scaleBy, 131 | this.frameHeight*scaleBy); 132 | } 133 | 134 | Animation.prototype.currentFrame = function() { 135 | return Math.floor(this.elapsedTime / this.frameDuration); 136 | } 137 | 138 | Animation.prototype.isDone = function() { 139 | return (this.elapsedTime >= this.totalTime); 140 | } 141 | 142 | function Timer() { 143 | this.gameTime = 0; 144 | this.maxStep = 0.05; 145 | this.wallLastTimestamp = 0; 146 | } 147 | 148 | Timer.prototype.tick = function() { 149 | var wallCurrent = Date.now(); 150 | var wallDelta = (wallCurrent - this.wallLastTimestamp) / 1000; 151 | this.wallLastTimestamp = wallCurrent; 152 | 153 | var gameDelta = Math.min(wallDelta, this.maxStep); 154 | this.gameTime += gameDelta; 155 | return gameDelta; 156 | } 157 | 158 | function GameEngine() { 159 | this.entities = []; 160 | this.ctx = null; 161 | this.click = null; 162 | this.mouse = null; 163 | this.timer = new Timer(); 164 | this.surfaceWidth = null; 165 | this.surfaceHeight = null; 166 | this.halfSurfaceWidth = null; 167 | this.halfSurfaceHeight = null; 168 | } 169 | 170 | GameEngine.prototype.init = function(ctx) { 171 | console.log('game initialized'); 172 | this.ctx = ctx; 173 | this.surfaceWidth = this.ctx.canvas.width; 174 | this.surfaceHeight = this.ctx.canvas.height; 175 | this.halfSurfaceWidth = this.surfaceWidth/2; 176 | this.halfSurfaceHeight = this.surfaceHeight/2; 177 | this.startInput(); 178 | } 179 | 180 | GameEngine.prototype.start = function() { 181 | console.log("starting game"); 182 | var that = this; 183 | (function gameLoop() { 184 | that.loop(); 185 | requestAnimFrame(gameLoop, that.ctx.canvas); 186 | })(); 187 | } 188 | 189 | GameEngine.prototype.startInput = function() { 190 | var getXandY = function(e) { 191 | var x = e.clientX - that.ctx.canvas.getBoundingClientRect().left - (that.ctx.canvas.width/2); 192 | var y = e.clientY - that.ctx.canvas.getBoundingClientRect().top - (that.ctx.canvas.height/2); 193 | return {x: x, y: y}; 194 | } 195 | 196 | var that = this; 197 | 198 | this.ctx.canvas.addEventListener("click", function(e) { 199 | that.click = getXandY(e); 200 | e.stopPropagation(); 201 | e.preventDefault(); 202 | }, false); 203 | 204 | this.ctx.canvas.addEventListener("mousemove", function(e) { 205 | that.mouse = getXandY(e); 206 | }, false); 207 | } 208 | 209 | GameEngine.prototype.addEntity = function(entity) { 210 | this.entities.push(entity); 211 | } 212 | 213 | GameEngine.prototype.draw = function(callback) { 214 | this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); 215 | this.ctx.save(); 216 | this.ctx.translate(this.ctx.canvas.width/2, this.ctx.canvas.height/2); 217 | for (var i = 0; i < this.entities.length; i++) { 218 | this.entities[i].draw(this.ctx); 219 | } 220 | if (callback) { 221 | callback(this); 222 | } 223 | this.ctx.restore(); 224 | } 225 | 226 | GameEngine.prototype.update = function() { 227 | var entitiesCount = this.entities.length; 228 | 229 | for (var i = 0; i < entitiesCount; i++) { 230 | var entity = this.entities[i]; 231 | 232 | if (!entity.removeFromWorld) { 233 | entity.update(); 234 | } 235 | } 236 | 237 | for (var i = this.entities.length-1; i >= 0; --i) { 238 | if (this.entities[i].removeFromWorld) { 239 | this.entities.splice(i, 1); 240 | } 241 | } 242 | } 243 | 244 | GameEngine.prototype.loop = function() { 245 | this.clockTick = this.timer.tick(); 246 | this.update(); 247 | this.draw(); 248 | this.click = null; 249 | } 250 | 251 | function Entity(game, x, y) { 252 | this.game = game; 253 | this.x = x; 254 | this.y = y; 255 | this.removeFromWorld = false; 256 | } 257 | 258 | Entity.prototype.update = function() { 259 | } 260 | 261 | Entity.prototype.draw = function(ctx) { 262 | if (this.game.showOutlines && this.radius) { 263 | ctx.beginPath(); 264 | ctx.strokeStyle = "green"; 265 | ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, false); 266 | ctx.stroke(); 267 | ctx.closePath(); 268 | } 269 | } 270 | 271 | Entity.prototype.drawSpriteCentered = function(ctx) { 272 | if (this.sprite && this.x && this.y) { 273 | var x = this.x - this.sprite.width/2; 274 | var y = this.y - this.sprite.height/2; 275 | ctx.drawImage(this.sprite, x, y); 276 | } 277 | } 278 | 279 | Entity.prototype.outsideScreen = function() { 280 | return (this.x > this.game.halfSurfaceWidth || this.x < -(this.game.halfSurfaceWidth) || 281 | this.y > this.game.halfSurfaceHeight || this.y < -(this.game.halfSurfaceHeight)); 282 | } 283 | 284 | Entity.prototype.rotateAndCache = function(image, angle) { 285 | var offscreenCanvas = document.createElement('canvas'); 286 | var size = Math.max(image.width, image.height); 287 | offscreenCanvas.width = size; 288 | offscreenCanvas.height = size; 289 | var offscreenCtx = offscreenCanvas.getContext('2d'); 290 | offscreenCtx.save(); 291 | offscreenCtx.translate(size/2, size/2); 292 | offscreenCtx.rotate(angle + Math.PI/2); 293 | offscreenCtx.drawImage(image, -(image.width/2), -(image.height/2)); 294 | offscreenCtx.restore(); 295 | //offscreenCtx.strokeStyle = "red"; 296 | //offscreenCtx.strokeRect(0,0,size,size); 297 | return offscreenCanvas; 298 | } 299 | 300 | function Alien(game, radial_distance, angle) { 301 | Entity.call(this, game); 302 | this.radial_distance = radial_distance; 303 | this.angle = angle; 304 | this.speed = 100; 305 | this.sprite = this.rotateAndCache(ASSET_MANAGER.getAsset('img/alien.png'), this.angle); 306 | this.radius = this.sprite.height/2; 307 | this.setCoords(); 308 | } 309 | Alien.prototype = new Entity(); 310 | Alien.prototype.constructor = Alien; 311 | 312 | Alien.prototype.setCoords = function() { 313 | this.x = this.radial_distance * Math.cos(this.angle); 314 | this.y = this.radial_distance * Math.sin(this.angle); 315 | } 316 | 317 | Alien.prototype.update = function() { 318 | this.setCoords(); 319 | this.radial_distance -= this.speed * this.game.clockTick; 320 | 321 | Entity.prototype.update.call(this); 322 | } 323 | 324 | Alien.prototype.draw = function(ctx) { 325 | this.drawSpriteCentered(ctx); 326 | 327 | Entity.prototype.draw.call(this, ctx); 328 | } 329 | 330 | function Sentry(game) { 331 | this.distanceFromEarthCenter = 85; 332 | Entity.call(this, game, 0, this.distanceFromEarthCenter); 333 | this.sprite = ASSET_MANAGER.getAsset('img/sentry.png'); 334 | this.radius = this.sprite.width / 2; 335 | this.angle = 0; 336 | } 337 | Sentry.prototype = new Entity(); 338 | Sentry.prototype.constructor = Sentry; 339 | 340 | Sentry.prototype.update = function() { 341 | if (this.game.mouse) { 342 | this.angle = Math.atan2(this.game.mouse.y, this.game.mouse.x); 343 | if (this.angle < 0) { 344 | this.angle += Math.PI * 2; 345 | } 346 | this.x = (Math.cos(this.angle) * this.distanceFromEarthCenter); 347 | this.y = (Math.sin(this.angle) * this.distanceFromEarthCenter); 348 | } 349 | if (this.game.click) { 350 | this.shoot(); 351 | } 352 | Entity.prototype.update.call(this); 353 | } 354 | 355 | Sentry.prototype.draw = function(ctx) { 356 | ctx.save(); 357 | ctx.translate(this.x, this.y); 358 | ctx.rotate(this.angle + Math.PI/2); 359 | ctx.drawImage(this.sprite, -this.sprite.width/2, -this.sprite.height/2); 360 | ctx.restore(); 361 | 362 | Entity.prototype.draw.call(this, ctx); 363 | } 364 | 365 | Sentry.prototype.shoot = function() { 366 | var bullet = new Bullet(this.game, this.x, this.y, this.angle, this.game.click); 367 | this.game.addEntity(bullet); 368 | ASSET_MANAGER.getSound('audio/bullet.mp3').play(); 369 | } 370 | 371 | function Bullet(game, x, y, angle, explodesAt) { 372 | Entity.call(this, game, x, y); 373 | this.angle = angle; 374 | this.explodesAt = explodesAt; 375 | this.speed = 250; 376 | this.radial_distance = 95; 377 | this.sprite = ASSET_MANAGER.getAsset('img/bullet.png'); 378 | this.animation = new Animation(this.sprite, 7, 0.05, true); 379 | } 380 | Bullet.prototype = new Entity(); 381 | Bullet.prototype.constructor = Bullet; 382 | 383 | Bullet.prototype.update = function() { 384 | if (this.outsideScreen()) { 385 | this.removeFromWorld = true; 386 | } else if (Math.abs(this.x) >= Math.abs(this.explodesAt.x) || Math.abs(this.y) >= Math.abs(this.explodesAt.y)) { 387 | ASSET_MANAGER.getSound('audio/bullet_boom.mp3').play(); 388 | this.game.addEntity(new BulletExplosion(this.game, this.explodesAt.x, this.explodesAt.y)); 389 | this.removeFromWorld = true; 390 | } else { 391 | this.x = this.radial_distance * Math.cos(this.angle); 392 | this.y = this.radial_distance * Math.sin(this.angle); 393 | this.radial_distance += this.speed * this.game.clockTick; 394 | } 395 | } 396 | 397 | Bullet.prototype.draw = function(ctx) { 398 | ctx.save(); 399 | ctx.translate(this.x, this.y); 400 | ctx.rotate(this.angle + Math.PI/2); 401 | ctx.translate(-this.x, -this.y); 402 | this.animation.drawFrame(this.game.clockTick, ctx, this.x, this.y); 403 | ctx.restore(); 404 | 405 | Entity.prototype.draw.call(this, ctx); 406 | } 407 | 408 | function BulletExplosion(game, x, y) { 409 | Entity.call(this, game, x, y); 410 | this.sprite = ASSET_MANAGER.getAsset('img/explosion.png'); 411 | this.animation = new Animation(this.sprite, 34, 0.05); 412 | this.radius = this.animation.frameWidth / 2; 413 | } 414 | BulletExplosion.prototype = new Entity(); 415 | BulletExplosion.prototype.constructor = BulletExplosion; 416 | 417 | BulletExplosion.prototype.update = function() { 418 | Entity.prototype.update.call(this); 419 | 420 | if (this.animation.isDone()) { 421 | this.removeFromWorld = true; 422 | return; 423 | } 424 | 425 | this.radius = (this.animation.frameWidth/2) * this.scaleFactor(); 426 | } 427 | 428 | BulletExplosion.prototype.scaleFactor = function() { 429 | return 1 + (this.animation.currentFrame() / 3); 430 | } 431 | 432 | BulletExplosion.prototype.draw = function(ctx) { 433 | this.animation.drawFrame(this.game.clockTick, ctx, this.x, this.y, this.scaleFactor()); 434 | 435 | Entity.prototype.draw.call(this, ctx); 436 | } 437 | 438 | function Earth(game) { 439 | Entity.call(this, game, 0, 0); 440 | this.sprite = ASSET_MANAGER.getAsset('img/earth.png'); 441 | } 442 | Earth.prototype = new Entity(); 443 | Earth.prototype.constructor = Earth; 444 | 445 | Earth.RADIUS = 67; 446 | 447 | Earth.prototype.draw = function(ctx) { 448 | ctx.drawImage(this.sprite, this.x - this.sprite.width/2, this.y - this.sprite.height/2); 449 | } 450 | 451 | function EvilAliens() { 452 | GameEngine.call(this); 453 | //this.showOutlines = true; 454 | } 455 | EvilAliens.prototype = new GameEngine(); 456 | EvilAliens.prototype.constructor = EvilAliens; 457 | 458 | EvilAliens.prototype.start = function() { 459 | this.sentry = new Sentry(this); 460 | this.earth = new Earth(this); 461 | this.addEntity(this.earth); 462 | this.addEntity(this.sentry); 463 | GameEngine.prototype.start.call(this); 464 | } 465 | 466 | EvilAliens.prototype.update = function() { 467 | if (this.lastAlienAddedAt == null || (this.timer.gameTime - this.lastAlienAddedAt) > 1) { 468 | this.addEntity(new Alien(this, this.ctx.canvas.width, Math.floor(Math.random() * Math.PI * 2))); 469 | this.lastAlienAddedAt = this.timer.gameTime; 470 | } 471 | 472 | GameEngine.prototype.update.call(this); 473 | } 474 | 475 | EvilAliens.prototype.draw = function() { 476 | GameEngine.prototype.draw.call(this); 477 | } 478 | 479 | var canvas = document.getElementById('surface'); 480 | var ctx = canvas.getContext('2d'); 481 | var game = new EvilAliens(); 482 | var ASSET_MANAGER = new AssetManager(); 483 | 484 | ASSET_MANAGER.queueDownload('img/alien.png'); 485 | ASSET_MANAGER.queueDownload('img/bullet.png'); 486 | ASSET_MANAGER.queueDownload('img/earth.png'); 487 | ASSET_MANAGER.queueDownload('img/sentry.png'); 488 | ASSET_MANAGER.queueDownload('img/explosion.png'); 489 | ASSET_MANAGER.queueSound('alien-boom', 'audio/alien_boom.mp3'); 490 | ASSET_MANAGER.queueSound('bullet-boom', 'audio/bullet_boom.mp3'); 491 | ASSET_MANAGER.queueSound('bullet', 'audio/bullet.mp3'); 492 | 493 | ASSET_MANAGER.downloadAll(function() { 494 | game.init(ctx); 495 | game.start(); 496 | }); -------------------------------------------------------------------------------- /static/phase11/scripts/app.js: -------------------------------------------------------------------------------- 1 | soundManager.url = 'swf/'; 2 | soundManager.flashVersion = 9; 3 | soundManager.debugFlash = false; 4 | soundManager.debugMode = false; 5 | 6 | window.requestAnimFrame = (function(){ 7 | return window.requestAnimationFrame || 8 | window.webkitRequestAnimationFrame || 9 | window.mozRequestAnimationFrame || 10 | window.oRequestAnimationFrame || 11 | window.msRequestAnimationFrame || 12 | function(/* function */ callback, /* DOMElement */ element){ 13 | window.setTimeout(callback, 1000 / 60); 14 | }; 15 | })(); 16 | 17 | function AssetManager() { 18 | this.successCount = 0; 19 | this.errorCount = 0; 20 | this.cache = {}; 21 | this.downloadQueue = []; 22 | this.soundsQueue = []; 23 | } 24 | 25 | AssetManager.prototype.queueDownload = function(path) { 26 | this.downloadQueue.push(path); 27 | } 28 | 29 | AssetManager.prototype.queueSound = function(id, path) { 30 | this.soundsQueue.push({id: id, path: path}); 31 | } 32 | 33 | AssetManager.prototype.downloadAll = function(callback) { 34 | if (this.downloadQueue.length === 0 && this.soundsQueue.length === 0) { 35 | callback(); 36 | } 37 | 38 | this.downloadSounds(callback); 39 | 40 | for (var i = 0; i < this.downloadQueue.length; i++) { 41 | var path = this.downloadQueue[i]; 42 | var img = new Image(); 43 | var that = this; 44 | img.addEventListener("load", function() { 45 | console.log(this.src + ' is loaded'); 46 | that.successCount += 1; 47 | if (that.isDone()) { 48 | callback(); 49 | } 50 | }, false); 51 | img.addEventListener("error", function() { 52 | that.errorCount += 1; 53 | if (that.isDone()) { 54 | callback(); 55 | } 56 | }, false); 57 | img.src = path; 58 | this.cache[path] = img; 59 | } 60 | } 61 | 62 | AssetManager.prototype.downloadSounds = function(callback) { 63 | var that = this; 64 | soundManager.onready(function() { 65 | console.log('soundManager ready'); 66 | for (var i = 0; i < that.soundsQueue.length; i++) { 67 | that.downloadSound(that.soundsQueue[i].id, that.soundsQueue[i].path, callback); 68 | } 69 | }); 70 | soundManager.ontimeout(function() { 71 | console.log('SM2 did not start'); 72 | }); 73 | } 74 | 75 | AssetManager.prototype.downloadSound = function(id, path, callback) { 76 | var that = this; 77 | this.cache[path] = soundManager.createSound({ 78 | id: id, 79 | autoLoad: true, 80 | url: path, 81 | onload: function() { 82 | console.log(this.url + ' is loaded'); 83 | that.successCount += 1; 84 | if (that.isDone()) { 85 | callback(); 86 | } 87 | } 88 | }); 89 | } 90 | 91 | AssetManager.prototype.getSound = function(path) { 92 | return this.cache[path]; 93 | } 94 | 95 | AssetManager.prototype.getAsset = function(path) { 96 | return this.cache[path]; 97 | } 98 | 99 | AssetManager.prototype.isDone = function() { 100 | return ((this.downloadQueue.length + this.soundsQueue.length) == this.successCount + this.errorCount); 101 | } 102 | 103 | function Animation(spriteSheet, frameWidth, frameDuration, loop) { 104 | this.spriteSheet = spriteSheet; 105 | this.frameWidth = frameWidth; 106 | this.frameDuration = frameDuration; 107 | this.frameHeight= this.spriteSheet.height; 108 | this.totalTime = (this.spriteSheet.width / this.frameWidth) * this.frameDuration; 109 | this.elapsedTime = 0; 110 | this.loop = loop; 111 | } 112 | 113 | Animation.prototype.drawFrame = function(tick, ctx, x, y, scaleBy) { 114 | var scaleBy = scaleBy || 1; 115 | this.elapsedTime += tick; 116 | if (this.loop) { 117 | if (this.isDone()) { 118 | this.elapsedTime = 0; 119 | } 120 | } else if (this.isDone()) { 121 | return; 122 | } 123 | var index = this.currentFrame(); 124 | var locX = x - (this.frameWidth/2) * scaleBy; 125 | var locY = y - (this.frameHeight/2) * scaleBy; 126 | ctx.drawImage(this.spriteSheet, 127 | index*this.frameWidth, 0, // source from sheet 128 | this.frameWidth, this.frameHeight, 129 | locX, locY, 130 | this.frameWidth*scaleBy, 131 | this.frameHeight*scaleBy); 132 | } 133 | 134 | Animation.prototype.currentFrame = function() { 135 | return Math.floor(this.elapsedTime / this.frameDuration); 136 | } 137 | 138 | Animation.prototype.isDone = function() { 139 | return (this.elapsedTime >= this.totalTime); 140 | } 141 | 142 | function Timer() { 143 | this.gameTime = 0; 144 | this.maxStep = 0.05; 145 | this.wallLastTimestamp = 0; 146 | } 147 | 148 | Timer.prototype.tick = function() { 149 | var wallCurrent = Date.now(); 150 | var wallDelta = (wallCurrent - this.wallLastTimestamp) / 1000; 151 | this.wallLastTimestamp = wallCurrent; 152 | 153 | var gameDelta = Math.min(wallDelta, this.maxStep); 154 | this.gameTime += gameDelta; 155 | return gameDelta; 156 | } 157 | 158 | function GameEngine() { 159 | this.entities = []; 160 | this.ctx = null; 161 | this.click = null; 162 | this.mouse = null; 163 | this.timer = new Timer(); 164 | this.surfaceWidth = null; 165 | this.surfaceHeight = null; 166 | this.halfSurfaceWidth = null; 167 | this.halfSurfaceHeight = null; 168 | } 169 | 170 | GameEngine.prototype.init = function(ctx) { 171 | console.log('game initialized'); 172 | this.ctx = ctx; 173 | this.surfaceWidth = this.ctx.canvas.width; 174 | this.surfaceHeight = this.ctx.canvas.height; 175 | this.halfSurfaceWidth = this.surfaceWidth/2; 176 | this.halfSurfaceHeight = this.surfaceHeight/2; 177 | this.startInput(); 178 | } 179 | 180 | GameEngine.prototype.start = function() { 181 | console.log("starting game"); 182 | var that = this; 183 | (function gameLoop() { 184 | that.loop(); 185 | requestAnimFrame(gameLoop, that.ctx.canvas); 186 | })(); 187 | } 188 | 189 | GameEngine.prototype.startInput = function() { 190 | var getXandY = function(e) { 191 | var x = e.clientX - that.ctx.canvas.getBoundingClientRect().left - (that.ctx.canvas.width/2); 192 | var y = e.clientY - that.ctx.canvas.getBoundingClientRect().top - (that.ctx.canvas.height/2); 193 | return {x: x, y: y}; 194 | } 195 | 196 | var that = this; 197 | 198 | this.ctx.canvas.addEventListener("click", function(e) { 199 | that.click = getXandY(e); 200 | e.stopPropagation(); 201 | e.preventDefault(); 202 | }, false); 203 | 204 | this.ctx.canvas.addEventListener("mousemove", function(e) { 205 | that.mouse = getXandY(e); 206 | }, false); 207 | } 208 | 209 | GameEngine.prototype.addEntity = function(entity) { 210 | this.entities.push(entity); 211 | } 212 | 213 | GameEngine.prototype.draw = function(callback) { 214 | this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); 215 | this.ctx.save(); 216 | this.ctx.translate(this.ctx.canvas.width/2, this.ctx.canvas.height/2); 217 | for (var i = 0; i < this.entities.length; i++) { 218 | this.entities[i].draw(this.ctx); 219 | } 220 | if (callback) { 221 | callback(this); 222 | } 223 | this.ctx.restore(); 224 | } 225 | 226 | GameEngine.prototype.update = function() { 227 | var entitiesCount = this.entities.length; 228 | 229 | for (var i = 0; i < entitiesCount; i++) { 230 | var entity = this.entities[i]; 231 | 232 | if (!entity.removeFromWorld) { 233 | entity.update(); 234 | } 235 | } 236 | 237 | for (var i = this.entities.length-1; i >= 0; --i) { 238 | if (this.entities[i].removeFromWorld) { 239 | this.entities.splice(i, 1); 240 | } 241 | } 242 | } 243 | 244 | GameEngine.prototype.loop = function() { 245 | this.clockTick = this.timer.tick(); 246 | this.update(); 247 | this.draw(); 248 | this.click = null; 249 | } 250 | 251 | function Entity(game, x, y) { 252 | this.game = game; 253 | this.x = x; 254 | this.y = y; 255 | this.removeFromWorld = false; 256 | } 257 | 258 | Entity.prototype.update = function() { 259 | } 260 | 261 | Entity.prototype.draw = function(ctx) { 262 | if (this.game.showOutlines && this.radius) { 263 | ctx.beginPath(); 264 | ctx.strokeStyle = "green"; 265 | ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, false); 266 | ctx.stroke(); 267 | ctx.closePath(); 268 | } 269 | } 270 | 271 | Entity.prototype.drawSpriteCentered = function(ctx) { 272 | if (this.sprite && this.x && this.y) { 273 | var x = this.x - this.sprite.width/2; 274 | var y = this.y - this.sprite.height/2; 275 | ctx.drawImage(this.sprite, x, y); 276 | } 277 | } 278 | 279 | Entity.prototype.outsideScreen = function() { 280 | return (this.x > this.game.halfSurfaceWidth || this.x < -(this.game.halfSurfaceWidth) || 281 | this.y > this.game.halfSurfaceHeight || this.y < -(this.game.halfSurfaceHeight)); 282 | } 283 | 284 | Entity.prototype.rotateAndCache = function(image, angle) { 285 | var offscreenCanvas = document.createElement('canvas'); 286 | var size = Math.max(image.width, image.height); 287 | offscreenCanvas.width = size; 288 | offscreenCanvas.height = size; 289 | var offscreenCtx = offscreenCanvas.getContext('2d'); 290 | offscreenCtx.save(); 291 | offscreenCtx.translate(size/2, size/2); 292 | offscreenCtx.rotate(angle + Math.PI/2); 293 | offscreenCtx.drawImage(image, -(image.width/2), -(image.height/2)); 294 | offscreenCtx.restore(); 295 | //offscreenCtx.strokeStyle = "red"; 296 | //offscreenCtx.strokeRect(0,0,size,size); 297 | return offscreenCanvas; 298 | } 299 | 300 | function Alien(game, radial_distance, angle) { 301 | Entity.call(this, game); 302 | this.radial_distance = radial_distance; 303 | this.angle = angle; 304 | this.speed = 100; 305 | this.sprite = this.rotateAndCache(ASSET_MANAGER.getAsset('img/alien.png'), this.angle); 306 | this.radius = this.sprite.height/2; 307 | this.setCoords(); 308 | } 309 | Alien.prototype = new Entity(); 310 | Alien.prototype.constructor = Alien; 311 | 312 | Alien.prototype.setCoords = function() { 313 | this.x = this.radial_distance * Math.cos(this.angle); 314 | this.y = this.radial_distance * Math.sin(this.angle); 315 | } 316 | 317 | Alien.prototype.update = function() { 318 | this.setCoords(); 319 | this.radial_distance -= this.speed * this.game.clockTick; 320 | 321 | if (this.hitPlanet()) { 322 | this.removeFromWorld = true; 323 | this.game.lives -= 1; 324 | } 325 | 326 | Entity.prototype.update.call(this); 327 | } 328 | 329 | Alien.prototype.hitPlanet = function() { 330 | var distance_squared = ((this.x * this.x) + (this.y * this.y)); 331 | var radii_squared = (this.radius + Earth.RADIUS) * (this.radius + Earth.RADIUS); 332 | return distance_squared < radii_squared; 333 | } 334 | 335 | Alien.prototype.draw = function(ctx) { 336 | this.drawSpriteCentered(ctx); 337 | 338 | Entity.prototype.draw.call(this, ctx); 339 | } 340 | 341 | Alien.prototype.explode = function() { 342 | this.removeFromWorld = true; 343 | this.game.addEntity(new AlienExplosion(this.game, this.x, this.y)); 344 | ASSET_MANAGER.getSound('audio/alien_boom.mp3').play(); 345 | } 346 | 347 | function AlienExplosion(game, x, y) { 348 | Entity.call(this, game, x, y); 349 | this.animation = new Animation(ASSET_MANAGER.getAsset('img/alien-explosion.png'), 69, 0.05); 350 | this.radius = this.animation.frameWidth / 2; 351 | } 352 | AlienExplosion.prototype = new Entity(); 353 | AlienExplosion.prototype.constructor = AlienExplosion; 354 | 355 | AlienExplosion.prototype.update = function() { 356 | Entity.prototype.update.call(this); 357 | if (this.animation.isDone()) { 358 | this.removeFromWorld = true; 359 | } 360 | } 361 | 362 | AlienExplosion.prototype.draw = function(ctx) { 363 | Entity.prototype.draw.call(this, ctx); 364 | this.animation.drawFrame(this.game.clockTick, ctx, this.x, this.y); 365 | } 366 | 367 | function Sentry(game) { 368 | this.distanceFromEarthCenter = 85; 369 | Entity.call(this, game, 0, this.distanceFromEarthCenter); 370 | this.sprite = ASSET_MANAGER.getAsset('img/sentry.png'); 371 | this.radius = this.sprite.width / 2; 372 | this.angle = 0; 373 | } 374 | Sentry.prototype = new Entity(); 375 | Sentry.prototype.constructor = Sentry; 376 | 377 | Sentry.prototype.update = function() { 378 | if (this.game.mouse) { 379 | this.angle = Math.atan2(this.game.mouse.y, this.game.mouse.x); 380 | if (this.angle < 0) { 381 | this.angle += Math.PI * 2; 382 | } 383 | this.x = (Math.cos(this.angle) * this.distanceFromEarthCenter); 384 | this.y = (Math.sin(this.angle) * this.distanceFromEarthCenter); 385 | } 386 | if (this.game.click) { 387 | this.shoot(); 388 | } 389 | Entity.prototype.update.call(this); 390 | } 391 | 392 | Sentry.prototype.draw = function(ctx) { 393 | ctx.save(); 394 | ctx.translate(this.x, this.y); 395 | ctx.rotate(this.angle + Math.PI/2); 396 | ctx.drawImage(this.sprite, -this.sprite.width/2, -this.sprite.height/2); 397 | ctx.restore(); 398 | 399 | Entity.prototype.draw.call(this, ctx); 400 | } 401 | 402 | Sentry.prototype.shoot = function() { 403 | var bullet = new Bullet(this.game, this.x, this.y, this.angle, this.game.click); 404 | this.game.addEntity(bullet); 405 | ASSET_MANAGER.getSound('audio/bullet.mp3').play(); 406 | } 407 | 408 | function Bullet(game, x, y, angle, explodesAt) { 409 | Entity.call(this, game, x, y); 410 | this.angle = angle; 411 | this.explodesAt = explodesAt; 412 | this.speed = 250; 413 | this.radial_distance = 95; 414 | this.sprite = ASSET_MANAGER.getAsset('img/bullet.png'); 415 | this.animation = new Animation(this.sprite, 7, 0.05, true); 416 | } 417 | Bullet.prototype = new Entity(); 418 | Bullet.prototype.constructor = Bullet; 419 | 420 | Bullet.prototype.update = function() { 421 | if (this.outsideScreen()) { 422 | this.removeFromWorld = true; 423 | } else if (Math.abs(this.x) >= Math.abs(this.explodesAt.x) || Math.abs(this.y) >= Math.abs(this.explodesAt.y)) { 424 | ASSET_MANAGER.getSound('audio/bullet_boom.mp3').play(); 425 | this.game.addEntity(new BulletExplosion(this.game, this.explodesAt.x, this.explodesAt.y)); 426 | this.removeFromWorld = true; 427 | } else { 428 | this.x = this.radial_distance * Math.cos(this.angle); 429 | this.y = this.radial_distance * Math.sin(this.angle); 430 | this.radial_distance += this.speed * this.game.clockTick; 431 | } 432 | } 433 | 434 | Bullet.prototype.draw = function(ctx) { 435 | ctx.save(); 436 | ctx.translate(this.x, this.y); 437 | ctx.rotate(this.angle + Math.PI/2); 438 | ctx.translate(-this.x, -this.y); 439 | this.animation.drawFrame(this.game.clockTick, ctx, this.x, this.y); 440 | ctx.restore(); 441 | 442 | Entity.prototype.draw.call(this, ctx); 443 | } 444 | 445 | function BulletExplosion(game, x, y) { 446 | Entity.call(this, game, x, y); 447 | this.sprite = ASSET_MANAGER.getAsset('img/explosion.png'); 448 | this.animation = new Animation(this.sprite, 34, 0.05); 449 | this.radius = this.animation.frameWidth / 2; 450 | } 451 | BulletExplosion.prototype = new Entity(); 452 | BulletExplosion.prototype.constructor = BulletExplosion; 453 | 454 | BulletExplosion.prototype.update = function() { 455 | Entity.prototype.update.call(this); 456 | 457 | if (this.animation.isDone()) { 458 | this.removeFromWorld = true; 459 | return; 460 | } 461 | 462 | this.radius = (this.animation.frameWidth/2) * this.scaleFactor(); 463 | 464 | for (var i = 0; i < this.game.entities.length; i++) { 465 | var alien = this.game.entities[i]; 466 | if (alien instanceof Alien && this.isCaughtInExplosion(alien)) { 467 | console.log("hit!"); 468 | this.game.score += 10; 469 | alien.explode(); 470 | } 471 | } 472 | } 473 | 474 | BulletExplosion.prototype.isCaughtInExplosion = function(alien) { 475 | var distance_squared = (((this.x - alien.x) * (this.x - alien.x)) + ((this.y - alien.y) * (this.y - alien.y))); 476 | var radii_squared = (this.radius + alien.radius) * (this.radius + alien.radius); 477 | return distance_squared < radii_squared; 478 | } 479 | 480 | BulletExplosion.prototype.scaleFactor = function() { 481 | return 1 + (this.animation.currentFrame() / 3); 482 | } 483 | 484 | BulletExplosion.prototype.draw = function(ctx) { 485 | this.animation.drawFrame(this.game.clockTick, ctx, this.x, this.y, this.scaleFactor()); 486 | 487 | Entity.prototype.draw.call(this, ctx); 488 | } 489 | 490 | function Earth(game) { 491 | Entity.call(this, game, 0, 0); 492 | this.sprite = ASSET_MANAGER.getAsset('img/earth.png'); 493 | } 494 | Earth.prototype = new Entity(); 495 | Earth.prototype.constructor = Earth; 496 | 497 | Earth.RADIUS = 67; 498 | 499 | Earth.prototype.draw = function(ctx) { 500 | ctx.drawImage(this.sprite, this.x - this.sprite.width/2, this.y - this.sprite.height/2); 501 | } 502 | 503 | function EvilAliens() { 504 | GameEngine.call(this); 505 | this.showOutlines = true; 506 | } 507 | EvilAliens.prototype = new GameEngine(); 508 | EvilAliens.prototype.constructor = EvilAliens; 509 | 510 | EvilAliens.prototype.start = function() { 511 | this.sentry = new Sentry(this); 512 | this.earth = new Earth(this); 513 | this.addEntity(this.earth); 514 | this.addEntity(this.sentry); 515 | GameEngine.prototype.start.call(this); 516 | } 517 | 518 | EvilAliens.prototype.update = function() { 519 | if (this.lastAlienAddedAt == null || (this.timer.gameTime - this.lastAlienAddedAt) > 1) { 520 | this.addEntity(new Alien(this, this.ctx.canvas.width, Math.floor(Math.random() * Math.PI * 2))); 521 | this.lastAlienAddedAt = this.timer.gameTime; 522 | } 523 | 524 | GameEngine.prototype.update.call(this); 525 | } 526 | 527 | EvilAliens.prototype.draw = function() { 528 | GameEngine.prototype.draw.call(this); 529 | } 530 | 531 | var canvas = document.getElementById('surface'); 532 | var ctx = canvas.getContext('2d'); 533 | var game = new EvilAliens(); 534 | var ASSET_MANAGER = new AssetManager(); 535 | 536 | ASSET_MANAGER.queueDownload('img/alien-explosion.png'); 537 | ASSET_MANAGER.queueDownload('img/alien.png'); 538 | ASSET_MANAGER.queueDownload('img/bullet.png'); 539 | ASSET_MANAGER.queueDownload('img/earth.png'); 540 | ASSET_MANAGER.queueDownload('img/sentry.png'); 541 | ASSET_MANAGER.queueDownload('img/explosion.png'); 542 | ASSET_MANAGER.queueSound('alien-boom', 'audio/alien_boom.mp3'); 543 | ASSET_MANAGER.queueSound('bullet-boom', 'audio/bullet_boom.mp3'); 544 | ASSET_MANAGER.queueSound('bullet', 'audio/bullet.mp3'); 545 | 546 | ASSET_MANAGER.downloadAll(function() { 547 | game.init(ctx); 548 | game.start(); 549 | }); -------------------------------------------------------------------------------- /static/phase12/scripts/app.js: -------------------------------------------------------------------------------- 1 | soundManager.url = 'swf/'; 2 | soundManager.flashVersion = 9; 3 | soundManager.debugFlash = false; 4 | soundManager.debugMode = false; 5 | 6 | window.requestAnimFrame = (function(){ 7 | return window.requestAnimationFrame || 8 | window.webkitRequestAnimationFrame || 9 | window.mozRequestAnimationFrame || 10 | window.oRequestAnimationFrame || 11 | window.msRequestAnimationFrame || 12 | function(/* function */ callback, /* DOMElement */ element){ 13 | window.setTimeout(callback, 1000 / 60); 14 | }; 15 | })(); 16 | 17 | function AssetManager() { 18 | this.successCount = 0; 19 | this.errorCount = 0; 20 | this.cache = {}; 21 | this.downloadQueue = []; 22 | this.soundsQueue = []; 23 | } 24 | 25 | AssetManager.prototype.queueDownload = function(path) { 26 | this.downloadQueue.push(path); 27 | } 28 | 29 | AssetManager.prototype.queueSound = function(id, path) { 30 | this.soundsQueue.push({id: id, path: path}); 31 | } 32 | 33 | AssetManager.prototype.downloadAll = function(callback) { 34 | if (this.downloadQueue.length === 0 && this.soundsQueue.length === 0) { 35 | callback(); 36 | } 37 | 38 | this.downloadSounds(callback); 39 | 40 | for (var i = 0; i < this.downloadQueue.length; i++) { 41 | var path = this.downloadQueue[i]; 42 | var img = new Image(); 43 | var that = this; 44 | img.addEventListener("load", function() { 45 | console.log(this.src + ' is loaded'); 46 | that.successCount += 1; 47 | if (that.isDone()) { 48 | callback(); 49 | } 50 | }, false); 51 | img.addEventListener("error", function() { 52 | that.errorCount += 1; 53 | if (that.isDone()) { 54 | callback(); 55 | } 56 | }, false); 57 | img.src = path; 58 | this.cache[path] = img; 59 | } 60 | } 61 | 62 | AssetManager.prototype.downloadSounds = function(callback) { 63 | var that = this; 64 | soundManager.onready(function() { 65 | console.log('soundManager ready'); 66 | for (var i = 0; i < that.soundsQueue.length; i++) { 67 | that.downloadSound(that.soundsQueue[i].id, that.soundsQueue[i].path, callback); 68 | } 69 | }); 70 | soundManager.ontimeout(function() { 71 | console.log('SM2 did not start'); 72 | }); 73 | } 74 | 75 | AssetManager.prototype.downloadSound = function(id, path, callback) { 76 | var that = this; 77 | this.cache[path] = soundManager.createSound({ 78 | id: id, 79 | autoLoad: true, 80 | url: path, 81 | onload: function() { 82 | console.log(this.url + ' is loaded'); 83 | that.successCount += 1; 84 | if (that.isDone()) { 85 | callback(); 86 | } 87 | } 88 | }); 89 | } 90 | 91 | AssetManager.prototype.getSound = function(path) { 92 | return this.cache[path]; 93 | } 94 | 95 | AssetManager.prototype.getAsset = function(path) { 96 | return this.cache[path]; 97 | } 98 | 99 | AssetManager.prototype.isDone = function() { 100 | return ((this.downloadQueue.length + this.soundsQueue.length) == this.successCount + this.errorCount); 101 | } 102 | 103 | function Animation(spriteSheet, frameWidth, frameDuration, loop) { 104 | this.spriteSheet = spriteSheet; 105 | this.frameWidth = frameWidth; 106 | this.frameDuration = frameDuration; 107 | this.frameHeight= this.spriteSheet.height; 108 | this.totalTime = (this.spriteSheet.width / this.frameWidth) * this.frameDuration; 109 | this.elapsedTime = 0; 110 | this.loop = loop; 111 | } 112 | 113 | Animation.prototype.drawFrame = function(tick, ctx, x, y, scaleBy) { 114 | var scaleBy = scaleBy || 1; 115 | this.elapsedTime += tick; 116 | if (this.loop) { 117 | if (this.isDone()) { 118 | this.elapsedTime = 0; 119 | } 120 | } else if (this.isDone()) { 121 | return; 122 | } 123 | var index = this.currentFrame(); 124 | var locX = x - (this.frameWidth/2) * scaleBy; 125 | var locY = y - (this.frameHeight/2) * scaleBy; 126 | ctx.drawImage(this.spriteSheet, 127 | index*this.frameWidth, 0, // source from sheet 128 | this.frameWidth, this.frameHeight, 129 | locX, locY, 130 | this.frameWidth*scaleBy, 131 | this.frameHeight*scaleBy); 132 | } 133 | 134 | Animation.prototype.currentFrame = function() { 135 | return Math.floor(this.elapsedTime / this.frameDuration); 136 | } 137 | 138 | Animation.prototype.isDone = function() { 139 | return (this.elapsedTime >= this.totalTime); 140 | } 141 | 142 | function Timer() { 143 | this.gameTime = 0; 144 | this.maxStep = 0.05; 145 | this.wallLastTimestamp = 0; 146 | } 147 | 148 | Timer.prototype.tick = function() { 149 | var wallCurrent = Date.now(); 150 | var wallDelta = (wallCurrent - this.wallLastTimestamp) / 1000; 151 | this.wallLastTimestamp = wallCurrent; 152 | 153 | var gameDelta = Math.min(wallDelta, this.maxStep); 154 | this.gameTime += gameDelta; 155 | return gameDelta; 156 | } 157 | 158 | function GameEngine() { 159 | this.entities = []; 160 | this.ctx = null; 161 | this.click = null; 162 | this.mouse = null; 163 | this.timer = new Timer(); 164 | this.surfaceWidth = null; 165 | this.surfaceHeight = null; 166 | this.halfSurfaceWidth = null; 167 | this.halfSurfaceHeight = null; 168 | } 169 | 170 | GameEngine.prototype.init = function(ctx) { 171 | console.log('game initialized'); 172 | this.ctx = ctx; 173 | this.surfaceWidth = this.ctx.canvas.width; 174 | this.surfaceHeight = this.ctx.canvas.height; 175 | this.halfSurfaceWidth = this.surfaceWidth/2; 176 | this.halfSurfaceHeight = this.surfaceHeight/2; 177 | this.startInput(); 178 | } 179 | 180 | GameEngine.prototype.start = function() { 181 | console.log("starting game"); 182 | var that = this; 183 | (function gameLoop() { 184 | that.loop(); 185 | requestAnimFrame(gameLoop, that.ctx.canvas); 186 | })(); 187 | } 188 | 189 | GameEngine.prototype.startInput = function() { 190 | var getXandY = function(e) { 191 | var x = e.clientX - that.ctx.canvas.getBoundingClientRect().left - (that.ctx.canvas.width/2); 192 | var y = e.clientY - that.ctx.canvas.getBoundingClientRect().top - (that.ctx.canvas.height/2); 193 | return {x: x, y: y}; 194 | } 195 | 196 | var that = this; 197 | 198 | this.ctx.canvas.addEventListener("click", function(e) { 199 | that.click = getXandY(e); 200 | e.stopPropagation(); 201 | e.preventDefault(); 202 | }, false); 203 | 204 | this.ctx.canvas.addEventListener("mousemove", function(e) { 205 | that.mouse = getXandY(e); 206 | }, false); 207 | } 208 | 209 | GameEngine.prototype.addEntity = function(entity) { 210 | this.entities.push(entity); 211 | } 212 | 213 | GameEngine.prototype.draw = function(callback) { 214 | this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); 215 | this.ctx.save(); 216 | this.ctx.translate(this.ctx.canvas.width/2, this.ctx.canvas.height/2); 217 | for (var i = 0; i < this.entities.length; i++) { 218 | this.entities[i].draw(this.ctx); 219 | } 220 | if (callback) { 221 | callback(this); 222 | } 223 | this.ctx.restore(); 224 | } 225 | 226 | GameEngine.prototype.update = function() { 227 | var entitiesCount = this.entities.length; 228 | 229 | for (var i = 0; i < entitiesCount; i++) { 230 | var entity = this.entities[i]; 231 | 232 | if (!entity.removeFromWorld) { 233 | entity.update(); 234 | } 235 | } 236 | 237 | for (var i = this.entities.length-1; i >= 0; --i) { 238 | if (this.entities[i].removeFromWorld) { 239 | this.entities.splice(i, 1); 240 | } 241 | } 242 | } 243 | 244 | GameEngine.prototype.loop = function() { 245 | this.clockTick = this.timer.tick(); 246 | this.update(); 247 | this.draw(); 248 | this.click = null; 249 | } 250 | 251 | function Entity(game, x, y) { 252 | this.game = game; 253 | this.x = x; 254 | this.y = y; 255 | this.removeFromWorld = false; 256 | } 257 | 258 | Entity.prototype.update = function() { 259 | } 260 | 261 | Entity.prototype.draw = function(ctx) { 262 | if (this.game.showOutlines && this.radius) { 263 | ctx.beginPath(); 264 | ctx.strokeStyle = "green"; 265 | ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, false); 266 | ctx.stroke(); 267 | ctx.closePath(); 268 | } 269 | } 270 | 271 | Entity.prototype.drawSpriteCentered = function(ctx) { 272 | if (this.sprite && this.x && this.y) { 273 | var x = this.x - this.sprite.width/2; 274 | var y = this.y - this.sprite.height/2; 275 | ctx.drawImage(this.sprite, x, y); 276 | } 277 | } 278 | 279 | Entity.prototype.outsideScreen = function() { 280 | return (this.x > this.game.halfSurfaceWidth || this.x < -(this.game.halfSurfaceWidth) || 281 | this.y > this.game.halfSurfaceHeight || this.y < -(this.game.halfSurfaceHeight)); 282 | } 283 | 284 | Entity.prototype.rotateAndCache = function(image, angle) { 285 | var offscreenCanvas = document.createElement('canvas'); 286 | var size = Math.max(image.width, image.height); 287 | offscreenCanvas.width = size; 288 | offscreenCanvas.height = size; 289 | var offscreenCtx = offscreenCanvas.getContext('2d'); 290 | offscreenCtx.save(); 291 | offscreenCtx.translate(size/2, size/2); 292 | offscreenCtx.rotate(angle + Math.PI/2); 293 | offscreenCtx.drawImage(image, -(image.width/2), -(image.height/2)); 294 | offscreenCtx.restore(); 295 | //offscreenCtx.strokeStyle = "red"; 296 | //offscreenCtx.strokeRect(0,0,size,size); 297 | return offscreenCanvas; 298 | } 299 | 300 | function Alien(game, radial_distance, angle) { 301 | Entity.call(this, game); 302 | this.radial_distance = radial_distance; 303 | this.angle = angle; 304 | this.speed = 100; 305 | this.sprite = this.rotateAndCache(ASSET_MANAGER.getAsset('img/alien.png'), this.angle); 306 | this.radius = this.sprite.height/2; 307 | this.setCoords(); 308 | } 309 | Alien.prototype = new Entity(); 310 | Alien.prototype.constructor = Alien; 311 | 312 | Alien.prototype.setCoords = function() { 313 | this.x = this.radial_distance * Math.cos(this.angle); 314 | this.y = this.radial_distance * Math.sin(this.angle); 315 | } 316 | 317 | Alien.prototype.update = function() { 318 | this.setCoords(); 319 | this.radial_distance -= this.speed * this.game.clockTick; 320 | 321 | if (this.hitPlanet()) { 322 | this.removeFromWorld = true; 323 | this.game.lives -= 1; 324 | } 325 | 326 | Entity.prototype.update.call(this); 327 | } 328 | 329 | Alien.prototype.hitPlanet = function() { 330 | var distance_squared = ((this.x * this.x) + (this.y * this.y)); 331 | var radii_squared = (this.radius + Earth.RADIUS) * (this.radius + Earth.RADIUS); 332 | return distance_squared < radii_squared; 333 | } 334 | 335 | Alien.prototype.draw = function(ctx) { 336 | this.drawSpriteCentered(ctx); 337 | 338 | Entity.prototype.draw.call(this, ctx); 339 | } 340 | 341 | Alien.prototype.explode = function() { 342 | this.removeFromWorld = true; 343 | this.game.addEntity(new AlienExplosion(this.game, this.x, this.y)); 344 | ASSET_MANAGER.getSound('audio/alien_boom.mp3').play(); 345 | } 346 | 347 | function AlienExplosion(game, x, y) { 348 | Entity.call(this, game, x, y); 349 | this.animation = new Animation(ASSET_MANAGER.getAsset('img/alien-explosion.png'), 69, 0.05); 350 | this.radius = this.animation.frameWidth / 2; 351 | } 352 | AlienExplosion.prototype = new Entity(); 353 | AlienExplosion.prototype.constructor = AlienExplosion; 354 | 355 | AlienExplosion.prototype.update = function() { 356 | Entity.prototype.update.call(this); 357 | if (this.animation.isDone()) { 358 | this.removeFromWorld = true; 359 | } 360 | } 361 | 362 | AlienExplosion.prototype.draw = function(ctx) { 363 | Entity.prototype.draw.call(this, ctx); 364 | this.animation.drawFrame(this.game.clockTick, ctx, this.x, this.y); 365 | } 366 | 367 | function Sentry(game) { 368 | this.distanceFromEarthCenter = 85; 369 | Entity.call(this, game, 0, this.distanceFromEarthCenter); 370 | this.sprite = ASSET_MANAGER.getAsset('img/sentry.png'); 371 | this.radius = this.sprite.width / 2; 372 | this.angle = 0; 373 | } 374 | Sentry.prototype = new Entity(); 375 | Sentry.prototype.constructor = Sentry; 376 | 377 | Sentry.prototype.update = function() { 378 | if (this.game.mouse) { 379 | this.angle = Math.atan2(this.game.mouse.y, this.game.mouse.x); 380 | if (this.angle < 0) { 381 | this.angle += Math.PI * 2; 382 | } 383 | this.x = (Math.cos(this.angle) * this.distanceFromEarthCenter); 384 | this.y = (Math.sin(this.angle) * this.distanceFromEarthCenter); 385 | } 386 | if (this.game.click) { 387 | this.shoot(); 388 | } 389 | Entity.prototype.update.call(this); 390 | } 391 | 392 | Sentry.prototype.draw = function(ctx) { 393 | ctx.save(); 394 | ctx.translate(this.x, this.y); 395 | ctx.rotate(this.angle + Math.PI/2); 396 | ctx.drawImage(this.sprite, -this.sprite.width/2, -this.sprite.height/2); 397 | ctx.restore(); 398 | 399 | Entity.prototype.draw.call(this, ctx); 400 | } 401 | 402 | Sentry.prototype.shoot = function() { 403 | var bullet = new Bullet(this.game, this.x, this.y, this.angle, this.game.click); 404 | this.game.addEntity(bullet); 405 | ASSET_MANAGER.getSound('audio/bullet.mp3').play(); 406 | } 407 | 408 | function Bullet(game, x, y, angle, explodesAt) { 409 | Entity.call(this, game, x, y); 410 | this.angle = angle; 411 | this.explodesAt = explodesAt; 412 | this.speed = 250; 413 | this.radial_distance = 95; 414 | this.sprite = ASSET_MANAGER.getAsset('img/bullet.png'); 415 | this.animation = new Animation(this.sprite, 7, 0.05, true); 416 | } 417 | Bullet.prototype = new Entity(); 418 | Bullet.prototype.constructor = Bullet; 419 | 420 | Bullet.prototype.update = function() { 421 | if (this.outsideScreen()) { 422 | this.removeFromWorld = true; 423 | } else if (Math.abs(this.x) >= Math.abs(this.explodesAt.x) || Math.abs(this.y) >= Math.abs(this.explodesAt.y)) { 424 | ASSET_MANAGER.getSound('audio/bullet_boom.mp3').play(); 425 | this.game.addEntity(new BulletExplosion(this.game, this.explodesAt.x, this.explodesAt.y)); 426 | this.removeFromWorld = true; 427 | } else { 428 | this.x = this.radial_distance * Math.cos(this.angle); 429 | this.y = this.radial_distance * Math.sin(this.angle); 430 | this.radial_distance += this.speed * this.game.clockTick; 431 | } 432 | } 433 | 434 | Bullet.prototype.draw = function(ctx) { 435 | ctx.save(); 436 | ctx.translate(this.x, this.y); 437 | ctx.rotate(this.angle + Math.PI/2); 438 | ctx.translate(-this.x, -this.y); 439 | this.animation.drawFrame(this.game.clockTick, ctx, this.x, this.y); 440 | ctx.restore(); 441 | 442 | Entity.prototype.draw.call(this, ctx); 443 | } 444 | 445 | function BulletExplosion(game, x, y) { 446 | Entity.call(this, game, x, y); 447 | this.sprite = ASSET_MANAGER.getAsset('img/explosion.png'); 448 | this.animation = new Animation(this.sprite, 34, 0.05); 449 | this.radius = this.animation.frameWidth / 2; 450 | } 451 | BulletExplosion.prototype = new Entity(); 452 | BulletExplosion.prototype.constructor = BulletExplosion; 453 | 454 | BulletExplosion.prototype.update = function() { 455 | Entity.prototype.update.call(this); 456 | 457 | if (this.animation.isDone()) { 458 | this.removeFromWorld = true; 459 | return; 460 | } 461 | 462 | this.radius = (this.animation.frameWidth/2) * this.scaleFactor(); 463 | 464 | for (var i = 0; i < this.game.entities.length; i++) { 465 | var alien = this.game.entities[i]; 466 | if (alien instanceof Alien && this.isCaughtInExplosion(alien)) { 467 | console.log("hit!"); 468 | this.game.score += 10; 469 | alien.explode(); 470 | } 471 | } 472 | } 473 | 474 | BulletExplosion.prototype.isCaughtInExplosion = function(alien) { 475 | var distance_squared = (((this.x - alien.x) * (this.x - alien.x)) + ((this.y - alien.y) * (this.y - alien.y))); 476 | var radii_squared = (this.radius + alien.radius) * (this.radius + alien.radius); 477 | return distance_squared < radii_squared; 478 | } 479 | 480 | BulletExplosion.prototype.scaleFactor = function() { 481 | return 1 + (this.animation.currentFrame() / 3); 482 | } 483 | 484 | BulletExplosion.prototype.draw = function(ctx) { 485 | this.animation.drawFrame(this.game.clockTick, ctx, this.x, this.y, this.scaleFactor()); 486 | 487 | Entity.prototype.draw.call(this, ctx); 488 | } 489 | 490 | function Earth(game) { 491 | Entity.call(this, game, 0, 0); 492 | this.sprite = ASSET_MANAGER.getAsset('img/earth.png'); 493 | } 494 | Earth.prototype = new Entity(); 495 | Earth.prototype.constructor = Earth; 496 | 497 | Earth.RADIUS = 67; 498 | 499 | Earth.prototype.draw = function(ctx) { 500 | ctx.drawImage(this.sprite, this.x - this.sprite.width/2, this.y - this.sprite.height/2); 501 | } 502 | 503 | function EvilAliens() { 504 | GameEngine.call(this); 505 | //this.showOutlines = true; 506 | this.lives = 10; 507 | this.score = 0; 508 | } 509 | EvilAliens.prototype = new GameEngine(); 510 | EvilAliens.prototype.constructor = EvilAliens; 511 | 512 | EvilAliens.prototype.start = function() { 513 | this.sentry = new Sentry(this); 514 | this.earth = new Earth(this); 515 | this.addEntity(this.earth); 516 | this.addEntity(this.sentry); 517 | GameEngine.prototype.start.call(this); 518 | } 519 | 520 | EvilAliens.prototype.update = function() { 521 | if (this.lastAlienAddedAt == null || (this.timer.gameTime - this.lastAlienAddedAt) > 1) { 522 | this.addEntity(new Alien(this, this.ctx.canvas.width, Math.floor(Math.random() * Math.PI * 2))); 523 | this.lastAlienAddedAt = this.timer.gameTime; 524 | } 525 | 526 | if (this.score <= 0) { 527 | // show game over screen 528 | } 529 | 530 | GameEngine.prototype.update.call(this); 531 | } 532 | 533 | EvilAliens.prototype.draw = function() { 534 | GameEngine.prototype.draw.call(this, function(game) { 535 | game.drawScore(); 536 | game.drawLives(); 537 | }); 538 | } 539 | 540 | EvilAliens.prototype.drawLives = function() { 541 | this.ctx.fillStyle = "red"; 542 | this.ctx.font = "bold 2em Arial"; 543 | this.ctx.fillText("Lives: " + this.lives, -this.ctx.canvas.width/2 + 50, this.ctx.canvas.height/2 - 80); 544 | } 545 | 546 | EvilAliens.prototype.drawScore = function() { 547 | this.ctx.fillStyle = "red"; 548 | this.ctx.font = "bold 2em Arial"; 549 | this.ctx.fillText("Score: " + this.score, -this.ctx.canvas.width/2 + 50, this.ctx.canvas.height/2 - 50); 550 | } 551 | 552 | var canvas = document.getElementById('surface'); 553 | var ctx = canvas.getContext('2d'); 554 | var game = new EvilAliens(); 555 | var ASSET_MANAGER = new AssetManager(); 556 | 557 | ASSET_MANAGER.queueDownload('img/alien-explosion.png'); 558 | ASSET_MANAGER.queueDownload('img/alien.png'); 559 | ASSET_MANAGER.queueDownload('img/bullet.png'); 560 | ASSET_MANAGER.queueDownload('img/earth.png'); 561 | ASSET_MANAGER.queueDownload('img/sentry.png'); 562 | ASSET_MANAGER.queueDownload('img/explosion.png'); 563 | ASSET_MANAGER.queueSound('alien-boom', 'audio/alien_boom.mp3'); 564 | ASSET_MANAGER.queueSound('bullet-boom', 'audio/bullet_boom.mp3'); 565 | ASSET_MANAGER.queueSound('bullet', 'audio/bullet.mp3'); 566 | 567 | ASSET_MANAGER.downloadAll(function() { 568 | game.init(ctx); 569 | game.start(); 570 | }); -------------------------------------------------------------------------------- /static/phase13/scripts/app.js: -------------------------------------------------------------------------------- 1 | soundManager.url = 'swf/'; 2 | soundManager.flashVersion = 9; 3 | soundManager.debugFlash = false; 4 | soundManager.debugMode = false; 5 | 6 | window.requestAnimFrame = (function(){ 7 | return window.requestAnimationFrame || 8 | window.webkitRequestAnimationFrame || 9 | window.mozRequestAnimationFrame || 10 | window.oRequestAnimationFrame || 11 | window.msRequestAnimationFrame || 12 | function(/* function */ callback, /* DOMElement */ element){ 13 | window.setTimeout(callback, 1000 / 60); 14 | }; 15 | })(); 16 | 17 | function AssetManager() { 18 | this.successCount = 0; 19 | this.errorCount = 0; 20 | this.cache = {}; 21 | this.downloadQueue = []; 22 | this.soundsQueue = []; 23 | } 24 | 25 | AssetManager.prototype.queueDownload = function(path) { 26 | this.downloadQueue.push(path); 27 | } 28 | 29 | AssetManager.prototype.queueSound = function(id, path) { 30 | this.soundsQueue.push({id: id, path: path}); 31 | } 32 | 33 | AssetManager.prototype.downloadAll = function(callback) { 34 | if (this.downloadQueue.length === 0 && this.soundsQueue.length === 0) { 35 | callback(); 36 | } 37 | 38 | this.downloadSounds(callback); 39 | 40 | for (var i = 0; i < this.downloadQueue.length; i++) { 41 | var path = this.downloadQueue[i]; 42 | var img = new Image(); 43 | var that = this; 44 | img.addEventListener("load", function() { 45 | console.log(this.src + ' is loaded'); 46 | that.successCount += 1; 47 | if (that.isDone()) { 48 | callback(); 49 | } 50 | }, false); 51 | img.addEventListener("error", function() { 52 | that.errorCount += 1; 53 | if (that.isDone()) { 54 | callback(); 55 | } 56 | }, false); 57 | img.src = path; 58 | this.cache[path] = img; 59 | } 60 | } 61 | 62 | AssetManager.prototype.downloadSounds = function(callback) { 63 | var that = this; 64 | soundManager.onready(function() { 65 | console.log('soundManager ready'); 66 | for (var i = 0; i < that.soundsQueue.length; i++) { 67 | that.downloadSound(that.soundsQueue[i].id, that.soundsQueue[i].path, callback); 68 | } 69 | }); 70 | soundManager.ontimeout(function() { 71 | console.log('SM2 did not start'); 72 | }); 73 | } 74 | 75 | AssetManager.prototype.downloadSound = function(id, path, callback) { 76 | var that = this; 77 | this.cache[path] = soundManager.createSound({ 78 | id: id, 79 | autoLoad: true, 80 | url: path, 81 | onload: function() { 82 | console.log(this.url + ' is loaded'); 83 | that.successCount += 1; 84 | if (that.isDone()) { 85 | callback(); 86 | } 87 | } 88 | }); 89 | } 90 | 91 | AssetManager.prototype.getSound = function(path) { 92 | return this.cache[path]; 93 | } 94 | 95 | AssetManager.prototype.getAsset = function(path) { 96 | return this.cache[path]; 97 | } 98 | 99 | AssetManager.prototype.isDone = function() { 100 | return ((this.downloadQueue.length + this.soundsQueue.length) == this.successCount + this.errorCount); 101 | } 102 | 103 | function Animation(spriteSheet, frameWidth, frameDuration, loop) { 104 | this.spriteSheet = spriteSheet; 105 | this.frameWidth = frameWidth; 106 | this.frameDuration = frameDuration; 107 | this.frameHeight= this.spriteSheet.height; 108 | this.totalTime = (this.spriteSheet.width / this.frameWidth) * this.frameDuration; 109 | this.elapsedTime = 0; 110 | this.loop = loop; 111 | } 112 | 113 | Animation.prototype.drawFrame = function(tick, ctx, x, y, scaleBy) { 114 | var scaleBy = scaleBy || 1; 115 | this.elapsedTime += tick; 116 | if (this.loop) { 117 | if (this.isDone()) { 118 | this.elapsedTime = 0; 119 | } 120 | } else if (this.isDone()) { 121 | return; 122 | } 123 | var index = this.currentFrame(); 124 | var locX = x - (this.frameWidth/2) * scaleBy; 125 | var locY = y - (this.frameHeight/2) * scaleBy; 126 | ctx.drawImage(this.spriteSheet, 127 | index*this.frameWidth, 0, // source from sheet 128 | this.frameWidth, this.frameHeight, 129 | locX, locY, 130 | this.frameWidth*scaleBy, 131 | this.frameHeight*scaleBy); 132 | } 133 | 134 | Animation.prototype.currentFrame = function() { 135 | return Math.floor(this.elapsedTime / this.frameDuration); 136 | } 137 | 138 | Animation.prototype.isDone = function() { 139 | return (this.elapsedTime >= this.totalTime); 140 | } 141 | 142 | function Timer() { 143 | this.gameTime = 0; 144 | this.maxStep = 0.05; 145 | this.wallLastTimestamp = 0; 146 | } 147 | 148 | Timer.prototype.tick = function() { 149 | var wallCurrent = Date.now(); 150 | var wallDelta = (wallCurrent - this.wallLastTimestamp) / 1000; 151 | this.wallLastTimestamp = wallCurrent; 152 | 153 | var gameDelta = Math.min(wallDelta, this.maxStep); 154 | this.gameTime += gameDelta; 155 | return gameDelta; 156 | } 157 | 158 | function GameEngine() { 159 | this.entities = []; 160 | this.ctx = null; 161 | this.click = null; 162 | this.mouse = null; 163 | this.timer = new Timer(); 164 | this.stats = new Stats(); 165 | this.surfaceWidth = null; 166 | this.surfaceHeight = null; 167 | this.halfSurfaceWidth = null; 168 | this.halfSurfaceHeight = null; 169 | } 170 | 171 | GameEngine.prototype.init = function(ctx) { 172 | console.log('game initialized'); 173 | this.ctx = ctx; 174 | this.surfaceWidth = this.ctx.canvas.width; 175 | this.surfaceHeight = this.ctx.canvas.height; 176 | this.halfSurfaceWidth = this.surfaceWidth/2; 177 | this.halfSurfaceHeight = this.surfaceHeight/2; 178 | this.startInput(); 179 | document.body.appendChild(this.stats.domElement); 180 | } 181 | 182 | GameEngine.prototype.start = function() { 183 | console.log("starting game"); 184 | var that = this; 185 | (function gameLoop() { 186 | that.loop(); 187 | requestAnimFrame(gameLoop, that.ctx.canvas); 188 | })(); 189 | } 190 | 191 | GameEngine.prototype.startInput = function() { 192 | var getXandY = function(e) { 193 | var x = e.clientX - that.ctx.canvas.getBoundingClientRect().left - (that.ctx.canvas.width/2); 194 | var y = e.clientY - that.ctx.canvas.getBoundingClientRect().top - (that.ctx.canvas.height/2); 195 | return {x: x, y: y}; 196 | } 197 | 198 | var that = this; 199 | 200 | this.ctx.canvas.addEventListener("click", function(e) { 201 | that.click = getXandY(e); 202 | e.stopPropagation(); 203 | e.preventDefault(); 204 | }, false); 205 | 206 | this.ctx.canvas.addEventListener("mousemove", function(e) { 207 | that.mouse = getXandY(e); 208 | }, false); 209 | } 210 | 211 | GameEngine.prototype.addEntity = function(entity) { 212 | this.entities.push(entity); 213 | } 214 | 215 | GameEngine.prototype.draw = function(callback) { 216 | this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); 217 | this.ctx.save(); 218 | this.ctx.translate(this.ctx.canvas.width/2, this.ctx.canvas.height/2); 219 | for (var i = 0; i < this.entities.length; i++) { 220 | this.entities[i].draw(this.ctx); 221 | } 222 | if (callback) { 223 | callback(this); 224 | } 225 | this.ctx.restore(); 226 | } 227 | 228 | GameEngine.prototype.update = function() { 229 | var entitiesCount = this.entities.length; 230 | 231 | for (var i = 0; i < entitiesCount; i++) { 232 | var entity = this.entities[i]; 233 | 234 | if (!entity.removeFromWorld) { 235 | entity.update(); 236 | } 237 | } 238 | 239 | for (var i = this.entities.length-1; i >= 0; --i) { 240 | if (this.entities[i].removeFromWorld) { 241 | this.entities.splice(i, 1); 242 | } 243 | } 244 | } 245 | 246 | GameEngine.prototype.loop = function() { 247 | this.clockTick = this.timer.tick(); 248 | this.update(); 249 | this.draw(); 250 | this.click = null; 251 | this.stats.update(); 252 | } 253 | 254 | function Entity(game, x, y) { 255 | this.game = game; 256 | this.x = x; 257 | this.y = y; 258 | this.removeFromWorld = false; 259 | } 260 | 261 | Entity.prototype.update = function() { 262 | } 263 | 264 | Entity.prototype.draw = function(ctx) { 265 | if (this.game.showOutlines && this.radius) { 266 | ctx.beginPath(); 267 | ctx.strokeStyle = "green"; 268 | ctx.arc(this.x, this.y, this.radius, 0, Math.PI*2, false); 269 | ctx.stroke(); 270 | ctx.closePath(); 271 | } 272 | } 273 | 274 | Entity.prototype.drawSpriteCentered = function(ctx) { 275 | if (this.sprite && this.x && this.y) { 276 | var x = this.x - this.sprite.width/2; 277 | var y = this.y - this.sprite.height/2; 278 | ctx.drawImage(this.sprite, x, y); 279 | } 280 | } 281 | 282 | Entity.prototype.outsideScreen = function() { 283 | return (this.x > this.game.halfSurfaceWidth || this.x < -(this.game.halfSurfaceWidth) || 284 | this.y > this.game.halfSurfaceHeight || this.y < -(this.game.halfSurfaceHeight)); 285 | } 286 | 287 | Entity.prototype.rotateAndCache = function(image, angle) { 288 | var offscreenCanvas = document.createElement('canvas'); 289 | var size = Math.max(image.width, image.height); 290 | offscreenCanvas.width = size; 291 | offscreenCanvas.height = size; 292 | var offscreenCtx = offscreenCanvas.getContext('2d'); 293 | offscreenCtx.save(); 294 | offscreenCtx.translate(size/2, size/2); 295 | offscreenCtx.rotate(angle + Math.PI/2); 296 | offscreenCtx.drawImage(image, -(image.width/2), -(image.height/2)); 297 | offscreenCtx.restore(); 298 | //offscreenCtx.strokeStyle = "red"; 299 | //offscreenCtx.strokeRect(0,0,size,size); 300 | return offscreenCanvas; 301 | } 302 | 303 | function Alien(game, radial_distance, angle) { 304 | Entity.call(this, game); 305 | this.radial_distance = radial_distance; 306 | this.angle = angle; 307 | this.speed = 100; 308 | this.sprite = this.rotateAndCache(ASSET_MANAGER.getAsset('img/alien.png'), this.angle); 309 | this.radius = this.sprite.height/2; 310 | this.setCoords(); 311 | } 312 | Alien.prototype = new Entity(); 313 | Alien.prototype.constructor = Alien; 314 | 315 | Alien.prototype.setCoords = function() { 316 | this.x = this.radial_distance * Math.cos(this.angle); 317 | this.y = this.radial_distance * Math.sin(this.angle); 318 | } 319 | 320 | Alien.prototype.update = function() { 321 | this.setCoords(); 322 | this.radial_distance -= this.speed * this.game.clockTick; 323 | 324 | if (this.hitPlanet()) { 325 | this.removeFromWorld = true; 326 | this.game.lives -= 1; 327 | } 328 | 329 | Entity.prototype.update.call(this); 330 | } 331 | 332 | Alien.prototype.hitPlanet = function() { 333 | var distance_squared = ((this.x * this.x) + (this.y * this.y)); 334 | var radii_squared = (this.radius + Earth.RADIUS) * (this.radius + Earth.RADIUS); 335 | return distance_squared < radii_squared; 336 | } 337 | 338 | Alien.prototype.draw = function(ctx) { 339 | this.drawSpriteCentered(ctx); 340 | 341 | Entity.prototype.draw.call(this, ctx); 342 | } 343 | 344 | Alien.prototype.explode = function() { 345 | this.removeFromWorld = true; 346 | this.game.addEntity(new AlienExplosion(this.game, this.x, this.y)); 347 | ASSET_MANAGER.getSound('audio/alien_boom.mp3').play(); 348 | } 349 | 350 | function AlienExplosion(game, x, y) { 351 | Entity.call(this, game, x, y); 352 | this.animation = new Animation(ASSET_MANAGER.getAsset('img/alien-explosion.png'), 69, 0.05); 353 | this.radius = this.animation.frameWidth / 2; 354 | } 355 | AlienExplosion.prototype = new Entity(); 356 | AlienExplosion.prototype.constructor = AlienExplosion; 357 | 358 | AlienExplosion.prototype.update = function() { 359 | Entity.prototype.update.call(this); 360 | if (this.animation.isDone()) { 361 | this.removeFromWorld = true; 362 | } 363 | } 364 | 365 | AlienExplosion.prototype.draw = function(ctx) { 366 | Entity.prototype.draw.call(this, ctx); 367 | this.animation.drawFrame(this.game.clockTick, ctx, this.x, this.y); 368 | } 369 | 370 | function Sentry(game) { 371 | this.distanceFromEarthCenter = 85; 372 | Entity.call(this, game, 0, this.distanceFromEarthCenter); 373 | this.sprite = ASSET_MANAGER.getAsset('img/sentry.png'); 374 | this.radius = this.sprite.width / 2; 375 | this.angle = 0; 376 | } 377 | Sentry.prototype = new Entity(); 378 | Sentry.prototype.constructor = Sentry; 379 | 380 | Sentry.prototype.update = function() { 381 | if (this.game.mouse) { 382 | this.angle = Math.atan2(this.game.mouse.y, this.game.mouse.x); 383 | if (this.angle < 0) { 384 | this.angle += Math.PI * 2; 385 | } 386 | this.x = (Math.cos(this.angle) * this.distanceFromEarthCenter); 387 | this.y = (Math.sin(this.angle) * this.distanceFromEarthCenter); 388 | } 389 | if (this.game.click) { 390 | this.shoot(); 391 | } 392 | Entity.prototype.update.call(this); 393 | } 394 | 395 | Sentry.prototype.draw = function(ctx) { 396 | ctx.save(); 397 | ctx.translate(this.x, this.y); 398 | ctx.rotate(this.angle + Math.PI/2); 399 | ctx.drawImage(this.sprite, -this.sprite.width/2, -this.sprite.height/2); 400 | ctx.restore(); 401 | 402 | Entity.prototype.draw.call(this, ctx); 403 | } 404 | 405 | Sentry.prototype.shoot = function() { 406 | var bullet = new Bullet(this.game, this.x, this.y, this.angle, this.game.click); 407 | this.game.addEntity(bullet); 408 | ASSET_MANAGER.getSound('audio/bullet.mp3').play(); 409 | } 410 | 411 | function Bullet(game, x, y, angle, explodesAt) { 412 | Entity.call(this, game, x, y); 413 | this.angle = angle; 414 | this.explodesAt = explodesAt; 415 | this.speed = 250; 416 | this.radial_distance = 95; 417 | this.sprite = ASSET_MANAGER.getAsset('img/bullet.png'); 418 | this.animation = new Animation(this.sprite, 7, 0.05, true); 419 | } 420 | Bullet.prototype = new Entity(); 421 | Bullet.prototype.constructor = Bullet; 422 | 423 | Bullet.prototype.update = function() { 424 | if (this.outsideScreen()) { 425 | this.removeFromWorld = true; 426 | } else if (Math.abs(this.x) >= Math.abs(this.explodesAt.x) || Math.abs(this.y) >= Math.abs(this.explodesAt.y)) { 427 | ASSET_MANAGER.getSound('audio/bullet_boom.mp3').play(); 428 | this.game.addEntity(new BulletExplosion(this.game, this.explodesAt.x, this.explodesAt.y)); 429 | this.removeFromWorld = true; 430 | } else { 431 | this.x = this.radial_distance * Math.cos(this.angle); 432 | this.y = this.radial_distance * Math.sin(this.angle); 433 | this.radial_distance += this.speed * this.game.clockTick; 434 | } 435 | } 436 | 437 | Bullet.prototype.draw = function(ctx) { 438 | ctx.save(); 439 | ctx.translate(this.x, this.y); 440 | ctx.rotate(this.angle + Math.PI/2); 441 | ctx.translate(-this.x, -this.y); 442 | this.animation.drawFrame(this.game.clockTick, ctx, this.x, this.y); 443 | ctx.restore(); 444 | 445 | Entity.prototype.draw.call(this, ctx); 446 | } 447 | 448 | function BulletExplosion(game, x, y) { 449 | Entity.call(this, game, x, y); 450 | this.sprite = ASSET_MANAGER.getAsset('img/explosion.png'); 451 | this.animation = new Animation(this.sprite, 34, 0.05); 452 | this.radius = this.animation.frameWidth / 2; 453 | } 454 | BulletExplosion.prototype = new Entity(); 455 | BulletExplosion.prototype.constructor = BulletExplosion; 456 | 457 | BulletExplosion.prototype.update = function() { 458 | Entity.prototype.update.call(this); 459 | 460 | if (this.animation.isDone()) { 461 | this.removeFromWorld = true; 462 | return; 463 | } 464 | 465 | this.radius = (this.animation.frameWidth/2) * this.scaleFactor(); 466 | 467 | for (var i = 0; i < this.game.entities.length; i++) { 468 | var alien = this.game.entities[i]; 469 | if (alien instanceof Alien && this.isCaughtInExplosion(alien)) { 470 | console.log("hit!"); 471 | this.game.score += 10; 472 | alien.explode(); 473 | } 474 | } 475 | } 476 | 477 | BulletExplosion.prototype.isCaughtInExplosion = function(alien) { 478 | var distance_squared = (((this.x - alien.x) * (this.x - alien.x)) + ((this.y - alien.y) * (this.y - alien.y))); 479 | var radii_squared = (this.radius + alien.radius) * (this.radius + alien.radius); 480 | return distance_squared < radii_squared; 481 | } 482 | 483 | BulletExplosion.prototype.scaleFactor = function() { 484 | return 1 + (this.animation.currentFrame() / 3); 485 | } 486 | 487 | BulletExplosion.prototype.draw = function(ctx) { 488 | this.animation.drawFrame(this.game.clockTick, ctx, this.x, this.y, this.scaleFactor()); 489 | 490 | Entity.prototype.draw.call(this, ctx); 491 | } 492 | 493 | function Earth(game) { 494 | Entity.call(this, game, 0, 0); 495 | this.sprite = ASSET_MANAGER.getAsset('img/earth.png'); 496 | } 497 | Earth.prototype = new Entity(); 498 | Earth.prototype.constructor = Earth; 499 | 500 | Earth.RADIUS = 67; 501 | 502 | Earth.prototype.draw = function(ctx) { 503 | ctx.drawImage(this.sprite, this.x - this.sprite.width/2, this.y - this.sprite.height/2); 504 | } 505 | 506 | function EvilAliens() { 507 | GameEngine.call(this); 508 | //this.showOutlines = true; 509 | this.lives = 10; 510 | this.score = 0; 511 | } 512 | EvilAliens.prototype = new GameEngine(); 513 | EvilAliens.prototype.constructor = EvilAliens; 514 | 515 | EvilAliens.prototype.start = function() { 516 | this.sentry = new Sentry(this); 517 | this.earth = new Earth(this); 518 | this.addEntity(this.earth); 519 | this.addEntity(this.sentry); 520 | GameEngine.prototype.start.call(this); 521 | } 522 | 523 | EvilAliens.prototype.update = function() { 524 | if (this.lastAlienAddedAt == null || (this.timer.gameTime - this.lastAlienAddedAt) > 1) { 525 | this.addEntity(new Alien(this, this.ctx.canvas.width, Math.floor(Math.random() * Math.PI * 2))); 526 | this.lastAlienAddedAt = this.timer.gameTime; 527 | } 528 | 529 | if (this.score <= 0) { 530 | // show game over screen 531 | } 532 | 533 | GameEngine.prototype.update.call(this); 534 | } 535 | 536 | EvilAliens.prototype.draw = function() { 537 | GameEngine.prototype.draw.call(this, function(game) { 538 | game.drawScore(); 539 | game.drawLives(); 540 | }); 541 | } 542 | 543 | EvilAliens.prototype.drawLives = function() { 544 | this.ctx.fillStyle = "red"; 545 | this.ctx.font = "bold 2em Arial"; 546 | this.ctx.fillText("Lives: " + this.lives, -this.ctx.canvas.width/2 + 50, this.ctx.canvas.height/2 - 80); 547 | } 548 | 549 | EvilAliens.prototype.drawScore = function() { 550 | this.ctx.fillStyle = "red"; 551 | this.ctx.font = "bold 2em Arial"; 552 | this.ctx.fillText("Score: " + this.score, -this.ctx.canvas.width/2 + 50, this.ctx.canvas.height/2 - 50); 553 | } 554 | 555 | var canvas = document.getElementById('surface'); 556 | var ctx = canvas.getContext('2d'); 557 | var game = new EvilAliens(); 558 | var ASSET_MANAGER = new AssetManager(); 559 | 560 | ASSET_MANAGER.queueDownload('img/alien-explosion.png'); 561 | ASSET_MANAGER.queueDownload('img/alien.png'); 562 | ASSET_MANAGER.queueDownload('img/bullet.png'); 563 | ASSET_MANAGER.queueDownload('img/earth.png'); 564 | ASSET_MANAGER.queueDownload('img/sentry.png'); 565 | ASSET_MANAGER.queueDownload('img/explosion.png'); 566 | ASSET_MANAGER.queueSound('alien-boom', 'audio/alien_boom.mp3'); 567 | ASSET_MANAGER.queueSound('bullet-boom', 'audio/bullet_boom.mp3'); 568 | ASSET_MANAGER.queueSound('bullet', 'audio/bullet.mp3'); 569 | 570 | ASSET_MANAGER.downloadAll(function() { 571 | game.init(ctx); 572 | game.start(); 573 | }); --------------------------------------------------------------------------------