diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..0181af3 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +max_line_length = 140 +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +max_line_length = off +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..af650cb --- /dev/null +++ b/.gitattributes @@ -0,0 +1,182 @@ +# https://html5boilerplate.com + +## AUTO-DETECT +* text=auto + +## SOURCE CODE +*.bat text eol=crlf +*.coffee text +*.css text +*.htm text +*.html text +*.inc text +*.ini text +*.js text +*.json text +*.jsx text +*.less text +*.od text +*.onlydata text +*.php text +*.pl text +*.py text +*.rb text +*.sass text +*.scm text +*.scss text +*.sh text eol=lf +*.sql text +*.styl text +*.tag text +*.ts text +*.tsx text +*.xml text +*.xhtml text + +## DOCKER +*.dockerignore text +Dockerfile text + +## DOCUMENTATION +*.markdown text +*.md text +*.mdwn text +*.mdown text +*.mkd text +*.mkdn text +*.mdtxt text +*.mdtext text +*.txt text +AUTHORS text +CHANGELOG text +CHANGES text +CONTRIBUTING text +COPYING text +copyright text +*COPYRIGHT* text +INSTALL text +license text +LICENSE text +NEWS text +readme text +*README* text +TODO text + +## TEMPLATES +*.dot text +*.ejs text +*.haml text +*.handlebars text +*.hbs text +*.hbt text +*.jade text +*.latte text +*.mustache text +*.njk text +*.phtml text +*.tmpl text +*.tpl text +*.twig text + +## LINTERS +.babelrc text +.csslintrc text +.eslintrc text +.htmlhintrc text +.jscsrc text +.jshintrc text +.jshintignore text +.prettierrc text +.stylelintrc text + +## CONFIGS +*.bowerrc text +*.cnf text +*.conf text +*.config text +.browserslistrc text +.editorconfig text +.gitattributes text +.gitconfig text +.gitignore text +.htaccess text +*.npmignore text +*.yaml text +*.yml text +browserslist text +Makefile text +makefile text + +## HEROKU +Procfile text +.slugignore text + +## GRAPHICS +*.ai binary +*.bmp binary +*.eps binary +*.gif binary +*.ico binary +*.jng binary +*.jp2 binary +*.jpg binary +*.jpeg binary +*.jpx binary +*.jxr binary +*.pdf binary +*.png binary +*.psb binary +*.psd binary +*.svg text +*.svgz binary +*.tif binary +*.tiff binary +*.wbmp binary +*.webp binary + +## AUDIO +*.kar binary +*.m4a binary +*.mid binary +*.midi binary +*.mp3 binary +*.ogg binary +*.ra binary + +## VIDEO +*.3gpp binary +*.3gp binary +*.as binary +*.asf binary +*.asx binary +*.fla binary +*.flv binary +*.m4v binary +*.mng binary +*.mov binary +*.mp4 binary +*.mpeg binary +*.mpg binary +*.ogv binary +*.swc binary +*.swf binary +*.webm binary + +## ARCHIVES +*.7z binary +*.gz binary +*.jar binary +*.rar binary +*.tar binary +*.zip binary + +## FONTS +*.ttf binary +*.eot binary +*.otf binary +*.woff binary +*.woff2 binary + +## EXECUTABLES +*.exe binary +*.pyc binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3ef4f30 --- /dev/null +++ b/.gitignore @@ -0,0 +1,48 @@ +# Compiled output +/build +/dist +/documentation +/out-tsc +/tmp + +# Composer +vendor +composer.lock + +# Dependencies +/node_modules +package-lock.json + +# IDEs + Editors +*.launch +*.sublime-workspace +/.idea +.c9 +.classpath +.history +.project +.settings + +# IDE - VSCode +.vscode/* +!.vscode/extensions.json +!.vscode/launch.json +!.vscode/settings.json +!.vscode/tasks.json + +# Misc +/.sass-cache +/connect.lock +/coverage +/libpeerconnection.log +/typings +npm-debug.log +php_errorlog +testem.log +yarn-error.log + +# System files +*/*.lnk +*desktop.ini +.DS_Store +Thumbs.db diff --git a/.markdownlint.json b/.markdownlint.json new file mode 100644 index 0000000..7e9c2ac --- /dev/null +++ b/.markdownlint.json @@ -0,0 +1,5 @@ +{ + "first-line-heading": false, + "line-length": false, + "no-inline-html": false +} diff --git a/Historical-Release 2013-2019.zip b/Historical-Release 2013-2019.zip deleted file mode 100644 index e417728..0000000 Binary files a/Historical-Release 2013-2019.zip and /dev/null differ diff --git a/src-2013/.editorconfig b/src-2013/.editorconfig new file mode 100755 index 0000000..21881c4 --- /dev/null +++ b/src-2013/.editorconfig @@ -0,0 +1,15 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +max_line_length = 140 +indent_size = 2 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +max_line_length = off +trim_trailing_whitespace = false + diff --git a/src-2013/Assets/Audio/EngineThruster.ogg b/src-2013/Assets/Audio/EngineThruster.ogg new file mode 100755 index 0000000..8370995 Binary files /dev/null and b/src-2013/Assets/Audio/EngineThruster.ogg differ diff --git a/src-2013/Assets/Audio/EngineThruster.wav b/src-2013/Assets/Audio/EngineThruster.wav new file mode 100755 index 0000000..9f29fb0 Binary files /dev/null and b/src-2013/Assets/Audio/EngineThruster.wav differ diff --git a/src-2013/Assets/Audio/Explosion-1.ogg b/src-2013/Assets/Audio/Explosion-1.ogg new file mode 100755 index 0000000..955dc9a Binary files /dev/null and b/src-2013/Assets/Audio/Explosion-1.ogg differ diff --git a/src-2013/Assets/Audio/Explosion-1.wav b/src-2013/Assets/Audio/Explosion-1.wav new file mode 100755 index 0000000..a0e9c03 Binary files /dev/null and b/src-2013/Assets/Audio/Explosion-1.wav differ diff --git a/src-2013/Assets/Audio/Explosion-2.ogg b/src-2013/Assets/Audio/Explosion-2.ogg new file mode 100755 index 0000000..12f248f Binary files /dev/null and b/src-2013/Assets/Audio/Explosion-2.ogg differ diff --git a/src-2013/Assets/Audio/Explosion-2.wav b/src-2013/Assets/Audio/Explosion-2.wav new file mode 100755 index 0000000..4d06397 Binary files /dev/null and b/src-2013/Assets/Audio/Explosion-2.wav differ diff --git a/src-2013/Assets/Audio/FunctionBlocked.ogg b/src-2013/Assets/Audio/FunctionBlocked.ogg new file mode 100755 index 0000000..7d9d2f7 Binary files /dev/null and b/src-2013/Assets/Audio/FunctionBlocked.ogg differ diff --git a/src-2013/Assets/Audio/FunctionBlocked.wav b/src-2013/Assets/Audio/FunctionBlocked.wav new file mode 100755 index 0000000..5797320 Binary files /dev/null and b/src-2013/Assets/Audio/FunctionBlocked.wav differ diff --git a/src-2013/Assets/Audio/Laser-1.ogg b/src-2013/Assets/Audio/Laser-1.ogg new file mode 100755 index 0000000..b3b8f85 Binary files /dev/null and b/src-2013/Assets/Audio/Laser-1.ogg differ diff --git a/src-2013/Assets/Audio/Laser-1.wav b/src-2013/Assets/Audio/Laser-1.wav new file mode 100755 index 0000000..466df6c Binary files /dev/null and b/src-2013/Assets/Audio/Laser-1.wav differ diff --git a/src-2013/Assets/Audio/Laser-2.ogg b/src-2013/Assets/Audio/Laser-2.ogg new file mode 100755 index 0000000..1012a69 Binary files /dev/null and b/src-2013/Assets/Audio/Laser-2.ogg differ diff --git a/src-2013/Assets/Audio/Laser-2.wav b/src-2013/Assets/Audio/Laser-2.wav new file mode 100755 index 0000000..5b268d5 Binary files /dev/null and b/src-2013/Assets/Audio/Laser-2.wav differ diff --git a/src-2013/Assets/Audio/LaserReady.ogg b/src-2013/Assets/Audio/LaserReady.ogg new file mode 100755 index 0000000..85599d2 Binary files /dev/null and b/src-2013/Assets/Audio/LaserReady.ogg differ diff --git a/src-2013/Assets/Audio/LaserReady.wav b/src-2013/Assets/Audio/LaserReady.wav new file mode 100755 index 0000000..369cac8 Binary files /dev/null and b/src-2013/Assets/Audio/LaserReady.wav differ diff --git a/src-2013/Assets/Audio/Music/DG-Space-Ambient.ogg b/src-2013/Assets/Audio/Music/DG-Space-Ambient.ogg new file mode 100755 index 0000000..aba7e5b Binary files /dev/null and b/src-2013/Assets/Audio/Music/DG-Space-Ambient.ogg differ diff --git a/src-2013/Assets/Audio/Music/DG-Space-Ambient.wav b/src-2013/Assets/Audio/Music/DG-Space-Ambient.wav new file mode 100755 index 0000000..bc3d1a1 Binary files /dev/null and b/src-2013/Assets/Audio/Music/DG-Space-Ambient.wav differ diff --git a/src-2013/Assets/Audio/ShieldCharge.ogg b/src-2013/Assets/Audio/ShieldCharge.ogg new file mode 100755 index 0000000..ead5725 Binary files /dev/null and b/src-2013/Assets/Audio/ShieldCharge.ogg differ diff --git a/src-2013/Assets/Audio/ShieldCharge.wav b/src-2013/Assets/Audio/ShieldCharge.wav new file mode 100755 index 0000000..f191860 Binary files /dev/null and b/src-2013/Assets/Audio/ShieldCharge.wav differ diff --git a/src-2013/Assets/Audio/ShieldEnabled.ogg b/src-2013/Assets/Audio/ShieldEnabled.ogg new file mode 100755 index 0000000..7eefce0 Binary files /dev/null and b/src-2013/Assets/Audio/ShieldEnabled.ogg differ diff --git a/src-2013/Assets/Audio/ShieldEnabled.wav b/src-2013/Assets/Audio/ShieldEnabled.wav new file mode 100755 index 0000000..9446774 Binary files /dev/null and b/src-2013/Assets/Audio/ShieldEnabled.wav differ diff --git a/src-2013/Assets/Image/Background/0.jpg b/src-2013/Assets/Image/Background/0.jpg new file mode 100755 index 0000000..ae9e213 Binary files /dev/null and b/src-2013/Assets/Image/Background/0.jpg differ diff --git a/src-2013/Assets/Image/Background/1.jpg b/src-2013/Assets/Image/Background/1.jpg new file mode 100755 index 0000000..6311ce6 Binary files /dev/null and b/src-2013/Assets/Image/Background/1.jpg differ diff --git a/src-2013/Assets/Image/Background/2.jpg b/src-2013/Assets/Image/Background/2.jpg new file mode 100755 index 0000000..ed49f54 Binary files /dev/null and b/src-2013/Assets/Image/Background/2.jpg differ diff --git a/src-2013/Assets/Image/Background/3.jpg b/src-2013/Assets/Image/Background/3.jpg new file mode 100755 index 0000000..03e9eda Binary files /dev/null and b/src-2013/Assets/Image/Background/3.jpg differ diff --git a/src-2013/Assets/Image/Background/4.jpg b/src-2013/Assets/Image/Background/4.jpg new file mode 100755 index 0000000..e25a471 Binary files /dev/null and b/src-2013/Assets/Image/Background/4.jpg differ diff --git a/src-2013/Assets/Image/Background/Static/0.jpg b/src-2013/Assets/Image/Background/Static/0.jpg new file mode 100755 index 0000000..61831d3 Binary files /dev/null and b/src-2013/Assets/Image/Background/Static/0.jpg differ diff --git a/src-2013/Assets/Image/Background/Static/1.jpg b/src-2013/Assets/Image/Background/Static/1.jpg new file mode 100755 index 0000000..ced489a Binary files /dev/null and b/src-2013/Assets/Image/Background/Static/1.jpg differ diff --git a/src-2013/Assets/Image/Background/Static/2.jpg b/src-2013/Assets/Image/Background/Static/2.jpg new file mode 100755 index 0000000..3307eb8 Binary files /dev/null and b/src-2013/Assets/Image/Background/Static/2.jpg differ diff --git a/src-2013/Assets/Image/Background/Static/3.jpg b/src-2013/Assets/Image/Background/Static/3.jpg new file mode 100755 index 0000000..6c12b54 Binary files /dev/null and b/src-2013/Assets/Image/Background/Static/3.jpg differ diff --git a/src-2013/Assets/Image/Foreground/0.png b/src-2013/Assets/Image/Foreground/0.png new file mode 100755 index 0000000..0d50fab Binary files /dev/null and b/src-2013/Assets/Image/Foreground/0.png differ diff --git a/src-2013/Assets/Image/Foreground/1.png b/src-2013/Assets/Image/Foreground/1.png new file mode 100755 index 0000000..89c490d Binary files /dev/null and b/src-2013/Assets/Image/Foreground/1.png differ diff --git a/src-2013/Assets/Image/Foreground/2.png b/src-2013/Assets/Image/Foreground/2.png new file mode 100755 index 0000000..c83a612 Binary files /dev/null and b/src-2013/Assets/Image/Foreground/2.png differ diff --git a/src-2013/Assets/Image/Foreground/3.png b/src-2013/Assets/Image/Foreground/3.png new file mode 100755 index 0000000..9cf3d60 Binary files /dev/null and b/src-2013/Assets/Image/Foreground/3.png differ diff --git a/src-2013/Assets/Image/Foreground/4.png b/src-2013/Assets/Image/Foreground/4.png new file mode 100755 index 0000000..b67314e Binary files /dev/null and b/src-2013/Assets/Image/Foreground/4.png differ diff --git a/src-2013/Assets/Image/Foreground/5.png b/src-2013/Assets/Image/Foreground/5.png new file mode 100755 index 0000000..ec42e1f Binary files /dev/null and b/src-2013/Assets/Image/Foreground/5.png differ diff --git a/src-2013/Assets/Image/Foreground/6.png b/src-2013/Assets/Image/Foreground/6.png new file mode 100755 index 0000000..1b195c4 Binary files /dev/null and b/src-2013/Assets/Image/Foreground/6.png differ diff --git a/src-2013/Assets/Image/Hud/Ship.png b/src-2013/Assets/Image/Hud/Ship.png new file mode 100755 index 0000000..924904b Binary files /dev/null and b/src-2013/Assets/Image/Hud/Ship.png differ diff --git a/src-2013/Assets/Image/Object/Particle.png b/src-2013/Assets/Image/Object/Particle.png new file mode 100755 index 0000000..2aa6d49 Binary files /dev/null and b/src-2013/Assets/Image/Object/Particle.png differ diff --git a/src-2013/Assets/Image/Object/Shield0.png b/src-2013/Assets/Image/Object/Shield0.png new file mode 100755 index 0000000..840e3b0 Binary files /dev/null and b/src-2013/Assets/Image/Object/Shield0.png differ diff --git a/src-2013/Assets/Image/Object/Shield1.png b/src-2013/Assets/Image/Object/Shield1.png new file mode 100755 index 0000000..1eff34b Binary files /dev/null and b/src-2013/Assets/Image/Object/Shield1.png differ diff --git a/src-2013/Assets/Image/Object/Ship.png b/src-2013/Assets/Image/Object/Ship.png new file mode 100755 index 0000000..2d6596f Binary files /dev/null and b/src-2013/Assets/Image/Object/Ship.png differ diff --git a/src-2013/Assets/Image/Object/ShipShielded.png b/src-2013/Assets/Image/Object/ShipShielded.png new file mode 100755 index 0000000..bb53def Binary files /dev/null and b/src-2013/Assets/Image/Object/ShipShielded.png differ diff --git a/src-2013/Assets/Image/Object/Weapon/Laser.png b/src-2013/Assets/Image/Object/Weapon/Laser.png new file mode 100755 index 0000000..a92bbf9 Binary files /dev/null and b/src-2013/Assets/Image/Object/Weapon/Laser.png differ diff --git a/src-2013/Assets/Image/Ship-Unused.png b/src-2013/Assets/Image/Ship-Unused.png new file mode 100755 index 0000000..35c2832 Binary files /dev/null and b/src-2013/Assets/Image/Ship-Unused.png differ diff --git a/src-2013/Assets/Video/Shield.mp4 b/src-2013/Assets/Video/Shield.mp4 new file mode 100755 index 0000000..c160712 Binary files /dev/null and b/src-2013/Assets/Video/Shield.mp4 differ diff --git a/src-2013/Assets/Video/Shield.ogv b/src-2013/Assets/Video/Shield.ogv new file mode 100755 index 0000000..c494dcb Binary files /dev/null and b/src-2013/Assets/Video/Shield.ogv differ diff --git a/src-2013/Assets/Video/Shield.webm b/src-2013/Assets/Video/Shield.webm new file mode 100755 index 0000000..8675366 Binary files /dev/null and b/src-2013/Assets/Video/Shield.webm differ diff --git a/src-2013/Css/Content.css b/src-2013/Css/Content.css new file mode 100755 index 0000000..0cc4784 --- /dev/null +++ b/src-2013/Css/Content.css @@ -0,0 +1,31 @@ +/* ---------------------------------------------------------------------------------------------------------- Content */ + + +p { + color: #333; + font-size: 0.75rem; + font-family: sans-serif; + display: inline-block; + margin: 5px 0; +} + +.tip { + color: #222; + display: inline-block; + float: right; +} + +.hidden { + display: none; +} + + +::-moz-selection { + color: #666; + background: #222; +} + +::selection { + color: #666; + background: #222; +} diff --git a/src-2013/Css/Layout.css b/src-2013/Css/Layout.css new file mode 100755 index 0000000..8ec5841 --- /dev/null +++ b/src-2013/Css/Layout.css @@ -0,0 +1,164 @@ +/* ----------------------------------------------------------------------------------------------------------- Layout */ + +html, +body { + background: #000; +} + +section#GameHolder { + margin: 10vh auto 0; + position: relative; + width: 800px; + height: 500px; + overflow: hidden; + border: 1px solid #222; +} + +section#Help { + margin: 0 auto; + width: 800px; +} + +#GameHolder canvas { + position: absolute; + left: 0; + top: 0; +} + +#Assets { + display: none; +} + + +.static-image #Background { + display: none; +} + +.static-image { + /* 5 min */ + animation-duration: 300s; + animation-name: move-bg; + animation-timing-function: ease-in-out; + animation-iteration-count: infinite; +} + +.static-image-0 { background: url("https://christianoellers.github.io/HTML5-Canvas--2D-Space-Shooter-Game/Assets/Image/Background/Static/0.jpg") 0% 0%; } +.static-image-1 { background: url("https://christianoellers.github.io/HTML5-Canvas--2D-Space-Shooter-Game/Assets/Image/Background/Static/1.jpg") 0% 0%; } +.static-image-2 { background: url("https://christianoellers.github.io/HTML5-Canvas--2D-Space-Shooter-Game/Assets/Image/Background/Static/2.jpg") 0% 0%; } +.static-image-3 { background: url("https://christianoellers.github.io/HTML5-Canvas--2D-Space-Shooter-Game/Assets/Image/Background/Static/3.jpg") 0% 0%; } + +@-webkit-keyframes move-bg { + 0% { + background-position: 0% 0% + } + 25% { + background-position: 100% 100% + } + 50% { + background-position: 50% 50% + } + 75% { + background-position: 100% 0% + } + 100% { + background-position: 0% 0% + } +} + + +/* --------------------------------------------------------------------------- Temp / Dev content */ + +.video-rendering { + margin: 50px 0; + display: block; + position: relative; + opacity: 0.2; +} + +.video-rendering:hover { + opacity: 1; +} + +.tmp { + border: 1px solid #222; +} + +#VideoShield { + position: relative; + display: inline-block; +} + + +/* -------------------------------------------------------------------------------------------------------- Animation */ +/* -------------------------------------------------------------------------------------- Fade in */ + +#Fade { + background: #000; + position: absolute; + left: 0; + top: 0; + width: 800px; + height: 500px; + opacity: 1; + animation-name: animateFade; + animation-delay: 0s; + animation-duration: 1s; + animation-iteration-count: 1; + animation-timing-function: ease-in-out; + animation-direction: alternate; + animation-fill-mode: forwards; + animation-play-state: running; +} + +@keyframes animateFade { + 0% { + background: rgba(0, 0, 0, 1); + } + 100% { + background: rgba(0, 0, 0, 0); + } +} + + +/* --------------------------------------------------------------------------------- Screen shake */ + +#Fx { + position: absolute; + left: 0; + top: 0; +} + +#Fx.shake { + left: 0px; + top: 0px; + animation-name: animateShakeFx; + animation-duration: 1s; + animation-iteration-count: 1; + animation-timing-function: ease-in-out; + animation-play-state: running; +} + +@keyframes animateShakeFx { + 0% { left: 0px; top: 0px; } + 5% { left: -1px; top: -1px; } + 10% { left: -1px; top: 1px; } + 15% { left: 2px; top: -1px; } + 20% { left: 1px; top: 1px; } + 25% { left: 1px; top: 1px; } + 30% { left: -1px; top: 2px; } + 35% { left: 2px; top: 0px; } + 40% { left: 1px; top: -1px; } + 45% { left: -1px; top: 1px; } + 50% { left: -1px; top: 0px; } + 55% { left: 2px; top: -1px; } + 60% { left: 0px; top: 0px; } + 65% { left: 1px; top: -1px; } + 70% { left: -1px; top: 1px; } + 75% { left: 0px; top: 2px; } + 80% { left: 0px; top: -1px; } + 85% { left: 2px; top: 1px; } + 90% { left: -1px; top: 0px; } + 95% { left: 1px; top: 0px; } + 100% { left: 0px; top: 0px; } +} + diff --git a/src-2013/Css/Reset.css b/src-2013/Css/Reset.css new file mode 100755 index 0000000..78bf933 --- /dev/null +++ b/src-2013/Css/Reset.css @@ -0,0 +1,103 @@ +/* ------------------------------------------------------------------------------------------------------------ (all) */ + +* { + margin:0; + padding:0; + -ms-text-size-adjust:100%; + -webkit-text-size-adjust:100%; + font-size:100%; + font-family:inherit; + font-weight:inherit; + font-style:inherit; + vertical-align:baseline; + -webkit-tap-highlight-color:rgba(0, 0, 0, 0); + -webkit-touch-callout:none; + -moz-box-sizing:border-box; + -ms-box-sizing:border-box; + -webkit-box-sizing:border-box; + box-sizing:border-box; +} + +body { + text-rendering:optimizeLegibility; +} + + +/* ---------------------------------------------------------------------------------------------------------- Content */ + +header, nav, section, +article, footer { + display:block; +} + +figure, video, audio { + display:block; +} + +iframe, img { + border:none; +} + +/** / +iframe::-webkit-scrollbar { + display:none; +} +/**/ + +a { + text-decoration:none; +} + +:focus { + outline:none; +} + + +/* ------------------------------------------------------------------------------------------------------------- Form */ + +form, fieldset, button, input, +textarea, select, option { + background-color:transparent; + border:none; +} + +textarea { + overflow:auto; + vertical-align:top; +} + +button, input, select, textarea { + font-size:100%; + vertical-align:baseline; + *vertical-align:middle; +} + +button, input { + line-height:normal; +} + +button, +input[type="button"], +input[type="reset"], +input[type="submit"] { + cursor:pointer; + -webkit-appearance:button; + *overflow:visible; +} + +input[type="search"], +input[type="search"]::-webkit-search-decoration { + -webkit-appearance:textfield; +} + +input[type="checkbox"], +input[type="radio"] { + padding:0; +} + +input::-moz-focus-inner, +button::-moz-focus-inner { + border:0; + padding:0; +} + diff --git a/src-2013/Js/Core/Fx.js b/src-2013/Js/Core/Fx.js new file mode 100755 index 0000000..70f254b --- /dev/null +++ b/src-2013/Js/Core/Fx.js @@ -0,0 +1,49 @@ +/** + * Game effects. + * + * @todo Improve method consistency by using events only. + * @module + */ +function Core_Fx () { + var self = this, + FxHolder = document.getElementById('Fx'); + + + // ------------------------------------------------------------------------------------------------------ Initialize + + /** + * Initialize module and add public events. + * + * @public + */ + this.init = function init () { + window.addEventListener('ShakeFx', this.onShakeFx); + }; + + + // ----------------------------------------------------------------------------------------------------------- Reset + + /** + * Remove CSS class to allow for + * a new CSS screen shake animation. + * + * @public + */ + this.reset = function reset () { + FxHolder.setAttribute('class', ''); + }; + + + // ---------------------------------------------------------------------------------------------------------- Events + + /** + * Start screen shake CSS animation. + * + * @private + */ + this.onShakeFx = function onShakeFx (event) { + FxHolder.setAttribute('class', 'shake'); + }; + +} + diff --git a/src-2013/Js/Core/Hud.js b/src-2013/Js/Core/Hud.js new file mode 100755 index 0000000..44fa658 --- /dev/null +++ b/src-2013/Js/Core/Hud.js @@ -0,0 +1,94 @@ +/** + * Game HUD. + * + * @module + */ +function Core_Hud () { + var self = this; + + this.spriteHudShip = document.getElementById('Asset_HudShip'); + this.canvas = document.getElementById('Hud'); + this.ctx = this.canvas.getContext('2d'); + + this.isUpdating = false; // Prevent parallel updates of the game. + this.hudBaseOpacity = 0.8; // 0-1; 1 = 100% opacity + + + // ------------------------------------------------------------------------------------------------------ Initialize + + /** + * Initialize module. + * + * @see Core_Stage + * @public + */ + this.init = function init () { + this.drawUpdate(); + + // Todo: Find a better way to handle external dependencies/events + window.addEventListener('PlayerShieldActive', this.onPlayerShieldActive); + }; + + + // ---------------------------------------------------------------------------------------------------------- Events + + /** + * Start screen shake CSS animation. + * + * @private + */ + this.onPlayerShieldActive = function onPlayerShieldActive (event) { + if (event.detail.active) { + self.hudBaseOpacity = 0.2 + Math.random() / 5; + } + else { + self.hudBaseOpacity = 0.8; + } + }; + + + // ------------------------------------------------------------------------------------------------------- Game loop + + /** + * Game loop extension. + * + * @see Core_Loop + * @public + */ + this.loop = function loop () { + if (!self.isUpdating) { + self.isUpdating = true; + self.drawUpdate(); + } + }; + + + // ------------------------------------------------------------------------------------------------------------ Draw + + /** + * Draw a slightly flickering HUD. + * + * @todo Increase intensity if ship is hit by an enemy; reduce with shields enabled (by sine curve). + * @private + */ + this.drawUpdate = function drawUpdate () { + var ctx = self.ctx, + canvas = self.canvas, + rnd = Math.random() * 0.75; + + CanvasHelper.clear(self.ctx); + ctx.save(); + + ctx.globalAlpha = self.hudBaseOpacity + rnd; + ctx.drawImage( + self.spriteHudShip, + canvas.width - 50 + rnd, + canvas.height - 90 + rnd + ); + + ctx.restore(); + self.isUpdating = false; + }; + +} + diff --git a/src-2013/Js/Core/Loop.js b/src-2013/Js/Core/Loop.js new file mode 100755 index 0000000..02f620f --- /dev/null +++ b/src-2013/Js/Core/Loop.js @@ -0,0 +1,30 @@ +/** + * Main loop for every frame. + * + * @todo Decouple Stage and Loop modules. + * @module + */ +function Core_Loop () { + var self = this; + + /** + * Infinite game loop that will also + * loop through all game objects on stage. + * + * @public + */ + this.run = function run () { + if (STOP) { + delete Core_Loop; + return; + } + + for (obj in Stage.objects) { + Stage.objects[obj].loop(); + } + + requestAnimFrame(self.run); + }; + +} + diff --git a/src-2013/Js/Core/Stage.js b/src-2013/Js/Core/Stage.js new file mode 100755 index 0000000..c649ac5 --- /dev/null +++ b/src-2013/Js/Core/Stage.js @@ -0,0 +1,38 @@ +/** + * Stage manager for objects and canvas elements. + * + * @todo Decouple Stage and Loop modules. + * @module + */ +function Core_Stage () { + this.objects = {}; + + /* + * Add object to target canvas DOM element + * and call its init() method if available. + * + * Note that all stage objects need to implement a + * loop() method which is used by the game loop. + * + * @todo canvasId should be an object reference instead. + * @param {Object} obj Game module object (instance). + * @param {String} canvasId Canvas DOM element ID. + * @return {Object} Self reference. + * @public + */ + this.add = function add (obj, canvasId) { + if (obj.loop && document.getElementById(canvasId)) { + this.objects[canvasId] = obj; + + if (obj.init) { + obj.init(); + } + + return this; + } + + return this; + }; + +} + diff --git a/src-2013/Js/Globals.js b/src-2013/Js/Globals.js new file mode 100755 index 0000000..b8006c2 --- /dev/null +++ b/src-2013/Js/Globals.js @@ -0,0 +1,9 @@ +/** + * Global vars for the project. + */ + +// --------------------------------------------------------------------------------------------- Globals vars, Constants + +var STOP = false, + FPS = 1000 / 60; + diff --git a/src-2013/Js/Init.js b/src-2013/Js/Init.js new file mode 100755 index 0000000..a1855da --- /dev/null +++ b/src-2013/Js/Init.js @@ -0,0 +1,55 @@ +/** + * Initialize global game modules. + */ +var CanvasHelper = new Lib_CanvasHelper(), + MathHelper = new Lib_MathHelper(), + Loop = new Core_Loop(), + Stage = new Core_Stage(), + Fx = new Core_Fx(), + Hud = new Core_Hud(), + Background = new Obj_Background(), + Ship_Player = new Obj_Ship_Player() + ; + + +// ----------------------------------------------------------------------------------------------------------------- Run + +/** + * Run the game. + */ +window.onload = function onload () { + // ----------------------------------------------------------------------------------------------------------- Music + + // Todo: Sometimes the music play() function breaks as the audio seems to be not loaded yet. + var music = document.getElementById('MusicAmbient'); + + music.volume = 0.5; + music.load(); + music.play(); + + + // --------------------------------------------------------------------------------------------------------- Effects + + Fx.init(); + + // Particles + for (var i = 0; i < 1; i++) { + Stage.add(new Obj_Particle(), 'Particles'); + } + + + // ----------------------------------------------------------------------------------------------------- Game engine + + Stage.add(Ship_Player, 'Game') + .add(Hud, 'Hud') + ; + + + var rndStaticBackground = Math.random() * 5 | 0; // 0-4 ^= 20% chance + + Background.init(); + document.getElementById('GameHolder').className = 'static-image static-image-' + (rndStaticBackground-1); + + Loop.run(); +}; + diff --git a/src-2013/Js/Lib/CanvasHelper.js b/src-2013/Js/Lib/CanvasHelper.js new file mode 100755 index 0000000..03a29b8 --- /dev/null +++ b/src-2013/Js/Lib/CanvasHelper.js @@ -0,0 +1,55 @@ +/** + * General canvas helper functions for graphics/animation development. + * + * @module + */ +function Lib_CanvasHelper () { + var self = this, + ctx = false; + + /** + * Draw visual helper points. + * Useful when creating new graphics and animations. + * + * @param {Number} x X coordinate + * @param {Number} y Y coordinate + * @param {String} color Color string. + * @return {Object} Self reference. + */ + this.drawHelperPoint = function (x, y, color) { + var ctx = self.ctx; + + ctx.fillStyle = color ? color : 'rgba(255, 0, 0, 1)'; + ctx.fillRect(x-1, y-1, 2, 2); + + return self; + }; + + /** + * Clear all canvas data but save settings like transformations. + * + * @see http://simonsarris.com/blog/346-how-you-clear-your-canvas-matters + * @param {Object} ctx Canvas context object. + * @return {Object} Self reference. + * @public + */ + this.clear = function clear (ctx) { + var canvas = false; + + if (!ctx) { + return self; + } + + canvas = ctx.canvas; + + ctx.save(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.restore(); + + + return self; + }; + +} + diff --git a/src-2013/Js/Lib/MathHelper.js b/src-2013/Js/Lib/MathHelper.js new file mode 100755 index 0000000..16ced73 --- /dev/null +++ b/src-2013/Js/Lib/MathHelper.js @@ -0,0 +1,23 @@ +/** + * Generic mathematical calculations. + * + * @module + */ +function Lib_MathHelper () { + var self = this; + + /** + * Get random value between given range. + * + * @param {Number} min Minimum value. + * @param {Number} max Maximum value. + * @returns {Number} + * @public + */ + this.getRandom = function getRandom (min, max) { + var rnd = Math.random() * max + min | 0; + + return rnd; + } +} + diff --git a/src-2013/Js/Lib/Web/ContextBlender.js b/src-2013/Js/Lib/Web/ContextBlender.js new file mode 100755 index 0000000..5def3b9 --- /dev/null +++ b/src-2013/Js/Lib/Web/ContextBlender.js @@ -0,0 +1,295 @@ +(function(){ + + var defaultOffsets = { + destX : 0, + destY : 0, + sourceX : 0, + sourceY : 0, + width : 'auto', + height : 'auto' + }; + + if (typeof require==='function' && typeof module==='object'){ + var canvas = require('canvas'); + addBlendMethod(canvas.Context2d.prototype); + module.exports = canvas; + } else addBlendMethod(this.CanvasRenderingContext2D && this.CanvasRenderingContext2D.prototype); + + function addBlendMethod(object){ + if (!object || typeof object.getImageData!=='function') return console.error("context blender called without a valid context prototype"); + Object.defineProperty(object,'blendOnto',{value:blendOnto}); + + // For querying of functionality from other libraries + var modes = blendOnto.supportedBlendModes = 'normal src-over screen multiply difference src-in plus add overlay hardlight colordodge dodge colorburn burn darken darker lighten lighter exclusion softlight luminosity color hue saturation lightercolor darkercolor'.split(' '); + var supports = blendOnto.supports = {}; + for (var i=modes.length;i--;) supports[modes[i]] = true; + blendOnto.aliases = { "src-over":"normal", plus:"add", dodge:"colordodge", burn:"colorburn", darker:"darken", lighter:"lighten" }; + return object; + } + + function blendOnto(destContext,blendMode,offsetOptions){ + var offsets={}; + for (var key in defaultOffsets){ + if (defaultOffsets.hasOwnProperty(key)){ + offsets[key] = (offsetOptions && offsetOptions[key]) || defaultOffsets[key]; + } + } + if (offsets.width =='auto') offsets.width =this.canvas.width; + if (offsets.height=='auto') offsets.height=this.canvas.height; + offsets.width = Math.min(offsets.width, this.canvas.width-offsets.sourceX, destContext.canvas.width-offsets.destX ); + offsets.height = Math.min(offsets.height,this.canvas.height-offsets.sourceY,destContext.canvas.height-offsets.destY); + + var srcD = this.getImageData(offsets.sourceX,offsets.sourceY,offsets.width,offsets.height); + var dstD = destContext.getImageData(offsets.destX,offsets.destY,offsets.width,offsets.height); + var src = srcD.data; + var dst = dstD.data; + var sA, dA, len=dst.length; + var sRA, sGA, sBA, dRA, dGA, dBA, dA2, + r1,g1,b1, r2,g2,b2; + var demultiply; + + function Fsoftlight(a,b) { + /* + http://en.wikipedia.org/wiki/Blend_modes#Soft_Light + 2ab+a^2 (1-2b), if b<0.5 + 2a(1-b) +sqrt(a)(2b-1), otherwise + */ + var b2=b<<1; + if (b<128) return (a*(b2+(a*(255-b2)>>8)))>>8; + else return (a*(511-b2)+(Math.sqrt(a<<8)*(b2-255)))>>8; + } + + function Foverlay(a,b) { + return a<128 ? + (a*b)>>7 : // (2*a*b)>>8 : + 255 - (( (255 - b) * (255 - a))>>7); + } + + function Fdodge(a,b) { + return (b==255 && a==0) ? 255 : Math.min(255,(a<<8)/(255-b)); + } + + function Fburn(a,b) { + return (b==255 && a==0) ? 0 : 255-Math.min(255,((255-a)<<8)/b); + } + + + /* + // yyy = similar to YCbCr + 0.2990 0.5870 0.1140 + -0.1687 -0.3313 0.5000 + 0.5000 -0.4187 -0.0813 + */ + function rgb2YCbCr(r,g,b) { + return { + r: 0.2990*r+0.5870*g+0.1140*b, + g: -0.1687*r-0.3313*g+0.5000*b, + b: 0.5000*r-0.4187*g-0.0813*b }; + } + + /* + 1.0000 -0.0000 1.4020 + 1.0000 -0.3441 -0.7141 + 1.0000 1.7720 0.0000 + */ + function YCbCr2rgb(r,g,b) { + return { + r: r +1.4020*b, + g: r-0.3441*g -0.7141*b, + b: r+1.7720*g }; + } + + function rgb2hsv(r,g,b) { + var c=rgb2YCbCr(r,g,b); + var s=Math.sqrt(c.g*c.g+c.b*c.b), + h=Math.atan2(c.g,c.b); + return {h:h, s:s, v:c.r }; + } + + function hsv2rgb(h,s,v) { + var g=s*Math.sin(h), + b=s*Math.cos(h); + return YCbCr2rgb(v,g,b); + } + + + for (var px=0;px0 ? {r:r1,g:g1,b:b1} : {r:r2,g:g2,b:b2}; + dst[px] = f1*rgb.r + f2*r1 + f3*r2; + dst[px+1] = f1*rgb.g + f2*g1 + f3*g2; + dst[px+2] = f1*rgb.b + f2*b1 + f3*b2; + break; + + case 'darkercolor': + var rgb = 2.623*(r1-r2)+5.15*(g1-g2)+b1-b2<0 ? {r:r1,g:g1,b:b1} : {r:r2,g:g2,b:b2}; + dst[px] = f1*rgb.r + f2*r1 + f3*r2; + dst[px+1] = f1*rgb.g + f2*g1 + f3*g2; + dst[px+2] = f1*rgb.b + f2*b1 + f3*b2; + break; + + default: // ******* UNSUPPORTED mode, produces yellow/magenta checkerboard + var col = (px/4) % this.canvas.width, + row = Math.floor((px/4) / this.canvas.width), + odd = (col%8<4 && row%8<4) || (col%8>3 && row%8>3); + dst[px] = dst[px+3] = 255; + dst[px+1] = odd ? 255 : 0; + dst[px+2] = odd ? 0 : 255; + } + } + destContext.putImageData(dstD,offsets.destX,offsets.destY); + } + + })(); \ No newline at end of file diff --git a/src-2013/Js/Lib/Web/KeyHandler.js b/src-2013/Js/Lib/Web/KeyHandler.js new file mode 100755 index 0000000..ac7d4ea --- /dev/null +++ b/src-2013/Js/Lib/Web/KeyHandler.js @@ -0,0 +1,29 @@ +/** + * Define all keys the project requires in this file. + * + * @see http://yojimbo87.github.com/2012/08/23/repeated-and-multiple-key-press-events-without-stuttering-in-javascript.html + */ +var keys = {}, + keysCount = 0, + keyInterval = false, + trackedKeys = { + // Steering + 119 : true, // W + 87 : true, // w + 115 : true, // S + 83 : true, // s + 97 : true, // A + 65 : true, // a + 100 : true, // D + 68 : true, // d + 37 : true, // VK_LEFT + 38 : true, // VK_UP + 39 : true, // VK_RIGHT + 40 : true, // VK_DOWN + + // Misc + 32 : true, // SPACE + 70 : true, // f + 67 : true // c + }; + diff --git a/src-2013/Js/Lib/Web/requestAnimationFrame.js b/src-2013/Js/Lib/Web/requestAnimationFrame.js new file mode 100755 index 0000000..956567a --- /dev/null +++ b/src-2013/Js/Lib/Web/requestAnimationFrame.js @@ -0,0 +1,63 @@ +/** + * requestAnimationFrame polyfill by Erik Möller with fixes from Paul Irish and Tino Zijdel. + * - Modified version for ES5 strict syntax. + * + * @example + * // Request a new frame before render which is better + * // for the timeout fallback because of the delay. + * (function renderLoop () { + * requestAnimationFrame(renderLoop); + * render(); + * })(); + * + * @see http://paulirish.com/2011/requestanimationframe-for-smart-animating/ + * http://updates.html5rocks.com/2012/05/requestAnimationFrame-API-now-with-sub-millisecond-precision + * @license MIT license + * @date 2015-03-03 + */ +(function animationFrameShim () { + 'use strict'; + + var x, lastTime = 0, + vendors = ['moz', 'webkit']; + + + if (!window.requestAnimationFrame) { + // Use vendor prefixed functions + for (x = 0; x < vendors.length; ++x) { + window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame']; + + /** @deprecated - Used by Webkit */ + window.cancelRequestAnimationFrame = window[vendors[x] + 'CancelRequestAnimationFrame']; + } + + // setTimeout() fallback + // *1) 4-16ms delay for ~60fps + window.requestAnimationFrame = function requestAnimationFrame (callback) { + var currTime = new Date().getTime(), + timeToCall = Math.max(0, 16 - (currTime - lastTime)), // *1 + id = window.setTimeout (function requestAnimationFrameTimeout () { + callback(currTime + timeToCall); + }, + timeToCall); + + lastTime = currTime + timeToCall; + return id; + }; + } + + + if (!window.cancelAnimationFrame) { + window.cancelAnimationFrame = function cancelAnimationFrame (id) { + clearTimeout(id); + }; + } + + + if (!window.requestAnimFrame) { + /** @deprecated - Old spec name */ + window.requestAnimFrame = window.requestAnimationFrame; + } + +}()); + diff --git a/src-2013/Js/Obj/Background.js b/src-2013/Js/Obj/Background.js new file mode 100755 index 0000000..5582e2e --- /dev/null +++ b/src-2013/Js/Obj/Background.js @@ -0,0 +1,282 @@ +/** + * Animated parallax background. + * + * @module + */ +function Obj_Background () { + var self = this; + + this.stopped = false; // Stop animation like this, because cancelAnimationFrame does not work. + this.tileHeight = 500; // Tile graphic height in px. + this.maxHeight = 500; // Level height, e.g. canvas0.height | fixed value. + this.scrollSpeed0 = 0.4; // How fast the level will scroll in px. + this.scrollSpeed1 = 1.0; // How fast the level will scroll in px. + this.TileTypes0Count = 4; // Different tile graphics (img assets), Number 0-N. + this.TileTypes1Count = 6; // Different tile graphics (img assets), Number 0-N. + this.TileTypes0 = []; // Available different tile types. + this.TileTypes1 = []; // Available different tile types. + this.Queue0 = []; // Current animation Queue0 with a few required tiles. + this.Queue1 = []; // Current animation Queue1 with a few required tiles. + + // Animation type + this.intervalDuration = 100; // Animation duration in frames. + this.useInterval = false; // If to use interval animation instead requestAnimFrame. + this.interval; // Interval object for alternate animation type. + + // Graphic + this.canvas0; // canvas0 DOM element + this.canvas1; // canvas1 DOM element + this.ctx0; // canvas0 context object + this.ctx1; // canvas1 context object + + + // ------------------------------------------------------------------------------------------------------ Initialize + + /** + * Set up objects, generate level and run animation loop. + * + * @public + */ + this.init = function init () { + self.canvas0 = document.getElementById('Background'); + self.canvas1 = document.getElementById('Foreground'); + self.ctx0 = self.canvas0.getContext('2d'); + self.ctx1 = self.canvas1.getContext('2d'); + + self.createTileTypeList(); + self.createQueue(); + + /** / + self.interval = window.setInterval(self.animate, 100); + self.useInterval = true; + /**/ + self.animate(); + }; + + + // ----------------------------------------------------------------------------------------------------------- Loops + + /** + * Main loop. + * + * @private + */ + this.animate = function animate () { + if (self.stopped) { + return; + } + + self.clearCanvas(); + self.drawLoop(); + + if (self.useInterval) { + if (self.intervalDuration > 0) { + self.intervalDuration--; + } + else { + clearInterval(self.interval); + return; + } + } + else { + requestAnimFrame(function() { + self.animate(); + }); + } + }; + + /** + * Main loop graphic updates. + * + * @private + */ + this.drawLoop = function drawLoop () { + self.updateQueue(); + self.moveTiles(); + self.drawTiles(); + }; + + + // ------------------------------------------------------------------------------------------------------------ Draw + + /** + * Draw tiles on stage. + * + * @private + */ + this.drawTiles = function drawTiles () { + var obj; + + for (obj in self.Queue0) { + self.ctx0.drawImage( + self.Queue0[obj].img, + 0, + self.Queue0[obj].posY, + self.canvas0.width, + self.tileHeight + ); + } + + for (obj in self.Queue1) { + self.ctx1.drawImage( + self.Queue1[obj].img, + 0, + self.Queue1[obj].posY, + self.canvas1.width, + self.tileHeight + ); + } + }; + + /** + * Clear used canvas elements. + * + * @private + */ + this.clearCanvas = function clearCanvas () { + self.clear(self.ctx0, self.canvas0); + self.clear(self.ctx1, self.canvas1); + }; + + /** + * Performant way to clear all canvas data but save the settings, like transformations. + * + * @see http://simonsarris.com/blog/346-how-you-clear-your-canvas-matters + * @private + */ + this.clear = function clear (ctx, canvas) { + ctx.save(); + ctx.setTransform(1, 0, 0, 1, 0, 0); + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.restore(); + }; + + + // ----------------------------------------------------------------------------------------------------------- Queue + + /** + * Generate tile Queue0 for this level. + * Set Y-position and use a random tile type. + * + * Do not change queue fixed length of '3'. + * + * @private + */ + this.createQueue = function createQueue () { + var posYIncrement = self.tileHeight, + obj, i; + + for (i = 0; i < 3; i++) { + obj = new Object(); + obj.posY = -self.tileHeight * i; + obj.img = self.getRandomTile0Type(); + self.Queue0.push(obj); + } + + for (i = 0; i < 3; i++) { + obj = new Object(); + obj.posY = -self.tileHeight * i; + obj.img = self.getRandomTile1Type(); + self.Queue1.push(obj); + } + }; + + /** + * Change tiles. + * + * @private + */ + this.updateQueue = function updateQueue () { + var i; + + for (i = 0; i < self.Queue0.length; i++) { + if (self.Queue0[i].posY >= self.maxHeight) { + self.Queue0[i].posY = self.getTopmostY(); + self.Queue0[i].img = self.getRandomTile0Type(); + } + } + + for (i = 0; i < self.Queue1.length; i++) { + if (self.Queue1[i].posY >= self.maxHeight) { + self.Queue1[i].posY = self.getTopmostY(); + self.Queue1[i].img = self.getRandomTile1Type(); + } + } + }; + + + // -------------------------------------------------------------------------------------------------------- Movement + + /** + * Get the topmost tile coordinate for positioning the next tile. + * + * @return {Number} Next free Y coordinate to create tile at. + * @private + */ + this.getTopmostY = function getTopmostY () { + var i, topY = self.Queue0[0].posY; + + for (i = 0; i < self.Queue0.length; i++) { + if (self.Queue0[i].posY < topY) { + topY = self.Queue0[i].posY; + } + } + + return topY - self.tileHeight; + }; + + /** + * Scroll available tiles vertically. + * + * @private + */ + this.moveTiles = function moveTiles () { + for (var obj in self.Queue0) { + self.Queue0[obj].posY += self.scrollSpeed0; + self.Queue1[obj].posY += self.scrollSpeed1; + } + }; + + + // ----------------------------------------------------------------------------------------------------------- Tiles + + /** + * Add all available tiles to a list. + * + * @private + */ + this.createTileTypeList = function createTileTypeList () { + var i; + + for (i = 0; i <= self.TileTypes0Count; i++) { + self.TileTypes0.push(document.getElementById('Tile-0-' + i)); + } + + for (i = 0; i <= self.TileTypes1Count; i++) { + self.TileTypes1.push(document.getElementById('Tile-1-' + i)); + } + }; + + /** + * Get random tile type from list. + * + * @private + */ + this.getRandomTile0Type = function getRandomTile0Type () { + var i = Math.round(Math.random() * self.TileTypes0Count); + + return self.TileTypes0[i]; + }; + + /** + * Get random tile type from list. + * + * @private + */ + this.getRandomTile1Type = function getRandomTile1Type () { + var i = Math.round(Math.random() * self.TileTypes1Count); + + return self.TileTypes1[i]; + }; +} + diff --git a/src-2013/Js/Obj/Particle.js b/src-2013/Js/Obj/Particle.js new file mode 100755 index 0000000..3de85be --- /dev/null +++ b/src-2013/Js/Obj/Particle.js @@ -0,0 +1,147 @@ +/** + * Particle effect. + * + * @todo Rewrite articles to support instances (-> refactor Canvas drawing + Context use) + * @module + */ +function Obj_Particle () { + var MathHelper = new Lib_MathHelper(); + var self = this; + + this.isUpdating = false; // Prevent parallel updates of the game. + this.remove = false; // Mark for removal from stage. + + // Graphic + this.sprite = document.getElementById('Asset_Particle'); + this.canvas = document.getElementById('Particles'); + this.ctx = this.canvas.getContext('2d'); + this.width = 5; // Sprite width in px + this.height = 5; // Sprite height in px + + // Object + this.y = 0; // Current position (y) + this.x = 0; // Current position (x) + this.speed = 0; // Current speed + this.rotation = 0; // Current rotation + this.lifetime = 0; // Object lifespan till reset + + + // ------------------------------------------------------------------------------------------------------ Initialize + + /** + * Initialize and run this object. + * + * @see Core_Stage + */ + this.init = function init () { + this.initParticle(); + this.drawUpdate(); + }; + + /** + * Init/Reset position, rotation and speed. + */ + this.initParticle = function initParticle () { + self.x = 50 + Math.random() * 700; + self.y = - (10 + Math.random() * 50); + self.rotation = -2 + Math.random() * 2; + self.speed = 4 + Math.random() * 3; + self.lifetime = 100 + Math.random() * 200; + }; + + + // ------------------------------------------------------------------------------------------------------- Game loop + + /** + * Game loop extension. + * + * @public + */ + this.loop = function loop () { + if (self.isUpdating) { + return; + } + + self.isUpdating = true; + + self.move() + .drawUpdate(); + }; + + + // ------------------------------------------------------------------------------------------------------------ Draw + + /** + * Draw object. + * + * @return {Object} Self reference. + * @private + */ + this.drawUpdate = function drawUpdate () { + var ctx = self.ctx; + + CanvasHelper.clear(ctx); + ctx.drawImage(self.sprite, self.x, self.y); + ctx.restore(); + + self.isUpdating = false; + return self; + }; + + + // -------------------------------------------------------------------------------------------------------- Movement + + /** + * Update position depending on its velocity. + * Mark object for removal if it has left the stage. + * + * @return {Object} Self reference. + * @private + */ + this.move = function move () { + self.x += self.getPosX() * 1; + self.y -= self.getPosY() * 1; + self.lifetime--; + + // Remove from stage + if (self.lifetime <= 0) { + self.initParticle(); + } + + customEvent = new CustomEvent('ParticleMove', {detail: {'x': self.x, 'y': self.y}}); + window.dispatchEvent(customEvent); + + return self; + }; + + + // -------------------------------------------------------------------------------------------------- Math + + /** + * Get x-position based on rotation and speed. + * + * @return {Number} Laser X position. + * @private + */ + this.getPosX = function getPosX () { + var rad = self.rotation * (Math.PI / 180), + pos = Math.sin(rad) * self.speed; + + return pos; + }; + + /** + * Get y-position based on rotation and speed. + * + * @return {Number} Laser Y position. + * @private + */ + this.getPosY = function getPosY () { + var rad = self.rotation * (Math.PI / 180), + pos = Math.cos(rad) * self.speed * -1; + + return pos; + }; + +} + diff --git a/src-2013/Js/Obj/Ship/Player.js b/src-2013/Js/Obj/Ship/Player.js new file mode 100755 index 0000000..0349295 --- /dev/null +++ b/src-2013/Js/Obj/Ship/Player.js @@ -0,0 +1,761 @@ +/** + * Player object: The space ship. + * + * @todo Outsource particles, shield, sound. + * @module + */ +function Obj_Ship_Player () { + var MathHelper = new Lib_MathHelper(); + var self = this; + + + // Prevent parallel updates of the game. + this.isUpdating = false; + + // Graphic + this.sprite = document.getElementById('Asset_Ship'); + this.spriteShielded = document.getElementById('Asset_ShipShielded'); + this.canvas = document.getElementById('Game'); + this.ctx = this.canvas.getContext('2d'); + + // Sound + this.SoundShieldCharge = document.getElementById('SoundShieldCharge'); + this.soundShieldEnabled = document.getElementById('SoundShieldEnabled'); + this.soundLaser = document.getElementById('SoundLaser-' + MathHelper.getRandom(1, 2)); + this.soundLaserReady = document.getElementById('SoundLaserReady'); + this.soundFuncBlocked = document.getElementById('SoundFunctionBlocked'); + this.soundEngineThruster = document.getElementById('SoundEngineThruster'); + + // Ship + this.x = 0; // Current position (x). + this.y = 0; // Current position (y). + this.friction = 0.93; // World friction: Close to 1 values create smooth surfaces. + this.velocityX = 0; // Horizontal velocity. + this.velocityY = 0; // Vertical velocity. + this.rotation = 0; // Current rotation. + this.rotationRadius = 5; // How fast the ship can rotate in degrees/frame. + this.speed = 0; // Current speed. + this.speedMax = 5 + Math.random() * 3; // Maximum speed. + this.acceleration = 0.3 + Math.random() * 0.25; // Speed +/- acceleration factor. + this.movingDirection = ''; // up, down, left, right + + // Shields + this.CanvasCopyUnder = document.getElementById('CanvasCopyUnder'); + this.CanvasCopyOver = document.getElementById('CanvasCopyOver'); + this.ctxCanvasCopyUnder = this.CanvasCopyUnder.getContext('2d'); + this.ctxCanvasCopyOver = this.CanvasCopyOver.getContext('2d'); + + this.videoShield = document.getElementById('VideoShield'); + this.spriteShield0 = document.getElementById('Asset_Shield0'); + this.spriteShield1 = document.getElementById('Asset_Shield1'); + this.shieldCooldownTime = (500 + Math.random() * 200) | 0; // Time in MS it takes until you can switch shield status. + this.shieldCooldown = false; // Flag (automatic) + this.shieldActive = false; // Flag (automatic) + + // Laser + this.laserCooldownTime = (1500 + Math.random() * 500) | 0; // Time in MS it takes until you can fire again. + this.laserCooldown = false; // Flag (automatic) + + // Engine + this.engineSparksMax = (10 + Math.random() * 50) | 0; + this.engineSparks = []; + + + // ------------------------------------------------------------------------------------------------------ Initialize + + /** + * Initialize and run this object. + * + * @see Core_Stage + */ + this.init = function init () { + console.info('This round`s ship specs:'); + console.log({ + "acceleration" : this.acceleration, + "speedMax" : this.speedMax, + "shieldCooldownTime" : this.shieldCooldownTime, + "laserCooldownTime" : this.laserCooldownTime, + "engineSparksMax" : this.engineSparksMax, + }); + + this.drawInit() + .controlInit(); + + + // Todo: Find a better way to handle external dependencies/events + window.addEventListener('ParticleMove', function(event) { + var x = event.detail.x; + y = event.detail.x; + playerX = self.x, + playerY = self.y; + + //console.log(self.x, self.y); + //console.log(x,y); + }); + + self.soundEngineThruster.volume = 0.02; + self.soundEngineThruster.play(); + }; + + + // ------------------------------------------------------------------------------------------------------- Game loop + + /** + * Game loop extension. + * - Lock updating status. + * - Update speed and position. + * - Redraw the changes. + * + * @public + */ + this.loop = function loop () { + if (!self.isUpdating) { + self.isUpdating = true; + + self.setSpeed() + .setPosition() + .boundsBounce() + .drawUpdate(); + } + }; + + + // ------------------------------------------------------------------------------------------------------------ Draw + + /** + * Create the ship within its Canvas context. + + * @return {Object} Self reference. + */ + this.drawInit = function drawInit () { + var ctx = self.ctx, + canvas = ctx.canvas; + + this.x = (canvas.width / 2); + this.y = (canvas.height / 2) + 100; + + ctx.filter = 'sharpen'; + + ctx.drawImage(this.sprite, this.x, this.y); + + return self; + }; + + /** + * Update the ship and its effects within its Canvas context. + + * @return {Object} Self reference. + */ + this.drawUpdate = function drawUpdate () { + var ctx = self.ctx, + ctxUnder = self.ctxCanvasCopyUnder, + ctxOver = self.ctxCanvasCopyOver, + videoShield = self.videoShield, + sprite = self.sprite; + + CanvasHelper.clear(self.ctx); + self.drawRotation(); + + + if (self.shieldActive) { + sprite = self.spriteShielded; + + ctx.drawImage(sprite, -(sprite.width / 2), -(sprite.height / 2)); + + /* * / + CanvasHelper + .clear(ctxUnder) + .clear(ctxOver); + + videoShield.play(); + + ctxUnder.drawImage(sprite, 0, 0); + /* */ + + // Todo - Fix: Blend mode doesn't work as desired and creates black border + /* * / + ctxOver.drawImage(videoShield, 13, 15); + ctxOver.blendOnto(ctxUnder, 'add'); + /* * / + // 'src-in' could create some sort of cloaking + // This is a dirty fix, still looking bad but less worse + ctxOver.drawImage(videoShield, 13, 25); + ctxOver.blendOnto(ctxUnder, 'src-in'); + + ctx.drawImage(self.CanvasCopyUnder, -(sprite.width / 2), -(sprite.height / 2)); + /* */ + } + else { + ctx.drawImage(sprite, -(sprite.width / 2), -(sprite.height / 2)); + } + + self.drawEngineFire(-14, 20) + .drawEngineFire(14, 20); + + ctx.restore(); + + /* * / + ctx.save(); + ctx.strokeStyle = 'white'; + ctx.strokeRect(self.x-20, self.y-70, 40, 100); + ctx.restore(); + /* */ + + self.isUpdating = false; + + + return self; + }; + + /** + * Rotate the canvas and reposition its content to the center. + * + * @see http://developer.apple.com/library/safari/#documentation/AudioVideo/Conceptual/HTML-canvas-guide/... + * @return {Object} Self reference. + */ + this.drawRotation = function drawRotation () { + var degree = self.rotation, + radian = degree * (Math.PI / 180), + ctx = self.ctx; + + ctx.save(); + ctx.translate(self.x, self.y); + ctx.rotate(radian); + + return self; + }; + + + // ----------------------------------------------------------------------------------- Particles + + /** + * Draw engine particle fire. + * + * @return {Object} Self reference. + */ + this.drawEngineFire = function drawEngineFire (posX, posY) { + var sparks = self.engineSparks, + ctx = self.ctx, + i, o, obj, vx, + x = posX, + y = posY, + renderMaxSparks = self.engineSparksMax / 4; + + if (self.movingDirection === 'up') { + renderMaxSparks = self.engineSparksMax; + } + + if (!sparks.length) { + for (i = 0; i <= self.engineSparksMax; i++) { + obj = new Object(); + obj.lifetime = Math.round(50 + Math.random() * 50); + obj.size = 1 + 2 * Math.random(); + obj.x = x + 5 * Math.random(); + obj.y = y + 10 * Math.random(); + sparks.push(obj); + } + } + else { + var i = renderMaxSparks; + + for (obj in sparks) { + if (i > 0) { + i--; + } + else { + // console.warn('STOP RENDERING AT: ' + i); + break; + } + + o = sparks[obj]; + o.fillStyle = 'hsla('+ (0 + Math.round(Math.random() * 50)) +', 100%, 70%, '+ (o.lifetime/100) +')'; + o.lifetime--; + + // Reset + if (!o.lifetime) { + o.lifetime = Math.round(50 + Math.random() * 50); + o.size = 1 + 2 * Math.random(); + o.x = x + 5 * Math.random(); + o.y = y + 10 * Math.random(); + } + + vx = -1 * Math.random() + 0.5; + o.x += vx * Math.random() * 2; + o.y += vx * Math.random() * 2; + + ctx.fillStyle = o.fillStyle; + ctx.fillRect(o.x-o.size/2, o.y-o.size/2, o.size, o.size); + } + } + + + return self; + }; + + + // -------------------------------------------------------------------------------------------------------- Controls + + /** + * Enable controls. + * + * @return {Object} Self reference. + * @private + */ + this.controlInit = function controlInit () { + document.addEventListener('keydown', this.keyControlDown, false); + document.addEventListener('keyup', this.keyControlUp, false); + + return self; + }; + + /** + * Control pressed keys. + * + * - All keys can be pressed simultaneously and repeated times, which allows for smooth movement. + * - Some keys require special settings to prevent multiple key presses (block the functionality). + * + * @todo Outsource interval closure function. + * @param {Object} event Keyboard event. + * @private + */ + this.keyControlDown = function keyControlDown (event) { + var code = event.which, + fireKeyPressed = false, + shieldKeyPressed = false; + + self.movingDirection = ''; + self.soundEngineThruster.volume = 0.02; + + + if (!trackedKeys[code]) { + return; + } + + if (!keys[code]) { + keys[code] = true; + keysCount++; + } + + + if (keyInterval !== null) { + return; + } + + keyInterval = setInterval(function onInterval () { + // Todo: Find a way to allow permanent fire as optional mode (it's also attached to audio and visuals) + var allowPermanentFire = false, + direction = ''; + + // F + if (keys[70] && (allowPermanentFire ? true : !fireKeyPressed)) { + if (self.shieldActive === false) { + fireKeyPressed = true; + direction = 'F'; + event.preventDefault(); + self.fireLaser(); + } + else { + // Laser trigger blocked + var sound = self.soundFuncBlocked; + sound.volume = 0.2; + sound.play(); + } + } + + // C (67) + if (keys[67] && !shieldKeyPressed) { + shieldKeyPressed = true; + direction = 'C'; + event.preventDefault(); + self.enableShield(); + } + + // Check if north or south + if (keys[119] || keys[87] || keys[38]) { + direction = 'n'; + event.preventDefault(); + self.moveUp(); + self.movingDirection = 'up'; + self.soundEngineThruster.volume = 0.06; + } + else if (keys[115] || keys[83] || keys[40]) { + direction = 's'; + event.preventDefault(); + self.moveDown(); + self.movingDirection = 'down'; + self.soundEngineThruster.volume = 0.04; + } + + // Concat west or east + if (keys[97] || keys[65] || keys[37]) { + direction += 'w'; + event.preventDefault(); + self.moveLeft(); + //self.rotateLeft(); + self.movingDirection = 'left'; + self.soundEngineThruster.volume = 0.05; + } + else if (keys[100] || keys[68] || keys[39]) { + direction += 'e'; + event.preventDefault(); + //self.rotateRight(); + self.moveRight(); + self.movingDirection = 'right'; + self.soundEngineThruster.volume = 0.05; + } + + //keyCallback(direction); + + }, FPS); + }; + + /** + * Control up keys and update all pressed ones. + * + * @param {Object} event Keyboard event. + * @private + */ + this.keyControlUp = function keyControlUp (event) { + var code = event.which; + + if (keys[code]) { + delete keys[code]; + keysCount--; + } + + // Check if keyboard movement stopped. + if ((trackedKeys[code]) && (keysCount === 0)) { + clearInterval(keyInterval); + keyInterval = null; + } + }; + + + // ----------------------------------------------------------------------------------------------------- Fire weapon + + /** + * Fire laser weapon, play sound and enable cooldown timer. + * + * @return {Object} Self reference. + * @private + */ + this.fireLaser = function fireLaser () { + var sound = self.soundFuncBlocked, + Weapon_Laser; + + + if (!self.laserCooldown) { + // Reset FX for next animation + Fx.reset(); + + self.soundLaser = document.getElementById('SoundLaser-' + MathHelper.getRandom(1, 2)); + self.soundLaser.play(); + + Weapon_Laser = new Obj_Weapon_Laser(); + Stage.add(Weapon_Laser, 'Weapons'); + Weapon_Laser.run(self.x - 110, self.y - 225, self.rotation); + + self.laserCooldown = true; + window.setTimeout(self.fireLaserCooldown, self.laserCooldownTime); + } + else { + // Laser trigger blocked + sound.volume = 0.2; + sound.play(); + } + + + return self; + }; + + /** + * Laser cooldown timer. + * Re-enable weapon. + * + * @return {Object} Self reference. + * @private + */ + this.fireLaserCooldown = function fireLaserCooldown () { + var sound = self.soundLaserReady; + + self.laserCooldown = false; + sound.volume = 0.4; + sound.play(); + + return self; + }; + + + // --------------------------------------------------------------------------------------------------------- Shields + + /** + * Shields. + * If shield does not require a cooldown, it can be either enabled or disabled. + * + * @return {Object} Self reference. + * @private + */ + this.enableShield = function enableShield () { + var audioShield, + audioShieldCharge; + + + if (!self.shieldCooldown) { + if (!self.shieldActive) { + self.shieldActive = true; + self.shieldCooldown = true; + + audioShieldCharge = self.SoundShieldCharge; + audioShieldCharge.volume = 0.2; + audioShieldCharge.play(); + + window.setTimeout(function() { + audioShield = self.soundShieldEnabled; + audioShield.volume = 0.3; + audioShield.play(); + }, 100); + + window.setTimeout(self.shieldSwitchCooldown, self.shieldCooldownTime); + } + else { + self.shieldActive = false; + } + + self.drawUpdate(); + + customEvent = new CustomEvent('PlayerShieldActive', {detail: {'active' : self.shieldActive}}); + window.dispatchEvent(customEvent); + } + else { + audioShield = self.soundFuncBlocked; + audioShield.volume = 0.3; + audioShield.play(); + } + + + return self; + }; + + /** + * Shield cooldown timer that + * indirectly re-enables the weapon. + * + * @private + */ + this.shieldSwitchCooldown = function shieldSwitchCooldown () { + self.shieldCooldown = false; + }; + + + // -------------------------------------------------------------------------------------------------------- Movement + // --------------------------------------------------------------------------------------- Speed + + /** + * Set speed depending on velocity. + * + * @return {Object} Self reference. + * @private + */ + this.setSpeed = function setSpeed () { + var friction = self.friction, + speed = self.speed, + speedMax = self.speedMax, + vx = self.velocityX, + vy = self.velocityY; + + self.velocityX *= friction; + self.velocityY *= friction; + self.speed *= friction; + + + if (speed > speedMax) { + self.speed = speedMax; + } + + if (vx > speedMax) { + self.velocityX = speedMax; + } + else if (vx < -speedMax) { + self.velocityX = -speedMax; + } + + if (vy > speedMax) { + self.velocityY = speedMax; + } + else if (vy < -speedMax) { + self.velocityY = -speedMax; + } + + + return self; + }; + + + // ---------------------------------------------------------------------------------------- Move + + /** + * Move ship to the left. + * + * @private + */ + this.moveLeft = function moveLeft () { + self.velocityX -= self.acceleration; + }; + + /** + * Move ship to the right. + * + * @private + */ + this.moveRight = function moveRight () { + self.velocityX += self.acceleration; + }; + + /** + * Move ship up / accelerate. + * + * @private + */ + this.moveUp = function moveUp () { + var acc = self.acceleration; + + self.velocityY -= acc; + self.speed += acc; + }; + + /** + * Move ship down / brake. + */ + this.moveDown = function moveDown () { + var acc = self.acceleration; + + self.velocityY += acc; + self.speed -= acc; + }; + + + // -------------------------------------------------------------------------------------- Rotate + + /** + * Set ship rotation in degrees. + * + * @private + */ + this.rotateLeft = function rotateLeft () { + self.rotation -= self.rotationRadius; + + if (self.rotation <= -360) { + self.rotation = 0; + } + }; + + /** + * Set ship rotation in degrees. + * + * @private + */ + this.rotateRight = function rotateRight () { + self.rotation += self.rotationRadius; + + if (self.rotation >= 360) { + self.rotation = 0; + } + }; + + + // --------------------------------------------------------------------------------- Positioning + + /** + * Update position depending on its velocity. + * + * Two variants: + * - Position depending on velocity. + * - Position depending on rotation. [disabled] + * + * @return {Object} Self reference. + * @private + */ + this.setPosition = function setPosition () { + /* */ + self.x += self.velocityX; + self.y += self.velocityY; + /* * / + self.speed = Number(self.speed.toFixed(2)); + self.x += self.getPosX() * self.friction; + self.y += self.getPosY() * self.friction; + /* */ + + return self; + }; + + /** + * Get x-position based on rotation and speed. + * + * @return {Number} Laser X position. + * @private + */ + this.getPosX = function getPosX () { + var radian = self.rotation * (Math.PI / 180), + pos = Math.sin(radian) * self.speed; + + return pos; + }; + + /** + * Get y-position based on rotation and speed. + * + * @return {Number} Laser Y position. + * @private + */ + this.getPosY = function getPosY () { + var radian = self.rotation * (Math.PI / 180), + pos = Math.cos(radian) * self.speed * -1; + + return pos; + }; + + + // ---------------------------------------------------------------------------------------------------------- Bounds + + /** + * Bounce off the boundaries. + * + * @todo Subtract object size. + * @return {Object} Self reference. + * @private + */ + this.boundsBounce = function boundsBounce () { + var canvas = self.ctx.canvas, + width = canvas.width, + height = canvas.height, + x = self.x, + y = self.y, + vx = self.velocityX; + vy = self.velocityY; + + + if (x >= width) { + self.x = width; + self.velocityX = -vx; + } + else if (x < 0) { + self.x = 0; + self.velocityX = -vx; + } + + + if (y >= height) { + self.y = height; + self.velocityY = -vy; + } + else if (y < 0) { + self.y = 0; + self.velocityY = -vy; + } + + + return self; + }; + +} + diff --git a/src-2013/Js/Obj/Weapon/Laser.js b/src-2013/Js/Obj/Weapon/Laser.js new file mode 100755 index 0000000..029108f --- /dev/null +++ b/src-2013/Js/Obj/Weapon/Laser.js @@ -0,0 +1,198 @@ +/** + * Laser weapon. + * + * @todo Outsource sound. + * @module + */ +function Obj_Weapon_Laser () { + var MathHelper = new Lib_MathHelper(); + var self = this; + + this.isUpdating = false; // Prevent parallel updates of the game. + this.remove = false; // Mark for removal from stage. + + // Graphic + this.sprite = document.getElementById('Asset_Weapon_Laser'); + this.canvas = document.getElementById('Weapons'); + this.ctx = this.canvas.getContext('2d'); + this.width = 8; // Sprite width in px + this.height = 25; // Sprite height in px + + // Sound + + this.audio = document.getElementById('SoundExplosion-' + MathHelper.getRandom(1, 2)); + this.audio.volume = 1; + + // Laser + this.originX = 0; // Initial X coordinate + this.originY = 0; // Initial Y coordinate + this.originRotation = 0; // Initial object rotation (degrees) + this.x = 0; // Current position (x) + this.y = 0; // Current position (y) + this.speed = 10; // Current speed + this.acceleration = 0.3; // Acceleration factor + this.rotation = 0; // Current rotation + + + // ------------------------------------------------------------------------------------------------------ Initialize + + /** + * Initialize object. + * + * @param {Number} x X coordinate to create object at. + * @param {Number} y Y coordinate to create object at. + * @param {Number} rotation Initial object rotation in degrees. + * @public + */ + this.run = function run (x, y, rotation) { + this.originX = x - self.width / 2; + this.originY = y; + this.originRotation = rotation; + }; + + + // ------------------------------------------------------------------------------------------------------- Game loop + + /** + * Game loop extension. + * + * @public + */ + this.loop = function loop () { + if (self.isUpdating) { + return; + } + + self.isUpdating = true; + + self.move() + .drawUpdate(); + }; + + + // ------------------------------------------------------------------------------------------------------------ Draw + + /** + * Draw laser. + * + * @return {Object} Self reference. + * @private + */ + this.drawUpdate = function drawUpdate () { + var ctx = self.ctx; + + CanvasHelper.clear(ctx); + + self.drawRotation(); + ctx.drawImage(self.sprite, self.x, self.y); + ctx.restore(); + + self.isUpdating = false; + return self; + }; + + /** + * Rotate laser. + * + * @see http://developer.apple.com/library/safari/#documentation/AudioVideo/Conceptual/HTML-canvas-guide/... + * @return {Object} Self reference. + * @private + */ + this.drawRotation = function drawRotation () { + var degree = self.originRotation, + radian = degree * (Math.PI / 180), + ctx = self.ctx; + + ctx.save(); + ctx.translate(self.originX, self.originY); + ctx.rotate(radian); + + return self; + }; + + + /** + * Destroy/remove object and explode. Also there's a chance + * of 20% to cause a screen shake effect with exploding sound. + * + * @return {Object} Self reference. + * @private + */ + this.destroySelf = function destroySelf () { + var rnd, + hitChance = Math.random() * 5 | 0, // ^= 0-50% + customEvent; + + if (self.remove) { + return; + } + + self.remove = true; + rnd = Math.round(Math.random()) * 10; + + if (rnd <= hitChance) { + + self.audio = document.getElementById('SoundExplosion-' + MathHelper.getRandom(1, 2)); + self.audio.play(); + + customEvent = new CustomEvent('ShakeFx'); + window.dispatchEvent(customEvent); + } + + return self; + }; + + + // -------------------------------------------------------------------------------------------------------- Movement + + /** + * Update position depending on its velocity. + * Mark object for removal if it has left the stage. + * + * @return {Object} Self reference. + * @private + */ + this.move = function move () { + self.x += self.getPosX() * 1; + self.y += self.getPosY() * 1; + self.speed += self.acceleration; + + // Remove from stage + if (self.y < -250) { + self.destroySelf(); + } + + return self; + }; + + + // -------------------------------------------------------------------------------------------------- Math + + /** + * Get x-position based on rotation and speed. + * + * @return {Number} Laser X position. + * @private + */ + this.getPosX = function getPosX () { + var rad = self.rotation * (Math.PI / 180), + pos = Math.sin(rad) * self.speed; + + return pos; + }; + + /** + * Get y-position based on rotation and speed. + * + * @return {Number} Laser Y position. + * @private + */ + this.getPosY = function getPosY () { + var rad = self.rotation * (Math.PI / 180), + pos = Math.cos(rad) * self.speed * -1; + + return pos; + }; + +} + diff --git a/src-2013/favicon.ico b/src-2013/favicon.ico new file mode 100755 index 0000000..46fd3ae Binary files /dev/null and b/src-2013/favicon.ico differ diff --git a/src-2013/index.html b/src-2013/index.html new file mode 100755 index 0000000..99c0d85 --- /dev/null +++ b/src-2013/index.html @@ -0,0 +1,122 @@ + + + + + SpaceGame + + + + + + +
+
+ + + + + + +
+
+
+ +
+

+ Arrows = Move, C = Shield, F = Fire. Active shield disallows fire.
+ Game is not finished and has no hit testing. +

+

Spoiler alert: Don't wait for any enemies ... ;)

+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +