├── .gitignore ├── INSTALL.md ├── README.md ├── app ├── .meteor │ ├── .gitignore │ └── packages ├── client │ ├── css │ │ ├── libs │ │ │ └── normalize.css │ │ └── project.css │ ├── index.html │ ├── js │ │ └── game │ │ │ ├── App.js │ │ │ ├── Game.js │ │ │ ├── Menu.js │ │ │ ├── MenuDashboard.js │ │ │ ├── MenuGameover.js │ │ │ ├── MenuProjects.js │ │ │ ├── MenuWebgl.js │ │ │ ├── SoundTrack.js │ │ │ ├── Stats.js │ │ │ └── resources │ │ │ ├── Branch.js │ │ │ ├── GamepadSupport.js │ │ │ └── Player.js │ ├── lib │ │ ├── jquery.js │ │ ├── lodash.js │ │ ├── modernizr.js │ │ └── webgl │ │ │ ├── Stats.js │ │ │ ├── THREEx.KeyboardState.js │ │ │ ├── THREEx.WindowResize.js │ │ │ ├── core │ │ │ └── three │ │ │ │ ├── Detector.js │ │ │ │ └── three.js │ │ │ └── tween │ │ │ └── tween.js │ ├── scss │ │ ├── _mixins.scss │ │ └── project.scss │ └── templates │ │ ├── choose.html │ │ ├── game │ │ ├── dashboard.html │ │ ├── level.html │ │ └── life.html │ │ ├── gamover.html │ │ ├── partial │ │ ├── about.html │ │ ├── help.html │ │ ├── projects.html │ │ ├── recentScores.html │ │ └── topScores.html │ │ ├── tutorial.html │ │ ├── webgl.html │ │ └── welcome.html ├── public │ ├── img │ │ ├── bg.png │ │ ├── crOrangeMCode.png │ │ ├── crOrangePCode.png │ │ ├── crPR.png │ │ ├── crPurpleMCode.png │ │ ├── crPurplePCode.png │ │ ├── crStar.png │ │ ├── crTealMCode.png │ │ ├── crTealPCode.png │ │ ├── gamepad-map.png │ │ ├── player0.png │ │ ├── player1.png │ │ ├── player2.png │ │ ├── poster.png │ │ ├── tutorial-bad.png │ │ └── tutorial-good.png │ └── music │ │ ├── 0.ogg │ │ ├── 1.ogg │ │ └── 2.ogg ├── server │ └── server.js └── shared.js ├── config.rb ├── misc ├── assets.psd └── bg.psd └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | .sass_cache 15 | .sass-cache 16 | .idea 17 | node_modules 18 | npm-debug.log 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | # The Core Committer Installation Guide 2 | 3 | ## Dependencies 4 | * Meteor (http://meteor.com) 5 | 6 | ## Running the source of the game 7 | * clone the source, make sure you have Meteor 0.5.2 8 | * ```cd app``` and ```meteor run``` 9 | 10 | ## Installation Notes 11 | * This game can save player's name to show off in the leader board, you need to configure GitHub OAuth. To do this, 12 | play a single game and in the Gameover screen, click on 'configure GitHub', follow the steps in the pop-up. 13 | 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Core Committer - The Game 2 | 3 | [ [WATCH GAME TRAILER](http://www.youtube.com/watch?v=BDPUvtY8Lr0 "Core Committer Game Trailer") ] 4 | 5 | ![](http://committer.meteor.com/img/poster.png "The Core Committer") 6 | 7 | ## Instructions & Story 8 | * __You are the core committer.__ 9 | * Choose an open source project and help that project by sorting code to proper branches. 10 | * Collect stars to make your project more popular. 11 | 12 | * allow the code blocks to hit the related branch color 13 | * good: ![](http://committer.meteor.com/img/tutorial-good.png) 14 | * bad: ![](http://committer.meteor.com/img/tutorial-bad.png) 15 | * destroy the blocks that do not match the branch color using your attack lasers ([SPACE]) 16 | * branches change if you destroy matching blocks or a block 17 | * when the branch changes, it lights up and the code starts flowing from a different direction 18 | 19 | * __you lose health if you mismatch the code & branch colors__ or __destroy good code blocks__ 20 | * when stars get to the branch, collect then with your committer 21 | * use branch teleporting ([TAB]) and 3D view ([E]) to your advantage 22 | * the game gets harder over time, be prepared! 23 | 24 | ## Controls 25 | 26 | ### Keyboard Controls 27 | 28 | * __arrow keys__ - move 29 | * __q / w__ - < , > teleport 30 | * __TAB__ - clockwise tele 31 | * __SPACE__ - attack 32 | * __e__ - enter or exit 3D 33 | 34 | 35 | ### GamePad Controls 36 | ![](http://committer.meteor.com/img/gamepad-map.png "GamePad Controls") 37 | * Plug in your controller, smart pressing buttons when you get into game mode (after the menus). 38 | 'Gamepad' in the bottom right corner should say 'on' 39 | * If you can't get it to work, make sure your controller works with the 40 | [HTML5 Rocks Tester](http://www.html5rocks.com/en/tutorials/doodles/gamepad/gamepad-tester/tester.html). 41 | Make sure to close the tester tab, because the controller can access only one browser tab at a time 42 | * Controllers tested: XBOX360, Logitech F310 GamePad & Attack3 Joystick, PS3. 43 | 44 | ## Features 45 | 46 | * 2D & 3D Game Modes 47 | * Compatible with WebGL enabled browsers (three.js support). 48 | * Tested in Chrome & Firefox, HTML5 GamePad support in Chrome 49 | * Top & Recent Scores 50 | * GitHub Auth to save your game score 51 | * 3 amazing wub wub wub music tracks (you can also turn the music off, if you are sick of the wub wub) 52 | 53 | ## Tech Used 54 | 55 | * Meteor (http://meteor.com) 56 | * three.js (WebGL) (http://mrdoob.github.com/three.js/) 57 | * Compass / SASS 58 | * Modernizr 59 | * Lo-dash 60 | * jQuery 61 | * HTML5 GamePad API 62 | 63 | ## Notes 64 | 65 | * All project names & trademarks are the property of their respective owners 66 | * Game music used under Creative Commons License. 67 | Thanks to [Flembaz](http://soundcloud.com/flembaz/sets/indigo/) (Written, produced, mixed, mastered by João Bandarra & Pedro R. Artur) 68 | * Source Installation instructions are in ```INSTALL.md``` 69 | -------------------------------------------------------------------------------- /app/.meteor/.gitignore: -------------------------------------------------------------------------------- 1 | local 2 | meteorite 3 | -------------------------------------------------------------------------------- /app/.meteor/packages: -------------------------------------------------------------------------------- 1 | # Meteor packages used by this project, one per line. 2 | # 3 | # 'meteor add' and 'meteor remove' will edit this file for you, 4 | # but you can also edit it by hand. 5 | 6 | #autopublish 7 | accounts-base 8 | accounts-oauth2-helper 9 | accounts-ui 10 | accounts-github 11 | -------------------------------------------------------------------------------- /app/client/css/libs/normalize.css: -------------------------------------------------------------------------------- 1 | /*! normalize.css v2.0.1 | MIT License | git.io/normalize */ 2 | 3 | /* ========================================================================== 4 | HTML5 display definitions 5 | ========================================================================== */ 6 | 7 | /* 8 | * Corrects `block` display not defined in IE 8/9. 9 | */ 10 | 11 | article, 12 | aside, 13 | details, 14 | figcaption, 15 | figure, 16 | footer, 17 | header, 18 | hgroup, 19 | nav, 20 | section, 21 | summary { 22 | display: block; 23 | } 24 | 25 | /* 26 | * Corrects `inline-block` display not defined in IE 8/9. 27 | */ 28 | 29 | audio, 30 | canvas, 31 | video { 32 | display: inline-block; 33 | } 34 | 35 | /* 36 | * Prevents modern browsers from displaying `audio` without controls. 37 | * Remove excess height in iOS 5 devices. 38 | */ 39 | 40 | audio:not([controls]) { 41 | display: none; 42 | height: 0; 43 | } 44 | 45 | /* 46 | * Addresses styling for `hidden` attribute not present in IE 8/9. 47 | */ 48 | 49 | [hidden] { 50 | display: none; 51 | } 52 | 53 | /* ========================================================================== 54 | Base 55 | ========================================================================== */ 56 | 57 | /* 58 | * 1. Sets default font family to sans-serif. 59 | * 2. Prevents iOS text size adjust after orientation change, without disabling 60 | * user zoom. 61 | */ 62 | 63 | html { 64 | font-family: sans-serif; /* 1 */ 65 | -webkit-text-size-adjust: 100%; /* 2 */ 66 | -ms-text-size-adjust: 100%; /* 2 */ 67 | } 68 | 69 | /* 70 | * Removes default margin. 71 | */ 72 | 73 | body { 74 | margin: 0; 75 | } 76 | 77 | /* ========================================================================== 78 | Links 79 | ========================================================================== */ 80 | 81 | /* 82 | * Addresses `outline` inconsistency between Chrome and other browsers. 83 | */ 84 | 85 | a:focus { 86 | outline: thin dotted; 87 | } 88 | 89 | /* 90 | * Improves readability when focused and also mouse hovered in all browsers. 91 | */ 92 | 93 | a:active, 94 | a:hover { 95 | outline: 0; 96 | } 97 | 98 | /* ========================================================================== 99 | Typography 100 | ========================================================================== */ 101 | 102 | /* 103 | * Addresses `h1` font sizes within `section` and `article` in Firefox 4+, 104 | * Safari 5, and Chrome. 105 | */ 106 | 107 | h1 { 108 | font-size: 2em; 109 | } 110 | 111 | /* 112 | * Addresses styling not present in IE 8/9, Safari 5, and Chrome. 113 | */ 114 | 115 | abbr[title] { 116 | border-bottom: 1px dotted; 117 | } 118 | 119 | /* 120 | * Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome. 121 | */ 122 | 123 | b, 124 | strong { 125 | font-weight: bold; 126 | } 127 | 128 | /* 129 | * Addresses styling not present in Safari 5 and Chrome. 130 | */ 131 | 132 | dfn { 133 | font-style: italic; 134 | } 135 | 136 | /* 137 | * Addresses styling not present in IE 8/9. 138 | */ 139 | 140 | mark { 141 | background: #ff0; 142 | color: #000; 143 | } 144 | 145 | 146 | /* 147 | * Corrects font family set oddly in Safari 5 and Chrome. 148 | */ 149 | 150 | code, 151 | kbd, 152 | pre, 153 | samp { 154 | font-family: monospace, serif; 155 | font-size: 1em; 156 | } 157 | 158 | /* 159 | * Improves readability of pre-formatted text in all browsers. 160 | */ 161 | 162 | pre { 163 | white-space: pre; 164 | white-space: pre-wrap; 165 | word-wrap: break-word; 166 | } 167 | 168 | /* 169 | * Sets consistent quote types. 170 | */ 171 | 172 | q { 173 | quotes: "\201C" "\201D" "\2018" "\2019"; 174 | } 175 | 176 | /* 177 | * Addresses inconsistent and variable font size in all browsers. 178 | */ 179 | 180 | small { 181 | font-size: 80%; 182 | } 183 | 184 | /* 185 | * Prevents `sub` and `sup` affecting `line-height` in all browsers. 186 | */ 187 | 188 | sub, 189 | sup { 190 | font-size: 75%; 191 | line-height: 0; 192 | position: relative; 193 | vertical-align: baseline; 194 | } 195 | 196 | sup { 197 | top: -0.5em; 198 | } 199 | 200 | sub { 201 | bottom: -0.25em; 202 | } 203 | 204 | /* ========================================================================== 205 | Embedded content 206 | ========================================================================== */ 207 | 208 | /* 209 | * Removes border when inside `a` element in IE 8/9. 210 | */ 211 | 212 | img { 213 | border: 0; 214 | } 215 | 216 | /* 217 | * Corrects overflow displayed oddly in IE 9. 218 | */ 219 | 220 | svg:not(:root) { 221 | overflow: hidden; 222 | } 223 | 224 | /* ========================================================================== 225 | Figures 226 | ========================================================================== */ 227 | 228 | /* 229 | * Addresses margin not present in IE 8/9 and Safari 5. 230 | */ 231 | 232 | figure { 233 | margin: 0; 234 | } 235 | 236 | /* ========================================================================== 237 | Forms 238 | ========================================================================== */ 239 | 240 | /* 241 | * Define consistent border, margin, and padding. 242 | */ 243 | 244 | fieldset { 245 | border: 1px solid #c0c0c0; 246 | margin: 0 2px; 247 | padding: 0.35em 0.625em 0.75em; 248 | } 249 | 250 | /* 251 | * 1. Corrects color not being inherited in IE 8/9. 252 | * 2. Remove padding so people aren't caught out if they zero out fieldsets. 253 | */ 254 | 255 | legend { 256 | border: 0; /* 1 */ 257 | padding: 0; /* 2 */ 258 | } 259 | 260 | /* 261 | * 1. Corrects font family not being inherited in all browsers. 262 | * 2. Corrects font size not being inherited in all browsers. 263 | * 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome 264 | */ 265 | 266 | button, 267 | input, 268 | select, 269 | textarea { 270 | font-family: inherit; /* 1 */ 271 | font-size: 100%; /* 2 */ 272 | margin: 0; /* 3 */ 273 | } 274 | 275 | /* 276 | * Addresses Firefox 4+ setting `line-height` on `input` using `!important` in 277 | * the UA stylesheet. 278 | */ 279 | 280 | button, 281 | input { 282 | line-height: normal; 283 | } 284 | 285 | /* 286 | * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` 287 | * and `video` controls. 288 | * 2. Corrects inability to style clickable `input` types in iOS. 289 | * 3. Improves usability and consistency of cursor style between image-type 290 | * `input` and others. 291 | */ 292 | 293 | button, 294 | html input[type="button"], /* 1 */ 295 | input[type="reset"], 296 | input[type="submit"] { 297 | -webkit-appearance: button; /* 2 */ 298 | cursor: pointer; /* 3 */ 299 | } 300 | 301 | /* 302 | * Re-set default cursor for disabled elements. 303 | */ 304 | 305 | button[disabled], 306 | input[disabled] { 307 | cursor: default; 308 | } 309 | 310 | /* 311 | * 1. Addresses box sizing set to `content-box` in IE 8/9. 312 | * 2. Removes excess padding in IE 8/9. 313 | */ 314 | 315 | input[type="checkbox"], 316 | input[type="radio"] { 317 | box-sizing: border-box; /* 1 */ 318 | padding: 0; /* 2 */ 319 | } 320 | 321 | /* 322 | * 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome. 323 | * 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome 324 | * (include `-moz` to future-proof). 325 | */ 326 | 327 | input[type="search"] { 328 | -webkit-appearance: textfield; /* 1 */ 329 | -moz-box-sizing: content-box; 330 | -webkit-box-sizing: content-box; /* 2 */ 331 | box-sizing: content-box; 332 | } 333 | 334 | /* 335 | * Removes inner padding and search cancel button in Safari 5 and Chrome 336 | * on OS X. 337 | */ 338 | 339 | input[type="search"]::-webkit-search-cancel-button, 340 | input[type="search"]::-webkit-search-decoration { 341 | -webkit-appearance: none; 342 | } 343 | 344 | /* 345 | * Removes inner padding and border in Firefox 4+. 346 | */ 347 | 348 | button::-moz-focus-inner, 349 | input::-moz-focus-inner { 350 | border: 0; 351 | padding: 0; 352 | } 353 | 354 | /* 355 | * 1. Removes default vertical scrollbar in IE 8/9. 356 | * 2. Improves readability and alignment in all browsers. 357 | */ 358 | 359 | textarea { 360 | overflow: auto; /* 1 */ 361 | vertical-align: top; /* 2 */ 362 | } 363 | 364 | /* ========================================================================== 365 | Tables 366 | ========================================================================== */ 367 | 368 | /* 369 | * Remove most spacing between table cells. 370 | */ 371 | 372 | table { 373 | border-collapse: collapse; 374 | border-spacing: 0; 375 | } -------------------------------------------------------------------------------- /app/client/css/project.css: -------------------------------------------------------------------------------- 1 | /* line 4, ../scss/project.scss */ 2 | a { 3 | color: #000011; 4 | font-weight: bold; 5 | text-decoration: none; 6 | } 7 | /* line 18, ../../../../../.rvm/gems/ruby-1.9.3-p194/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/typography/links/_link-colors.scss */ 8 | a:visited { 9 | color: #000011; 10 | } 11 | /* line 21, ../../../../../.rvm/gems/ruby-1.9.3-p194/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/typography/links/_link-colors.scss */ 12 | a:focus { 13 | color: silver; 14 | } 15 | /* line 24, ../../../../../.rvm/gems/ruby-1.9.3-p194/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/typography/links/_link-colors.scss */ 16 | a:hover { 17 | color: silver; 18 | } 19 | /* line 27, ../../../../../.rvm/gems/ruby-1.9.3-p194/gems/compass-0.12.2/frameworks/compass/stylesheets/compass/typography/links/_link-colors.scss */ 20 | a:active { 21 | color: #000011; 22 | } 23 | 24 | /* line 10, ../scss/project.scss */ 25 | * { 26 | font-family: 'Gudea', sans-serif; 27 | text-transform: lowercase; 28 | } 29 | 30 | /* line 15, ../scss/project.scss */ 31 | h3 { 32 | margin: 0px; 33 | } 34 | 35 | /* line 19, ../scss/project.scss */ 36 | ul, li { 37 | list-style-type: none; 38 | margin: 0px; 39 | padding: 0px; 40 | } 41 | 42 | /* line 25, ../scss/project.scss */ 43 | body { 44 | background-color: #222222; 45 | background: url("/img/bg.png"); 46 | overflow: hidden; 47 | } 48 | 49 | /* line 32, ../scss/project.scss */ 50 | button, .topper-info { 51 | position: relative; 52 | overflow: visible; 53 | display: inline-block; 54 | padding: 0.5em 1em; 55 | margin: 0; 56 | text-decoration: none; 57 | text-align: center; 58 | text-shadow: 1px 1px 0 #fff; 59 | font-size: 20px; 60 | color: #333; 61 | } 62 | /* line 46, ../scss/project.scss */ 63 | button.new-game, button.continue, .topper-info.new-game, .topper-info.continue { 64 | font-weight: bold; 65 | margin: 30px 0px 30px 275px; 66 | width: 350px; 67 | padding: 10px 20px; 68 | } 69 | 70 | /* line 55, ../scss/project.scss */ 71 | button, .topper-info, .ossprojects li, .view-gamepad { 72 | border: 1px solid #d4d4d4; 73 | white-space: nowrap; 74 | cursor: pointer; 75 | outline: none; 76 | background-color: #ececec; 77 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f4f4f4), to(#ececec)); 78 | background-image: -moz-linear-gradient(#f4f4f4, #ececec); 79 | background-image: -ms-linear-gradient(#f4f4f4, #ececec); 80 | background-image: -o-linear-gradient(#f4f4f4, #ececec); 81 | background-image: linear-gradient(#f4f4f4, #ececec); 82 | -moz-background-clip: padding; 83 | /* for Firefox 3.6 */ 84 | background-clip: padding-box; 85 | border-radius: 0.2em; 86 | } 87 | 88 | /* line 72, ../scss/project.scss */ 89 | .view-gamepad { 90 | display: block; 91 | text-align: center; 92 | padding: 2px; 93 | margin-top: 5px; 94 | font-weight: normal; 95 | } 96 | 97 | /* line 80, ../scss/project.scss */ 98 | button:hover, .ossprojects li:hover { 99 | background-color: #ffffff; 100 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f4f4f4), to(white)); 101 | background-image: -moz-linear-gradient(#f4f4f4, white); 102 | background-image: -ms-linear-gradient(#f4f4f4, white); 103 | background-image: -o-linear-gradient(#f4f4f4, white); 104 | background-image: linear-gradient(#f4f4f4, #ffffff); 105 | } 106 | 107 | /* line 89, ../scss/project.scss */ 108 | .topper-info { 109 | color: #FFF; 110 | text-transform: lowercase; 111 | border-radius: 0; 112 | cursor: none; 113 | width: 100%; 114 | font-size: 10px; 115 | color: #000011; 116 | } 117 | 118 | /* line 99, ../scss/project.scss */ 119 | .control-help { 120 | font-size: 10px; 121 | color: #FFF; 122 | width: 100px; 123 | position: absolute; 124 | bottom: 0px; 125 | right: 0px; 126 | width: 100px; 127 | } 128 | 129 | /* line 109, ../scss/project.scss */ 130 | .gamepad-status { 131 | position: absolute; 132 | bottom: 100px; 133 | right: 0px; 134 | font-size: 12px; 135 | width: 100px; 136 | color: #FFF; 137 | } 138 | /* line 116, ../scss/project.scss */ 139 | .gamepad-status .connected { 140 | background: #baff72; 141 | } 142 | 143 | /* line 121, ../scss/project.scss */ 144 | .req-webgl .continue { 145 | margin: 85px 0px 30px 170px; 146 | width: 600px; 147 | font-size: 23px; 148 | } 149 | 150 | /* line 127, ../scss/project.scss */ 151 | .player-list li { 152 | display: inline; 153 | } 154 | 155 | /* line 132, ../scss/project.scss */ 156 | .req-welcome h3 { 157 | color: #333333; 158 | margin-left: 6px; 159 | } 160 | 161 | /* line 139, ../scss/project.scss */ 162 | .req-gameover h1 { 163 | margin-lefT: 75px; 164 | } 165 | /* line 142, ../scss/project.scss */ 166 | .req-gameover .window p { 167 | margin-left: 35px; 168 | } 169 | 170 | /* line 148, ../scss/project.scss */ 171 | .req-project aside { 172 | font-size: 20px; 173 | margin: 20px 0px 20px 35px; 174 | } 175 | 176 | /* line 154, ../scss/project.scss */ 177 | .frames { 178 | width: 990px; 179 | margin: 0 auto; 180 | position: relative; 181 | } 182 | 183 | /* line 160, ../scss/project.scss */ 184 | .life { 185 | height: 20px; 186 | width: 100%; 187 | background-color: #cdeb8e; 188 | background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #cdeb8e), color-stop(100%, #a5c956)); 189 | background-image: -webkit-linear-gradient(top, #cdeb8e 0%, #a5c956 100%); 190 | background-image: -moz-linear-gradient(top, #cdeb8e 0%, #a5c956 100%); 191 | background-image: -o-linear-gradient(top, #cdeb8e 0%, #a5c956 100%); 192 | background-image: linear-gradient(top, #cdeb8e 0%, #a5c956 100%); 193 | text-transform: lowercase; 194 | } 195 | 196 | /* line 172, ../scss/project.scss */ 197 | .frame { 198 | position: absolute; 199 | width: 990px; 200 | top: -1000px; 201 | margin: 0 auto; 202 | } 203 | /* line 177, ../scss/project.scss */ 204 | .frame h1 { 205 | font-weight: normal; 206 | font-size: 60px; 207 | text-shadow: 2px 1px 4px #272626; 208 | height: 90px; 209 | color: #F3F5F7; 210 | margin: 0px 0px 0px 45px; 211 | padding: 0px; 212 | text-transform: lowercase; 213 | } 214 | /* line 187, ../scss/project.scss */ 215 | .frame .window { 216 | -webkit-box-shadow: "inset 0px 1px 0px rgba(255,255,255, 0.25), 0px 10px 20px rgba(0,0,0, 0.7)"; 217 | -moz-box-shadow: "inset 0px 1px 0px rgba(255,255,255, 0.25), 0px 10px 20px rgba(0,0,0, 0.7)"; 218 | box-shadow: "inset 0px 1px 0px rgba(255,255,255, 0.25), 0px 10px 20px rgba(0,0,0, 0.7)"; 219 | width: 900px; 220 | overflow: hidden; 221 | background: #FFF; 222 | position: relative; 223 | min-height: 250px; 224 | max-height: 460px; 225 | margin: 0 auto; 226 | } 227 | /* line 196, ../scss/project.scss */ 228 | .frame .window .part { 229 | width: 25%; 230 | float: left; 231 | } 232 | /* line 199, ../scss/project.scss */ 233 | .frame .window .part h3 { 234 | margin: 20px 0px 0px 0px; 235 | } 236 | /* line 203, ../scss/project.scss */ 237 | .frame .window .part-large { 238 | float: left; 239 | width: 70%; 240 | padding-right: 4%; 241 | } 242 | /* line 207, ../scss/project.scss */ 243 | .frame .window .part-large ul { 244 | margin-left: 5%; 245 | } 246 | /* line 210, ../scss/project.scss */ 247 | .frame .window .part-large li { 248 | font-size: 18px; 249 | margin: 10px 0px 10px 0px; 250 | list-style-type: square; 251 | } 252 | /* line 216, ../scss/project.scss */ 253 | .frame .window h1 { 254 | margin: 0px; 255 | text-align: center; 256 | font-weight: bold; 257 | text-transform: uppercase; 258 | color: #c0c0c0; 259 | } 260 | /* line 222, ../scss/project.scss */ 261 | .frame .window h1.logo span { 262 | color: #000011; 263 | } 264 | /* line 226, ../scss/project.scss */ 265 | .frame .window .under-logo { 266 | position: absolute; 267 | top: 47px; 268 | left: 350px; 269 | } 270 | 271 | /* line 234, ../scss/project.scss */ 272 | .plays { 273 | clear: both; 274 | } 275 | /* line 236, ../scss/project.scss */ 276 | .plays .play { 277 | overflow: hidden; 278 | float: left; 279 | width: 9%; 280 | font-size: 10px; 281 | border-left: 1px solid grey; 282 | padding: 0.7%; 283 | margin: 0.7%; 284 | white-space: nowrap; 285 | } 286 | 287 | /* line 248, ../scss/project.scss */ 288 | .progress { 289 | display: none; 290 | } 291 | 292 | /* line 252, ../scss/project.scss */ 293 | .ossprojects { 294 | overflow: hidden; 295 | margin: 0px 0px 20px 25px; 296 | } 297 | /* line 253, ../scss/project.scss */ 298 | .ossprojects li { 299 | cursor: pointer; 300 | float: left; 301 | width: 20%; 302 | margin: 1%; 303 | padding: 1%; 304 | height: 100px; 305 | overflow: hidden; 306 | } 307 | /* line 261, ../scss/project.scss */ 308 | .ossprojects li span { 309 | display: block; 310 | } 311 | /* line 265, ../scss/project.scss */ 312 | .ossprojects li .name { 313 | font-weight: bold; 314 | text-align: center; 315 | font-size: 24px; 316 | margin-bottom: 30px; 317 | } 318 | 319 | /* line 279, ../scss/project.scss */ 320 | .about { 321 | position: absolute; 322 | color: #FFF; 323 | left: 0px; 324 | bottom: 0px; 325 | font-size: 10px; 326 | } 327 | /* line 285, ../scss/project.scss */ 328 | .about a { 329 | color: #FFF; 330 | } 331 | 332 | /* line 290, ../scss/project.scss */ 333 | .again { 334 | position: absolute; 335 | top: 40px; 336 | right: 36px; 337 | width: 200px; 338 | } 339 | 340 | /* line 297, ../scss/project.scss */ 341 | .g-on { 342 | color: green; 343 | } 344 | 345 | /* line 301, ../scss/project.scss */ 346 | .level { 347 | filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=70); 348 | opacity: 0.7; 349 | display: none; 350 | background-color: #fc4e1e; 351 | border-radius: 10px; 352 | height: 100px; 353 | width: 10%; 354 | top: 80px; 355 | margin-left: -10%; 356 | left: 50%; 357 | text-align: center; 358 | padding: 20px; 359 | position: absolute; 360 | } 361 | /* line 314, ../scss/project.scss */ 362 | .level div { 363 | margin-top: 10px; 364 | font-size: 24px; 365 | } 366 | /* line 317, ../scss/project.scss */ 367 | .level div span { 368 | font-size: 45px; 369 | display: block; 370 | font-weight: bold; 371 | margin: 0auto; 372 | } 373 | 374 | /* line 326, ../scss/project.scss */ 375 | .window button.music { 376 | padding: 2px; 377 | font-size: 15px; 378 | margin-top: 5px; 379 | } 380 | 381 | /* line 331, ../scss/project.scss */ 382 | body > .control-help .music { 383 | font-size: 12px; 384 | padding: 0px 5px; 385 | margin: 0px; 386 | border: 0px; 387 | text-align: left; 388 | text-shadow: none; 389 | } 390 | -------------------------------------------------------------------------------- /app/client/index.html: -------------------------------------------------------------------------------- 1 | 2 | Core Committer - The Game 3 | 4 | 5 | 6 | 7 | 8 |
9 |
10 | {{> gameover }} 11 |
12 | 13 |
14 | {{> choose }} 15 |
16 | 17 | {{> tutorial }} 18 | {{> welcome }} 19 | {{> webgl }} 20 |
21 | 22 |
23 | {{> dashboard}} 24 | {{> life}} 25 | {{> level}} 26 |
27 | 28 |
29 | {{> help}} 30 |
31 | 32 | {{> about}} 33 | 34 | -------------------------------------------------------------------------------- /app/client/js/game/App.js: -------------------------------------------------------------------------------- 1 | var PX_HIDE = -1500, 2 | PX_SHOW = 0, 3 | PX_SPEED = 300, 4 | MUSIC; 5 | 6 | var RecentScores = new Meteor.Collection("recent_scores"), 7 | TopScores = new Meteor.Collection("top_scores"); 8 | 9 | 10 | // shim layer with setTimeout fallback 11 | window.requestAnimFrame = (function(){ 12 | return window.requestAnimationFrame || 13 | window.webkitRequestAnimationFrame || 14 | window.mozRequestAnimationFrame || 15 | window.oRequestAnimationFrame || 16 | window.msRequestAnimationFrame || 17 | function( callback ){ 18 | window.setTimeout(callback, 1000 / 60); 19 | }; 20 | })(); 21 | 22 | Meteor.subscribe('projects'); 23 | Meteor.subscribe("recent_scores"); 24 | Meteor.subscribe("top_scores"); 25 | 26 | 27 | /** 28 | * Meteor startup! 29 | */ 30 | Meteor.startup(function () { 31 | Session.set('gameOn', false); 32 | Session.set('sessionLevel', 0); 33 | Session.set('sessionStars', 0); 34 | Session.set('sessionCommits', 0); 35 | Session.set('pid', null); 36 | Session.set('stats', null); 37 | Session.set('gamepadStatus', 'off'); 38 | 39 | if (! Session.get('musicStatus')) Session.set('musicStatus', 'on'); 40 | 41 | if (Modernizr.audio) { 42 | MUSIC = new SoundTrack(); 43 | } 44 | //if (Modernizr.webgl){ 45 | if ( Detector.webgl ) { 46 | //$('.req-webgl').animate({ top: PX_SHOW }); 47 | $('.req-welcome').animate({ top: PX_SHOW }); 48 | //startGame(); // sup debug 49 | 50 | } else { 51 | $('.req-webgl').animate({ top: PX_SHOW }); 52 | } 53 | }); 54 | 55 | 56 | /** 57 | * Meteor autorun 58 | */ 59 | Meteor.autorun(function() { 60 | if (Meteor.user() && Session.equals("gameOver", true)) { 61 | Meteor.call('userUpdate', { data: Session.get('st') }); 62 | Session.set('gameOver', false); 63 | } 64 | }); 65 | 66 | /** 67 | * GitHub Login setup 68 | */ 69 | Meteor.loginWithGithub({ 70 | requestPermissions: ['user', 'public_repo'] 71 | }, function (err) { 72 | if (err) { 73 | Session.set('errorMessage', err.reason || 'Unknown error'); 74 | } 75 | }); 76 | 77 | 78 | /** 79 | * Let the games begin 80 | */ 81 | function startGame() { 82 | 83 | var cc; 84 | var $lifeBar = $('.life'), 85 | $progress = $('.progress'); 86 | 87 | 88 | cc = new CoreCommit(Session.get("pid")); 89 | cc.addEventListener('decreaseLife', function (e) { 90 | $lifeBar.css('width', e.data + '%'); 91 | }); 92 | 93 | cc.addEventListener('decreaseLife', function (e) { 94 | $lifeBar.css('width', e.data + '%'); 95 | }); 96 | 97 | cc.addEventListener('start', function (e) { 98 | $progress.fadeIn(); 99 | }); 100 | 101 | cc.addEventListener('session', function (e) { 102 | Session.set('sessionCommits', e.detail.COMMITS); 103 | Session.set('sessionStars', e.detail.STARS); 104 | Meteor.call('sessionUpdate', { data: e.detail }); 105 | }); 106 | 107 | cc.addEventListener('gamepadConnected', function (e) { 108 | Session.set('gamepadStatus', 'on'); 109 | }); 110 | 111 | cc.addEventListener('gamepadDisconnected', function (e) { 112 | Session.set('gamepadStatus', 'off'); 113 | }); 114 | 115 | cc.addEventListener('levelUpdate', function (e) { 116 | Session.set("sessionLevel", e.detail); 117 | }); 118 | 119 | cc.addEventListener('gameOver', function (e) { 120 | $('.req-gameover').animate({ top: PX_SHOW }); 121 | Session.set("gameOver", true); 122 | if (e.detail.LEVEL == 0 && e.detail.COMMITS == 0 && e.detail.STARS == 0) { 123 | e.detail.EPIC_FAIL = true; 124 | } 125 | Session.set('stats', e.detail); 126 | Session.set('st', e.detail.START_TIME); 127 | $progress.slideUp(); 128 | Session.set('gameOn', false); 129 | }); 130 | 131 | cc.start(); 132 | Session.set('gameOn', true); 133 | if (Session.get('musicStatus') == 'on') { 134 | MUSIC.play(); 135 | } 136 | 137 | } 138 | -------------------------------------------------------------------------------- /app/client/js/game/Game.js: -------------------------------------------------------------------------------- 1 | (function (exports) { 2 | 3 | var CoreCommit = function (project) { 4 | // standard global variables 5 | var container, scene, camera, stats, self = this; 6 | 7 | this.setGameVariables(project); 8 | // enable events for this object 9 | this.extendAsEventDispatcher(); 10 | // add custom events 11 | this.addCustomEvents(); 12 | 13 | // CAMERA 14 | var VIEW_ANGLE = 45, ASPECT = this.SCREEN_WIDTH / this.SCREEN_HEIGHT, NEAR = 0.1, FAR = 20000; 15 | camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR); 16 | camera.position.set(1200, 0, 4000); 17 | 18 | this.camera = camera; 19 | this.camera.up.set(0,0,0); 20 | 21 | // SCENE 22 | scene = new THREE.Scene(); 23 | scene.position.set(0, 0, 0); 24 | this.scene = scene; 25 | scene.add(camera); 26 | 27 | // ROTATOR 28 | var crateMaterial = new THREE.MeshBasicMaterial({ color:0xff4747, transparent:true, opacity:0 }); 29 | this.__rotator = new THREE.Mesh(THREE.GeometryUtils.clone(new THREE.CubeGeometry(60, 25, 25)), crateMaterial); 30 | this.__rotator.position.set(0,0,0); 31 | this.scene.add(this.__rotator); 32 | 33 | // RENDERER 34 | this.renderer = new THREE.WebGLRenderer({antialias:false}); 35 | this.renderer.setSize(this.SCREEN_WIDTH, this.SCREEN_HEIGHT); 36 | 37 | container = document.createElement('div'); 38 | document.body.appendChild(container); 39 | container.appendChild(this.renderer.domElement); 40 | 41 | // EVENTS 42 | THREEx.WindowResize(this.renderer, this.camera); 43 | //THREEx.FullScreen.bindKey({ charCode : 'm'.charCodeAt(0) }); 44 | // CONTROLS 45 | //controls = new THREE.TrackballControls( camera ); 46 | 47 | // STATS 48 | stats = new Stats(); 49 | stats.domElement.style.position = 'absolute'; 50 | stats.domElement.style.bottom = '0px'; 51 | stats.domElement.style.zIndex = 100; 52 | this.stats = stats; 53 | //container.appendChild(stats.domElement); 54 | 55 | // Create git branches 56 | this.generateBranches(); 57 | // Load Textures 58 | this.loadTextures(); 59 | 60 | // GAMEPAD SUPPORT 61 | // Add gamepad support 62 | this.prevRawGamepadTypes = []; 63 | this.gamepad = new GamepadSupport(); 64 | 65 | // Add a new player 66 | this.player = new Player(); 67 | // set the current view 68 | this.player.view = 4; 69 | scene.add(this.player.mesh); 70 | }; 71 | 72 | 73 | /** 74 | * Start the game 75 | */ 76 | CoreCommit.prototype.start = function () { 77 | var self = this; 78 | this.dispatchEvent({type:'start'}); 79 | var endAnim = new TWEEN.Tween(this.camera.position) 80 | .onUpdate(function(){ 81 | self.camera.lookAt(self.scene.position); 82 | }) 83 | .to({ x:0, z:2200 }, 1500) 84 | .easing(TWEEN.Easing.Sinusoidal.In).start(); 85 | 86 | 87 | 88 | this.animate(); // keep last 89 | 90 | this.addEventListener('handleCollision', function (e) { 91 | self.handleCollision(e.data); 92 | }); 93 | }; 94 | 95 | 96 | /** 97 | * Animate the game 98 | */ 99 | CoreCommit.prototype.animate = function () { 100 | // support gamepads 101 | this.gamepad.checkGamepadSupport(); 102 | 103 | // game frame update 104 | this.update(); 105 | 106 | // animates tweens 107 | TWEEN.update(); 108 | 109 | // increase frames 110 | this.FRAME++; 111 | 112 | // game over 113 | if (!this.PAUSED) window.requestAnimFrame(this.animate.bind(this)); 114 | }; 115 | 116 | 117 | CoreCommit.prototype.sendSession = function (s) { 118 | this.dispatchEvent(this.sendSessionEvent); 119 | }; 120 | 121 | 122 | /** 123 | * increase game level, change difficulty and speeds 124 | */ 125 | CoreCommit.prototype.trackGameLevel = function () { 126 | var level, 127 | minSpeed = 15000; 128 | 129 | if (this.FRAME > 1000) { 130 | level = parseInt(this.FRAME / 1000, 10); 131 | if (level > this.SESSION.LEVEL) { 132 | // up level! 133 | this.SESSION.LEVEL = level; 134 | this.dispatchEvent(new CustomEvent('levelUpdate', { 'detail':this.SESSION.LEVEL })); 135 | 136 | if (this.__CRATE_RATE > 65) this.__CRATE_RATE -= 8; 137 | // Increase speed here too. 138 | if (this.SPEED > minSpeed) { this.SPEED -= 700 } else { this.speed -=300 }; 139 | } 140 | } 141 | }; 142 | 143 | 144 | /** 145 | * update frame 146 | */ 147 | CoreCommit.prototype.update = function () { 148 | 149 | this.renderer.render(this.scene, this.camera); 150 | 151 | var self = this, 152 | kb = this.keyboard; // keyboard 153 | 154 | 155 | if (this.LIFE <= 0) { 156 | this.endGame(); 157 | } 158 | 159 | // up the speeds, show notifications 160 | this.trackGameLevel(); 161 | 162 | if (this.gamepad.GAMEPADS && this.gamepad.GAMEPADS.length > 0) { 163 | if (! this.gamepadConnected) { 164 | this.gamepadConnected = true; 165 | this.dispatchEvent(new CustomEvent('gamepadConnected')); 166 | } 167 | for (var i in this.gamepad.GAMEPADS) { 168 | var gamepad = this.gamepad.GAMEPADS[i]; 169 | if (gamepad.buttons[0] && this.AMMO) { // A - attack 170 | this.AMMO = false; 171 | this.NEW_BULLET = this.FRAME + 20; 172 | self.playerShoot(); 173 | } 174 | 175 | if (gamepad.buttons[8] || gamepad.buttons[9] || gamepad.buttons[4] || gamepad.buttons[5]) { 176 | if (!this.SWITCHING) { 177 | if (this.player.view == this.VIEW.TOP) { 178 | self.switchMode('3d'); 179 | } else { 180 | self.switchMode('top'); 181 | } 182 | } 183 | } 184 | 185 | if (gamepad.buttons[2]) { 186 | if (!this.SWITCHING && this.player.view != this.VIEW.TOP) { 187 | self.rotateMode('left', true); 188 | } else if (!this._teleporting) { 189 | self.teleportPlayer(null, 'left'); 190 | } 191 | } 192 | 193 | if (gamepad.buttons[3]) { 194 | if (!this.SWITCHING && this.player.view != this.VIEW.TOP) { 195 | self.rotateMode('right', true); 196 | } else if (!this._teleporting) { 197 | self.teleportPlayer(); 198 | } 199 | } 200 | 201 | // feature disabled, always true 202 | if (gamepad.buttons[6]) { 203 | if (!this.SWITCHING && this.player.view != this.VIEW.TOP) { 204 | self.rotateMode('left', true); 205 | } else if (!this._teleporting) { 206 | self.teleportPlayer(null, 'left'); 207 | } 208 | } 209 | 210 | if (gamepad.buttons[7] || gamepad.buttons[3]) { 211 | if (!this.SWITCHING && this.player.view != this.VIEW.TOP) { 212 | self.rotateMode('right', true); 213 | } else if (!this._teleporting) { 214 | self.teleportPlayer(); 215 | } 216 | } 217 | 218 | if (gamepad.buttons[1]) { 219 | if (!this._teleporting) { 220 | self.teleportPlayer(); 221 | } 222 | } 223 | 224 | if (gamepad.buttons[12] || gamepad.axes[1] < -0.25 || gamepad.axes[3] < -0.25) { // up 225 | this.player.moveUpLeft(this.player.branch, this.VIEW); 226 | } 227 | if (gamepad.buttons[13] || gamepad.axes[1] > 0.25 || gamepad.axes[3] > 0.25) { // down 228 | this.player.moveDownRight(this.player.branch, this.VIEW); 229 | } 230 | if (gamepad.buttons[14] || gamepad.axes[0] < -0.25 || gamepad.axes[2] < -0.25) { // left 231 | this.player.moveUpLeft(this.player.branch, this.VIEW); 232 | } 233 | if (gamepad.buttons[15] || gamepad.axes[0] > 0.25 || gamepad.axes[2] > 0.25) { // right 234 | this.player.moveDownRight(this.player.branch, this.VIEW); 235 | } 236 | } 237 | } else { 238 | if (this.gamepadConnected) { 239 | this.gamepadConnected = false; 240 | this.dispatchEvent('gamepadDisconnected'); 241 | } 242 | } 243 | 244 | // teleport between branches 245 | if (kb.pressed('tab')) { 246 | if (!this.SWITCHING && this.player.view != this.VIEW.TOP) { 247 | self.rotateMode('right', true); 248 | } else if (!this._teleporting) { 249 | self.teleportPlayer(); 250 | } 251 | } 252 | 253 | if (( kb.pressed('up') || kb.pressed('left') )) { 254 | this.player.moveUpLeft(this.player.branch, this.VIEW); 255 | } 256 | 257 | if (kb.pressed('down') || kb.pressed('right')) { 258 | this.player.moveDownRight(this.player.branch, this.VIEW); 259 | } 260 | 261 | 262 | if (kb.pressed('space') && (this.AMMO)) { 263 | this.AMMO = false; 264 | this.NEW_BULLET = this.FRAME + 20; 265 | self.playerShoot(); 266 | } 267 | 268 | if ((this.NEW_BULLET == this.FRAME)) { 269 | this.AMMO = true; 270 | } 271 | 272 | if (kb.pressed('e')) { 273 | if (!this.SWITCHING) { 274 | if (this.player.view == this.VIEW.TOP) { 275 | self.switchMode('3d'); 276 | } else { 277 | self.switchMode('top'); 278 | } 279 | } 280 | } 281 | 282 | 283 | if (kb.pressed('q')) { 284 | if (!this.SWITCHING && this.player.view != this.VIEW.TOP) { 285 | self.rotateMode('left', true); 286 | } else if (!this._teleporting) { 287 | self.teleportPlayer(null, 'left'); 288 | } 289 | } 290 | 291 | if (kb.pressed('w')) { 292 | if (!this.SWITCHING && this.player.view != this.VIEW.TOP) { 293 | self.rotateMode('right', true); 294 | } else if (!this._teleporting) { 295 | self.teleportPlayer(); 296 | } 297 | } 298 | 299 | // generate game grates 300 | if (this.NEW_CRATE_TIMER == this.FRAME) { 301 | this.NEW_CRATE_TIMER = this.FRAME + this.__CRATE_RATE; 302 | self.generateCrates(); 303 | } 304 | 305 | _.each(self.BULLETS, function (bullet) { 306 | // detect bullet collisions 307 | self.detectCollision(bullet); 308 | 309 | }); 310 | 311 | var timer = ( Date.now() * 0.04 ); 312 | if (self.CRATES_GENERATING) { 313 | _.each(self.CRATES, function (crate) { 314 | 315 | crate.rotation.y = 0.01 * timer; 316 | crate.rotation.x = 0.01 * timer; 317 | }); 318 | } 319 | 320 | // STATS 321 | //this.stats.update(); 322 | }; 323 | 324 | 325 | /** 326 | * detect collision 327 | * @param bullet 328 | */ 329 | CoreCommit.prototype.detectCollision = function (bullet) { 330 | var self = this, 331 | cObj, 332 | originPoint = bullet.position.clone(); 333 | 334 | // strange issue here, .intersectObjects(objects) used to report a lot of false collisions. 335 | _.each(self.CRATES, function (crate) { 336 | for (var vertexIndex = 0; vertexIndex < bullet.geometry.vertices.length; vertexIndex++) { 337 | var localVertex = bullet.geometry.vertices[vertexIndex].clone(); 338 | //var globalVertex = bullet.matrix.multiplyVector3(localVertex); 339 | //var directionVector = globalVertex.subSelf(bullet.position); 340 | 341 | var ray = new THREE.Ray(originPoint, localVertex.clone().normalize()); 342 | var collisionResults = ray.intersectObject(crate); 343 | if (collisionResults.length > 0 ) { 344 | cObj = collisionResults[0].object; 345 | break; 346 | } 347 | } 348 | }); 349 | 350 | if (cObj) { 351 | self.scene.remove(cObj); 352 | self.scene.remove(bullet); 353 | cObj.dead = true; 354 | bullet.tween.stop(); 355 | self.BULLETS = _.without(self.BULLETS, bullet); 356 | self.CRATES = _.without(self.CRATES, cObj); 357 | 358 | // TODO: this works, but wrong. fix later 359 | var e = {type:'handleCollision', data:cObj }; 360 | self.dispatchEvent(e); 361 | } 362 | }; 363 | 364 | 365 | /** 366 | * switch to a different branch 367 | */ 368 | CoreCommit.prototype.gameSwitchBranch = function () { 369 | var newBranch = this.GAME_BRANCH; 370 | while (newBranch == this.GAME_BRANCH) { 371 | newBranch = parseInt(Math.random() * 3, 10); // choose a branch from 0 to 2 372 | } 373 | this.GAME_BRANCH = newBranch; 374 | _.each(this.branches, function (branch) { 375 | branch.unsetCurrent(); 376 | }); 377 | this.branches[this.GAME_BRANCH].setCurrent(); 378 | }; 379 | 380 | 381 | /** 382 | * handleCollision for the crates 383 | * @param crate 384 | */ 385 | CoreCommit.prototype.handleCollision = function (crate) { 386 | var crateType = crate.type; 387 | if (crate.regularCrate) { 388 | // switch branch if you killed a good code crate :( 389 | if (crate.goodCrate) { 390 | this.decreaseLife(); 391 | if (this.GAME_BRANCH == crate.targetBranch) { 392 | this.gameSwitchBranch(); 393 | } 394 | } 395 | } else { 396 | switch (crateType) { 397 | case 0: // changeBranch 398 | this.gameSwitchBranch(); 399 | break; 400 | case 1: // star 401 | this.sendSession(this.SESSION); 402 | break; 403 | default: 404 | this.sendSession(this.SESSION); 405 | break 406 | } 407 | } 408 | }; 409 | 410 | 411 | /** 412 | * endGame 413 | */ 414 | CoreCommit.prototype.endGame = function () { 415 | var self = this; 416 | 417 | if (! self._gameover_anim) { 418 | self._gameover_anim = true; 419 | this.SESSION.END_TIME = Date.now(); 420 | this.SESSION.GAMEOVER = true; 421 | var endAnim = new TWEEN.Tween(self.camera.position) 422 | .to({ z:25000 }, 4000) 423 | .easing(TWEEN.Easing.Sinusoidal.In).start(); 424 | 425 | endAnim.onComplete(function () { 426 | self.PAUSED = true; 427 | }); 428 | 429 | this.dispatchEvent(this.gameOverEvent); 430 | this.dispatchEvent(this.sendSessionEvent); 431 | } 432 | }; 433 | 434 | 435 | /** 436 | * rotate 3d view; 437 | * @param type , type of rotation 438 | */ 439 | CoreCommit.prototype.rotateMode = function (type, teleport) { 440 | this.SWITCHING = true; 441 | var self = this; 442 | // TODO: this can be optimized. 443 | var poses = [ 444 | new THREE.Vector3(0, -1600, 900), 445 | new THREE.Vector3(-1600, 0, 900), 446 | new THREE.Vector3(0, 1600, 900) 447 | ]; 448 | 449 | if (type == 'left') { 450 | this.player.view = (this.player.view < 2) ? ++this.player.view : 0; 451 | } else { 452 | this.player.view = (this.player.view > 0) ? --this.player.view : 2; 453 | } 454 | 455 | var anim2 = new TWEEN.Tween(this.camera.position).to(poses[this.player.view], 500) 456 | .easing(TWEEN.Easing.Back.Out) 457 | .onUpdate(function () { 458 | self.camera.lookAt(self.__rotator.position); 459 | }) 460 | .onComplete(function(){ 461 | self.SWITCHING = false; 462 | }) 463 | .start(); 464 | 465 | if (teleport) { 466 | this.teleportPlayer(this.player.view); 467 | } 468 | }; 469 | 470 | 471 | /** 472 | * switch view mode 473 | * @param direction 474 | */ 475 | CoreCommit.prototype.switchMode = function (direction) { 476 | var self = this, 477 | animPos, 478 | cameraLookAt; 479 | 480 | this.SWITCHING = true; 481 | 482 | // switch mode between top and 3d 483 | if(direction == 'top') { 484 | this.camera.up.set(0,0,0); 485 | this.player.view = this.VIEW.TOP; 486 | animPos = new THREE.Vector3(0, 0 , 2200); 487 | } else { 488 | animPos = this.branches[this.player.branch]._cameraPos; 489 | } 490 | 491 | if(direction == '3d') { 492 | this.camera.up.set(0,0,1); 493 | // if we are entering 3D, go to the player branch 494 | animPos = this.branches[this.player.branch]._cameraPos; 495 | this.player.view = this.player.branch; 496 | 497 | } 498 | 499 | var anim2 = new TWEEN.Tween(this.camera.position).to(animPos, 250) 500 | .easing(TWEEN.Easing.Linear.None) 501 | .onUpdate(function () { 502 | self.camera.lookAt(self.__rotator.position); 503 | }) 504 | .start(); 505 | 506 | anim2.onComplete(function () { 507 | self.SWITCHING = false; 508 | }); 509 | }; 510 | 511 | 512 | /** 513 | * handle shooting 514 | */ 515 | CoreCommit.prototype.playerShoot = function () { 516 | var self = this, 517 | shootRate = 1200, 518 | shootAt, 519 | geom; 520 | 521 | // TODO: fix this 522 | var crateMaterial = new THREE.MeshBasicMaterial({ color:0xff4747, transparent:true, opacity:1 }); 523 | 524 | if (this.player.branch == 0 || this.player.branch == 2) { 525 | geom = new THREE.CubeGeometry(25, 60, 25) 526 | } else { 527 | geom = new THREE.CubeGeometry(60, 25, 25) 528 | } 529 | 530 | var bullet = new THREE.Mesh(THREE.GeometryUtils.clone(geom), crateMaterial); 531 | 532 | bullet.position.set(this.player.mesh.position.x, this.player.mesh.position.y, 0); 533 | bullet.bulletId = self.BULLETS.length; 534 | this.scene.add(bullet); 535 | 536 | 537 | switch (this.player.branch) { 538 | case 0: 539 | shootAt = { y:950 }; 540 | break; 541 | case 1: 542 | shootAt = { x:950 }; 543 | break; 544 | case 2: 545 | shootAt = { y:-950 }; 546 | break; 547 | } 548 | 549 | bullet.tween = new TWEEN.Tween(bullet.position).to(shootAt, shootRate) 550 | .easing(TWEEN.Easing.Linear.None).start(); 551 | 552 | bullet.tween.onComplete(function () { 553 | if (bullet) { 554 | self.BULLETS = _.without(self.BULLETS, bullet); 555 | self.scene.remove(bullet); 556 | } 557 | }); 558 | 559 | self.BULLETS.push(bullet); 560 | }; 561 | 562 | 563 | /** 564 | * Generate game crates 565 | */ 566 | CoreCommit.prototype.generateCrates = function () { 567 | var self = this, 568 | textureFile, 569 | regularCrate = false, 570 | crateType, 571 | cStartDistance = 3100, 572 | extraDistance = 0; 573 | 574 | this.CRATES_GENERATING = true; 575 | 576 | // decide if we want to create a special crate 577 | var lucky = parseInt(Math.random() * 50, 10); // random between 0 - 5 578 | if (lucky < 10) { 579 | // set the lucky crate type 580 | crateType = parseInt(Math.random() * 2, 10); // random between 0 =>< 3 581 | textureFile = self.otherCrateList[crateType]; 582 | regularCrate = false; 583 | if (crateType == 1) { // if it is a star 584 | extraDistance = 95; 585 | } 586 | } else { 587 | // create a branch create 588 | regularCrate = true; 589 | crateType = parseInt(Math.random() * 5, 10); // random between 0 - 5 590 | textureFile = self.branchCrateList[crateType]; 591 | } 592 | 593 | this.__CRATE_SIZE = (this.__CRATE_SIZE >= 50) ? --this.__CRATE_SIZE : 50; 594 | var size = this.__CRATE_SIZE; 595 | // crate mesh 596 | var crateMaterial = new THREE.MeshBasicMaterial({ map:textureFile }), 597 | crate = new THREE.Mesh(THREE.GeometryUtils.clone(new THREE.CubeGeometry(size, size, size)), crateMaterial); 598 | 599 | // crate points 600 | var landingPointTween, 601 | startingP = Math.floor(Math.random() * 931) - 465, // random starting point 602 | landingRandomPoint = Math.floor(Math.random() * 931) - 465; // random landing point 603 | 604 | // depending on the branch, the starting point should be different 605 | // also define the tween to fly properly 606 | if (this.GAME_BRANCH == 0) { // from the right 607 | crate.position.set(startingP, cStartDistance, 0); 608 | landingPointTween = { x:landingRandomPoint, y:-485 - extraDistance }; 609 | } 610 | else if (this.GAME_BRANCH == 1) { // from the top 611 | crate.position.set(cStartDistance, startingP, 0); 612 | landingPointTween = { x:-485 - extraDistance, y:landingRandomPoint }; 613 | } 614 | else if (this.GAME_BRANCH == 2) { // from the bottom 615 | crate.position.set(startingP, -cStartDistance, 0); 616 | landingPointTween = { x:landingRandomPoint, y:485 + extraDistance }; 617 | } 618 | 619 | crate.regularCrate = regularCrate; 620 | crate.type = crateType; 621 | crate.dead = false; 622 | crate.targetPosition = landingPointTween; 623 | 624 | // set the targetBranch 625 | var tb; 626 | if ((crate.type == 4) || (crate.type == 5)) tb = 0; 627 | if ((crate.type == 2) || (crate.type == 3)) tb = 1; 628 | if ((crate.type == 0) || (crate.type == 1)) tb = 2; 629 | 630 | crate.branch = tb; 631 | crate.targetBranch = this.GAME_BRANCH; 632 | crate.goodCrate = !!((this.GAME_BRANCH == tb)); 633 | this.scene.add(crate); 634 | 635 | crate.animation = new TWEEN.Tween(crate.position) 636 | .to(landingPointTween, this.SPEED).start(); 637 | 638 | crate.animation.onComplete(function () { // the crate is landing 639 | if (crate) { 640 | self.scene.remove(crate); 641 | self.CRATES = _.without(self.CRATES, crate); 642 | self.checkCrateLanding(crate); 643 | } 644 | }); 645 | 646 | this.CRATES.push(crate); 647 | }; 648 | 649 | 650 | /** 651 | * Checking if the landed crate is in the the right branch 652 | * checking if the player captured the crate 653 | */ 654 | CoreCommit.prototype.checkCrateLanding = function (crate) { 655 | var pl = this.player.mesh; 656 | 657 | // branch switcher 658 | if (!crate.regularCrate && crate.type == 0) { 659 | this.gameSwitchBranch(); 660 | } 661 | 662 | // star 663 | if (!crate.regularCrate && crate.type == 1) { 664 | if (pl.position.x) { 665 | var originPoint = pl.position.clone(); 666 | 667 | for (var vertexIndex = 0; vertexIndex < pl.geometry.vertices.length; vertexIndex++) { 668 | var localVertex = pl.geometry.vertices[vertexIndex].clone(); 669 | var globalVertex = pl.matrix.multiplyVector3(localVertex); 670 | var directionVector = globalVertex.subSelf(pl.position); 671 | 672 | var ray = new THREE.Ray(originPoint, directionVector.clone().normalize()); 673 | var collisionResults = ray.intersectObject(crate); 674 | 675 | // if the player position is close to a crate, the player captures it. 676 | if (collisionResults.length > 0 && 677 | collisionResults[0].distance < directionVector.length()) { 678 | this.SESSION.STARS++; 679 | this.dispatchEvent(this.sendSessionEvent); 680 | this.branches[crate.targetBranch].commit(); 681 | this.collectStar(crate); 682 | break; 683 | } 684 | } 685 | } 686 | } 687 | 688 | // regular crates 689 | if ((!crate.dead) && crate.regularCrate) { 690 | if ((crate.targetBranch != crate.branch)) { 691 | // if the crate is not dead, and if it does not match a proper branch 692 | this.decreaseLife(); 693 | // bad collision, add effects to this branch 694 | this.branches[crate.targetBranch].hit(); 695 | } else { 696 | this.SESSION.COMMITS++; 697 | this.dispatchEvent(this.sendSessionEvent); 698 | this.collectStar(crate); 699 | } 700 | } 701 | }; 702 | 703 | 704 | /** 705 | * decreaseLife 706 | */ 707 | CoreCommit.prototype.decreaseLife = function () { 708 | this.LIFE -= 10; 709 | // TODO: fix this. 710 | var e = {type:'decreaseLife', data:this.LIFE }; 711 | this.dispatchEvent(e); 712 | }; 713 | 714 | 715 | /** 716 | * collectStar 717 | */ 718 | CoreCommit.prototype.collectStar = function (crate) { 719 | var self = this, 720 | starMesh = crate.material, 721 | starCollect = new THREE.Mesh(THREE.GeometryUtils.clone(new THREE.CubeGeometry(50, 50, 50)), starMesh); 722 | 723 | starCollect.position.set(crate.targetPosition.x, crate.targetPosition.y, 0); 724 | this.scene.add(starCollect); 725 | var cP = this.camera.position; 726 | var cameraPos = new THREE.Vector3(cP.x, cP.y, cP.z); 727 | cameraPos.x -= 200; 728 | starCollect.animation = new TWEEN.Tween(starCollect.position) 729 | .to(cameraPos, 500) 730 | .easing(TWEEN.Easing.Quadratic.Out) 731 | .start(); 732 | 733 | starCollect.animation.onComplete(function () { 734 | if (starCollect) { 735 | self.scene.remove(starCollect); 736 | } 737 | }); 738 | }; 739 | 740 | 741 | /** 742 | * teleportPlayer between branch positions 743 | */ 744 | CoreCommit.prototype.teleportPlayer = function (branch, direction) { 745 | var self = this; 746 | 747 | this._teleporting = true; 748 | // depending on which branch you are on, clockwise teleport between branches 749 | if (branch != null) { 750 | this.player.branch = branch; 751 | } else { 752 | if (direction == "left") { 753 | this.player.branch = (this.player.branch == 0) ? 2 : this.player.branch - 1; 754 | } else { 755 | this.player.branch = (this.player.branch == 2) ? 0 : this.player.branch + 1; 756 | } 757 | } 758 | var r = new TWEEN.Tween(this.player.mesh.position) 759 | .to(this.branches[this.player.branch].getCenter(), 300) 760 | .easing(TWEEN.Easing.Back.InOut).start(); 761 | 762 | r.onComplete(function () { 763 | self.player.changeTexture(self.player.branch); 764 | self._teleporting = false; 765 | }); 766 | }; 767 | 768 | 769 | 770 | /** 771 | * Create Game Branches 772 | */ 773 | CoreCommit.prototype.generateBranches = function () { 774 | this.branches = [ 775 | // posX, posY, posZ, color, cameraPos, cameraLookAtPos, playerShift 776 | new Branch(0, -480, 50, 0xd23aa1, new THREE.Vector3(0, -1600, 900), new THREE.Vector3(0, 1000, -100), -75), // purple 777 | new Branch(-480, 0, 50, 0xffb139, new THREE.Vector3(-1600, 0, 900), new THREE.Vector3(1000, 0, -100), -75), // orange 778 | new Branch(0, 480, 50, 0x2bc3db, new THREE.Vector3(0, 1600, 900), new THREE.Vector3(0, -1000, 220), 75) // teal 779 | ]; 780 | 781 | this.branches[0].mesh.rotation.z = 90 * Math.PI / 180; 782 | this.branches[2].mesh.rotation.z = 90 * Math.PI / 180; 783 | 784 | this.branches[this.GAME_BRANCH].setCurrent(); 785 | 786 | this.scene.add(this.branches[0].mesh); 787 | this.scene.add(this.branches[1].mesh); 788 | this.scene.add(this.branches[2].mesh); 789 | 790 | }; 791 | 792 | 793 | /** 794 | * Load crate textures 795 | */ 796 | CoreCommit.prototype.loadTextures = function () { 797 | 798 | // Code crate textures 799 | this.branchCrateList = [ 800 | new THREE.ImageUtils.loadTexture('img/crTealPCode.png'), // 0 801 | new THREE.ImageUtils.loadTexture('img/crTealMCode.png'), // 1 802 | new THREE.ImageUtils.loadTexture('img/crOrangePCode.png'), // 2 803 | new THREE.ImageUtils.loadTexture('img/crOrangeMCode.png'), // 3 804 | new THREE.ImageUtils.loadTexture('img/crPurplePCode.png'), // 4 805 | new THREE.ImageUtils.loadTexture('img/crPurplePCode.png') // 5 806 | ]; 807 | 808 | // Other crate textures 809 | this.otherCrateList = [ 810 | new THREE.ImageUtils.loadTexture('img/crPR.png'), // 0 811 | new THREE.ImageUtils.loadTexture('img/crStar.png') // 1 812 | ]; 813 | }; 814 | 815 | 816 | /** 817 | * Setup custom events 818 | */ 819 | CoreCommit.prototype.addCustomEvents = function () { 820 | this.sendSessionEvent = new CustomEvent('session', { 'detail':this.SESSION }); 821 | this.gameOverEvent = new CustomEvent('gameOver', { 'detail':this.SESSION }); 822 | }; 823 | 824 | 825 | /** 826 | * Setup custom events 827 | */ 828 | CoreCommit.prototype.setGameVariables = function (project) { 829 | // game session 830 | this.SESSION = { 831 | START_TIME:Date.now(), END_TIME:Date.now(), LEVEL:0, STARS:0, COMMITS:0, GAMEOVER:false, 832 | SWITCHED_BRANCH:0, PROJECT:project 833 | }; 834 | 835 | // types of views 836 | this.VIEW = {TOP:4, CENTER:1, LEFT:2, RIGHT:0}; 837 | 838 | // game branch 839 | this.GAME_BRANCH = 1; // current git branch bottom - 0, left - 1, top - 2 840 | 841 | // create a counter for frames 842 | this.FRAME = 0; 843 | this.SPEED = 25000; 844 | this.__CRATE_RATE = 145; 845 | this.__CRATE_SIZE = 100; 846 | 847 | // a timer for crates 848 | this.NEW_CRATE_TIMER = 0; 849 | // a timer for bullets 850 | this.NEW_BULLET = 0; 851 | this.AMMO = true; 852 | 853 | // paused state 854 | this.PAUSED = false; 855 | 856 | // enable keyboard plugin 857 | this.keyboard = new THREEx.KeyboardState(); 858 | 859 | // setup player life level 860 | this.LIFE = 100; 861 | 862 | // CAMERA 863 | this.SCREEN_WIDTH = window.innerWidth; 864 | this.SCREEN_HEIGHT = window.innerHeight; 865 | 866 | 867 | this.BULLETS = []; // track bullets 868 | this.CRATES = []; // track crates 869 | }; 870 | 871 | 872 | /** 873 | * Object that during their initialization can call this function 874 | * This will extend the calling object with basic 875 | * Event Dispatching functionality 876 | * 877 | */ 878 | CoreCommit.prototype.extendAsEventDispatcher = function () { 879 | if (this._listeners == null) { 880 | this._listeners = []; 881 | } 882 | this.isEventDispatcher = true; 883 | if (typeof(this.dispatchEvent) == 'undefined') { 884 | this.dispatchEvent = function (eventObject) { 885 | for (var i = 0; i < this._listeners.length; i++) { 886 | var test = this._listeners[i]; 887 | if (test.type === eventObject.type) { 888 | test.callback(eventObject); 889 | break; 890 | } 891 | } 892 | }; 893 | } 894 | if (typeof(this.addEventListener) == 'undefined') { 895 | this.addEventListener = function (type, callback, capture) { 896 | // no dupes 897 | var declared = false; 898 | for (var i = 0; i < this._listeners.length; i++) { 899 | var test = this._listeners[i]; 900 | if (test.type === type && test.callback === callback) { 901 | declared = true; 902 | break; 903 | } 904 | } 905 | if (!declared) { 906 | this._listeners.push({'type':type, 'callback':callback, 'capture':capture}); 907 | } 908 | }; 909 | } 910 | }; 911 | 912 | exports.CoreCommit = CoreCommit; 913 | 914 | })(window); 915 | -------------------------------------------------------------------------------- /app/client/js/game/Menu.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Welcome Notice 3 | */ 4 | Template.welcome.events({ 5 | 'click .continue': function () { 6 | $('.req-welcome').animate({ top: PX_HIDE }, PX_SPEED, function() { 7 | $('.req-tutorial').animate({ top: PX_SHOW }); 8 | }); 9 | } 10 | }); 11 | 12 | 13 | /** 14 | * Project Choice 15 | */ 16 | Template.choose.events({ 17 | 'click .project': function (event, template) { 18 | Session.set("pid", this._id); 19 | 20 | $('.req-project').animate({ top: PX_HIDE }, PX_SPEED, function() { }); 21 | startGame(); 22 | 23 | return false; 24 | } 25 | }); 26 | 27 | 28 | /** 29 | * Welcome Notice 30 | */ 31 | Template.tutorial.events({ 32 | 'click .continue': function () { 33 | $('.req-tutorial').animate({ top: PX_HIDE }, PX_SPEED, function() { 34 | $('.req-project').animate({ top: PX_SHOW }); 35 | }); 36 | } 37 | }); 38 | 39 | Template.help.status = function () { 40 | return Session.get('gamepadStatus'); 41 | }; 42 | 43 | Template.help.musicStatus = function () { 44 | return Session.get('musicStatus'); 45 | }; 46 | 47 | 48 | /** 49 | * Project Choice 50 | */ 51 | Template.help.events({ 52 | 'click .music': function (event, template) { 53 | if (Session.equals('musicStatus', 'on')) { 54 | Session.set('musicStatus', 'off'); 55 | MUSIC.pause(); 56 | } else { 57 | Session.set('musicStatus', 'on'); 58 | if (Session.get('gameOn')) { 59 | MUSIC.play(); 60 | } 61 | } 62 | } 63 | }); 64 | 65 | 66 | -------------------------------------------------------------------------------- /app/client/js/game/MenuDashboard.js: -------------------------------------------------------------------------------- 1 | Template.dashboard.stars = function () { 2 | var stars = Session.get('sessionStars'); 3 | return (stars) ? stars : 0; 4 | }; 5 | 6 | 7 | Template.dashboard.project = function () { 8 | var p = Projects.findOne({_id:Session.get('pid')}); 9 | if (p && p.name) { 10 | return p.name 11 | } else { 12 | return 'a project' 13 | } 14 | }; 15 | 16 | 17 | Template.dashboard.commits = function () { 18 | var commits = Session.get('sessionCommits'); 19 | return (commits) ? commits : 0; 20 | }; 21 | 22 | 23 | Template.dashboard.level = function () { 24 | var lvl = Session.get('sessionLevel'); 25 | return (lvl) ? lvl : 0; 26 | 27 | }; 28 | 29 | Template.dashboard.rendered = function(){ 30 | // quick hack. 31 | var s = Session.get('sessionLevel'), 32 | b = Session.get('sessionLevelBackup'); 33 | 34 | if (s != b) { 35 | Session.set("sessionLevelBackup", s); 36 | $('.level').fadeIn(300).delay(500).fadeOut(400); 37 | } 38 | }; 39 | 40 | 41 | Template.level.count = function () { 42 | var lvl = Session.get('sessionLevel'); 43 | return (lvl) ? lvl : 0; 44 | 45 | }; 46 | 47 | -------------------------------------------------------------------------------- /app/client/js/game/MenuGameover.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Return currently logged in user 3 | */ 4 | Template.gameover.user = function() { 5 | return Meteor.user(); 6 | }; 7 | 8 | 9 | /** 10 | * Show game stats 11 | */ 12 | Template.gameover.stats = function() { 13 | return Session.get('stats'); 14 | }; 15 | 16 | 17 | /** 18 | * List the projects for the gameover menu 19 | */ 20 | Template.gameover.list = function() { 21 | return Projects.find(); 22 | }; 23 | 24 | 25 | /** 26 | * Game Over Notice 27 | */ 28 | Template.gameover.events({ 29 | 'click .again': function () { 30 | window.location.reload(); 31 | } 32 | }); 33 | -------------------------------------------------------------------------------- /app/client/js/game/MenuProjects.js: -------------------------------------------------------------------------------- 1 | /** 2 | * List the project for the choosing menu 3 | */ 4 | Template.projects.list = function() { 5 | return Projects.find(); 6 | }; 7 | 8 | /** 9 | * Chosen project id 10 | */ 11 | Template.projects.selected = function() { 12 | return Session.get('pid'); 13 | }; 14 | 15 | -------------------------------------------------------------------------------- /app/client/js/game/MenuWebgl.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * WebGl Notice 4 | */ 5 | Template.webgl.events({ 6 | 'click .continue': function () { 7 | $('.req-webgl').animate({ top: PX_HIDE }, PX_SPEED, function() { 8 | $('.req-welcome').animate({ top: PX_SHOW }); 9 | }); 10 | } 11 | }); -------------------------------------------------------------------------------- /app/client/js/game/SoundTrack.js: -------------------------------------------------------------------------------- 1 | (function (exports) { 2 | 3 | var SoundTrack = function () { 4 | var self = this; 5 | this.audio = new Audio(); 6 | var tracks = [ 7 | "music/0.ogg", 8 | "music/1.ogg", 9 | "music/2.ogg" 10 | ]; 11 | this.audio.src = tracks[parseInt(Math.random() * 3, 10)]; 12 | this.audio.volume = 0.1; 13 | this.audio.addEventListener('ended', function() { 14 | self.audio.src = tracks[parseInt(Math.random() * 3, 10)]; 15 | self.play(); 16 | }, false); 17 | }; 18 | 19 | SoundTrack.prototype.play = function() { 20 | this.audio.play(); 21 | }; 22 | 23 | SoundTrack.prototype.pause = function() { 24 | this.audio.pause(); 25 | }; 26 | 27 | exports.SoundTrack = SoundTrack; 28 | })(window); 29 | -------------------------------------------------------------------------------- /app/client/js/game/Stats.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Show game stats 3 | */ 4 | Template.recentScores.list = function() { 5 | // the server controls the limit, but we are forcing it here as well. 6 | return RecentScores.find().fetch().slice(0,8); 7 | }; 8 | 9 | 10 | /** 11 | * Show top scores 12 | */ 13 | Template.topScores.list = function() { 14 | return TopScores.find().fetch().slice(0,8); 15 | }; -------------------------------------------------------------------------------- /app/client/js/game/resources/Branch.js: -------------------------------------------------------------------------------- 1 | (function (exports) { 2 | 3 | /** 4 | * Game Branch 5 | * @param posX 6 | * @param posY 7 | * @param posZ 8 | * @param color 9 | * @param cameraPos 10 | * @param cameraLookAtPos 11 | * @constructor 12 | */ 13 | var Branch = function (posX, posY, posZ, color, cameraPos, cameraLookAtPos, playerShift) { 14 | this._posX = posX; 15 | this._posY = posY; 16 | this._posZ = posZ; 17 | this._cameraPos = cameraPos; 18 | this._cameraLookAtPos = cameraLookAtPos; 19 | this._orientation = (posY == 0) ? 'x' : 'y'; 20 | this._playerShift = playerShift; 21 | 22 | // branch 1 - left 23 | this.mesh = new THREE.Mesh( 24 | THREE.GeometryUtils.clone(new THREE.CubeGeometry(60, 900, 120)), 25 | new THREE.MeshBasicMaterial({ color:color, transparent:true, opacity:0.35 })); 26 | this.mesh.position.set(posX, posY, posZ); 27 | }; 28 | 29 | 30 | /** 31 | * Branch hit by an object 32 | */ 33 | Branch.prototype.hit = function () { 34 | if (!this._animated) { 35 | this._animated = true; 36 | this._end = new Date(Date.now() + 500); 37 | this._anim(); 38 | } 39 | }; 40 | 41 | 42 | /** 43 | * Set this branch as current 44 | */ 45 | Branch.prototype.setCurrent = function () { 46 | this.current = true; 47 | this.mesh.material.opacity = 1.0; 48 | }; 49 | 50 | 51 | /** 52 | * Unset this branch as current 53 | */ 54 | Branch.prototype.unsetCurrent = function () { 55 | this.current = false; 56 | this.mesh.material.opacity = 0.35; 57 | }; 58 | 59 | 60 | /** 61 | * Animate the branch hit 62 | * @private 63 | */ 64 | Branch.prototype._anim = function () { 65 | var self = this; 66 | 67 | var timer = Date.now() * 0.01; // SPEED 68 | var cos = Math.cos(timer); // Amp 69 | //this.mesh.position.x += (cos > 0) ? -0.3 : 0.3; 70 | this.mesh.material.opacity = Math.abs(cos); 71 | if (this._end < Date.now()) { 72 | this._animated = false; 73 | 74 | this.mesh.material.opacity = (this.current) ? 1.0 : 0.35 ; 75 | } 76 | 77 | if (this._animated) window.requestAnimFrame(this._anim.bind(this)); 78 | }; 79 | 80 | 81 | Branch.prototype.commit = function () { 82 | // opacity up? 83 | }; 84 | 85 | 86 | /** 87 | * Get the center position for the player teleport 88 | * @return {Object} 89 | */ 90 | Branch.prototype.getCenter = function () { 91 | // TODO: patch this. 92 | if (this._orientation == 'y') { 93 | return {x: this._posX, y: this._posY + this._playerShift, z: this._posZ}; 94 | } else { 95 | return {x: this._posX + this._playerShift, y: this._posY, z: this._posZ}; 96 | } 97 | }; 98 | 99 | exports.Branch = Branch; 100 | })(window); 101 | -------------------------------------------------------------------------------- /app/client/js/game/resources/GamepadSupport.js: -------------------------------------------------------------------------------- 1 | (function (exports) { 2 | 3 | 4 | var GamepadSupport = function () { 5 | 6 | window.addEventListener('MozGamepadConnected', this.onGamepadConnect, false); 7 | window.addEventListener('MozGamepadDisconnected', this.onGamepadDisconnect, false); 8 | this.prevRawGamepadTypes = [] 9 | }; 10 | 11 | /** 12 | * Enable gamepad support if available 13 | */ 14 | GamepadSupport.prototype.checkGamepadSupport = function () { 15 | 16 | if (Modernizr.gamepads) { 17 | var rawGamepads = 18 | (navigator.webkitGetGamepads && navigator.webkitGetGamepads()) || navigator.webkitGamepads; 19 | 20 | if (rawGamepads) { 21 | this.GAMEPADS = []; 22 | if (typeof rawGamepads[i] != this.prevRawGamepadTypes[i]) { 23 | //this.dispatchEvent('gamepadConnected'); 24 | //console.log('gamepadConnected'); 25 | for (var i = 0; i < rawGamepads.length; i++) { 26 | if (rawGamepads[i]) { 27 | this.GAMEPADS.push(rawGamepads[i]); 28 | } 29 | } 30 | } 31 | } 32 | } 33 | }; 34 | 35 | 36 | /** 37 | * onGamepadConnect 38 | * @type {Function} 39 | */ 40 | GamepadSupport.prototype.onGamepadConnect = function (event) { 41 | this.dispatchEvent('gamepadConnected'); 42 | this.GAMEPADS.push(event.gamepad); 43 | }; 44 | 45 | 46 | /** 47 | * onGamepadConnect 48 | * @param event 49 | */ 50 | GamepadSupport.prototype.onGamepadDisconnect = function (event) { 51 | for (var i in this.GAMEPADS) { 52 | if (this.GAMEPADS[i].index == event.gamepad.index) { 53 | this.GAMEPADS.splice(i, 1); 54 | break; 55 | } 56 | } 57 | //console.log('gamepadDisconnected'); 58 | //this.dispatchEvent('gamepadDisconnected'); 59 | }; 60 | 61 | exports.GamepadSupport = GamepadSupport; 62 | })(window); 63 | -------------------------------------------------------------------------------- /app/client/js/game/resources/Player.js: -------------------------------------------------------------------------------- 1 | (function (exports) { 2 | 3 | /** 4 | * Setup the game player 5 | * @constructor 6 | */ 7 | var Player = function () { 8 | this.branch = 1; 9 | this.branchTexture = [ 10 | new THREE.ImageUtils.loadTexture('img/player0.png'), 11 | new THREE.ImageUtils.loadTexture('img/player1.png'), 12 | new THREE.ImageUtils.loadTexture('img/player2.png') 13 | ] 14 | var crateTexture = this.branchTexture[1]; 15 | this.mesh = new THREE.Mesh( 16 | THREE.GeometryUtils.clone( new THREE.CubeGeometry(90, 90, 90) ), 17 | new THREE.MeshBasicMaterial({ map:crateTexture }) 18 | ); 19 | this.mesh.position.set(-550, 0, 50); 20 | }; 21 | 22 | // TODO: clean this up; 23 | /** 24 | * Move player up / left 25 | * @type {Function} 26 | */ 27 | Player.prototype.moveUpLeft = function (branch, view) { 28 | var pl = this.mesh, 29 | axis; 30 | axis = (branch == 1) ? pl.position.y : pl.position.x; 31 | 32 | if (axis > -435 && axis < 435) { 33 | if (branch == 1) { 34 | if (axis != 420 ) pl.position.y += 15 35 | } else { 36 | // TODO: fix quick hack here 37 | 38 | if (this.view == 2 && this.branch == 2 && axis != 420) { 39 | pl.position.x += 15; 40 | } else if (axis != -420) { 41 | pl.position.x -= 15; 42 | } 43 | } 44 | } 45 | }; 46 | 47 | 48 | /** 49 | * Move player down / right 50 | */ 51 | Player.prototype.moveDownRight = function (branch, view) { 52 | var pl = this.mesh, 53 | axis; 54 | axis = (branch == 1) ? pl.position.y : pl.position.x; 55 | 56 | 57 | if (axis > -435 && axis < 435) { 58 | if (branch == 1) { 59 | if (axis != -420 ) pl.position.y -= 15 60 | } else { 61 | // TODO: fix quick hack here 62 | 63 | if (this.view == 2 && this.branch == 2 && axis != -420) { 64 | pl.position.x -= 15; 65 | } else if (axis != 420 ) { 66 | pl.position.x += 15; 67 | } 68 | } 69 | } 70 | }; 71 | 72 | /** 73 | * Player change texture 74 | * @type {Function} 75 | */ 76 | Player.prototype.changeTexture = function (branch) { 77 | this.mesh.material.map = this.branchTexture[branch]; 78 | }; 79 | 80 | exports.Player = Player; 81 | })(window); 82 | -------------------------------------------------------------------------------- /app/client/lib/lodash.js: -------------------------------------------------------------------------------- 1 | /*! 2 | Lo-Dash 0.8.2 lodash.com/license 3 | Underscore.js 1.4.2 underscorejs.org/LICENSE 4 | */ 5 | ;(function(e,t){function s(e){if(e&&e.__wrapped__)return e;if(!(this instanceof s))return new s(e);this.__wrapped__=e}function o(e,t,n){t||(t=0);var r=e.length,i=r-t>=(n||H),s=i?{}:e;if(i)for(n=t-1;++nn||e===t)return 1;if(es;s++)i+="i='"+e.o[s]+"';if(","constructor"==e.o[s]&&(i+="!(g&&g.prototype===j)&&"),i+="h.call(j,i)){A=j[i];"+ 9 | e.l.h+"}"}if(e.c||e.m)i+="}"}return i+=e.e+";return t",Function("D,E,F,I,e,f,J,h,M,O,Q,S,T,X,Y,l,q,v,w,y,z","var G=function("+t+"){"+i+"};return G")(Dt,_,L,u,Q,f,en,Z,T,v,$t,m,Jt,mt,Ht,ut,tt,nt,yt,rt)}function c(e){return"\\"+Bt[e]}function h(e){return Qt[e]}function p(){}function d(e){return Gt[e]}function v(e){return rt.call(e)==ct}function m(e){return"function"==typeof e}function g(e){var t=i;if(!e||"object"!=typeof e||v(e))return t;var n=e.constructor;return(!Lt||"function"==typeof e.toString||"string"!=typeof 10 | (e+""))&&(!m(n)||n instanceof n)?xt?(en(e,function(e,n,r){return t=!Z.call(r,n),i}),t===i):(en(e,function(e,n){t=n}),t===i||Z.call(e,t)):t}function y(e,t,n,s,o){if(e==r)return e;n&&(t=i);if(n=Ht[typeof e]){var u=rt.call(e);if(!Pt[u]||Nt&&v(e))return e;var a=u==ht,n=a||(u==mt?Jt(e):n)}if(!n||!t)return n?a?nt.call(e):Zt({},e):e;n=e.constructor;switch(u){case pt:case dt:return new n(+e);case vt:case yt:return new n(e);case gt:return n(e.source,U.exec(e))}s||(s=[]),o||(o=[]);for(u=s.length;u--;)if(s[ 11 | u]==e)return o[u];var f=a?n(e.length):{};return s.push(e),o.push(f),(a?mn:tn)(e,function(e,n){f[n]=y(e,t,r,s,o)}),f}function b(e,t,s,o){if(e==r||t==r)return e===t;if(e===t)return 0!==e||1/e==1/t;if(Ht[typeof e]||Ht[typeof t])e=e.__wrapped__||e,t=t.__wrapped__||t;var u=rt.call(e);if(u!=rt.call(t))return i;switch(u){case pt:case dt:return+e==+t;case vt:return e!=+e?t!=+t:0==e?1/e==1/t:e==+t;case gt:case yt:return e==t+""}var a=Dt[u];if(Nt&&!a&&(a=v(e))&&!v(t)||!a&&(u!=mt||Lt&&("function"!=typeof e. 12 | toString&&"string"==typeof (e+"")||"function"!=typeof t.toString&&"string"==typeof (t+""))))return i;s||(s=[]),o||(o=[]);for(u=s.length;u--;)if(s[u]==e)return o[u]==t;var u=-1,f=n,l=0;s.push(e),o.push(t);if(a){l=e.length;if(f=l==t.length)for(;l--&&(f=b(e[l],t[l],s,o)););return f}a=e.constructor,f=t.constructor;if(a!=f&&(!m(a)||!(a instanceof a&&m(f)&&f instanceof f)))return i;for(var c in e)if(Z.call(e,c)&&(l++,!Z.call(t,c)||!b(e[c],t[c],s,o)))return i;for(c in t)if(Z.call(t,c)&&!(l--))return i;if( 13 | Et)for(;7>++u;)if(c=J[u],Z.call(e,c)&&(!Z.call(t,c)||!b(e[c],t[c],s,o)))return i;return n}function w(e,t,n){var r=-Infinity,i=-1,s=e?e.length:0,o=r;if(t||s!==+s)t=f(t,n),mn(e,function(e,n,i){n=t(e,n,i),n>r&&(r=n,o=e)});else for(;++io&&(o=e[i]);return o}function E(e,t,n,r){var s=e,o=e?e.length:0,u=3>arguments.length;if(o!==+o)var a=sn(e),o=a.length;else kt&&rt.call(e)==yt&&(s=e.split(""));return mn(e,function(e,f,l){f=a?a[--o]:--o,n=u?(u=i,s[f]):t.call(r,n,s[f],f,l)}),n}function S(e,t,n){ 14 | if(e)return t==r||n?e[0]:nt.call(e,0,t)}function x(e,t){for(var n=-1,r=e?e.length:0,i=[];++nn?at(0,i+n):n||0)-1;else if(n)return r=C(e,t),e[r]===t?r:-1;for(;++r>>1,n(e[r])>>1,e[r]T(a,r))a.push(r),u.push(e[s]);return u}function L(e,t){return Ot||it&&2|{(\/]|\[\D|\b(?:delete|in|instanceof|new|typeof|void)\b/,F=/&(?:amp|lt|gt|quot|#x27);/g,I=/\b__p\+='';/g,q=/\b(__p\+=)''\+/g,R=/(__e\(.*?\)|\b__t\))\+'';/g,U=/\w*$/,z=/(?:__e|__t=)\(\s*(?![\d\s"']|this\.)/g,W=RegExp("^"+(D.valueOf+"" 17 | ).replace(/[.*+?^=!:${}()|[\]\/\\]/g,"\\$&").replace(/valueOf|for [^\]]+/g,".+?")+"$"),X=/($^)/,V=/[&<>"']/g,$=/['\n\r\t\u2028\u2029\\]/g,J="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" "),K=Math.ceil,Q=_.concat,G=Math.floor,Y=W.test(Y=Object.getPrototypeOf)&&Y,Z=D.hasOwnProperty,et=_.push,tt=D.propertyIsEnumerable,nt=_.slice,rt=D.toString,it=W.test(it=nt.bind)&&it,st=W.test(st=Array.isArray)&&st,ot=e.isFinite,ut=W.test(ut=Object.keys)&&ut 18 | ,at=Math.max,ft=Math.min,lt=Math.random,ct="[object Arguments]",ht="[object Array]",pt="[object Boolean]",dt="[object Date]",vt="[object Number]",mt="[object Object]",gt="[object RegExp]",yt="[object String]",bt=e.clearTimeout,wt=e.setTimeout,Et,St,xt,Tt=n;(function(){function e(){this.x=1}var t={0:1,length:1},n=[];e.prototype={valueOf:1,y:1};for(var r in new e)n.push(r);for(r in arguments)Tt=!r;Et=4>(n+"").length,xt="x"!=n[0],St=(n.splice.call(t,0,1),t[0])})(1);var Nt=!v(arguments),Ct="x"!=nt.call("x" 19 | )[0],kt="xx"!="x"[0]+Object("x")[0];try{var Lt=("[object Object]",rt.call(e.document||0)==mt)}catch(At){}var Ot=it&&/\n|Opera/.test(it+rt.call(e.opera)),Mt=ut&&/^.+$|true/.test(ut+!!e.attachEvent),_t=!Ot,Dt={};Dt[pt]=Dt[dt]=Dt["[object Function]"]=Dt[vt]=Dt[mt]=Dt[gt]=i,Dt[ct]=Dt[ht]=Dt[yt]=n;var Pt={};Pt[ct]=Pt["[object Function]"]=i,Pt[ht]=Pt[pt]=Pt[dt]=Pt[vt]=Pt[mt]=Pt[gt]=Pt[yt]=n;var Ht={"boolean":i,"function":n,object:n,number:i,string:i,"undefined":i,unknown:n},Bt={"\\":"\\","'":"'","\n":"n" 20 | ,"\r":"r"," ":"t","\u2028":"u2028","\u2029":"u2029"};s.templateSettings={escape:/<%-([\s\S]+?)%>/g,evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,variable:""};var jt={a:"d,c,x",p:"c=f(c,x)",h:"if(c(A,i,d)===false)return t"},Ft={i:"{}",p:"c=f(c,x)",h:"var p=c(A,i,d);(h.call(t,p)?t[p]++:t[p]=1)"},It={i:"true",h:"if(!c(A,i,d))return!t"},qt={q:i,r:i,a:"m",p:"for(var a=1,b=arguments.length;a":">",'"':""","'":"'"},Gt=Vt(Qt),Yt=l(qt,{h:"if(t[i]==null)"+qt.h}),Zt=l(qt),en=l(jt,Ut,zt,{q:i}),tn=l(jt,Ut,zt),nn=l({q:i,a:"m",i:"[]",h:"S(A)&&t.push(i)",e:"t.sort()"}),rn=l({a:"A",i:"true",p:"var H=y.call(A),k=A.length;if(D[H]"+(Nt?"||O(A)":"")+"||(H==X&&k===+k&&S(A.splice)))return!k",h:{k:"return false"}}),sn=ut?function( 23 | e){var t=typeof e;return"function"==t&&tt.call(e,"prototype")?Kt(e):e&&Ht[t]?ut(e):[]}:Kt,on=l(qt,{a:"m,dd,N",p:"var P,C=arguments,a=0;if(N==I){var b=2,ee=C[3],ff=C[4]}else var b=C.length,ee=[],ff=[];while(++a-1"},h:"if(A===hh)return true"}),hn=l(jt,Ft),pn=l(jt,It),dn=l(jt,Rt),vn=l(jt,Ut,{i:"z",h:"if(c(A,i,d))return A"}),mn=l(jt,Ut),gn=l(jt,Ft,{h:"var p=c(A,i,d);(h.call(t,p)?t[p]:t[p]=[]).push(A)"}),yn=l(Wt,{a:"d,U" 25 | ,p:"var C=v.call(arguments,2),R=typeof U=='function'",h:{b:"t[i]=(R?U:A[U]).apply(A,C)",k:"t"+(Mt?"[n]=":".push")+"((R?U:A[U]).apply(A,C))"}}),bn=l(jt,Wt),wn=l(Wt,{a:"d,bb",h:{b:"t[i]=A[bb]",k:"t"+(Mt?"[n]=":".push")+"(A[bb])"}}),En=l({a:"d,c,B,x",i:"B",p:"var V=arguments.length<3;c=f(c,x)",d:{b:"if(V)t=j[++i]"},h:{b:"t=c(t,A,i,d)",k:"t=V?(V=false,A):c(t,A,i,d)"}}),Sn=l(jt,Rt,{h:"!"+Rt.h}),xn=l(jt,It,{i:"false",h:It.h.replace("!","")}),Tn=l(jt,Ft,Wt,{h:{b:"t[i]={a:c(A,i,d),b:i,c:A}",k:"t"+(Mt?"[n]=" 26 | :".push")+"({a:c(A,i,d),b:i,c:A})"},e:"t.sort(I);k=t.length;while(k--)t[k]=t[k].c"}),Nn=l(Rt,{a:"d,aa",p:"var s=[];J(aa,function(A,p){s.push(p)});var cc=s.length",h:"for(var Z=true,r=0;r1){while(++ie?t():function(){if(1>--e)return t.apply(this,arguments)}},s.bind=L,s.bindAll= 27 | Cn,s.chain=function(e){return e=new s(e),e.__chain__=n,e},s.clone=y,s.compact=function(e){for(var t=-1,n=e?e.length:0,r=[];++tT(s,u)){for(var a=1;an?at(0,r+n):ft(n,r-1))+1);r--;)if(e[r]===t)return r;return-1},s.lateBind=function(e,t){return a(t,e,nt.call(arguments,2))},s.map=bn,s.max=w,s.memoize=function(e,t){var n={};return function(){var r=t?t.apply(this,arguments):arguments[0];return Z.call(n,r)?n[r]:n[r]=e.apply(this,arguments)}},s.merge=on,s.min=function(e,t,n){var r= 32 | Infinity,i=-1,s=e?e.length:0,o=r;if(t||s!==+s)t=f(t,n),mn(e,function(e,n,i){n=t(e,n,i),n=f?(bt(u),a=r,s=e.apply(o,i)):u||(u=wt(n,f)),s}},s.times=function(e,t,n){for(var e=+e||0,r=-1,i=Array(e);++rT(r,i)&&r.push(i)}return r},s.uniq=k,s.uniqueId=function(e){var t=P++;return e?e+t:t},s.values=ln,s.where=Nn,s.without=function(e){for(var t=-1,n=e?e.length:0,r=o(arguments,1,20),i=[];++t', rule, ''].join(''); 67 | div.id = mod; 68 | (body ? div : fakeBody).innerHTML += style; 69 | fakeBody.appendChild(div); 70 | if ( !body ) { 71 | fakeBody.style.background = ''; 72 | fakeBody.style.overflow = 'hidden'; 73 | docOverflow = docElement.style.overflow; 74 | docElement.style.overflow = 'hidden'; 75 | docElement.appendChild(fakeBody); 76 | } 77 | 78 | ret = callback(div, rule); 79 | if ( !body ) { 80 | fakeBody.parentNode.removeChild(fakeBody); 81 | docElement.style.overflow = docOverflow; 82 | } else { 83 | div.parentNode.removeChild(div); 84 | } 85 | 86 | return !!ret; 87 | 88 | }, 89 | 90 | 91 | 92 | isEventSupported = (function() { 93 | 94 | var TAGNAMES = { 95 | 'select': 'input', 'change': 'input', 96 | 'submit': 'form', 'reset': 'form', 97 | 'error': 'img', 'load': 'img', 'abort': 'img' 98 | }; 99 | 100 | function isEventSupported( eventName, element ) { 101 | 102 | element = element || document.createElement(TAGNAMES[eventName] || 'div'); 103 | eventName = 'on' + eventName; 104 | 105 | var isSupported = eventName in element; 106 | 107 | if ( !isSupported ) { 108 | if ( !element.setAttribute ) { 109 | element = document.createElement('div'); 110 | } 111 | if ( element.setAttribute && element.removeAttribute ) { 112 | element.setAttribute(eventName, ''); 113 | isSupported = is(element[eventName], 'function'); 114 | 115 | if ( !is(element[eventName], 'undefined') ) { 116 | element[eventName] = undefined; 117 | } 118 | element.removeAttribute(eventName); 119 | } 120 | } 121 | 122 | element = null; 123 | return isSupported; 124 | } 125 | return isEventSupported; 126 | })(), 127 | 128 | 129 | _hasOwnProperty = ({}).hasOwnProperty, hasOwnProp; 130 | 131 | if ( !is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined') ) { 132 | hasOwnProp = function (object, property) { 133 | return _hasOwnProperty.call(object, property); 134 | }; 135 | } 136 | else { 137 | hasOwnProp = function (object, property) { 138 | return ((property in object) && is(object.constructor.prototype[property], 'undefined')); 139 | }; 140 | } 141 | 142 | 143 | if (!Function.prototype.bind) { 144 | Function.prototype.bind = function bind(that) { 145 | 146 | var target = this; 147 | 148 | if (typeof target != "function") { 149 | throw new TypeError(); 150 | } 151 | 152 | var args = slice.call(arguments, 1), 153 | bound = function () { 154 | 155 | if (this instanceof bound) { 156 | 157 | var F = function(){}; 158 | F.prototype = target.prototype; 159 | var self = new F(); 160 | 161 | var result = target.apply( 162 | self, 163 | args.concat(slice.call(arguments)) 164 | ); 165 | if (Object(result) === result) { 166 | return result; 167 | } 168 | return self; 169 | 170 | } else { 171 | 172 | return target.apply( 173 | that, 174 | args.concat(slice.call(arguments)) 175 | ); 176 | 177 | } 178 | 179 | }; 180 | 181 | return bound; 182 | }; 183 | } 184 | 185 | function setCss( str ) { 186 | mStyle.cssText = str; 187 | } 188 | 189 | function setCssAll( str1, str2 ) { 190 | return setCss(prefixes.join(str1 + ';') + ( str2 || '' )); 191 | } 192 | 193 | function is( obj, type ) { 194 | return typeof obj === type; 195 | } 196 | 197 | function contains( str, substr ) { 198 | return !!~('' + str).indexOf(substr); 199 | } 200 | 201 | function testProps( props, prefixed ) { 202 | for ( var i in props ) { 203 | var prop = props[i]; 204 | if ( !contains(prop, "-") && mStyle[prop] !== undefined ) { 205 | return prefixed == 'pfx' ? prop : true; 206 | } 207 | } 208 | return false; 209 | } 210 | 211 | function testDOMProps( props, obj, elem ) { 212 | for ( var i in props ) { 213 | var item = obj[props[i]]; 214 | if ( item !== undefined) { 215 | 216 | if (elem === false) return props[i]; 217 | 218 | if (is(item, 'function')){ 219 | return item.bind(elem || obj); 220 | } 221 | 222 | return item; 223 | } 224 | } 225 | return false; 226 | } 227 | 228 | function testPropsAll( prop, prefixed, elem ) { 229 | 230 | var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1), 231 | props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' '); 232 | 233 | if(is(prefixed, "string") || is(prefixed, "undefined")) { 234 | return testProps(props, prefixed); 235 | 236 | } else { 237 | props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' '); 238 | return testDOMProps(props, prefixed, elem); 239 | } 240 | } tests['flexbox'] = function() { 241 | return testPropsAll('flexWrap'); 242 | }; tests['canvas'] = function() { 243 | var elem = document.createElement('canvas'); 244 | return !!(elem.getContext && elem.getContext('2d')); 245 | }; 246 | 247 | tests['canvastext'] = function() { 248 | return !!(Modernizr['canvas'] && is(document.createElement('canvas').getContext('2d').fillText, 'function')); 249 | }; 250 | 251 | 252 | 253 | tests['webgl'] = function() { 254 | return !!window.WebGLRenderingContext; 255 | }; 256 | 257 | 258 | tests['touch'] = function() { 259 | var bool; 260 | 261 | if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) { 262 | bool = true; 263 | } else { 264 | injectElementWithStyles(['@media (',prefixes.join('touch-enabled),('),mod,')','{#modernizr{top:9px;position:absolute}}'].join(''), function( node ) { 265 | bool = node.offsetTop === 9; 266 | }); 267 | } 268 | 269 | return bool; 270 | }; 271 | 272 | 273 | 274 | tests['geolocation'] = function() { 275 | return 'geolocation' in navigator; 276 | }; 277 | 278 | 279 | tests['postmessage'] = function() { 280 | return !!window.postMessage; 281 | }; 282 | 283 | 284 | tests['websqldatabase'] = function() { 285 | return !!window.openDatabase; 286 | }; 287 | 288 | tests['indexedDB'] = function() { 289 | return !!testPropsAll("indexedDB", window); 290 | }; 291 | 292 | tests['hashchange'] = function() { 293 | return isEventSupported('hashchange', window) && (document.documentMode === undefined || document.documentMode > 7); 294 | }; 295 | 296 | tests['history'] = function() { 297 | return !!(window.history && history.pushState); 298 | }; 299 | 300 | tests['draganddrop'] = function() { 301 | var div = document.createElement('div'); 302 | return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div); 303 | }; 304 | 305 | tests['websockets'] = function() { 306 | return 'WebSocket' in window || 'MozWebSocket' in window; 307 | }; 308 | 309 | 310 | tests['rgba'] = function() { 311 | setCss('background-color:rgba(150,255,150,.5)'); 312 | 313 | return contains(mStyle.backgroundColor, 'rgba'); 314 | }; 315 | 316 | tests['hsla'] = function() { 317 | setCss('background-color:hsla(120,40%,100%,.5)'); 318 | 319 | return contains(mStyle.backgroundColor, 'rgba') || contains(mStyle.backgroundColor, 'hsla'); 320 | }; 321 | 322 | tests['multiplebgs'] = function() { 323 | setCss('background:url(https://),url(https://),red url(https://)'); 324 | 325 | return (/(url\s*\(.*?){3}/).test(mStyle.background); 326 | }; tests['backgroundsize'] = function() { 327 | return testPropsAll('backgroundSize'); 328 | }; 329 | 330 | tests['borderimage'] = function() { 331 | return testPropsAll('borderImage'); 332 | }; 333 | 334 | 335 | 336 | tests['borderradius'] = function() { 337 | return testPropsAll('borderRadius'); 338 | }; 339 | 340 | tests['boxshadow'] = function() { 341 | return testPropsAll('boxShadow'); 342 | }; 343 | 344 | tests['textshadow'] = function() { 345 | return document.createElement('div').style.textShadow === ''; 346 | }; 347 | 348 | 349 | tests['opacity'] = function() { 350 | setCssAll('opacity:.55'); 351 | 352 | return (/^0.55$/).test(mStyle.opacity); 353 | }; 354 | 355 | 356 | tests['cssanimations'] = function() { 357 | return testPropsAll('animationName'); 358 | }; 359 | 360 | 361 | tests['csscolumns'] = function() { 362 | return testPropsAll('columnCount'); 363 | }; 364 | 365 | 366 | tests['cssgradients'] = function() { 367 | var str1 = 'background-image:', 368 | str2 = 'gradient(linear,left top,right bottom,from(#9f9),to(white));', 369 | str3 = 'linear-gradient(left top,#9f9, white);'; 370 | 371 | setCss( 372 | (str1 + '-webkit- '.split(' ').join(str2 + str1) + 373 | prefixes.join(str3 + str1)).slice(0, -str1.length) 374 | ); 375 | 376 | return contains(mStyle.backgroundImage, 'gradient'); 377 | }; 378 | 379 | 380 | tests['cssreflections'] = function() { 381 | return testPropsAll('boxReflect'); 382 | }; 383 | 384 | 385 | tests['csstransforms'] = function() { 386 | return !!testPropsAll('transform'); 387 | }; 388 | 389 | 390 | tests['csstransforms3d'] = function() { 391 | 392 | var ret = !!testPropsAll('perspective'); 393 | 394 | if ( ret && 'webkitPerspective' in docElement.style ) { 395 | 396 | injectElementWithStyles('@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}', function( node, rule ) { 397 | ret = node.offsetLeft === 9 && node.offsetHeight === 3; 398 | }); 399 | } 400 | return ret; 401 | }; 402 | 403 | 404 | tests['csstransitions'] = function() { 405 | return testPropsAll('transition'); 406 | }; 407 | 408 | 409 | 410 | tests['fontface'] = function() { 411 | var bool; 412 | 413 | injectElementWithStyles('@font-face {font-family:"font";src:url("https://")}', function( node, rule ) { 414 | var style = document.getElementById('smodernizr'), 415 | sheet = style.sheet || style.styleSheet, 416 | cssText = sheet ? (sheet.cssRules && sheet.cssRules[0] ? sheet.cssRules[0].cssText : sheet.cssText || '') : ''; 417 | 418 | bool = /src/i.test(cssText) && cssText.indexOf(rule.split(' ')[0]) === 0; 419 | }); 420 | 421 | return bool; 422 | }; 423 | 424 | tests['generatedcontent'] = function() { 425 | var bool; 426 | 427 | injectElementWithStyles(['#',mod,'{font:0/0 a}#',mod,':after{content:"',smile,'";visibility:hidden;font:3px/1 a}'].join(''), function( node ) { 428 | bool = node.offsetHeight >= 3; 429 | }); 430 | 431 | return bool; 432 | }; 433 | tests['video'] = function() { 434 | var elem = document.createElement('video'), 435 | bool = false; 436 | 437 | try { 438 | if ( bool = !!elem.canPlayType ) { 439 | bool = new Boolean(bool); 440 | bool.ogg = elem.canPlayType('video/ogg; codecs="theora"') .replace(/^no$/,''); 441 | 442 | bool.h264 = elem.canPlayType('video/mp4; codecs="avc1.42E01E"') .replace(/^no$/,''); 443 | 444 | bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,''); 445 | } 446 | 447 | } catch(e) { } 448 | 449 | return bool; 450 | }; 451 | 452 | tests['audio'] = function() { 453 | var elem = document.createElement('audio'), 454 | bool = false; 455 | 456 | try { 457 | if ( bool = !!elem.canPlayType ) { 458 | bool = new Boolean(bool); 459 | bool.ogg = elem.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,''); 460 | bool.mp3 = elem.canPlayType('audio/mpeg;') .replace(/^no$/,''); 461 | 462 | bool.wav = elem.canPlayType('audio/wav; codecs="1"') .replace(/^no$/,''); 463 | bool.m4a = ( elem.canPlayType('audio/x-m4a;') || 464 | elem.canPlayType('audio/aac;')) .replace(/^no$/,''); 465 | } 466 | } catch(e) { } 467 | 468 | return bool; 469 | }; 470 | 471 | 472 | tests['localstorage'] = function() { 473 | try { 474 | localStorage.setItem(mod, mod); 475 | localStorage.removeItem(mod); 476 | return true; 477 | } catch(e) { 478 | return false; 479 | } 480 | }; 481 | 482 | tests['sessionstorage'] = function() { 483 | try { 484 | sessionStorage.setItem(mod, mod); 485 | sessionStorage.removeItem(mod); 486 | return true; 487 | } catch(e) { 488 | return false; 489 | } 490 | }; 491 | 492 | 493 | tests['webworkers'] = function() { 494 | return !!window.Worker; 495 | }; 496 | 497 | 498 | tests['applicationcache'] = function() { 499 | return !!window.applicationCache; 500 | }; 501 | 502 | 503 | tests['svg'] = function() { 504 | return !!document.createElementNS && !!document.createElementNS(ns.svg, 'svg').createSVGRect; 505 | }; 506 | 507 | tests['inlinesvg'] = function() { 508 | var div = document.createElement('div'); 509 | div.innerHTML = ''; 510 | return (div.firstChild && div.firstChild.namespaceURI) == ns.svg; 511 | }; 512 | 513 | tests['smil'] = function() { 514 | return !!document.createElementNS && /SVGAnimate/.test(toString.call(document.createElementNS(ns.svg, 'animate'))); 515 | }; 516 | 517 | 518 | tests['svgclippaths'] = function() { 519 | return !!document.createElementNS && /SVGClipPath/.test(toString.call(document.createElementNS(ns.svg, 'clipPath'))); 520 | }; 521 | 522 | function webforms() { 523 | Modernizr['input'] = (function( props ) { 524 | for ( var i = 0, len = props.length; i < len; i++ ) { 525 | attrs[ props[i] ] = !!(props[i] in inputElem); 526 | } 527 | if (attrs.list){ 528 | attrs.list = !!(document.createElement('datalist') && window.HTMLDataListElement); 529 | } 530 | return attrs; 531 | })('autocomplete autofocus list placeholder max min multiple pattern required step'.split(' ')); 532 | Modernizr['inputtypes'] = (function(props) { 533 | 534 | for ( var i = 0, bool, inputElemType, defaultView, len = props.length; i < len; i++ ) { 535 | 536 | inputElem.setAttribute('type', inputElemType = props[i]); 537 | bool = inputElem.type !== 'text'; 538 | 539 | if ( bool ) { 540 | 541 | inputElem.value = smile; 542 | inputElem.style.cssText = 'position:absolute;visibility:hidden;'; 543 | 544 | if ( /^range$/.test(inputElemType) && inputElem.style.WebkitAppearance !== undefined ) { 545 | 546 | docElement.appendChild(inputElem); 547 | defaultView = document.defaultView; 548 | 549 | bool = defaultView.getComputedStyle && 550 | defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' && 551 | (inputElem.offsetHeight !== 0); 552 | 553 | docElement.removeChild(inputElem); 554 | 555 | } else if ( /^(search|tel)$/.test(inputElemType) ){ 556 | } else if ( /^(url|email)$/.test(inputElemType) ) { 557 | bool = inputElem.checkValidity && inputElem.checkValidity() === false; 558 | 559 | } else { 560 | bool = inputElem.value != smile; 561 | } 562 | } 563 | 564 | inputs[ props[i] ] = !!bool; 565 | } 566 | return inputs; 567 | })('search tel url email datetime date month week time datetime-local number range color'.split(' ')); 568 | } 569 | for ( var feature in tests ) { 570 | if ( hasOwnProp(tests, feature) ) { 571 | featureName = feature.toLowerCase(); 572 | Modernizr[featureName] = tests[feature](); 573 | 574 | classes.push((Modernizr[featureName] ? '' : 'no-') + featureName); 575 | } 576 | } 577 | 578 | Modernizr.input || webforms(); 579 | 580 | 581 | Modernizr.addTest = function ( feature, test ) { 582 | if ( typeof feature == 'object' ) { 583 | for ( var key in feature ) { 584 | if ( hasOwnProp( feature, key ) ) { 585 | Modernizr.addTest( key, feature[ key ] ); 586 | } 587 | } 588 | } else { 589 | 590 | feature = feature.toLowerCase(); 591 | 592 | if ( Modernizr[feature] !== undefined ) { 593 | return Modernizr; 594 | } 595 | 596 | test = typeof test == 'function' ? test() : test; 597 | 598 | if (typeof enableClasses !== "undefined" && enableClasses) { 599 | docElement.className += ' ' + (test ? '' : 'no-') + feature; 600 | } 601 | Modernizr[feature] = test; 602 | 603 | } 604 | 605 | return Modernizr; 606 | }; 607 | 608 | 609 | setCss(''); 610 | modElem = inputElem = null; 611 | 612 | ;(function(window, document) { 613 | var options = window.html5 || {}; 614 | 615 | var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i; 616 | 617 | var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i; 618 | 619 | var supportsHtml5Styles; 620 | 621 | var expando = '_html5shiv'; 622 | 623 | var expanID = 0; 624 | 625 | var expandoData = {}; 626 | 627 | var supportsUnknownElements; 628 | 629 | (function() { 630 | try { 631 | var a = document.createElement('a'); 632 | a.innerHTML = ''; 633 | supportsHtml5Styles = ('hidden' in a); 634 | 635 | supportsUnknownElements = a.childNodes.length == 1 || (function() { 636 | (document.createElement)('a'); 637 | var frag = document.createDocumentFragment(); 638 | return ( 639 | typeof frag.cloneNode == 'undefined' || 640 | typeof frag.createDocumentFragment == 'undefined' || 641 | typeof frag.createElement == 'undefined' 642 | ); 643 | }()); 644 | } catch(e) { 645 | supportsHtml5Styles = true; 646 | supportsUnknownElements = true; 647 | } 648 | 649 | }()); function addStyleSheet(ownerDocument, cssText) { 650 | var p = ownerDocument.createElement('p'), 651 | parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement; 652 | 653 | p.innerHTML = 'x'; 654 | return parent.insertBefore(p.lastChild, parent.firstChild); 655 | } 656 | 657 | function getElements() { 658 | var elements = html5.elements; 659 | return typeof elements == 'string' ? elements.split(' ') : elements; 660 | } 661 | 662 | function getExpandoData(ownerDocument) { 663 | var data = expandoData[ownerDocument[expando]]; 664 | if (!data) { 665 | data = {}; 666 | expanID++; 667 | ownerDocument[expando] = expanID; 668 | expandoData[expanID] = data; 669 | } 670 | return data; 671 | } 672 | 673 | function createElement(nodeName, ownerDocument, data){ 674 | if (!ownerDocument) { 675 | ownerDocument = document; 676 | } 677 | if(supportsUnknownElements){ 678 | return ownerDocument.createElement(nodeName); 679 | } 680 | if (!data) { 681 | data = getExpandoData(ownerDocument); 682 | } 683 | var node; 684 | 685 | if (data.cache[nodeName]) { 686 | node = data.cache[nodeName].cloneNode(); 687 | } else if (saveClones.test(nodeName)) { 688 | node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode(); 689 | } else { 690 | node = data.createElem(nodeName); 691 | } 692 | 693 | return node.canHaveChildren && !reSkip.test(nodeName) ? data.frag.appendChild(node) : node; 694 | } 695 | 696 | function createDocumentFragment(ownerDocument, data){ 697 | if (!ownerDocument) { 698 | ownerDocument = document; 699 | } 700 | if(supportsUnknownElements){ 701 | return ownerDocument.createDocumentFragment(); 702 | } 703 | data = data || getExpandoData(ownerDocument); 704 | var clone = data.frag.cloneNode(), 705 | i = 0, 706 | elems = getElements(), 707 | l = elems.length; 708 | for(;ip+1E3)l=Math.round(o*1E3/(i-p)),q=Math.min(q,l),r=Math.max(r,l),j.textContent=l+" FPS ("+q+"-"+r+")",a=Math.min(30,30-l/ 7 | 100*30),f.appendChild(f.firstChild).style.height=a+"px",p=i,o=0}}}; 8 | -------------------------------------------------------------------------------- /app/client/lib/webgl/THREEx.KeyboardState.js: -------------------------------------------------------------------------------- 1 | // THREEx.KeyboardState.js keep the current state of the keyboard. 2 | // It is possible to query it at any time. No need of an event. 3 | // This is particularly convenient in loop driven case, like in 4 | // 3D demos or games. 5 | // 6 | // # Usage 7 | // 8 | // **Step 1**: Create the object 9 | // 10 | // ```var keyboard = new THREEx.KeyboardState();``` 11 | // 12 | // **Step 2**: Query the keyboard state 13 | // 14 | // This will return true if shift and A are pressed, false otherwise 15 | // 16 | // ```keyboard.pressed("shift+A")``` 17 | // 18 | // **Step 3**: Stop listening to the keyboard 19 | // 20 | // ```keyboard.destroy()``` 21 | // 22 | // NOTE: this library may be nice as standaline. independant from three.js 23 | // - rename it keyboardForGame 24 | // 25 | // # Code 26 | // 27 | 28 | /** @namespace */ 29 | var THREEx = THREEx || {}; 30 | 31 | /** 32 | * - NOTE: it would be quite easy to push event-driven too 33 | * - microevent.js for events handling 34 | * - in this._onkeyChange, generate a string from the DOM event 35 | * - use this as event name 36 | */ 37 | THREEx.KeyboardState = function() 38 | { 39 | // to store the current state 40 | this.keyCodes = {}; 41 | this.modifiers = {}; 42 | 43 | // create callback to bind/unbind keyboard events 44 | var self = this; 45 | this._onKeyDown = function(event){ 46 | event.preventDefault(); 47 | self._onKeyChange(event, true); 48 | }; 49 | this._onKeyUp = function(event){ 50 | event.preventDefault(); 51 | self._onKeyChange(event, false); 52 | }; 53 | 54 | // bind keyEvents 55 | document.addEventListener("keydown", this._onKeyDown, false); 56 | document.addEventListener("keyup", this._onKeyUp, false); 57 | }; 58 | 59 | /** 60 | * To stop listening of the keyboard events 61 | */ 62 | THREEx.KeyboardState.prototype.destroy = function() 63 | { 64 | // unbind keyEvents 65 | document.removeEventListener("keydown", this._onKeyDown, false); 66 | document.removeEventListener("keyup", this._onKeyUp, false); 67 | }; 68 | 69 | THREEx.KeyboardState.MODIFIERS = ['shift', 'ctrl', 'alt', 'meta']; 70 | THREEx.KeyboardState.ALIAS = { 71 | 'left' : 37, 72 | 'up' : 38, 73 | 'right' : 39, 74 | 'down' : 40, 75 | 'space' : 32, 76 | 'pageup' : 33, 77 | 'pagedown' : 34, 78 | 'tab' : 9 79 | }; 80 | 81 | /** 82 | * to process the keyboard dom event 83 | */ 84 | THREEx.KeyboardState.prototype._onKeyChange = function(event, pressed) 85 | { 86 | // log to debug 87 | //console.log("onKeyChange", event, pressed, event.keyCode, event.shiftKey, event.ctrlKey, event.altKey, event.metaKey) 88 | 89 | // update this.keyCodes 90 | var keyCode = event.keyCode; 91 | this.keyCodes[keyCode] = pressed; 92 | 93 | // update this.modifiers 94 | this.modifiers['shift']= event.shiftKey; 95 | this.modifiers['ctrl'] = event.ctrlKey; 96 | this.modifiers['alt'] = event.altKey; 97 | this.modifiers['meta'] = event.metaKey; 98 | }; 99 | 100 | /** 101 | * query keyboard state to know if a key is pressed of not 102 | * 103 | * @param {String} keyDesc the description of the key. format : modifiers+key e.g shift+A 104 | * @returns {Boolean} true if the key is pressed, false otherwise 105 | */ 106 | THREEx.KeyboardState.prototype.pressed = function(keyDesc) 107 | { 108 | var keys = keyDesc.split("+"); 109 | for(var i = 0; i < keys.length; i++){ 110 | var key = keys[i]; 111 | var pressed; 112 | if( THREEx.KeyboardState.MODIFIERS.indexOf( key ) !== -1 ){ 113 | pressed = this.modifiers[key]; 114 | }else if( Object.keys(THREEx.KeyboardState.ALIAS).indexOf( key ) != -1 ){ 115 | pressed = this.keyCodes[ THREEx.KeyboardState.ALIAS[key] ]; 116 | }else { 117 | pressed = this.keyCodes[key.toUpperCase().charCodeAt(0)] 118 | } 119 | if( !pressed) return false; 120 | } 121 | return true; 122 | }; -------------------------------------------------------------------------------- /app/client/lib/webgl/THREEx.WindowResize.js: -------------------------------------------------------------------------------- 1 | // This THREEx helper makes it easy to handle window resize. 2 | // It will update renderer and camera when window is resized. 3 | // 4 | // # Usage 5 | // 6 | // **Step 1**: Start updating renderer and camera 7 | // 8 | // ```var windowResize = THREEx.WindowResize(aRenderer, aCamera)``` 9 | // 10 | // **Step 2**: Start updating renderer and camera 11 | // 12 | // ```windowResize.stop()``` 13 | // # Code 14 | 15 | // 16 | 17 | /** @namespace */ 18 | var THREEx = THREEx || {}; 19 | 20 | /** 21 | * Update renderer and camera when the window is resized 22 | * 23 | * @param {Object} renderer the renderer to update 24 | * @param {Object} Camera the camera to update 25 | */ 26 | THREEx.WindowResize = function(renderer, camera){ 27 | var callback = function(){ 28 | // notify the renderer of the size change 29 | renderer.setSize( window.innerWidth, window.innerHeight ); 30 | // update the camera 31 | camera.aspect = window.innerWidth / window.innerHeight; 32 | camera.updateProjectionMatrix(); 33 | } 34 | // bind the resize event 35 | window.addEventListener('resize', callback, false); 36 | // return .stop() the function to stop watching window resize 37 | return { 38 | /** 39 | * Stop watching window resize 40 | */ 41 | stop : function(){ 42 | window.removeEventListener('resize', callback); 43 | } 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /app/client/lib/webgl/core/three/Detector.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author alteredq / http://alteredqualia.com/ 3 | * @author mr.doob / http://mrdoob.com/ 4 | */ 5 | 6 | var Detector = { 7 | 8 | canvas: !! window.CanvasRenderingContext2D, 9 | webgl: ( function () { try { return !! window.WebGLRenderingContext && !! document.createElement( 'canvas' ).getContext( 'experimental-webgl' ); } catch( e ) { return false; } } )(), 10 | workers: !! window.Worker, 11 | fileapi: window.File && window.FileReader && window.FileList && window.Blob, 12 | 13 | getWebGLErrorMessage: function () { 14 | 15 | var element = document.createElement( 'div' ); 16 | element.id = 'webgl-error-message'; 17 | element.style.fontFamily = 'monospace'; 18 | element.style.fontSize = '13px'; 19 | element.style.fontWeight = 'normal'; 20 | element.style.textAlign = 'center'; 21 | element.style.background = '#fff'; 22 | element.style.color = '#000'; 23 | element.style.padding = '1.5em'; 24 | element.style.width = '400px'; 25 | element.style.margin = '5em auto 0'; 26 | 27 | if ( ! this.webgl ) { 28 | 29 | element.innerHTML = window.WebGLRenderingContext ? [ 30 | 'Your graphics card does not seem to support WebGL.
', 31 | 'Find out how to get it here.' 32 | ].join( '\n' ) : [ 33 | 'Your browser does not seem to support WebGL.
', 34 | 'Find out how to get it here.' 35 | ].join( '\n' ); 36 | 37 | } 38 | 39 | return element; 40 | 41 | }, 42 | 43 | addGetWebGLMessage: function ( parameters ) { 44 | 45 | var parent, id, element; 46 | 47 | parameters = parameters || {}; 48 | 49 | parent = parameters.parent !== undefined ? parameters.parent : document.body; 50 | id = parameters.id !== undefined ? parameters.id : 'oldie'; 51 | 52 | element = Detector.getWebGLErrorMessage(); 53 | element.id = id; 54 | 55 | parent.appendChild( element ); 56 | 57 | } 58 | 59 | }; -------------------------------------------------------------------------------- /app/client/lib/webgl/tween/tween.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author sole / http://soledadpenades.com 3 | * @author mrdoob / http://mrdoob.com 4 | * @author Robert Eisele / http://www.xarg.org 5 | * @author Philippe / http://philippe.elsass.me 6 | * @author Robert Penner / http://www.robertpenner.com/easing_terms_of_use.html 7 | * @author Paul Lewis / http://www.aerotwist.com/ 8 | * @author lechecacharro 9 | * @author Josh Faul / http://jocafa.com/ 10 | * @author egraether / http://egraether.com/ 11 | */ 12 | 13 | var TWEEN = TWEEN || ( function () { 14 | 15 | var _tweens = []; 16 | 17 | return { 18 | 19 | REVISION: '7', 20 | 21 | getAll: function () { 22 | 23 | return _tweens; 24 | 25 | }, 26 | 27 | removeAll: function () { 28 | 29 | _tweens = []; 30 | 31 | }, 32 | 33 | add: function ( tween ) { 34 | 35 | _tweens.push( tween ); 36 | 37 | }, 38 | 39 | remove: function ( tween ) { 40 | 41 | var i = _tweens.indexOf( tween ); 42 | 43 | if ( i !== -1 ) { 44 | 45 | _tweens.splice( i, 1 ); 46 | 47 | } 48 | 49 | }, 50 | 51 | update: function ( time ) { 52 | 53 | if ( _tweens.length === 0 ) return false; 54 | 55 | var i = 0, l = _tweens.length; 56 | 57 | time = time !== undefined ? time : Date.now(); 58 | 59 | while ( i < l ) { 60 | 61 | if ( _tweens[ i ].update( time ) ) { 62 | 63 | i ++; 64 | 65 | } else { 66 | 67 | _tweens.splice( i, 1 ); 68 | 69 | l --; 70 | 71 | } 72 | 73 | } 74 | 75 | return true; 76 | 77 | } 78 | 79 | }; 80 | 81 | } )(); 82 | 83 | TWEEN.Tween = function ( object ) { 84 | 85 | var _object = object; 86 | var _valuesStart = {}; 87 | var _valuesEnd = {}; 88 | var _duration = 1000; 89 | var _delayTime = 0; 90 | var _startTime = null; 91 | var _easingFunction = TWEEN.Easing.Linear.None; 92 | var _interpolationFunction = TWEEN.Interpolation.Linear; 93 | var _chainedTweens = []; 94 | var _onStartCallback = null; 95 | var _onStartCallbackFired = false; 96 | var _onUpdateCallback = null; 97 | var _onCompleteCallback = null; 98 | 99 | this.to = function ( properties, duration ) { 100 | 101 | if ( duration !== undefined ) { 102 | 103 | _duration = duration; 104 | 105 | } 106 | 107 | _valuesEnd = properties; 108 | 109 | return this; 110 | 111 | }; 112 | 113 | this.start = function ( time ) { 114 | 115 | TWEEN.add( this ); 116 | 117 | _onStartCallbackFired = false; 118 | 119 | _startTime = time !== undefined ? time : Date.now(); 120 | _startTime += _delayTime; 121 | 122 | for ( var property in _valuesEnd ) { 123 | 124 | // This prevents the engine from interpolating null values 125 | if ( _object[ property ] === null ) { 126 | 127 | continue; 128 | 129 | } 130 | 131 | // check if an Array was provided as property value 132 | if ( _valuesEnd[ property ] instanceof Array ) { 133 | 134 | if ( _valuesEnd[ property ].length === 0 ) { 135 | 136 | continue; 137 | 138 | } 139 | 140 | // create a local copy of the Array with the start value at the front 141 | _valuesEnd[ property ] = [ _object[ property ] ].concat( _valuesEnd[ property ] ); 142 | 143 | } 144 | 145 | _valuesStart[ property ] = _object[ property ]; 146 | 147 | } 148 | 149 | return this; 150 | 151 | }; 152 | 153 | this.stop = function () { 154 | 155 | TWEEN.remove( this ); 156 | return this; 157 | 158 | }; 159 | 160 | this.delay = function ( amount ) { 161 | 162 | _delayTime = amount; 163 | return this; 164 | 165 | }; 166 | 167 | this.easing = function ( easing ) { 168 | 169 | _easingFunction = easing; 170 | return this; 171 | 172 | }; 173 | 174 | this.interpolation = function ( interpolation ) { 175 | 176 | _interpolationFunction = interpolation; 177 | return this; 178 | 179 | }; 180 | 181 | this.chain = function () { 182 | 183 | _chainedTweens = arguments; 184 | return this; 185 | 186 | }; 187 | 188 | this.onStart = function ( callback ) { 189 | 190 | _onStartCallback = callback; 191 | return this; 192 | 193 | }; 194 | 195 | this.onUpdate = function ( callback ) { 196 | 197 | _onUpdateCallback = callback; 198 | return this; 199 | 200 | }; 201 | 202 | this.onComplete = function ( callback ) { 203 | 204 | _onCompleteCallback = callback; 205 | return this; 206 | 207 | }; 208 | 209 | this.update = function ( time ) { 210 | 211 | if ( time < _startTime ) { 212 | 213 | return true; 214 | 215 | } 216 | 217 | if ( _onStartCallbackFired === false ) { 218 | 219 | if ( _onStartCallback !== null ) { 220 | 221 | _onStartCallback.call( _object ); 222 | 223 | } 224 | 225 | _onStartCallbackFired = true; 226 | 227 | } 228 | 229 | var elapsed = ( time - _startTime ) / _duration; 230 | elapsed = elapsed > 1 ? 1 : elapsed; 231 | 232 | var value = _easingFunction( elapsed ); 233 | 234 | for ( var property in _valuesStart ) { 235 | 236 | var start = _valuesStart[ property ]; 237 | var end = _valuesEnd[ property ]; 238 | 239 | if ( end instanceof Array ) { 240 | 241 | _object[ property ] = _interpolationFunction( end, value ); 242 | 243 | } else { 244 | 245 | _object[ property ] = start + ( end - start ) * value; 246 | 247 | } 248 | 249 | } 250 | 251 | if ( _onUpdateCallback !== null ) { 252 | 253 | _onUpdateCallback.call( _object, value ); 254 | 255 | } 256 | 257 | if ( elapsed == 1 ) { 258 | 259 | if ( _onCompleteCallback !== null ) { 260 | 261 | _onCompleteCallback.call( _object ); 262 | 263 | } 264 | 265 | for ( var i = 0, l = _chainedTweens.length; i < l; i ++ ) { 266 | 267 | _chainedTweens[ i ].start( time ); 268 | 269 | } 270 | 271 | return false; 272 | 273 | } 274 | 275 | return true; 276 | 277 | }; 278 | 279 | }; 280 | 281 | TWEEN.Easing = { 282 | 283 | Linear: { 284 | 285 | None: function ( k ) { 286 | 287 | return k; 288 | 289 | } 290 | 291 | }, 292 | 293 | Quadratic: { 294 | 295 | In: function ( k ) { 296 | 297 | return k * k; 298 | 299 | }, 300 | 301 | Out: function ( k ) { 302 | 303 | return k * ( 2 - k ); 304 | 305 | }, 306 | 307 | InOut: function ( k ) { 308 | 309 | if ( ( k *= 2 ) < 1 ) return 0.5 * k * k; 310 | return - 0.5 * ( --k * ( k - 2 ) - 1 ); 311 | 312 | } 313 | 314 | }, 315 | 316 | Cubic: { 317 | 318 | In: function ( k ) { 319 | 320 | return k * k * k; 321 | 322 | }, 323 | 324 | Out: function ( k ) { 325 | 326 | return --k * k * k + 1; 327 | 328 | }, 329 | 330 | InOut: function ( k ) { 331 | 332 | if ( ( k *= 2 ) < 1 ) return 0.5 * k * k * k; 333 | return 0.5 * ( ( k -= 2 ) * k * k + 2 ); 334 | 335 | } 336 | 337 | }, 338 | 339 | Quartic: { 340 | 341 | In: function ( k ) { 342 | 343 | return k * k * k * k; 344 | 345 | }, 346 | 347 | Out: function ( k ) { 348 | 349 | return 1 - ( --k * k * k * k ); 350 | 351 | }, 352 | 353 | InOut: function ( k ) { 354 | 355 | if ( ( k *= 2 ) < 1) return 0.5 * k * k * k * k; 356 | return - 0.5 * ( ( k -= 2 ) * k * k * k - 2 ); 357 | 358 | } 359 | 360 | }, 361 | 362 | Quintic: { 363 | 364 | In: function ( k ) { 365 | 366 | return k * k * k * k * k; 367 | 368 | }, 369 | 370 | Out: function ( k ) { 371 | 372 | return --k * k * k * k * k + 1; 373 | 374 | }, 375 | 376 | InOut: function ( k ) { 377 | 378 | if ( ( k *= 2 ) < 1 ) return 0.5 * k * k * k * k * k; 379 | return 0.5 * ( ( k -= 2 ) * k * k * k * k + 2 ); 380 | 381 | } 382 | 383 | }, 384 | 385 | Sinusoidal: { 386 | 387 | In: function ( k ) { 388 | 389 | return 1 - Math.cos( k * Math.PI / 2 ); 390 | 391 | }, 392 | 393 | Out: function ( k ) { 394 | 395 | return Math.sin( k * Math.PI / 2 ); 396 | 397 | }, 398 | 399 | InOut: function ( k ) { 400 | 401 | return 0.5 * ( 1 - Math.cos( Math.PI * k ) ); 402 | 403 | } 404 | 405 | }, 406 | 407 | Exponential: { 408 | 409 | In: function ( k ) { 410 | 411 | return k === 0 ? 0 : Math.pow( 1024, k - 1 ); 412 | 413 | }, 414 | 415 | Out: function ( k ) { 416 | 417 | return k === 1 ? 1 : 1 - Math.pow( 2, - 10 * k ); 418 | 419 | }, 420 | 421 | InOut: function ( k ) { 422 | 423 | if ( k === 0 ) return 0; 424 | if ( k === 1 ) return 1; 425 | if ( ( k *= 2 ) < 1 ) return 0.5 * Math.pow( 1024, k - 1 ); 426 | return 0.5 * ( - Math.pow( 2, - 10 * ( k - 1 ) ) + 2 ); 427 | 428 | } 429 | 430 | }, 431 | 432 | Circular: { 433 | 434 | In: function ( k ) { 435 | 436 | return 1 - Math.sqrt( 1 - k * k ); 437 | 438 | }, 439 | 440 | Out: function ( k ) { 441 | 442 | return Math.sqrt( 1 - ( --k * k ) ); 443 | 444 | }, 445 | 446 | InOut: function ( k ) { 447 | 448 | if ( ( k *= 2 ) < 1) return - 0.5 * ( Math.sqrt( 1 - k * k) - 1); 449 | return 0.5 * ( Math.sqrt( 1 - ( k -= 2) * k) + 1); 450 | 451 | } 452 | 453 | }, 454 | 455 | Elastic: { 456 | 457 | In: function ( k ) { 458 | 459 | var s, a = 0.1, p = 0.4; 460 | if ( k === 0 ) return 0; 461 | if ( k === 1 ) return 1; 462 | if ( !a || a < 1 ) { a = 1; s = p / 4; } 463 | else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI ); 464 | return - ( a * Math.pow( 2, 10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) ); 465 | 466 | }, 467 | 468 | Out: function ( k ) { 469 | 470 | var s, a = 0.1, p = 0.4; 471 | if ( k === 0 ) return 0; 472 | if ( k === 1 ) return 1; 473 | if ( !a || a < 1 ) { a = 1; s = p / 4; } 474 | else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI ); 475 | return ( a * Math.pow( 2, - 10 * k) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) + 1 ); 476 | 477 | }, 478 | 479 | InOut: function ( k ) { 480 | 481 | var s, a = 0.1, p = 0.4; 482 | if ( k === 0 ) return 0; 483 | if ( k === 1 ) return 1; 484 | if ( !a || a < 1 ) { a = 1; s = p / 4; } 485 | else s = p * Math.asin( 1 / a ) / ( 2 * Math.PI ); 486 | if ( ( k *= 2 ) < 1 ) return - 0.5 * ( a * Math.pow( 2, 10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) ); 487 | return a * Math.pow( 2, -10 * ( k -= 1 ) ) * Math.sin( ( k - s ) * ( 2 * Math.PI ) / p ) * 0.5 + 1; 488 | 489 | } 490 | 491 | }, 492 | 493 | Back: { 494 | 495 | In: function ( k ) { 496 | 497 | var s = 1.70158; 498 | return k * k * ( ( s + 1 ) * k - s ); 499 | 500 | }, 501 | 502 | Out: function ( k ) { 503 | 504 | var s = 1.70158; 505 | return --k * k * ( ( s + 1 ) * k + s ) + 1; 506 | 507 | }, 508 | 509 | InOut: function ( k ) { 510 | 511 | var s = 1.70158 * 1.525; 512 | if ( ( k *= 2 ) < 1 ) return 0.5 * ( k * k * ( ( s + 1 ) * k - s ) ); 513 | return 0.5 * ( ( k -= 2 ) * k * ( ( s + 1 ) * k + s ) + 2 ); 514 | 515 | } 516 | 517 | }, 518 | 519 | Bounce: { 520 | 521 | In: function ( k ) { 522 | 523 | return 1 - TWEEN.Easing.Bounce.Out( 1 - k ); 524 | 525 | }, 526 | 527 | Out: function ( k ) { 528 | 529 | if ( k < ( 1 / 2.75 ) ) { 530 | 531 | return 7.5625 * k * k; 532 | 533 | } else if ( k < ( 2 / 2.75 ) ) { 534 | 535 | return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75; 536 | 537 | } else if ( k < ( 2.5 / 2.75 ) ) { 538 | 539 | return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375; 540 | 541 | } else { 542 | 543 | return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375; 544 | 545 | } 546 | 547 | }, 548 | 549 | InOut: function ( k ) { 550 | 551 | if ( k < 0.5 ) return TWEEN.Easing.Bounce.In( k * 2 ) * 0.5; 552 | return TWEEN.Easing.Bounce.Out( k * 2 - 1 ) * 0.5 + 0.5; 553 | 554 | } 555 | 556 | } 557 | 558 | }; 559 | 560 | TWEEN.Interpolation = { 561 | 562 | Linear: function ( v, k ) { 563 | 564 | var m = v.length - 1, f = m * k, i = Math.floor( f ), fn = TWEEN.Interpolation.Utils.Linear; 565 | 566 | if ( k < 0 ) return fn( v[ 0 ], v[ 1 ], f ); 567 | if ( k > 1 ) return fn( v[ m ], v[ m - 1 ], m - f ); 568 | 569 | return fn( v[ i ], v[ i + 1 > m ? m : i + 1 ], f - i ); 570 | 571 | }, 572 | 573 | Bezier: function ( v, k ) { 574 | 575 | var b = 0, n = v.length - 1, pw = Math.pow, bn = TWEEN.Interpolation.Utils.Bernstein, i; 576 | 577 | for ( i = 0; i <= n; i++ ) { 578 | b += pw( 1 - k, n - i ) * pw( k, i ) * v[ i ] * bn( n, i ); 579 | } 580 | 581 | return b; 582 | 583 | }, 584 | 585 | CatmullRom: function ( v, k ) { 586 | 587 | var m = v.length - 1, f = m * k, i = Math.floor( f ), fn = TWEEN.Interpolation.Utils.CatmullRom; 588 | 589 | if ( v[ 0 ] === v[ m ] ) { 590 | 591 | if ( k < 0 ) i = Math.floor( f = m * ( 1 + k ) ); 592 | 593 | return fn( v[ ( i - 1 + m ) % m ], v[ i ], v[ ( i + 1 ) % m ], v[ ( i + 2 ) % m ], f - i ); 594 | 595 | } else { 596 | 597 | if ( k < 0 ) return v[ 0 ] - ( fn( v[ 0 ], v[ 0 ], v[ 1 ], v[ 1 ], -f ) - v[ 0 ] ); 598 | if ( k > 1 ) return v[ m ] - ( fn( v[ m ], v[ m ], v[ m - 1 ], v[ m - 1 ], f - m ) - v[ m ] ); 599 | 600 | return fn( v[ i ? i - 1 : 0 ], v[ i ], v[ m < i + 1 ? m : i + 1 ], v[ m < i + 2 ? m : i + 2 ], f - i ); 601 | 602 | } 603 | 604 | }, 605 | 606 | Utils: { 607 | 608 | Linear: function ( p0, p1, t ) { 609 | 610 | return ( p1 - p0 ) * t + p0; 611 | 612 | }, 613 | 614 | Bernstein: function ( n , i ) { 615 | 616 | var fc = TWEEN.Interpolation.Utils.Factorial; 617 | return fc( n ) / fc( i ) / fc( n - i ); 618 | 619 | }, 620 | 621 | Factorial: ( function () { 622 | 623 | var a = [ 1 ]; 624 | 625 | return function ( n ) { 626 | 627 | var s = 1, i; 628 | if ( a[ n ] ) return a[ n ]; 629 | for ( i = n; i > 1; i-- ) s *= i; 630 | return a[ n ] = s; 631 | 632 | }; 633 | 634 | } )(), 635 | 636 | CatmullRom: function ( p0, p1, p2, p3, t ) { 637 | 638 | var v0 = ( p2 - p0 ) * 0.5, v1 = ( p3 - p1 ) * 0.5, t2 = t * t, t3 = t * t2; 639 | return ( 2 * p1 - 2 * p2 + v0 + v1 ) * t3 + ( - 3 * p1 + 3 * p2 - 2 * v0 - v1 ) * t2 + v0 * t + p1; 640 | 641 | } 642 | 643 | } 644 | 645 | }; -------------------------------------------------------------------------------- /app/client/scss/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Box Shadow 2 | @mixin box-shadow($value) { 3 | -webkit-box-shadow: #{$value}; 4 | -moz-box-shadow: #{$value}; 5 | -ms-box-shadow: #{$value}; 6 | -o-box-shadow: #{$value}; 7 | box-shadow: #{$value}; 8 | } 9 | 10 | 11 | // Linear Gradient 12 | @mixin background-linear-gradient($collection) { 13 | 14 | } 15 | 16 | -------------------------------------------------------------------------------- /app/client/scss/project.scss: -------------------------------------------------------------------------------- 1 | @import "_mixins"; 2 | @import "compass"; 3 | 4 | a { 5 | @include link-colors(#000011, #c0c0c0, #000011, #000011, #c0c0c0); 6 | font-weight: bold; 7 | text-decoration: none; 8 | } 9 | 10 | * { 11 | font-family: 'Gudea', sans-serif; 12 | text-transform: lowercase; 13 | } 14 | 15 | h3 { 16 | margin: 0px; 17 | } 18 | 19 | ul, li { 20 | list-style-type: none; 21 | margin: 0px; 22 | padding: 0px; 23 | } 24 | 25 | body { 26 | background-color: #222222; 27 | background: url("/img/bg.png"); 28 | overflow: hidden; 29 | 30 | } 31 | 32 | button, .topper-info { 33 | position: relative; 34 | overflow: visible; 35 | display: inline-block; 36 | padding: 0.5em 1em; 37 | 38 | margin: 0; 39 | text-decoration: none; 40 | text-align: center; 41 | text-shadow: 1px 1px 0 #fff; 42 | font-size: 20px; 43 | color: #333; 44 | 45 | 46 | &.new-game, &.continue { 47 | font-weight: bold; 48 | margin: 30px 0px 30px 275px; 49 | width: 350px; 50 | padding: 10px 20px; 51 | } 52 | } 53 | 54 | 55 | button, .topper-info, .ossprojects li, .view-gamepad { 56 | border: 1px solid #d4d4d4; 57 | white-space: nowrap; 58 | cursor: pointer; 59 | outline: none; 60 | background-color: #ececec; 61 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f4f4f4), to(#ececec)); 62 | background-image: -moz-linear-gradient(#f4f4f4, #ececec); 63 | background-image: -ms-linear-gradient(#f4f4f4, #ececec); 64 | background-image: -o-linear-gradient(#f4f4f4, #ececec); 65 | background-image: linear-gradient(#f4f4f4, #ececec); 66 | -moz-background-clip: padding; /* for Firefox 3.6 */ 67 | background-clip: padding-box; 68 | border-radius: 0.2em; 69 | } 70 | 71 | 72 | .view-gamepad { 73 | display: block; 74 | text-align: center; 75 | padding: 2px; 76 | margin-top: 5px; 77 | font-weight: normal; 78 | } 79 | 80 | button:hover, .ossprojects li:hover { 81 | background-color: #ffffff; 82 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f4f4f4), to(#ffffff)); 83 | background-image: -moz-linear-gradient(#f4f4f4, #ffffff); 84 | background-image: -ms-linear-gradient(#f4f4f4, #ffffff); 85 | background-image: -o-linear-gradient(#f4f4f4, #ffffff); 86 | background-image: linear-gradient(#f4f4f4, #ffffff); 87 | } 88 | 89 | .topper-info { 90 | color: #FFF; 91 | text-transform: lowercase; 92 | border-radius: 0; 93 | cursor: none; 94 | width: 100%; 95 | font-size: 10px; 96 | color: #000011; 97 | } 98 | 99 | .control-help { 100 | font-size: 10px; 101 | color: #FFF; 102 | width: 100px; 103 | position: absolute; 104 | bottom: 0px; 105 | right: 0px; 106 | width: 100px; 107 | } 108 | 109 | .gamepad-status { 110 | position: absolute; 111 | bottom: 100px; 112 | right: 0px; 113 | font-size: 12px; 114 | width: 100px; 115 | color: #FFF; 116 | .connected { 117 | background: #baff72; 118 | } 119 | } 120 | 121 | .req-webgl .continue { 122 | margin: 85px 0px 30px 170px; 123 | width: 600px; 124 | font-size: 23px; 125 | } 126 | 127 | .player-list li { 128 | display: inline; 129 | } 130 | 131 | .req-welcome { 132 | h3 { 133 | color: #333333; 134 | margin-left: 6px; 135 | } 136 | } 137 | 138 | .req-gameover { 139 | h1 { 140 | margin-lefT: 75px; 141 | } 142 | .window p { 143 | margin-left: 35px; 144 | } 145 | } 146 | 147 | .req-project { 148 | aside { 149 | font-size: 20px; 150 | margin: 20px 0px 20px 35px; 151 | } 152 | } 153 | 154 | .frames { 155 | width: 990px; 156 | margin: 0 auto; 157 | position: relative; 158 | } 159 | 160 | .life { 161 | height: 20px; 162 | width: 100%; 163 | // TODO: fix this later 164 | // needs latest Compass, add '@import "compass"' to your scss 165 | background-color: #cdeb8e; 166 | // Old browsers 167 | @include background-image(linear-gradient(top, #cdeb8e 0%, #a5c956 100%)); 168 | text-transform: lowercase; 169 | 170 | } 171 | 172 | .frame { 173 | position: absolute; 174 | width: 990px; 175 | top: -1000px; 176 | margin: 0 auto; 177 | h1 { 178 | font-weight: normal; 179 | font-size: 60px; 180 | text-shadow: 2px 1px 4px rgb(39, 38, 38); 181 | height: 90px; 182 | color: #F3F5F7; 183 | margin: 0px 0px 0px 45px; 184 | padding: 0px; 185 | text-transform: lowercase; 186 | } 187 | .window { 188 | @include box-shadow("inset 0px 1px 0px rgba(255,255,255, 0.25), 0px 10px 20px rgba(0,0,0, 0.7)"); 189 | width: 900px; 190 | overflow: hidden; 191 | background: #FFF; 192 | position: relative; 193 | min-height: 250px; 194 | max-height: 460px; 195 | margin: 0 auto; 196 | .part { 197 | width: 25%; 198 | float: left; 199 | h3 { 200 | margin: 20px 0px 0px 0px; 201 | } 202 | } 203 | .part-large { 204 | float: left; 205 | width: 70%; 206 | padding-right: 4%; 207 | ul { 208 | margin-left: 5%; 209 | } 210 | li { 211 | font-size: 18px; 212 | margin: 10px 0px 10px 0px; 213 | list-style-type: square; 214 | } 215 | } 216 | h1 { 217 | margin: 0px; 218 | text-align: center; 219 | font-weight: bold; 220 | text-transform: uppercase; 221 | color: #c0c0c0; 222 | &.logo span { 223 | color: #000011; 224 | } 225 | } 226 | .under-logo { 227 | position: absolute; 228 | top: 47px; 229 | left: 350px; 230 | } 231 | } 232 | } 233 | 234 | .plays { 235 | clear: both; 236 | .play { 237 | overflow: hidden; 238 | float: left; 239 | width: 9%; 240 | font-size: 10px; 241 | border-left: 1px solid grey; 242 | padding: 0.7%; 243 | margin: 0.7%; 244 | white-space: nowrap; 245 | } 246 | } 247 | 248 | .progress { 249 | display: none; 250 | } 251 | 252 | .ossprojects { 253 | li { 254 | cursor: pointer; 255 | float: left; 256 | width: 20%; 257 | margin: 1%; 258 | padding: 1%; 259 | height: 100px; 260 | overflow: hidden; 261 | span { 262 | display: block; 263 | } 264 | 265 | .name { 266 | font-weight: bold; 267 | text-align: center; 268 | font-size: 24px; 269 | margin-bottom: 30px; 270 | } 271 | .data { 272 | 273 | } 274 | } 275 | overflow: hidden; 276 | margin: 0px 0px 20px 25px; 277 | } 278 | 279 | .about { 280 | position: absolute; 281 | color: #FFF; 282 | left: 0px; 283 | bottom: 0px; 284 | font-size: 10px; 285 | a { 286 | color: #FFF; 287 | } 288 | } 289 | 290 | .again { 291 | position: absolute; 292 | top: 40px; 293 | right: 36px; 294 | width: 200px; 295 | } 296 | 297 | .g-on { 298 | color: green; 299 | } 300 | 301 | .level { 302 | @include opacity(0.7); 303 | display: none; 304 | background-color: #fc4e1e; 305 | border-radius: 10px; 306 | height: 100px; 307 | width: 10%; 308 | top:80px; 309 | margin-left: -10%; 310 | left: 50%; 311 | text-align: center; 312 | padding: 20px; 313 | position: absolute; 314 | div { 315 | margin-top: 10px; 316 | font-size: 24px; 317 | span { 318 | font-size: 45px; 319 | display: block; 320 | font-weight: bold; 321 | margin: 0auto; 322 | } 323 | } 324 | } 325 | 326 | .window button.music { 327 | padding: 2px; 328 | font-size: 15px; 329 | margin-top: 5px; 330 | } 331 | body > .control-help .music { 332 | font-size: 12px; 333 | padding: 0px 5px; 334 | margin: 0px; 335 | border: 0px; 336 | text-align: left; 337 | text-shadow: none; 338 | } -------------------------------------------------------------------------------- /app/client/templates/choose.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /app/client/templates/game/dashboard.html: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /app/client/templates/game/level.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /app/client/templates/game/life.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /app/client/templates/gamover.html: -------------------------------------------------------------------------------- 1 | 21 | -------------------------------------------------------------------------------- /app/client/templates/partial/about.html: -------------------------------------------------------------------------------- 1 | 4 | -------------------------------------------------------------------------------- /app/client/templates/partial/help.html: -------------------------------------------------------------------------------- 1 | 13 | -------------------------------------------------------------------------------- /app/client/templates/partial/projects.html: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /app/client/templates/partial/recentScores.html: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /app/client/templates/partial/topScores.html: -------------------------------------------------------------------------------- 1 | 22 | -------------------------------------------------------------------------------- /app/client/templates/tutorial.html: -------------------------------------------------------------------------------- 1 | 30 | -------------------------------------------------------------------------------- /app/client/templates/webgl.html: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /app/client/templates/welcome.html: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /app/public/img/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladikoff/game-off-2012/f1d0ceefcb0a88e7d3d9687f953d33695c4f328e/app/public/img/bg.png -------------------------------------------------------------------------------- /app/public/img/crOrangeMCode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladikoff/game-off-2012/f1d0ceefcb0a88e7d3d9687f953d33695c4f328e/app/public/img/crOrangeMCode.png -------------------------------------------------------------------------------- /app/public/img/crOrangePCode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladikoff/game-off-2012/f1d0ceefcb0a88e7d3d9687f953d33695c4f328e/app/public/img/crOrangePCode.png -------------------------------------------------------------------------------- /app/public/img/crPR.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladikoff/game-off-2012/f1d0ceefcb0a88e7d3d9687f953d33695c4f328e/app/public/img/crPR.png -------------------------------------------------------------------------------- /app/public/img/crPurpleMCode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladikoff/game-off-2012/f1d0ceefcb0a88e7d3d9687f953d33695c4f328e/app/public/img/crPurpleMCode.png -------------------------------------------------------------------------------- /app/public/img/crPurplePCode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladikoff/game-off-2012/f1d0ceefcb0a88e7d3d9687f953d33695c4f328e/app/public/img/crPurplePCode.png -------------------------------------------------------------------------------- /app/public/img/crStar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladikoff/game-off-2012/f1d0ceefcb0a88e7d3d9687f953d33695c4f328e/app/public/img/crStar.png -------------------------------------------------------------------------------- /app/public/img/crTealMCode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladikoff/game-off-2012/f1d0ceefcb0a88e7d3d9687f953d33695c4f328e/app/public/img/crTealMCode.png -------------------------------------------------------------------------------- /app/public/img/crTealPCode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladikoff/game-off-2012/f1d0ceefcb0a88e7d3d9687f953d33695c4f328e/app/public/img/crTealPCode.png -------------------------------------------------------------------------------- /app/public/img/gamepad-map.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladikoff/game-off-2012/f1d0ceefcb0a88e7d3d9687f953d33695c4f328e/app/public/img/gamepad-map.png -------------------------------------------------------------------------------- /app/public/img/player0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladikoff/game-off-2012/f1d0ceefcb0a88e7d3d9687f953d33695c4f328e/app/public/img/player0.png -------------------------------------------------------------------------------- /app/public/img/player1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladikoff/game-off-2012/f1d0ceefcb0a88e7d3d9687f953d33695c4f328e/app/public/img/player1.png -------------------------------------------------------------------------------- /app/public/img/player2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladikoff/game-off-2012/f1d0ceefcb0a88e7d3d9687f953d33695c4f328e/app/public/img/player2.png -------------------------------------------------------------------------------- /app/public/img/poster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladikoff/game-off-2012/f1d0ceefcb0a88e7d3d9687f953d33695c4f328e/app/public/img/poster.png -------------------------------------------------------------------------------- /app/public/img/tutorial-bad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladikoff/game-off-2012/f1d0ceefcb0a88e7d3d9687f953d33695c4f328e/app/public/img/tutorial-bad.png -------------------------------------------------------------------------------- /app/public/img/tutorial-good.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladikoff/game-off-2012/f1d0ceefcb0a88e7d3d9687f953d33695c4f328e/app/public/img/tutorial-good.png -------------------------------------------------------------------------------- /app/public/music/0.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladikoff/game-off-2012/f1d0ceefcb0a88e7d3d9687f953d33695c4f328e/app/public/music/0.ogg -------------------------------------------------------------------------------- /app/public/music/1.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladikoff/game-off-2012/f1d0ceefcb0a88e7d3d9687f953d33695c4f328e/app/public/music/1.ogg -------------------------------------------------------------------------------- /app/public/music/2.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladikoff/game-off-2012/f1d0ceefcb0a88e7d3d9687f953d33695c4f328e/app/public/music/2.ogg -------------------------------------------------------------------------------- /app/server/server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Meteor startup 3 | */ 4 | Meteor.startup(function () { 5 | // On server startup, create some players if the database is empty. 6 | if (Projects.find().count() === 0) { 7 | populateProjects(); 8 | } 9 | }); 10 | 11 | 12 | /** 13 | * Publish a list of projects 14 | */ 15 | Meteor.publish("projects", function () { 16 | return Projects.find({}, { sort:{ commits: -1, stars: -1 }, fields:{} }); 17 | }); 18 | 19 | 20 | /** 21 | * Publish Recent Scores 22 | */ 23 | Meteor.publish("recent_scores", function () { 24 | var self = this; 25 | 26 | var handle = Plays.find({}, { sort:{ start_time:-1 }, limit:8, fields:{}}).observe({ 27 | added:function (item) { 28 | self.set("recent_scores", item._id, item); 29 | self.flush(); 30 | }, 31 | changed:function (item) { 32 | self.set("recent_scores", item._id, item); 33 | self.flush(); 34 | } 35 | }); 36 | 37 | this.onStop(function () { 38 | handle.stop(); 39 | }); 40 | }); 41 | 42 | 43 | /** 44 | * Publish top scores 45 | */ 46 | Meteor.publish("top_scores", function () { 47 | var self = this; 48 | 49 | var handle = Plays.find({}, { sort:{ commits:-1 }, limit:8, fields:{}}).observe({ 50 | added:function (item) { 51 | self.set("top_scores", item._id, item); 52 | self.flush(); 53 | }, 54 | changed:function (item) { 55 | self.set("top_scores", item._id, item); 56 | self.flush(); 57 | } 58 | }); 59 | 60 | this.onStop(function () { 61 | handle.stop(); 62 | }); 63 | }); 64 | 65 | 66 | /** 67 | * Populate the projects 68 | * runs on app 'install' 69 | */ 70 | function populateProjects() { 71 | 72 | var projects = [ 73 | { 74 | code:'jquery', 75 | name:'jQuery', 76 | commits:0, 77 | stars:0, 78 | color:'#3584ad' 79 | }, 80 | { 81 | code:'grunt', 82 | name:'Grunt', 83 | commits:0, 84 | stars:0, 85 | color:'#5a360f' 86 | }, 87 | { 88 | code:'lodash', 89 | name:'Lo-dash', 90 | commits:0, 91 | stars:0, 92 | color:'#0a1629' 93 | }, 94 | { 95 | code:'android', 96 | name:'Android', 97 | commits:0, 98 | stars:0, 99 | color:'#99c726' 100 | }, 101 | { 102 | code:'node', 103 | name:'Node', 104 | commits:0, 105 | stars:0, 106 | color:'#8bc451' 107 | }, 108 | { 109 | code:'three', 110 | name:'Three.js', 111 | commits:0, 112 | stars:0, 113 | color:'#444449' 114 | }, 115 | { 116 | code:'firefox', 117 | name:'Firefox', 118 | commits:0, 119 | stars:0, 120 | color:'#d74618' 121 | }, 122 | { 123 | code:'modernizr', 124 | name:'Modernizr', 125 | commits:0, 126 | stars:0, 127 | color:'#de2d75' 128 | } 129 | ].forEach(function (project) { 130 | Projects.insert(project); 131 | }); 132 | } 133 | 134 | 135 | /** 136 | * Server methods 137 | * Used with .call by the client 138 | */ 139 | Meteor.methods({ 140 | // options should include: title, description, x, y, public 141 | sessionUpdate:function (e) { 142 | if (e.data) { 143 | var p = Projects.findOne({_id:e.data.PROJECT}); 144 | 145 | if (p) { 146 | var u = Meteor.user(); 147 | 148 | 149 | if (e.data.GAMEOVER && Plays.find({user:u, start_time:e.data.START_TIME }).count() == 0) { 150 | // TODO: fix this later, no time right now. 151 | e.data.SECONDS_PLAYED = (e.data.END_TIME - e.data.START_TIME) / 1000; 152 | e.data.SECONDS_PLAYED_STRING = secondsToString(e.data.SECONDS_PLAYED); 153 | Plays.insert({ 154 | user:u, 155 | start_time:e.data.START_TIME, 156 | commits:e.data.COMMITS, 157 | session:e.data, 158 | project_name:p.name 159 | }); 160 | Projects.update({_id:e.data.PROJECT}, { $inc:{ stars:e.data.STARS, commits:e.data.COMMITS } }); 161 | } 162 | } 163 | } 164 | }, 165 | userUpdate:function (e) { 166 | Plays.update({start_time:e.data},{$set: {user: Meteor.user()}}); 167 | } 168 | }); 169 | 170 | 171 | /** 172 | * Create User event 173 | */ 174 | Accounts.onCreateUser(function (options, user) { 175 | if (options.profile) 176 | user.profile = options.profile; 177 | return user; 178 | }); 179 | 180 | 181 | /** 182 | * Temporary quick util 183 | * Quick seconds util 184 | * TODO: Remove. 185 | * @param seconds 186 | * @return {String} 187 | */ 188 | function secondsToString(seconds) { 189 | var numminutes = Math.floor((((seconds % 31536000) % 86400) % 3600) / 60); 190 | var numseconds = (((seconds % 31536000) % 86400) % 3600) % 60; 191 | return numminutes + "min " + parseInt(numseconds, 10) + "sec"; 192 | 193 | } 194 | -------------------------------------------------------------------------------- /app/shared.js: -------------------------------------------------------------------------------- 1 | Projects = new Meteor.Collection("projects"); 2 | Plays = new Meteor.Collection("plays"); 3 | 4 | 5 | 6 | SessionUpdates = new Meteor.Collection("sessions"); 7 | /** 8 | Projects.allow({ 9 | update: function (userId, parties, fields, modifier) { 10 | return true; 11 | } 12 | }); 13 | 14 | */ 15 | -------------------------------------------------------------------------------- /config.rb: -------------------------------------------------------------------------------- 1 | # Require any additional compass plugins here. 2 | 3 | # Set this to the root of your project when deployed: 4 | http_path = "/" 5 | css_dir = "app/client/css" 6 | sass_dir = "app/client/scss" 7 | images_dir = "app/public/img" 8 | images_path = "static/img" 9 | sprite_load_path = "static/img/sprites" 10 | # You can select your preferred output style here (can be overridden via the command line): 11 | # output_style = :expanded or :nested or :compact or :compressed 12 | 13 | # To enable relative paths to assets via compass helper functions. Uncomment: 14 | # relative_assets = true 15 | 16 | # To disable debugging comments that display the original location of your selectors. Uncomment: 17 | # line_comments = false 18 | 19 | 20 | # If you prefer the indented syntax, you might want to regenerate this 21 | # project again passing --syntax sass, or you can uncomment this: 22 | # preferred_syntax = :sass 23 | # and then run: 24 | # sass-convert -R --from scss --to sass sass scss && rm -rf sass && mv scss sass 25 | -------------------------------------------------------------------------------- /misc/assets.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladikoff/game-off-2012/f1d0ceefcb0a88e7d3d9687f953d33695c4f328e/misc/assets.psd -------------------------------------------------------------------------------- /misc/bg.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vladikoff/game-off-2012/f1d0ceefcb0a88e7d3d9687f953d33695c4f328e/misc/bg.psd -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "game-off-2012", 3 | "version": "0.0.1", 4 | "private": true, 5 | "description": "", 6 | "engines": { 7 | "node": "0.8.x" 8 | } 9 | } 10 | --------------------------------------------------------------------------------