├── .generators ├── component │ ├── actions.coffee.hbs │ ├── generator.coffee │ ├── index.coffee.hbs │ ├── template.jade.hbs │ └── test.coffee.hbs ├── generator │ ├── default-generator.coffee.hbs │ ├── dummy.json.hbs │ └── generator.coffee └── scene │ ├── aggregator.coffee.hbs │ ├── generator.coffee │ ├── index.coffee.hbs │ ├── subscriber.coffee.hbs │ └── test.coffee.hbs ├── .gitignore ├── README.md ├── deploy ├── bundle.js ├── index.html └── style.css ├── docs ├── dev-guide.md └── intro.md ├── domains ├── battlefield │ ├── .gitignore │ ├── gulpfile.coffee │ ├── package.json │ ├── src │ │ ├── behaviours │ │ │ └── constant-friction.coffee │ │ ├── core.ts │ │ ├── entities │ │ │ ├── battlers │ │ │ │ ├── battler.ts │ │ │ │ ├── enemies │ │ │ │ │ └── enemy.ts │ │ │ │ └── player.ts │ │ │ ├── entity.ts │ │ │ └── objects │ │ │ │ ├── block.ts │ │ │ │ ├── bullets │ │ │ │ └── bullet.ts │ │ │ │ ├── polygon.ts │ │ │ │ └── traps │ │ │ │ └── bullet-trap.ts │ │ ├── entry.ts │ │ ├── stages │ │ │ ├── battle-stage.ts │ │ │ ├── stage.ts │ │ │ └── task-runner.ts │ │ ├── tasks │ │ │ ├── create-bullet-trap.ts │ │ │ ├── create-bullet.ts │ │ │ ├── death-checker.ts │ │ │ ├── remove-entity.ts │ │ │ ├── simple-spawner.ts │ │ │ └── task.ts │ │ ├── types.d.ts │ │ ├── utils │ │ │ └── serialize.ts │ │ └── values │ │ │ ├── group-id.ts │ │ │ └── priority.ts │ └── test │ │ └── stages │ │ └── stage-test.coffee └── map-editor │ ├── debug.html │ ├── index.js │ └── src │ ├── component │ ├── actions.coffee │ ├── index.coffee │ └── template.jade │ ├── index.js │ └── scene │ ├── aggregator.coffee │ ├── index.coffee │ └── subscriber.coffee ├── dtsm.json ├── gulpfile.coffee ├── package.json ├── public └── index.html ├── scripts ├── build └── watch ├── src ├── components │ ├── field │ │ ├── actions.coffee │ │ ├── index.coffee │ │ └── template.jade │ ├── menu │ │ ├── actions.coffee │ │ ├── index.coffee │ │ └── template.jade │ └── opening │ │ ├── actions.coffee │ │ ├── index.coffee │ │ └── template.jade ├── index.coffee ├── router.coffee ├── scenes │ ├── field │ │ ├── aggregator.coffee │ │ ├── index.coffee │ │ └── subscriber.coffee │ ├── menu │ │ ├── aggregator.coffee │ │ ├── index.coffee │ │ └── subscriber.coffee │ └── opening │ │ ├── aggregator.coffee │ │ ├── index.coffee │ │ └── subscriber.coffee └── setup.coffee ├── styles ├── mixins.scss ├── style.scss └── variables.scss ├── test ├── components │ ├── menu-test.coffee │ └── opening-test.coffee ├── scenes │ ├── menu-test.coffee │ └── opening-test.coffee └── test-helper.coffee └── webpack.config.js /.generators/component/actions.coffee.hbs: -------------------------------------------------------------------------------- 1 | module.exports = 2 | onClick: -> 3 | @emit '{{$1}}:event', {} 4 | -------------------------------------------------------------------------------- /.generators/component/generator.coffee: -------------------------------------------------------------------------------- 1 | changeCase = require 'change-case' 2 | module.exports = (g, {$1}) -> 3 | g.gen "index.coffee.hbs", "src/components/#{$1}/index.coffee" 4 | g.gen "template.jade.hbs", "src/components/#{$1}/template.jade" 5 | g.gen "actions.coffee.hbs", "src/components/#{$1}/actions.coffee" 6 | g.gen "test.coffee.hbs", "test/components/#{$1}-test.coffee" 7 | -------------------------------------------------------------------------------- /.generators/component/index.coffee.hbs: -------------------------------------------------------------------------------- 1 | template = require './template.jade' 2 | actions = require './actions' 3 | extend = require 'extend' 4 | 5 | module.exports = React.createClass 6 | mixins: [Overworld.mixinFor(-> app), actions] 7 | render: -> 8 | template extend {}, @, @props, @state 9 | -------------------------------------------------------------------------------- /.generators/component/template.jade.hbs: -------------------------------------------------------------------------------- 1 | .{{$1}} 2 | span {{$1}} 3 | -------------------------------------------------------------------------------- /.generators/component/test.coffee.hbs: -------------------------------------------------------------------------------- 1 | require '../test-helper' 2 | component = require '../../src/components/{{$1}}' 3 | element = React.createFactory component 4 | 5 | describe 'components/{{$1}}', -> 6 | describe '#render', -> 7 | it 'should validate output', -> 8 | rendered = React.renderToString(element {}) 9 | -------------------------------------------------------------------------------- /.generators/generator/default-generator.coffee.hbs: -------------------------------------------------------------------------------- 1 | module.exports = (g, {$1}) -> 2 | g.gen "{{$1}}.json.hbs", "app/{{$1}}.json" -------------------------------------------------------------------------------- /.generators/generator/dummy.json.hbs: -------------------------------------------------------------------------------- 1 | {"name": "{{$1}}"} -------------------------------------------------------------------------------- /.generators/generator/generator.coffee: -------------------------------------------------------------------------------- 1 | module.exports = (g, {$1}) -> 2 | g.gen 'default-generator.coffee.hbs', ".generators/#{$1}/generator.coffee" 3 | g.gen 'dummy.json.hbs', ".generators/#{$1}/#{$1}.json.hbs" -------------------------------------------------------------------------------- /.generators/scene/aggregator.coffee.hbs: -------------------------------------------------------------------------------- 1 | module.exports = 2 | initState: (props) -> {} 3 | aggregate: (props, state) -> {} 4 | -------------------------------------------------------------------------------- /.generators/scene/generator.coffee: -------------------------------------------------------------------------------- 1 | changeCase = require 'change-case' 2 | module.exports = (g, {$1}) -> 3 | g.gen "index.coffee.hbs", "src/scenes/#{$1}/index.coffee" 4 | g.gen "aggregator.coffee.hbs", "src/scenes/#{$1}/aggregator.coffee" 5 | g.gen "subscriber.coffee.hbs", "src/scenes/#{$1}/subscriber.coffee" 6 | g.gen "test.coffee.hbs", "test/scenes/#{$1}-test.coffee" 7 | -------------------------------------------------------------------------------- /.generators/scene/index.coffee.hbs: -------------------------------------------------------------------------------- 1 | module.exports = 2 | class extends Overworld.World 3 | @component : require '../../components/{{$1}}' 4 | @aggregator: require './aggregator' 5 | @subscriber: require './subscriber' 6 | -------------------------------------------------------------------------------- /.generators/scene/subscriber.coffee.hbs: -------------------------------------------------------------------------------- 1 | module.exports = (subscribe) -> 2 | subscribe '{{$1}}:update', (context) -> (args...) -> 3 | context.update {} 4 | -------------------------------------------------------------------------------- /.generators/scene/test.coffee.hbs: -------------------------------------------------------------------------------- 1 | require '../test-helper' 2 | Scene = require '../../src/scenes/{{$1}}' 3 | 4 | describe 'scenes/{{$1}}', -> 5 | it 'should be written', -> 6 | new Scene 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### https://raw.github.com/github/gitignore/d866fb556184cc1edffd9d0f1ca205fe1916a7f6/Node.gitignore 2 | 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # node-waf configuration 22 | .lock-wscript 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directory 28 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 29 | node_modules 30 | 31 | 32 | ### https://raw.github.com/github/gitignore/d866fb556184cc1edffd9d0f1ca205fe1916a7f6/Global/OSX.gitignore 33 | 34 | .DS_Store 35 | .AppleDouble 36 | .LSOverride 37 | 38 | # Icon must end with two \r 39 | Icon 40 | 41 | # Thumbnails 42 | ._* 43 | 44 | # Files that might appear on external disk 45 | .Spotlight-V100 46 | .Trashes 47 | 48 | # Directories potentially created on remote AFP share 49 | .AppleDB 50 | .AppleDesktop 51 | Network Trash Folder 52 | Temporary Items 53 | .apdisk 54 | .tmp 55 | public/bundle.js 56 | public/style.css 57 | lib 58 | typings 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Actrogue2 2 | 3 | Hack and Slash written by javascript 4 | 5 | See docs/intro.md 6 | 7 | ## Development 8 | 9 | Build 10 | 11 | ```shell 12 | npm install -g Microsoft/typescript # Install typescript 1.4 13 | npm install 14 | ./scripts/build 15 | open public/index.html 16 | ``` 17 | 18 | Watch 19 | 20 | ``` 21 | ./scripts/build 22 | ``` 23 | -------------------------------------------------------------------------------- /deploy/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AR 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /deploy/style.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.2.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | /* FONT PATH 6 | * -------------------------- */ 7 | @font-face { 8 | font-family: 'FontAwesome'; 9 | src: url('../node_modules/font-awesome/fonts/fontawesome-webfont.eot?v=4.2.0'); 10 | src: url('../node_modules/font-awesome/fonts/fontawesome-webfont.eot?#iefix&v=4.2.0') format('embedded-opentype'), url('../node_modules/font-awesome/fonts/fontawesome-webfont.woff?v=4.2.0') format('woff'), url('../node_modules/font-awesome/fonts/fontawesome-webfont.ttf?v=4.2.0') format('truetype'), url('../node_modules/font-awesome/fonts/fontawesome-webfont.svg?v=4.2.0#fontawesomeregular') format('svg'); 11 | font-weight: normal; 12 | font-style: normal; } 13 | 14 | .fa { 15 | display: inline-block; 16 | font: normal normal normal 14px/1 FontAwesome; 17 | font-size: inherit; 18 | text-rendering: auto; 19 | -webkit-font-smoothing: antialiased; 20 | -moz-osx-font-smoothing: grayscale; } 21 | 22 | /* makes the font 33% larger relative to the icon container */ 23 | .fa-lg { 24 | font-size: 1.33333em; 25 | line-height: 0.75em; 26 | vertical-align: -15%; } 27 | 28 | .fa-2x { 29 | font-size: 2em; } 30 | 31 | .fa-3x { 32 | font-size: 3em; } 33 | 34 | .fa-4x { 35 | font-size: 4em; } 36 | 37 | .fa-5x { 38 | font-size: 5em; } 39 | 40 | .fa-fw { 41 | width: 1.28571em; 42 | text-align: center; } 43 | 44 | .fa-ul { 45 | padding-left: 0; 46 | margin-left: 2.14286em; 47 | list-style-type: none; } 48 | .fa-ul > li { 49 | position: relative; } 50 | 51 | .fa-li { 52 | position: absolute; 53 | left: -2.14286em; 54 | width: 2.14286em; 55 | top: 0.14286em; 56 | text-align: center; } 57 | .fa-li.fa-lg { 58 | left: -1.85714em; } 59 | 60 | .fa-border { 61 | padding: 0.2em 0.25em 0.15em; 62 | border: solid 0.08em #eee; 63 | border-radius: 0.1em; } 64 | 65 | .pull-right { 66 | float: right; } 67 | 68 | .pull-left { 69 | float: left; } 70 | 71 | .fa.pull-left { 72 | margin-right: 0.3em; } 73 | .fa.pull-right { 74 | margin-left: 0.3em; } 75 | 76 | .fa-spin { 77 | -webkit-animation: fa-spin 2s infinite linear; 78 | animation: fa-spin 2s infinite linear; } 79 | 80 | @-webkit-keyframes fa-spin { 81 | 0% { 82 | -webkit-transform: rotate(0deg); 83 | transform: rotate(0deg); } 84 | 85 | 100% { 86 | -webkit-transform: rotate(359deg); 87 | transform: rotate(359deg); } } 88 | 89 | @keyframes fa-spin { 90 | 0% { 91 | -webkit-transform: rotate(0deg); 92 | transform: rotate(0deg); } 93 | 94 | 100% { 95 | -webkit-transform: rotate(359deg); 96 | transform: rotate(359deg); } } 97 | 98 | .fa-rotate-90 { 99 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); 100 | -webkit-transform: rotate(90deg); 101 | -ms-transform: rotate(90deg); 102 | transform: rotate(90deg); } 103 | 104 | .fa-rotate-180 { 105 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); 106 | -webkit-transform: rotate(180deg); 107 | -ms-transform: rotate(180deg); 108 | transform: rotate(180deg); } 109 | 110 | .fa-rotate-270 { 111 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); 112 | -webkit-transform: rotate(270deg); 113 | -ms-transform: rotate(270deg); 114 | transform: rotate(270deg); } 115 | 116 | .fa-flip-horizontal { 117 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0); 118 | -webkit-transform: scale(-1, 1); 119 | -ms-transform: scale(-1, 1); 120 | transform: scale(-1, 1); } 121 | 122 | .fa-flip-vertical { 123 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); 124 | -webkit-transform: scale(1, -1); 125 | -ms-transform: scale(1, -1); 126 | transform: scale(1, -1); } 127 | 128 | :root .fa-rotate-90, :root .fa-rotate-180, :root .fa-rotate-270, :root .fa-flip-horizontal, :root .fa-flip-vertical { 129 | filter: none; } 130 | 131 | .fa-stack { 132 | position: relative; 133 | display: inline-block; 134 | width: 2em; 135 | height: 2em; 136 | line-height: 2em; 137 | vertical-align: middle; } 138 | 139 | .fa-stack-1x, .fa-stack-2x { 140 | position: absolute; 141 | left: 0; 142 | width: 100%; 143 | text-align: center; } 144 | 145 | .fa-stack-1x { 146 | line-height: inherit; } 147 | 148 | .fa-stack-2x { 149 | font-size: 2em; } 150 | 151 | .fa-inverse { 152 | color: #fff; } 153 | 154 | /* Font Awesome uses the Unicode Private Use Area (PUA) to ensure screen 155 | readers do not read off random characters that represent icons */ 156 | .fa-glass:before { 157 | content: "\f000"; } 158 | 159 | .fa-music:before { 160 | content: "\f001"; } 161 | 162 | .fa-search:before { 163 | content: "\f002"; } 164 | 165 | .fa-envelope-o:before { 166 | content: "\f003"; } 167 | 168 | .fa-heart:before { 169 | content: "\f004"; } 170 | 171 | .fa-star:before { 172 | content: "\f005"; } 173 | 174 | .fa-star-o:before { 175 | content: "\f006"; } 176 | 177 | .fa-user:before { 178 | content: "\f007"; } 179 | 180 | .fa-film:before { 181 | content: "\f008"; } 182 | 183 | .fa-th-large:before { 184 | content: "\f009"; } 185 | 186 | .fa-th:before { 187 | content: "\f00a"; } 188 | 189 | .fa-th-list:before { 190 | content: "\f00b"; } 191 | 192 | .fa-check:before { 193 | content: "\f00c"; } 194 | 195 | .fa-remove:before, .fa-close:before, .fa-times:before { 196 | content: "\f00d"; } 197 | 198 | .fa-search-plus:before { 199 | content: "\f00e"; } 200 | 201 | .fa-search-minus:before { 202 | content: "\f010"; } 203 | 204 | .fa-power-off:before { 205 | content: "\f011"; } 206 | 207 | .fa-signal:before { 208 | content: "\f012"; } 209 | 210 | .fa-gear:before, .fa-cog:before { 211 | content: "\f013"; } 212 | 213 | .fa-trash-o:before { 214 | content: "\f014"; } 215 | 216 | .fa-home:before { 217 | content: "\f015"; } 218 | 219 | .fa-file-o:before { 220 | content: "\f016"; } 221 | 222 | .fa-clock-o:before { 223 | content: "\f017"; } 224 | 225 | .fa-road:before { 226 | content: "\f018"; } 227 | 228 | .fa-download:before { 229 | content: "\f019"; } 230 | 231 | .fa-arrow-circle-o-down:before { 232 | content: "\f01a"; } 233 | 234 | .fa-arrow-circle-o-up:before { 235 | content: "\f01b"; } 236 | 237 | .fa-inbox:before { 238 | content: "\f01c"; } 239 | 240 | .fa-play-circle-o:before { 241 | content: "\f01d"; } 242 | 243 | .fa-rotate-right:before, .fa-repeat:before { 244 | content: "\f01e"; } 245 | 246 | .fa-refresh:before { 247 | content: "\f021"; } 248 | 249 | .fa-list-alt:before { 250 | content: "\f022"; } 251 | 252 | .fa-lock:before { 253 | content: "\f023"; } 254 | 255 | .fa-flag:before { 256 | content: "\f024"; } 257 | 258 | .fa-headphones:before { 259 | content: "\f025"; } 260 | 261 | .fa-volume-off:before { 262 | content: "\f026"; } 263 | 264 | .fa-volume-down:before { 265 | content: "\f027"; } 266 | 267 | .fa-volume-up:before { 268 | content: "\f028"; } 269 | 270 | .fa-qrcode:before { 271 | content: "\f029"; } 272 | 273 | .fa-barcode:before { 274 | content: "\f02a"; } 275 | 276 | .fa-tag:before { 277 | content: "\f02b"; } 278 | 279 | .fa-tags:before { 280 | content: "\f02c"; } 281 | 282 | .fa-book:before { 283 | content: "\f02d"; } 284 | 285 | .fa-bookmark:before { 286 | content: "\f02e"; } 287 | 288 | .fa-print:before { 289 | content: "\f02f"; } 290 | 291 | .fa-camera:before { 292 | content: "\f030"; } 293 | 294 | .fa-font:before { 295 | content: "\f031"; } 296 | 297 | .fa-bold:before { 298 | content: "\f032"; } 299 | 300 | .fa-italic:before { 301 | content: "\f033"; } 302 | 303 | .fa-text-height:before { 304 | content: "\f034"; } 305 | 306 | .fa-text-width:before { 307 | content: "\f035"; } 308 | 309 | .fa-align-left:before { 310 | content: "\f036"; } 311 | 312 | .fa-align-center:before { 313 | content: "\f037"; } 314 | 315 | .fa-align-right:before { 316 | content: "\f038"; } 317 | 318 | .fa-align-justify:before { 319 | content: "\f039"; } 320 | 321 | .fa-list:before { 322 | content: "\f03a"; } 323 | 324 | .fa-dedent:before, .fa-outdent:before { 325 | content: "\f03b"; } 326 | 327 | .fa-indent:before { 328 | content: "\f03c"; } 329 | 330 | .fa-video-camera:before { 331 | content: "\f03d"; } 332 | 333 | .fa-photo:before, .fa-image:before, .fa-picture-o:before { 334 | content: "\f03e"; } 335 | 336 | .fa-pencil:before { 337 | content: "\f040"; } 338 | 339 | .fa-map-marker:before { 340 | content: "\f041"; } 341 | 342 | .fa-adjust:before { 343 | content: "\f042"; } 344 | 345 | .fa-tint:before { 346 | content: "\f043"; } 347 | 348 | .fa-edit:before, .fa-pencil-square-o:before { 349 | content: "\f044"; } 350 | 351 | .fa-share-square-o:before { 352 | content: "\f045"; } 353 | 354 | .fa-check-square-o:before { 355 | content: "\f046"; } 356 | 357 | .fa-arrows:before { 358 | content: "\f047"; } 359 | 360 | .fa-step-backward:before { 361 | content: "\f048"; } 362 | 363 | .fa-fast-backward:before { 364 | content: "\f049"; } 365 | 366 | .fa-backward:before { 367 | content: "\f04a"; } 368 | 369 | .fa-play:before { 370 | content: "\f04b"; } 371 | 372 | .fa-pause:before { 373 | content: "\f04c"; } 374 | 375 | .fa-stop:before { 376 | content: "\f04d"; } 377 | 378 | .fa-forward:before { 379 | content: "\f04e"; } 380 | 381 | .fa-fast-forward:before { 382 | content: "\f050"; } 383 | 384 | .fa-step-forward:before { 385 | content: "\f051"; } 386 | 387 | .fa-eject:before { 388 | content: "\f052"; } 389 | 390 | .fa-chevron-left:before { 391 | content: "\f053"; } 392 | 393 | .fa-chevron-right:before { 394 | content: "\f054"; } 395 | 396 | .fa-plus-circle:before { 397 | content: "\f055"; } 398 | 399 | .fa-minus-circle:before { 400 | content: "\f056"; } 401 | 402 | .fa-times-circle:before { 403 | content: "\f057"; } 404 | 405 | .fa-check-circle:before { 406 | content: "\f058"; } 407 | 408 | .fa-question-circle:before { 409 | content: "\f059"; } 410 | 411 | .fa-info-circle:before { 412 | content: "\f05a"; } 413 | 414 | .fa-crosshairs:before { 415 | content: "\f05b"; } 416 | 417 | .fa-times-circle-o:before { 418 | content: "\f05c"; } 419 | 420 | .fa-check-circle-o:before { 421 | content: "\f05d"; } 422 | 423 | .fa-ban:before { 424 | content: "\f05e"; } 425 | 426 | .fa-arrow-left:before { 427 | content: "\f060"; } 428 | 429 | .fa-arrow-right:before { 430 | content: "\f061"; } 431 | 432 | .fa-arrow-up:before { 433 | content: "\f062"; } 434 | 435 | .fa-arrow-down:before { 436 | content: "\f063"; } 437 | 438 | .fa-mail-forward:before, .fa-share:before { 439 | content: "\f064"; } 440 | 441 | .fa-expand:before { 442 | content: "\f065"; } 443 | 444 | .fa-compress:before { 445 | content: "\f066"; } 446 | 447 | .fa-plus:before { 448 | content: "\f067"; } 449 | 450 | .fa-minus:before { 451 | content: "\f068"; } 452 | 453 | .fa-asterisk:before { 454 | content: "\f069"; } 455 | 456 | .fa-exclamation-circle:before { 457 | content: "\f06a"; } 458 | 459 | .fa-gift:before { 460 | content: "\f06b"; } 461 | 462 | .fa-leaf:before { 463 | content: "\f06c"; } 464 | 465 | .fa-fire:before { 466 | content: "\f06d"; } 467 | 468 | .fa-eye:before { 469 | content: "\f06e"; } 470 | 471 | .fa-eye-slash:before { 472 | content: "\f070"; } 473 | 474 | .fa-warning:before, .fa-exclamation-triangle:before { 475 | content: "\f071"; } 476 | 477 | .fa-plane:before { 478 | content: "\f072"; } 479 | 480 | .fa-calendar:before { 481 | content: "\f073"; } 482 | 483 | .fa-random:before { 484 | content: "\f074"; } 485 | 486 | .fa-comment:before { 487 | content: "\f075"; } 488 | 489 | .fa-magnet:before { 490 | content: "\f076"; } 491 | 492 | .fa-chevron-up:before { 493 | content: "\f077"; } 494 | 495 | .fa-chevron-down:before { 496 | content: "\f078"; } 497 | 498 | .fa-retweet:before { 499 | content: "\f079"; } 500 | 501 | .fa-shopping-cart:before { 502 | content: "\f07a"; } 503 | 504 | .fa-folder:before { 505 | content: "\f07b"; } 506 | 507 | .fa-folder-open:before { 508 | content: "\f07c"; } 509 | 510 | .fa-arrows-v:before { 511 | content: "\f07d"; } 512 | 513 | .fa-arrows-h:before { 514 | content: "\f07e"; } 515 | 516 | .fa-bar-chart-o:before, .fa-bar-chart:before { 517 | content: "\f080"; } 518 | 519 | .fa-twitter-square:before { 520 | content: "\f081"; } 521 | 522 | .fa-facebook-square:before { 523 | content: "\f082"; } 524 | 525 | .fa-camera-retro:before { 526 | content: "\f083"; } 527 | 528 | .fa-key:before { 529 | content: "\f084"; } 530 | 531 | .fa-gears:before, .fa-cogs:before { 532 | content: "\f085"; } 533 | 534 | .fa-comments:before { 535 | content: "\f086"; } 536 | 537 | .fa-thumbs-o-up:before { 538 | content: "\f087"; } 539 | 540 | .fa-thumbs-o-down:before { 541 | content: "\f088"; } 542 | 543 | .fa-star-half:before { 544 | content: "\f089"; } 545 | 546 | .fa-heart-o:before { 547 | content: "\f08a"; } 548 | 549 | .fa-sign-out:before { 550 | content: "\f08b"; } 551 | 552 | .fa-linkedin-square:before { 553 | content: "\f08c"; } 554 | 555 | .fa-thumb-tack:before { 556 | content: "\f08d"; } 557 | 558 | .fa-external-link:before { 559 | content: "\f08e"; } 560 | 561 | .fa-sign-in:before { 562 | content: "\f090"; } 563 | 564 | .fa-trophy:before { 565 | content: "\f091"; } 566 | 567 | .fa-github-square:before { 568 | content: "\f092"; } 569 | 570 | .fa-upload:before { 571 | content: "\f093"; } 572 | 573 | .fa-lemon-o:before { 574 | content: "\f094"; } 575 | 576 | .fa-phone:before { 577 | content: "\f095"; } 578 | 579 | .fa-square-o:before { 580 | content: "\f096"; } 581 | 582 | .fa-bookmark-o:before { 583 | content: "\f097"; } 584 | 585 | .fa-phone-square:before { 586 | content: "\f098"; } 587 | 588 | .fa-twitter:before { 589 | content: "\f099"; } 590 | 591 | .fa-facebook:before { 592 | content: "\f09a"; } 593 | 594 | .fa-github:before { 595 | content: "\f09b"; } 596 | 597 | .fa-unlock:before { 598 | content: "\f09c"; } 599 | 600 | .fa-credit-card:before { 601 | content: "\f09d"; } 602 | 603 | .fa-rss:before { 604 | content: "\f09e"; } 605 | 606 | .fa-hdd-o:before { 607 | content: "\f0a0"; } 608 | 609 | .fa-bullhorn:before { 610 | content: "\f0a1"; } 611 | 612 | .fa-bell:before { 613 | content: "\f0f3"; } 614 | 615 | .fa-certificate:before { 616 | content: "\f0a3"; } 617 | 618 | .fa-hand-o-right:before { 619 | content: "\f0a4"; } 620 | 621 | .fa-hand-o-left:before { 622 | content: "\f0a5"; } 623 | 624 | .fa-hand-o-up:before { 625 | content: "\f0a6"; } 626 | 627 | .fa-hand-o-down:before { 628 | content: "\f0a7"; } 629 | 630 | .fa-arrow-circle-left:before { 631 | content: "\f0a8"; } 632 | 633 | .fa-arrow-circle-right:before { 634 | content: "\f0a9"; } 635 | 636 | .fa-arrow-circle-up:before { 637 | content: "\f0aa"; } 638 | 639 | .fa-arrow-circle-down:before { 640 | content: "\f0ab"; } 641 | 642 | .fa-globe:before { 643 | content: "\f0ac"; } 644 | 645 | .fa-wrench:before { 646 | content: "\f0ad"; } 647 | 648 | .fa-tasks:before { 649 | content: "\f0ae"; } 650 | 651 | .fa-filter:before { 652 | content: "\f0b0"; } 653 | 654 | .fa-briefcase:before { 655 | content: "\f0b1"; } 656 | 657 | .fa-arrows-alt:before { 658 | content: "\f0b2"; } 659 | 660 | .fa-group:before, .fa-users:before { 661 | content: "\f0c0"; } 662 | 663 | .fa-chain:before, .fa-link:before { 664 | content: "\f0c1"; } 665 | 666 | .fa-cloud:before { 667 | content: "\f0c2"; } 668 | 669 | .fa-flask:before { 670 | content: "\f0c3"; } 671 | 672 | .fa-cut:before, .fa-scissors:before { 673 | content: "\f0c4"; } 674 | 675 | .fa-copy:before, .fa-files-o:before { 676 | content: "\f0c5"; } 677 | 678 | .fa-paperclip:before { 679 | content: "\f0c6"; } 680 | 681 | .fa-save:before, .fa-floppy-o:before { 682 | content: "\f0c7"; } 683 | 684 | .fa-square:before { 685 | content: "\f0c8"; } 686 | 687 | .fa-navicon:before, .fa-reorder:before, .fa-bars:before { 688 | content: "\f0c9"; } 689 | 690 | .fa-list-ul:before { 691 | content: "\f0ca"; } 692 | 693 | .fa-list-ol:before { 694 | content: "\f0cb"; } 695 | 696 | .fa-strikethrough:before { 697 | content: "\f0cc"; } 698 | 699 | .fa-underline:before { 700 | content: "\f0cd"; } 701 | 702 | .fa-table:before { 703 | content: "\f0ce"; } 704 | 705 | .fa-magic:before { 706 | content: "\f0d0"; } 707 | 708 | .fa-truck:before { 709 | content: "\f0d1"; } 710 | 711 | .fa-pinterest:before { 712 | content: "\f0d2"; } 713 | 714 | .fa-pinterest-square:before { 715 | content: "\f0d3"; } 716 | 717 | .fa-google-plus-square:before { 718 | content: "\f0d4"; } 719 | 720 | .fa-google-plus:before { 721 | content: "\f0d5"; } 722 | 723 | .fa-money:before { 724 | content: "\f0d6"; } 725 | 726 | .fa-caret-down:before { 727 | content: "\f0d7"; } 728 | 729 | .fa-caret-up:before { 730 | content: "\f0d8"; } 731 | 732 | .fa-caret-left:before { 733 | content: "\f0d9"; } 734 | 735 | .fa-caret-right:before { 736 | content: "\f0da"; } 737 | 738 | .fa-columns:before { 739 | content: "\f0db"; } 740 | 741 | .fa-unsorted:before, .fa-sort:before { 742 | content: "\f0dc"; } 743 | 744 | .fa-sort-down:before, .fa-sort-desc:before { 745 | content: "\f0dd"; } 746 | 747 | .fa-sort-up:before, .fa-sort-asc:before { 748 | content: "\f0de"; } 749 | 750 | .fa-envelope:before { 751 | content: "\f0e0"; } 752 | 753 | .fa-linkedin:before { 754 | content: "\f0e1"; } 755 | 756 | .fa-rotate-left:before, .fa-undo:before { 757 | content: "\f0e2"; } 758 | 759 | .fa-legal:before, .fa-gavel:before { 760 | content: "\f0e3"; } 761 | 762 | .fa-dashboard:before, .fa-tachometer:before { 763 | content: "\f0e4"; } 764 | 765 | .fa-comment-o:before { 766 | content: "\f0e5"; } 767 | 768 | .fa-comments-o:before { 769 | content: "\f0e6"; } 770 | 771 | .fa-flash:before, .fa-bolt:before { 772 | content: "\f0e7"; } 773 | 774 | .fa-sitemap:before { 775 | content: "\f0e8"; } 776 | 777 | .fa-umbrella:before { 778 | content: "\f0e9"; } 779 | 780 | .fa-paste:before, .fa-clipboard:before { 781 | content: "\f0ea"; } 782 | 783 | .fa-lightbulb-o:before { 784 | content: "\f0eb"; } 785 | 786 | .fa-exchange:before { 787 | content: "\f0ec"; } 788 | 789 | .fa-cloud-download:before { 790 | content: "\f0ed"; } 791 | 792 | .fa-cloud-upload:before { 793 | content: "\f0ee"; } 794 | 795 | .fa-user-md:before { 796 | content: "\f0f0"; } 797 | 798 | .fa-stethoscope:before { 799 | content: "\f0f1"; } 800 | 801 | .fa-suitcase:before { 802 | content: "\f0f2"; } 803 | 804 | .fa-bell-o:before { 805 | content: "\f0a2"; } 806 | 807 | .fa-coffee:before { 808 | content: "\f0f4"; } 809 | 810 | .fa-cutlery:before { 811 | content: "\f0f5"; } 812 | 813 | .fa-file-text-o:before { 814 | content: "\f0f6"; } 815 | 816 | .fa-building-o:before { 817 | content: "\f0f7"; } 818 | 819 | .fa-hospital-o:before { 820 | content: "\f0f8"; } 821 | 822 | .fa-ambulance:before { 823 | content: "\f0f9"; } 824 | 825 | .fa-medkit:before { 826 | content: "\f0fa"; } 827 | 828 | .fa-fighter-jet:before { 829 | content: "\f0fb"; } 830 | 831 | .fa-beer:before { 832 | content: "\f0fc"; } 833 | 834 | .fa-h-square:before { 835 | content: "\f0fd"; } 836 | 837 | .fa-plus-square:before { 838 | content: "\f0fe"; } 839 | 840 | .fa-angle-double-left:before { 841 | content: "\f100"; } 842 | 843 | .fa-angle-double-right:before { 844 | content: "\f101"; } 845 | 846 | .fa-angle-double-up:before { 847 | content: "\f102"; } 848 | 849 | .fa-angle-double-down:before { 850 | content: "\f103"; } 851 | 852 | .fa-angle-left:before { 853 | content: "\f104"; } 854 | 855 | .fa-angle-right:before { 856 | content: "\f105"; } 857 | 858 | .fa-angle-up:before { 859 | content: "\f106"; } 860 | 861 | .fa-angle-down:before { 862 | content: "\f107"; } 863 | 864 | .fa-desktop:before { 865 | content: "\f108"; } 866 | 867 | .fa-laptop:before { 868 | content: "\f109"; } 869 | 870 | .fa-tablet:before { 871 | content: "\f10a"; } 872 | 873 | .fa-mobile-phone:before, .fa-mobile:before { 874 | content: "\f10b"; } 875 | 876 | .fa-circle-o:before { 877 | content: "\f10c"; } 878 | 879 | .fa-quote-left:before { 880 | content: "\f10d"; } 881 | 882 | .fa-quote-right:before { 883 | content: "\f10e"; } 884 | 885 | .fa-spinner:before { 886 | content: "\f110"; } 887 | 888 | .fa-circle:before { 889 | content: "\f111"; } 890 | 891 | .fa-mail-reply:before, .fa-reply:before { 892 | content: "\f112"; } 893 | 894 | .fa-github-alt:before { 895 | content: "\f113"; } 896 | 897 | .fa-folder-o:before { 898 | content: "\f114"; } 899 | 900 | .fa-folder-open-o:before { 901 | content: "\f115"; } 902 | 903 | .fa-smile-o:before { 904 | content: "\f118"; } 905 | 906 | .fa-frown-o:before { 907 | content: "\f119"; } 908 | 909 | .fa-meh-o:before { 910 | content: "\f11a"; } 911 | 912 | .fa-gamepad:before { 913 | content: "\f11b"; } 914 | 915 | .fa-keyboard-o:before { 916 | content: "\f11c"; } 917 | 918 | .fa-flag-o:before { 919 | content: "\f11d"; } 920 | 921 | .fa-flag-checkered:before { 922 | content: "\f11e"; } 923 | 924 | .fa-terminal:before { 925 | content: "\f120"; } 926 | 927 | .fa-code:before { 928 | content: "\f121"; } 929 | 930 | .fa-mail-reply-all:before, .fa-reply-all:before { 931 | content: "\f122"; } 932 | 933 | .fa-star-half-empty:before, .fa-star-half-full:before, .fa-star-half-o:before { 934 | content: "\f123"; } 935 | 936 | .fa-location-arrow:before { 937 | content: "\f124"; } 938 | 939 | .fa-crop:before { 940 | content: "\f125"; } 941 | 942 | .fa-code-fork:before { 943 | content: "\f126"; } 944 | 945 | .fa-unlink:before, .fa-chain-broken:before { 946 | content: "\f127"; } 947 | 948 | .fa-question:before { 949 | content: "\f128"; } 950 | 951 | .fa-info:before { 952 | content: "\f129"; } 953 | 954 | .fa-exclamation:before { 955 | content: "\f12a"; } 956 | 957 | .fa-superscript:before { 958 | content: "\f12b"; } 959 | 960 | .fa-subscript:before { 961 | content: "\f12c"; } 962 | 963 | .fa-eraser:before { 964 | content: "\f12d"; } 965 | 966 | .fa-puzzle-piece:before { 967 | content: "\f12e"; } 968 | 969 | .fa-microphone:before { 970 | content: "\f130"; } 971 | 972 | .fa-microphone-slash:before { 973 | content: "\f131"; } 974 | 975 | .fa-shield:before { 976 | content: "\f132"; } 977 | 978 | .fa-calendar-o:before { 979 | content: "\f133"; } 980 | 981 | .fa-fire-extinguisher:before { 982 | content: "\f134"; } 983 | 984 | .fa-rocket:before { 985 | content: "\f135"; } 986 | 987 | .fa-maxcdn:before { 988 | content: "\f136"; } 989 | 990 | .fa-chevron-circle-left:before { 991 | content: "\f137"; } 992 | 993 | .fa-chevron-circle-right:before { 994 | content: "\f138"; } 995 | 996 | .fa-chevron-circle-up:before { 997 | content: "\f139"; } 998 | 999 | .fa-chevron-circle-down:before { 1000 | content: "\f13a"; } 1001 | 1002 | .fa-html5:before { 1003 | content: "\f13b"; } 1004 | 1005 | .fa-css3:before { 1006 | content: "\f13c"; } 1007 | 1008 | .fa-anchor:before { 1009 | content: "\f13d"; } 1010 | 1011 | .fa-unlock-alt:before { 1012 | content: "\f13e"; } 1013 | 1014 | .fa-bullseye:before { 1015 | content: "\f140"; } 1016 | 1017 | .fa-ellipsis-h:before { 1018 | content: "\f141"; } 1019 | 1020 | .fa-ellipsis-v:before { 1021 | content: "\f142"; } 1022 | 1023 | .fa-rss-square:before { 1024 | content: "\f143"; } 1025 | 1026 | .fa-play-circle:before { 1027 | content: "\f144"; } 1028 | 1029 | .fa-ticket:before { 1030 | content: "\f145"; } 1031 | 1032 | .fa-minus-square:before { 1033 | content: "\f146"; } 1034 | 1035 | .fa-minus-square-o:before { 1036 | content: "\f147"; } 1037 | 1038 | .fa-level-up:before { 1039 | content: "\f148"; } 1040 | 1041 | .fa-level-down:before { 1042 | content: "\f149"; } 1043 | 1044 | .fa-check-square:before { 1045 | content: "\f14a"; } 1046 | 1047 | .fa-pencil-square:before { 1048 | content: "\f14b"; } 1049 | 1050 | .fa-external-link-square:before { 1051 | content: "\f14c"; } 1052 | 1053 | .fa-share-square:before { 1054 | content: "\f14d"; } 1055 | 1056 | .fa-compass:before { 1057 | content: "\f14e"; } 1058 | 1059 | .fa-toggle-down:before, .fa-caret-square-o-down:before { 1060 | content: "\f150"; } 1061 | 1062 | .fa-toggle-up:before, .fa-caret-square-o-up:before { 1063 | content: "\f151"; } 1064 | 1065 | .fa-toggle-right:before, .fa-caret-square-o-right:before { 1066 | content: "\f152"; } 1067 | 1068 | .fa-euro:before, .fa-eur:before { 1069 | content: "\f153"; } 1070 | 1071 | .fa-gbp:before { 1072 | content: "\f154"; } 1073 | 1074 | .fa-dollar:before, .fa-usd:before { 1075 | content: "\f155"; } 1076 | 1077 | .fa-rupee:before, .fa-inr:before { 1078 | content: "\f156"; } 1079 | 1080 | .fa-cny:before, .fa-rmb:before, .fa-yen:before, .fa-jpy:before { 1081 | content: "\f157"; } 1082 | 1083 | .fa-ruble:before, .fa-rouble:before, .fa-rub:before { 1084 | content: "\f158"; } 1085 | 1086 | .fa-won:before, .fa-krw:before { 1087 | content: "\f159"; } 1088 | 1089 | .fa-bitcoin:before, .fa-btc:before { 1090 | content: "\f15a"; } 1091 | 1092 | .fa-file:before { 1093 | content: "\f15b"; } 1094 | 1095 | .fa-file-text:before { 1096 | content: "\f15c"; } 1097 | 1098 | .fa-sort-alpha-asc:before { 1099 | content: "\f15d"; } 1100 | 1101 | .fa-sort-alpha-desc:before { 1102 | content: "\f15e"; } 1103 | 1104 | .fa-sort-amount-asc:before { 1105 | content: "\f160"; } 1106 | 1107 | .fa-sort-amount-desc:before { 1108 | content: "\f161"; } 1109 | 1110 | .fa-sort-numeric-asc:before { 1111 | content: "\f162"; } 1112 | 1113 | .fa-sort-numeric-desc:before { 1114 | content: "\f163"; } 1115 | 1116 | .fa-thumbs-up:before { 1117 | content: "\f164"; } 1118 | 1119 | .fa-thumbs-down:before { 1120 | content: "\f165"; } 1121 | 1122 | .fa-youtube-square:before { 1123 | content: "\f166"; } 1124 | 1125 | .fa-youtube:before { 1126 | content: "\f167"; } 1127 | 1128 | .fa-xing:before { 1129 | content: "\f168"; } 1130 | 1131 | .fa-xing-square:before { 1132 | content: "\f169"; } 1133 | 1134 | .fa-youtube-play:before { 1135 | content: "\f16a"; } 1136 | 1137 | .fa-dropbox:before { 1138 | content: "\f16b"; } 1139 | 1140 | .fa-stack-overflow:before { 1141 | content: "\f16c"; } 1142 | 1143 | .fa-instagram:before { 1144 | content: "\f16d"; } 1145 | 1146 | .fa-flickr:before { 1147 | content: "\f16e"; } 1148 | 1149 | .fa-adn:before { 1150 | content: "\f170"; } 1151 | 1152 | .fa-bitbucket:before { 1153 | content: "\f171"; } 1154 | 1155 | .fa-bitbucket-square:before { 1156 | content: "\f172"; } 1157 | 1158 | .fa-tumblr:before { 1159 | content: "\f173"; } 1160 | 1161 | .fa-tumblr-square:before { 1162 | content: "\f174"; } 1163 | 1164 | .fa-long-arrow-down:before { 1165 | content: "\f175"; } 1166 | 1167 | .fa-long-arrow-up:before { 1168 | content: "\f176"; } 1169 | 1170 | .fa-long-arrow-left:before { 1171 | content: "\f177"; } 1172 | 1173 | .fa-long-arrow-right:before { 1174 | content: "\f178"; } 1175 | 1176 | .fa-apple:before { 1177 | content: "\f179"; } 1178 | 1179 | .fa-windows:before { 1180 | content: "\f17a"; } 1181 | 1182 | .fa-android:before { 1183 | content: "\f17b"; } 1184 | 1185 | .fa-linux:before { 1186 | content: "\f17c"; } 1187 | 1188 | .fa-dribbble:before { 1189 | content: "\f17d"; } 1190 | 1191 | .fa-skype:before { 1192 | content: "\f17e"; } 1193 | 1194 | .fa-foursquare:before { 1195 | content: "\f180"; } 1196 | 1197 | .fa-trello:before { 1198 | content: "\f181"; } 1199 | 1200 | .fa-female:before { 1201 | content: "\f182"; } 1202 | 1203 | .fa-male:before { 1204 | content: "\f183"; } 1205 | 1206 | .fa-gittip:before { 1207 | content: "\f184"; } 1208 | 1209 | .fa-sun-o:before { 1210 | content: "\f185"; } 1211 | 1212 | .fa-moon-o:before { 1213 | content: "\f186"; } 1214 | 1215 | .fa-archive:before { 1216 | content: "\f187"; } 1217 | 1218 | .fa-bug:before { 1219 | content: "\f188"; } 1220 | 1221 | .fa-vk:before { 1222 | content: "\f189"; } 1223 | 1224 | .fa-weibo:before { 1225 | content: "\f18a"; } 1226 | 1227 | .fa-renren:before { 1228 | content: "\f18b"; } 1229 | 1230 | .fa-pagelines:before { 1231 | content: "\f18c"; } 1232 | 1233 | .fa-stack-exchange:before { 1234 | content: "\f18d"; } 1235 | 1236 | .fa-arrow-circle-o-right:before { 1237 | content: "\f18e"; } 1238 | 1239 | .fa-arrow-circle-o-left:before { 1240 | content: "\f190"; } 1241 | 1242 | .fa-toggle-left:before, .fa-caret-square-o-left:before { 1243 | content: "\f191"; } 1244 | 1245 | .fa-dot-circle-o:before { 1246 | content: "\f192"; } 1247 | 1248 | .fa-wheelchair:before { 1249 | content: "\f193"; } 1250 | 1251 | .fa-vimeo-square:before { 1252 | content: "\f194"; } 1253 | 1254 | .fa-turkish-lira:before, .fa-try:before { 1255 | content: "\f195"; } 1256 | 1257 | .fa-plus-square-o:before { 1258 | content: "\f196"; } 1259 | 1260 | .fa-space-shuttle:before { 1261 | content: "\f197"; } 1262 | 1263 | .fa-slack:before { 1264 | content: "\f198"; } 1265 | 1266 | .fa-envelope-square:before { 1267 | content: "\f199"; } 1268 | 1269 | .fa-wordpress:before { 1270 | content: "\f19a"; } 1271 | 1272 | .fa-openid:before { 1273 | content: "\f19b"; } 1274 | 1275 | .fa-institution:before, .fa-bank:before, .fa-university:before { 1276 | content: "\f19c"; } 1277 | 1278 | .fa-mortar-board:before, .fa-graduation-cap:before { 1279 | content: "\f19d"; } 1280 | 1281 | .fa-yahoo:before { 1282 | content: "\f19e"; } 1283 | 1284 | .fa-google:before { 1285 | content: "\f1a0"; } 1286 | 1287 | .fa-reddit:before { 1288 | content: "\f1a1"; } 1289 | 1290 | .fa-reddit-square:before { 1291 | content: "\f1a2"; } 1292 | 1293 | .fa-stumbleupon-circle:before { 1294 | content: "\f1a3"; } 1295 | 1296 | .fa-stumbleupon:before { 1297 | content: "\f1a4"; } 1298 | 1299 | .fa-delicious:before { 1300 | content: "\f1a5"; } 1301 | 1302 | .fa-digg:before { 1303 | content: "\f1a6"; } 1304 | 1305 | .fa-pied-piper:before { 1306 | content: "\f1a7"; } 1307 | 1308 | .fa-pied-piper-alt:before { 1309 | content: "\f1a8"; } 1310 | 1311 | .fa-drupal:before { 1312 | content: "\f1a9"; } 1313 | 1314 | .fa-joomla:before { 1315 | content: "\f1aa"; } 1316 | 1317 | .fa-language:before { 1318 | content: "\f1ab"; } 1319 | 1320 | .fa-fax:before { 1321 | content: "\f1ac"; } 1322 | 1323 | .fa-building:before { 1324 | content: "\f1ad"; } 1325 | 1326 | .fa-child:before { 1327 | content: "\f1ae"; } 1328 | 1329 | .fa-paw:before { 1330 | content: "\f1b0"; } 1331 | 1332 | .fa-spoon:before { 1333 | content: "\f1b1"; } 1334 | 1335 | .fa-cube:before { 1336 | content: "\f1b2"; } 1337 | 1338 | .fa-cubes:before { 1339 | content: "\f1b3"; } 1340 | 1341 | .fa-behance:before { 1342 | content: "\f1b4"; } 1343 | 1344 | .fa-behance-square:before { 1345 | content: "\f1b5"; } 1346 | 1347 | .fa-steam:before { 1348 | content: "\f1b6"; } 1349 | 1350 | .fa-steam-square:before { 1351 | content: "\f1b7"; } 1352 | 1353 | .fa-recycle:before { 1354 | content: "\f1b8"; } 1355 | 1356 | .fa-automobile:before, .fa-car:before { 1357 | content: "\f1b9"; } 1358 | 1359 | .fa-cab:before, .fa-taxi:before { 1360 | content: "\f1ba"; } 1361 | 1362 | .fa-tree:before { 1363 | content: "\f1bb"; } 1364 | 1365 | .fa-spotify:before { 1366 | content: "\f1bc"; } 1367 | 1368 | .fa-deviantart:before { 1369 | content: "\f1bd"; } 1370 | 1371 | .fa-soundcloud:before { 1372 | content: "\f1be"; } 1373 | 1374 | .fa-database:before { 1375 | content: "\f1c0"; } 1376 | 1377 | .fa-file-pdf-o:before { 1378 | content: "\f1c1"; } 1379 | 1380 | .fa-file-word-o:before { 1381 | content: "\f1c2"; } 1382 | 1383 | .fa-file-excel-o:before { 1384 | content: "\f1c3"; } 1385 | 1386 | .fa-file-powerpoint-o:before { 1387 | content: "\f1c4"; } 1388 | 1389 | .fa-file-photo-o:before, .fa-file-picture-o:before, .fa-file-image-o:before { 1390 | content: "\f1c5"; } 1391 | 1392 | .fa-file-zip-o:before, .fa-file-archive-o:before { 1393 | content: "\f1c6"; } 1394 | 1395 | .fa-file-sound-o:before, .fa-file-audio-o:before { 1396 | content: "\f1c7"; } 1397 | 1398 | .fa-file-movie-o:before, .fa-file-video-o:before { 1399 | content: "\f1c8"; } 1400 | 1401 | .fa-file-code-o:before { 1402 | content: "\f1c9"; } 1403 | 1404 | .fa-vine:before { 1405 | content: "\f1ca"; } 1406 | 1407 | .fa-codepen:before { 1408 | content: "\f1cb"; } 1409 | 1410 | .fa-jsfiddle:before { 1411 | content: "\f1cc"; } 1412 | 1413 | .fa-life-bouy:before, .fa-life-buoy:before, .fa-life-saver:before, .fa-support:before, .fa-life-ring:before { 1414 | content: "\f1cd"; } 1415 | 1416 | .fa-circle-o-notch:before { 1417 | content: "\f1ce"; } 1418 | 1419 | .fa-ra:before, .fa-rebel:before { 1420 | content: "\f1d0"; } 1421 | 1422 | .fa-ge:before, .fa-empire:before { 1423 | content: "\f1d1"; } 1424 | 1425 | .fa-git-square:before { 1426 | content: "\f1d2"; } 1427 | 1428 | .fa-git:before { 1429 | content: "\f1d3"; } 1430 | 1431 | .fa-hacker-news:before { 1432 | content: "\f1d4"; } 1433 | 1434 | .fa-tencent-weibo:before { 1435 | content: "\f1d5"; } 1436 | 1437 | .fa-qq:before { 1438 | content: "\f1d6"; } 1439 | 1440 | .fa-wechat:before, .fa-weixin:before { 1441 | content: "\f1d7"; } 1442 | 1443 | .fa-send:before, .fa-paper-plane:before { 1444 | content: "\f1d8"; } 1445 | 1446 | .fa-send-o:before, .fa-paper-plane-o:before { 1447 | content: "\f1d9"; } 1448 | 1449 | .fa-history:before { 1450 | content: "\f1da"; } 1451 | 1452 | .fa-circle-thin:before { 1453 | content: "\f1db"; } 1454 | 1455 | .fa-header:before { 1456 | content: "\f1dc"; } 1457 | 1458 | .fa-paragraph:before { 1459 | content: "\f1dd"; } 1460 | 1461 | .fa-sliders:before { 1462 | content: "\f1de"; } 1463 | 1464 | .fa-share-alt:before { 1465 | content: "\f1e0"; } 1466 | 1467 | .fa-share-alt-square:before { 1468 | content: "\f1e1"; } 1469 | 1470 | .fa-bomb:before { 1471 | content: "\f1e2"; } 1472 | 1473 | .fa-soccer-ball-o:before, .fa-futbol-o:before { 1474 | content: "\f1e3"; } 1475 | 1476 | .fa-tty:before { 1477 | content: "\f1e4"; } 1478 | 1479 | .fa-binoculars:before { 1480 | content: "\f1e5"; } 1481 | 1482 | .fa-plug:before { 1483 | content: "\f1e6"; } 1484 | 1485 | .fa-slideshare:before { 1486 | content: "\f1e7"; } 1487 | 1488 | .fa-twitch:before { 1489 | content: "\f1e8"; } 1490 | 1491 | .fa-yelp:before { 1492 | content: "\f1e9"; } 1493 | 1494 | .fa-newspaper-o:before { 1495 | content: "\f1ea"; } 1496 | 1497 | .fa-wifi:before { 1498 | content: "\f1eb"; } 1499 | 1500 | .fa-calculator:before { 1501 | content: "\f1ec"; } 1502 | 1503 | .fa-paypal:before { 1504 | content: "\f1ed"; } 1505 | 1506 | .fa-google-wallet:before { 1507 | content: "\f1ee"; } 1508 | 1509 | .fa-cc-visa:before { 1510 | content: "\f1f0"; } 1511 | 1512 | .fa-cc-mastercard:before { 1513 | content: "\f1f1"; } 1514 | 1515 | .fa-cc-discover:before { 1516 | content: "\f1f2"; } 1517 | 1518 | .fa-cc-amex:before { 1519 | content: "\f1f3"; } 1520 | 1521 | .fa-cc-paypal:before { 1522 | content: "\f1f4"; } 1523 | 1524 | .fa-cc-stripe:before { 1525 | content: "\f1f5"; } 1526 | 1527 | .fa-bell-slash:before { 1528 | content: "\f1f6"; } 1529 | 1530 | .fa-bell-slash-o:before { 1531 | content: "\f1f7"; } 1532 | 1533 | .fa-trash:before { 1534 | content: "\f1f8"; } 1535 | 1536 | .fa-copyright:before { 1537 | content: "\f1f9"; } 1538 | 1539 | .fa-at:before { 1540 | content: "\f1fa"; } 1541 | 1542 | .fa-eyedropper:before { 1543 | content: "\f1fb"; } 1544 | 1545 | .fa-paint-brush:before { 1546 | content: "\f1fc"; } 1547 | 1548 | .fa-birthday-cake:before { 1549 | content: "\f1fd"; } 1550 | 1551 | .fa-area-chart:before { 1552 | content: "\f1fe"; } 1553 | 1554 | .fa-pie-chart:before { 1555 | content: "\f200"; } 1556 | 1557 | .fa-line-chart:before { 1558 | content: "\f201"; } 1559 | 1560 | .fa-lastfm:before { 1561 | content: "\f202"; } 1562 | 1563 | .fa-lastfm-square:before { 1564 | content: "\f203"; } 1565 | 1566 | .fa-toggle-off:before { 1567 | content: "\f204"; } 1568 | 1569 | .fa-toggle-on:before { 1570 | content: "\f205"; } 1571 | 1572 | .fa-bicycle:before { 1573 | content: "\f206"; } 1574 | 1575 | .fa-bus:before { 1576 | content: "\f207"; } 1577 | 1578 | .fa-ioxhost:before { 1579 | content: "\f208"; } 1580 | 1581 | .fa-angellist:before { 1582 | content: "\f209"; } 1583 | 1584 | .fa-cc:before { 1585 | content: "\f20a"; } 1586 | 1587 | .fa-shekel:before, .fa-sheqel:before, .fa-ils:before { 1588 | content: "\f20b"; } 1589 | 1590 | .fa-meanpath:before { 1591 | content: "\f20c"; } 1592 | 1593 | body { 1594 | margin: 0; 1595 | -webkit-font-smoothing: antialiased; } 1596 | 1597 | select { 1598 | color: inherit; 1599 | font: inherit; 1600 | margin: 0; } 1601 | 1602 | ul { 1603 | list-style-type: none; 1604 | padding-left: 2px; } 1605 | 1606 | input { 1607 | outline: 0; } 1608 | 1609 | html, body { 1610 | height: 100%; } 1611 | 1612 | body { 1613 | width: 100%; 1614 | overflow: hidden; 1615 | font-family: "Helvetica Neue", Helvetica, '游ゴシック', YuGothic, "ヒラギノ角ゴ ProN W3", "Hiragino Kaku Gothic ProN", "メイリオ", Meiryo, sans-serif; } 1616 | -------------------------------------------------------------------------------- /docs/dev-guide.md: -------------------------------------------------------------------------------- 1 | # 技術スタック 2 | 3 | - typescript: ドメイン層 4 | - coffeescript: UI層 5 | - React: View層 6 | - Overworld: 画面遷移管理 7 | - SVG: 計算量の少ないRect/Circleでだいたいのものを表現 8 | - minimongo: ストレージ層 9 | -------------------------------------------------------------------------------- /docs/intro.md: -------------------------------------------------------------------------------- 1 | # はじめに 2 | 3 | ## 目標・コンセプト 4 | 5 | - WASD+マウスで全方位に派手に爆発するDiablo風アクション 6 | - PDCAサイクルを楽しめるハックアンドスラッシュ 7 | - カスタマイズ性の高いスキルツリー 8 | - リプレイ性のあるダンジョン 9 | - 技術的に: React+SVGのポテンシャル証明 10 | - おこづかい的に: 投げ銭でちょっとお小遣い入る程度に 11 | 12 | ## シナリオ 13 | 14 | - 薄く 15 | - プレーに関係ない程度に自己満足で書く 16 | 17 | ## 画面 18 | 19 | TODO: あとでモックを書く 20 | 21 | ## 参考 22 | 23 | - http://inishie-dungeon.com/ 24 | - [ABA Games](http://www.asahi-net.or.jp/~cs8k-cyu/ "ABA Games") 25 | -------------------------------------------------------------------------------- /domains/battlefield/.gitignore: -------------------------------------------------------------------------------- 1 | ### https://raw.github.com/github/gitignore/d866fb556184cc1edffd9d0f1ca205fe1916a7f6/Node.gitignore 2 | 3 | # Logs 4 | logs 5 | *.log 6 | 7 | # Runtime data 8 | pids 9 | *.pid 10 | *.seed 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 19 | .grunt 20 | 21 | # node-waf configuration 22 | .lock-wscript 23 | 24 | # Compiled binary addons (http://nodejs.org/api/addons.html) 25 | build/Release 26 | 27 | # Dependency directory 28 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git- 29 | node_modules 30 | 31 | 32 | ### https://raw.github.com/github/gitignore/d866fb556184cc1edffd9d0f1ca205fe1916a7f6/Global/OSX.gitignore 33 | 34 | .DS_Store 35 | .AppleDouble 36 | .LSOverride 37 | 38 | # Icon must end with two \r 39 | Icon 40 | 41 | # Thumbnails 42 | ._* 43 | 44 | # Files that might appear on external disk 45 | .Spotlight-V100 46 | .Trashes 47 | 48 | # Directories potentially created on remote AFP share 49 | .AppleDB 50 | .AppleDesktop 51 | Network Trash Folder 52 | Temporary Items 53 | .apdisk 54 | .tmp 55 | public/bundle.js 56 | lib 57 | typings 58 | -------------------------------------------------------------------------------- /domains/battlefield/gulpfile.coffee: -------------------------------------------------------------------------------- 1 | gulp = require 'gulp' 2 | shell = require 'gulp-shell' 3 | coffee = require 'gulp-coffee' 4 | 5 | gulp.task 'build:ts', shell.task [ 6 | 'tsc -m commonjs --target es5 --outDir lib src/entry.ts' 7 | ] 8 | 9 | gulp.task 'build:coffee', -> 10 | gulp.src('./src/**/*.coffee') 11 | .pipe(coffee()) 12 | .pipe(gulp.dest('./lib')) 13 | 14 | gulp.task 'watch', ['build'], -> 15 | gulp.watch 'src/**/*.ts', ['build:ts'] 16 | gulp.watch 'src/**/*.coffee', ['build:coffee'] 17 | 18 | gulp.task 'build', ['clear', 'build:ts', 'build:coffee'] 19 | gulp.task 'default', ['build'] 20 | 21 | gulp.task 'clear', shell.task [ 22 | 'rm -r lib' 23 | ] 24 | -------------------------------------------------------------------------------- /domains/battlefield/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "battlefield", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "lib/core.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "", 13 | "license": "MIT", 14 | "dependencies": { 15 | "PhysicsJS": "git://github.com/wellcaffeinated/PhysicsJS", 16 | "bluebird": "^2.3.11", 17 | "lodash": "^2.4.1" 18 | }, 19 | "devDependencies": { 20 | "coffee-loader": "^0.7.2", 21 | "coffee-script": "^1.8.0", 22 | "gulp": "^3.8.10", 23 | "gulp-shell": "^0.2.11", 24 | "typescript": "git://github.com/microsoft/typescript" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /domains/battlefield/src/behaviours/constant-friction.coffee: -------------------------------------------------------------------------------- 1 | Physics = require 'PhysicsJS' 2 | Physics.behavior 'constant-friction', ( parent ) -> 3 | defaults = {f: 0.00002} 4 | init: ( options ) -> 5 | parent.init.call( this ); 6 | @options.defaults( defaults ); 7 | @options( options ); 8 | @_v = new Physics.vector(); 9 | 10 | behave: ( data ) -> 11 | bodies = this.getTargets(); 12 | f = 0.0002 13 | 14 | for body in bodies 15 | ax = 0 16 | ay = 0 17 | cof = body.cof 18 | 19 | if body.state.vel.x > 0 20 | ax -= f * cof 21 | else if body.state.vel.x < 0 22 | ax += f * cof 23 | 24 | if body.state.vel.y > 0 25 | ay -= f * cof 26 | else if body.state.vel.y < 0 27 | ay += f * cof 28 | 29 | @_v.clone {x: ax, y: ay} 30 | body.accelerate( @_v ); 31 | -------------------------------------------------------------------------------- /domains/battlefield/src/core.ts: -------------------------------------------------------------------------------- 1 | global.EventEmitter = require('events').EventEmitter; 2 | declare var app: any; 3 | require('./behaviours/constant-friction'); 4 | 5 | import Player = require('./entities/battlers/player'); 6 | import Stage = require('./stages/stage'); 7 | import BattleStage = require('./stages/battle-stage'); 8 | import serialize = require('./utils/serialize'); 9 | 10 | var instance; 11 | export = Game; 12 | class Game extends EventEmitter { 13 | static instance: Game = null 14 | score: number = 0; // temporary game logic 15 | addScore(n: number) {this.score+=n;} 16 | 17 | player: Player; 18 | inputBuffer: any; 19 | stage: Stage; 20 | fps: number; 21 | running: boolean; 22 | 23 | static getInstance(): Game { 24 | if(this.instance == null) 25 | this.instance = new Game(); 26 | return this.instance; 27 | } 28 | 29 | static getActiveStage(): Stage { 30 | return this.getInstance().stage; 31 | } 32 | 33 | constructor(){ 34 | super(); 35 | global.game = this; 36 | this.stage = null 37 | this.inputBuffer = { 38 | left: false, 39 | right: false, 40 | up: false, 41 | down: false, 42 | mouseLeft: false, 43 | mouseRight: false, 44 | focus: {x: 0, y: 0} 45 | } 46 | this.player = new Player(this.inputBuffer); 47 | this.player.setPosition(100, 100); 48 | this.fps = ~~(1000/60); 49 | 50 | this.on('io:update-focus', (pos) => { 51 | this.updateFocus(pos); 52 | }); 53 | 54 | this.on('io:update-key', (key, val) => { 55 | this.updateKey(key, val); 56 | }); 57 | } 58 | 59 | updateKey(key, val){ 60 | /*if(this.inputBuffer[key] == null) 61 | throw key+'is unknown';*/ 62 | this.inputBuffer[key] = val; 63 | } 64 | 65 | updateFocus(pos){ 66 | this.inputBuffer.focus.x = pos.x+this.player.x-320; 67 | this.inputBuffer.focus.y = pos.y+this.player.y-240; 68 | } 69 | 70 | createNewStage(){ 71 | this.stage = new BattleStage(); 72 | this.stage.addChild(this.player); 73 | } 74 | 75 | public serialize(){ 76 | return serialize(this, this.stage, this.player); 77 | } 78 | 79 | start(){ 80 | if(this.running){ 81 | console.info('game already running'); 82 | return; 83 | } 84 | this.running = true; 85 | 86 | if(this.stage == null) 87 | throw 'you should initialize stage first'; 88 | 89 | var fn = () => { 90 | if(!this.running) return; 91 | 92 | var beforeUpdate = Date.now(); 93 | this.stage.update().then(() => { 94 | var emitter = app.getActiveEmitter(); 95 | emitter.emit('stage:update', this.serialize()); 96 | var afterUpdate = Date.now(); 97 | setTimeout(fn, this.fps-(afterUpdate-beforeUpdate)); 98 | }); 99 | } 100 | fn(); 101 | } 102 | 103 | pause(){ 104 | this.running = false; 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /domains/battlefield/src/entities/battlers/battler.ts: -------------------------------------------------------------------------------- 1 | import Entity = require('../entity'); 2 | 3 | export = Battler; 4 | class Battler extends Entity { 5 | static type = 'battler'; 6 | 7 | } 8 | -------------------------------------------------------------------------------- /domains/battlefield/src/entities/battlers/enemies/enemy.ts: -------------------------------------------------------------------------------- 1 | import Battler = require('../battler'); 2 | import GroupId = require('../../../values/group-id') 3 | import CreateBullet = require('../../../tasks/create-bullet'); 4 | 5 | export = Enemy; 6 | class Enemy extends Battler { 7 | static type = 'enemy'; 8 | 9 | private cnt: number; 10 | constructor(){ 11 | super(); 12 | this.life = 1; 13 | this.groupId = GroupId.ENEMY; 14 | this.cnt = 0; 15 | } 16 | 17 | step(){ 18 | this.cnt++; 19 | if(this.cnt % 12 === 0) { 20 | this.dir = 360*Math.random(); 21 | this.stage.addTask(new CreateBullet(this, this.x, this.y, this.dir)); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /domains/battlefield/src/entities/battlers/player.ts: -------------------------------------------------------------------------------- 1 | import Battler = require('./battler'); 2 | import CreateBullet = require('../../tasks/create-bullet'); 3 | import CreateBulletTrap = require('../../tasks/create-bullet-trap'); 4 | import GroupId = require('../../values/group-id') 5 | 6 | declare var game: any; 7 | 8 | export = Player; 9 | 10 | class Player extends Battler { 11 | static type = 'player'; 12 | 13 | public focusDir: number = 0; 14 | constructor(public inputBuffer: any){ 15 | super(); 16 | this.groupId = GroupId.ALLY; 17 | this.life = Infinity; 18 | } 19 | 20 | private updateVelocity(){ 21 | var speed = 0.3; 22 | var nx = 0; 23 | var ny = 0; 24 | // update pos 25 | if(this.inputBuffer.left) { 26 | nx = -speed; 27 | } else if(this.inputBuffer.right){ 28 | nx = +speed; 29 | } 30 | 31 | if(this.inputBuffer.up) { 32 | ny = -speed; 33 | } else if(this.inputBuffer.down) { 34 | ny = +speed; 35 | } 36 | 37 | this.physicsBody.state.vel.set(nx, ny); 38 | } 39 | 40 | /*public get rad(): number { 41 | var mx = this.inputBuffer.focus.x; 42 | var my = this.inputBuffer.focus.y; 43 | return ~~(Math.atan2(my-this.y, mx-this.x)*180/3.14); 44 | } 45 | public set rad(v) {}*/ 46 | 47 | private updateDirection(){ 48 | var mx = this.inputBuffer.focus.x; 49 | var my = this.inputBuffer.focus.y; 50 | this.focusDir = ~~(Math.atan2(my-this.y, mx-this.x)*180/3.14); 51 | this.dir = this.focusDir; 52 | } 53 | 54 | private leftCooldown = 0; 55 | private rightCooldown = 0; 56 | private execSkills(){ 57 | var mx = this.inputBuffer.focus.x; 58 | var my = this.inputBuffer.focus.y; 59 | if(this.inputBuffer.mouseLeft && this.leftCooldown <= 0) { 60 | this.stage.addTask(new CreateBullet(this, this.x, this.y, this.focusDir)); 61 | this.leftCooldown = 3; 62 | } else if(this.leftCooldown > 0) this.leftCooldown--; 63 | 64 | if(this.inputBuffer.mouseRight && this.rightCooldown <= 0) { 65 | this.stage.addTask(new CreateBulletTrap(this, mx, my, this.focusDir)); 66 | this.rightCooldown = 60; 67 | } else if(this.rightCooldown > 0) this.rightCooldown--; 68 | } 69 | 70 | step(){ 71 | this.physicsBody.sleep(false); 72 | this.updateDirection(); 73 | this.updateVelocity(); 74 | this.execSkills(); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /domains/battlefield/src/entities/entity.ts: -------------------------------------------------------------------------------- 1 | var uuid = require('node-uuid'); 2 | var _ = require('lodash'); 3 | var Physics = require('PhysicsJS'); 4 | 5 | global.EventEmitter = require('events').EventEmitter; 6 | import Stage = require('../stages/stage'); 7 | import GroupId = require('../values/group-id'); 8 | import RemoveEntity = require('../tasks/remove-entity'); 9 | 10 | export = Entity; 11 | class Entity extends EventEmitter { 12 | public static type:string = 'entity'; 13 | 14 | public id: string; 15 | /*public rad: number;*/ 16 | public life: number; 17 | public groupId: GroupId; 18 | public physicsBody: any; 19 | public stage: Stage; // attached by addChild 20 | public willRemove: boolean; 21 | 22 | // Alias to Physics world 23 | public get x(): number {return this.physicsBody.state.pos.x;} 24 | public set x(val) {throw 'Can\t set x'} 25 | public get y(): number {return this.physicsBody.state.pos.y;} 26 | public set y(val) {throw 'Can\t set y'} 27 | 28 | public setPosition(x: number, y: number) { 29 | this.physicsBody.state.pos.set(x, y); 30 | } 31 | 32 | public get vx(): number {return this.physicsBody.state.vel.vx;} 33 | public get vy(): number {return this.physicsBody.state.vel.vy;} 34 | public setVelocity(vx: number, vy: number) { 35 | this.physicsBody.state.vel.set(vx, vy); 36 | } 37 | 38 | public get dir(): number { 39 | return this.physicsBody.state.angular.pos; 40 | } 41 | 42 | public set dir(v: number) { 43 | this.physicsBody.state.angular.pos = v; 44 | this.physicsBody.state.angular.vel = 0; 45 | this.physicsBody.state.angular.acc = 0; 46 | } 47 | 48 | constructor() { 49 | super(); 50 | this.stage = null; 51 | this.physicsBody = this.createPhysicsShape(); 52 | 53 | this.id = uuid(); 54 | this.setPosition(0, 0); 55 | this.life = 1; 56 | this.willRemove = false; 57 | } 58 | 59 | public createPhysicsShape() { 60 | // default shape 61 | return this.physicsBody = Physics.body('circle', { 62 | radius: 10 63 | }); 64 | } 65 | 66 | step(stage?: Stage): Promise | any{} 67 | 68 | public isAlive(): boolean { return this.life > 0; } 69 | 70 | public isDead(): boolean { return !this.isAlive();} 71 | 72 | remove(): void { 73 | this.willRemove = true; 74 | this.stage.addTask(new RemoveEntity(this.id)); 75 | } 76 | 77 | public dispose(){} 78 | 79 | public onHit(other: Entity){} 80 | 81 | public suffer(damage: number): void { 82 | if(this.isAlive()) 83 | this.life -= damage; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /domains/battlefield/src/entities/objects/block.ts: -------------------------------------------------------------------------------- 1 | import Entity = require('../entity'); 2 | import Battler = require('../battlers/battler'); 3 | import GroupId = require('../../values/group-id'); 4 | var Physics = require('PhysicsJS'); 5 | 6 | export = Block; 7 | 8 | class Block extends Entity { 9 | static type = 'wall'; 10 | public size: number; 11 | constructor(size: number) { 12 | this.size = size; 13 | super(); 14 | this.life = Infinity; 15 | } 16 | 17 | isAlive() {return true;} 18 | 19 | public createPhysicsShape() { 20 | return Physics.body('rectangle', { 21 | width: this.size, 22 | height: this.size, 23 | treatment: 'static' 24 | }); 25 | } 26 | 27 | public step(stage){ 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /domains/battlefield/src/entities/objects/bullets/bullet.ts: -------------------------------------------------------------------------------- 1 | import Entity = require('../../entity'); 2 | import Battler = require('../../battlers/battler'); 3 | import GroupId = require('../../../values/group-id'); 4 | import Game = require('../../../core'); 5 | 6 | var Physics = require('PhysicsJS'); 7 | 8 | export = Bullet; 9 | class Bullet extends Entity { 10 | static type = 'bullet'; 11 | private cnt: number; 12 | constructor(public owner: Battler) { 13 | super(); 14 | this.life = 1; 15 | this.cnt = 0; 16 | this.groupId = owner.groupId; 17 | } 18 | 19 | public createPhysicsShape() { 20 | // default shape 21 | return this.physicsBody = Physics.body('circle', { 22 | radius: 10, 23 | cof: 0, 24 | }); 25 | } 26 | 27 | onHit(other: Battler) { 28 | if(other.groupId && this.groupId !== other.groupId){ 29 | this.attack(other); 30 | this.remove(); 31 | } 32 | } 33 | 34 | private computeAttackPower(){ return 1; } 35 | 36 | public attack(other: Battler) { 37 | // TODO 対象と自分のパラメータからダメージ量を算出 38 | var damage = this.computeAttackPower(); 39 | other.suffer(damage); 40 | } 41 | 42 | public step(stage){ 43 | this.cnt++ 44 | if(this.cnt > 40) 45 | this.remove() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /domains/battlefield/src/entities/objects/polygon.ts: -------------------------------------------------------------------------------- 1 | import Entity = require('../entity'); 2 | import GroupId = require('../../values/group-id'); 3 | var Physics = require('PhysicsJS'); 4 | 5 | export = Polygon; 6 | 7 | class Polygon extends Entity { 8 | static type = 'polygon'; 9 | constructor() { 10 | super(); 11 | this.life = Infinity; 12 | } 13 | 14 | public createPhysicsShape() { 15 | return Physics.body('convex-polygon', { 16 | vertices: [ 17 | { x: 0 , y: -30}, 18 | { x: -29, y: -9 }, 19 | { x: -18, y: 24 }, 20 | { x: 18 , y: 24 }, 21 | { x: 29 , y: -9 } 22 | ], 23 | treatment: 'static', 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /domains/battlefield/src/entities/objects/traps/bullet-trap.ts: -------------------------------------------------------------------------------- 1 | import Entity = require('../../entity'); 2 | import Battler = require('../../battlers/battler'); 3 | import GroupId = require('../../../values/group-id'); 4 | import RemoveEntity = require('../../../tasks/remove-entity'); 5 | import CreateBullet = require('../../../tasks/create-bullet'); 6 | import Game = require('../../../core'); 7 | 8 | declare var game: Game; 9 | 10 | export = BulletTrap; 11 | class BulletTrap extends Battler { 12 | static type = 'bullet'; 13 | private cnt: number; 14 | constructor(public owner: Battler) { 15 | super(); 16 | this.life = 1; 17 | this.cnt = 0; 18 | this.groupId = owner.groupId; 19 | } 20 | 21 | private fire(){ 22 | game.stage.addTask(new CreateBullet(this, this.x, this.y, this.dir)); 23 | } 24 | 25 | public step(stage){ 26 | this.cnt++; 27 | this.dir += 10; 28 | if(this.cnt%21 === 0) this.fire(); 29 | if(this.cnt > 120) this.remove() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /domains/battlefield/src/entry.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import Game = require('./core'); 3 | -------------------------------------------------------------------------------- /domains/battlefield/src/stages/battle-stage.ts: -------------------------------------------------------------------------------- 1 | import Stage = require('./stage'); 2 | import DeathChecker = require('../tasks/death-checker'); 3 | import SimpleSpawner = require('../tasks/simple-spawner'); 4 | import Block = require('../entities/objects/block'); 5 | import Polygon = require('../entities/objects/polygon'); 6 | 7 | var _ = require('lodash'); 8 | 9 | export = BattleStage; 10 | 11 | class BattleStage extends Stage { 12 | loadMap(){ 13 | /*_.range(10).forEach((i: number)=>{ 14 | var polygon = new Polygon(); 15 | polygon.setPosition(100*i, 40) 16 | this.addChild(polygon); 17 | });*/ 18 | 19 | var map = [ 20 | [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], 21 | [1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 22 | [1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 23 | [1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 24 | [1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 25 | [1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1], 26 | [1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1], 27 | [1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1], 28 | [1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1], 29 | [1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1], 30 | [1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1], 31 | [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1], 32 | [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 33 | [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 34 | [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 35 | [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1], 36 | [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 37 | [1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1], 38 | [1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1], 39 | [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1], 40 | [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 41 | [1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 42 | [1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 43 | [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] 44 | ]; 45 | 46 | var x_size = map[0].length; 47 | var y_size = map.length; 48 | 49 | var size = 30; 50 | 51 | this.width = x_size * size; 52 | this.height = y_size * size; 53 | 54 | this.physicsWorld = this.createWorld(this.width, this.height); 55 | 56 | _.range(y_size).forEach(y => { 57 | _.range(x_size).forEach(x => { 58 | if(map[y][x]) { 59 | var block = new Block(size); 60 | block.setPosition(x*size+size/2, y*size+size/2) 61 | this.addChild(block); 62 | } 63 | }); 64 | }); 65 | 66 | /*_.range(10).forEach((i: number)=>{ 67 | var block = new Block(size); 68 | block.setPosition(i*size, 240) 69 | this.addChild(block); 70 | });*/ 71 | } 72 | 73 | constructor(){ 74 | super(); 75 | this.loadMap(); 76 | this.addTask(new DeathChecker()); 77 | this.addTask(new SimpleSpawner()); 78 | 79 | /*_.range(10).forEach((i: number)=>{ 80 | var polygon = new Polygon(); 81 | polygon.setPosition(100*i, 40) 82 | this.addChild(polygon); 83 | }); 84 | 85 | _.range(10).forEach((i: number)=>{ 86 | var block = new Block(); 87 | block.setPosition(100*i, 240) 88 | this.addChild(block); 89 | });*/ 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /domains/battlefield/src/stages/stage.ts: -------------------------------------------------------------------------------- 1 | global.EventEmitter = require('events').EventEmitter; 2 | var Physics = require('PhysicsJS'); 3 | 4 | import Player = require('../entities/battlers/player'); 5 | import Entity = require('../entities/entity'); 6 | import Task = require('../tasks/task'); 7 | import Priority = require('../values/priority'); 8 | import TaskRunner = require('./task-runner'); 9 | 10 | declare var Physics: any; 11 | var _ = require('lodash'); 12 | 13 | export = Stage; 14 | class Stage extends EventEmitter { 15 | public cnt: number; 16 | public entities: Entity[]; 17 | /*public taskQueues: Task[];*/ 18 | public taskRunner: TaskRunner; 19 | public physicsWorld: any; 20 | 21 | /*public width: number = 1000; 22 | public height: number = 1000;*/ 23 | public width; 24 | public height; 25 | 26 | addChild(entity: Entity){ 27 | this.entities.push(entity); 28 | entity.stage = this; 29 | this.physicsWorld.add(entity.physicsBody); 30 | } 31 | 32 | public get taskQueueCount(): number { return this.taskRunner.taskQueues.length; } 33 | 34 | constructor(){ 35 | super(); 36 | this.cnt = 0; 37 | this.entities = []; 38 | this.taskRunner = new TaskRunner(); 39 | } 40 | 41 | createWorld(width: number, height: number){ 42 | var world = Physics({ 43 | integrator: 'verlet', 44 | maxIPF: 16, 45 | timestep: 1000.0 / 60 46 | }); 47 | 48 | var viewportBounds = Physics.aabb(0, 0, width, height); 49 | var edgeBounce = Physics.behavior('edge-collision-detection',{ 50 | aabb: viewportBounds, 51 | restitution: 0.5, 52 | cof: 0.05 53 | }); 54 | 55 | var friction = Physics.behavior('constant-friction'); 56 | 57 | world.add([ 58 | Physics.behavior('body-impulse-response'), 59 | Physics.behavior('body-collision-detection'), 60 | Physics.behavior('sweep-prune'), 61 | friction, 62 | edgeBounce 63 | ]); 64 | 65 | world.on('collisions:detected', (data) => { 66 | data.collisions.forEach((col)=>{ 67 | var bodyA = col.bodyA; 68 | var bodyB = col.bodyB; 69 | var entityA = _.find(this.entities, e => e.physicsBody.uid === bodyA.uid); 70 | var entityB = _.find(this.entities, e => e.physicsBody.uid === bodyB.uid); 71 | //TODO: research why get null object 72 | if(entityA && entityB) { 73 | entityA.onHit(entityB); 74 | entityB.onHit(entityA); 75 | } 76 | }); 77 | }); 78 | 79 | global.world = world; 80 | return world; 81 | } 82 | 83 | public addTask(task: Task): void{ 84 | this.taskRunner.addTask(task); 85 | } 86 | 87 | private updatePhysicsWorld(){ 88 | this.physicsWorld.step(Date.now()); 89 | } 90 | 91 | public update(): Promise{ 92 | this.cnt++; 93 | this.updatePhysicsWorld(); 94 | 95 | return new Promise(done => { 96 | Promise.all(this.entities.map(e => e.step(this))).then(() => { 97 | done(this.taskRunner.run(this)); 98 | }); 99 | }); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /domains/battlefield/src/stages/task-runner.ts: -------------------------------------------------------------------------------- 1 | import Stage = require('./stage'); 2 | import Entity = require('../entities/entity'); 3 | import Task = require('../tasks/task'); 4 | import Priority = require('../values/priority'); 5 | var _ = require('lodash'); 6 | 7 | export = TaskRunner; 8 | class TaskRunner { 9 | public taskQueues: Task[] = []; 10 | 11 | constructor(){ 12 | this.taskQueues = []; 13 | } 14 | 15 | public addTask(task: Task): void{ 16 | this.taskQueues.push(task); 17 | } 18 | 19 | private sortTasks(): void { 20 | this.taskQueues = _.sortBy(this.taskQueues, task => { 21 | return task.priority ? task.priority : Priority.LOW 22 | }); 23 | } 24 | 25 | private execAllTasks(stage: Stage){ 26 | this.sortTasks(); 27 | var nextTasks: Task[] = []; 28 | var taskQueues = this.taskQueues.slice(); 29 | this.taskQueues = []; 30 | 31 | return new Promise(done => { 32 | (Promise).reduce(taskQueues, (p, task) => { 33 | return new Promise(done=> { 34 | Promise.resolve(task.exec(stage)).then((val)=> { 35 | if(val === true) nextTasks.push(task); 36 | done(); 37 | }); 38 | }); 39 | }, Promise.resolve()).then(()=>{ 40 | this.taskQueues = this.taskQueues.concat(nextTasks); 41 | done(); 42 | }); 43 | }); 44 | } 45 | 46 | public run(stage: Stage): Promise{ 47 | return new Promise(done => { 48 | this.execAllTasks(stage).then(done); 49 | }); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /domains/battlefield/src/tasks/create-bullet-trap.ts: -------------------------------------------------------------------------------- 1 | import Task = require('./task'); 2 | import BulletTrap = require('../entities/objects/traps/bullet-trap'); 3 | import Battler = require('../entities/battlers/battler') 4 | 5 | export = CreateBulletTrap; 6 | 7 | class CreateBulletTrap implements Task { 8 | constructor( 9 | public owner:Battler, 10 | public x: number, public y: number, public dir: number 11 | ){} 12 | 13 | exec(stage){ 14 | var bulletTrap = new BulletTrap(this.owner); 15 | bulletTrap.setPosition(this.x, this.y); 16 | bulletTrap.dir = this.dir; 17 | 18 | stage.addChild(bulletTrap); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /domains/battlefield/src/tasks/create-bullet.ts: -------------------------------------------------------------------------------- 1 | import Task = require('./task'); 2 | import Bullet = require('../entities/objects/bullets/bullet'); 3 | import Battler = require('../entities/battlers/battler') 4 | 5 | export = CreateBullet; 6 | 7 | class CreateBullet implements Task { 8 | constructor( 9 | public owner:Battler, 10 | public x: number, public y: number, public dir: number 11 | ){ 12 | } 13 | 14 | exec(stage){ 15 | var bullet = new Bullet(this.owner); 16 | bullet.setPosition(this.x, this.y) 17 | bullet.dir = this.dir; 18 | stage.addChild(bullet); 19 | 20 | var speed = 0.5; 21 | var rad = this.dir/180*3.14; 22 | var vx = Math.cos(rad) * speed; 23 | var vy = Math.sin(rad) * speed; 24 | bullet.setVelocity(vx, vy); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /domains/battlefield/src/tasks/death-checker.ts: -------------------------------------------------------------------------------- 1 | import Task = require('./task'); 2 | import RemoveEntity = require('./remove-entity'); 3 | import Priority = require('../values/priority'); 4 | import GroupId = require('../values/group-id'); 5 | import Game = require('../core'); 6 | declare var game: Game; 7 | var _ = require('lodash'); 8 | 9 | // Sweep all dead entities 10 | // Always active if it exists. 11 | // TODO: Giving exp and gold is here. 12 | export = DeathChecker; 13 | class DeathChecker implements Task { 14 | public get priority(): Priority {return Priority.DEATH_CHECKER;} 15 | exec(stage){ 16 | // calc point 17 | var deadEntities = stage.entities.filter( 18 | e => e.groupId === GroupId.ENEMY && e.isDead() 19 | ); 20 | game.addScore(deadEntities.length); 21 | 22 | // TODO: work around for miss about death 23 | /*var deadPhysicsIds = deadEntities.map(e => e.physicsBody.uid); 24 | stage.physicsWorld.getBodies().forEach(body => { 25 | if(_.include(deadPhysicsIds, body.uid)){ 26 | stage.physicsWorld.remove(body); 27 | } 28 | }); 29 | stage.entities = stage.entities 30 | .filter(e => e.isAlive());*/ 31 | 32 | stage.entities 33 | .filter(e => e.isDead()) 34 | .forEach(e => e.remove()); 35 | 36 | return true; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /domains/battlefield/src/tasks/remove-entity.ts: -------------------------------------------------------------------------------- 1 | import Task = require('./task'); 2 | import Priority = require('../values/priority'); 3 | export = RemoveEntity; 4 | var _ = require('lodash'); 5 | 6 | class RemoveEntity implements Task { 7 | constructor(public entityId: string){} 8 | public get priority(): Priority {return Priority.REMOVE_ENTITIES;} 9 | exec(stage){ 10 | var target = _.find(stage.entities, e => e.id === this.entityId); 11 | if(target) stage.physicsWorld.remove(target.physicsBody); 12 | stage.entities = stage.entities.filter(e => e.id !== this.entityId); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /domains/battlefield/src/tasks/simple-spawner.ts: -------------------------------------------------------------------------------- 1 | import Task = require('./task'); 2 | import Priority = require('../values/priority'); 3 | import GroupId = require('../values/group-id'); 4 | import Enemy = require('../entities/battlers/enemies/enemy'); 5 | var _ = require('lodash'); 6 | 7 | export = SimpleSpawner; 8 | class SimpleSpawner implements Task { 9 | public get priority(): Priority {return Priority.SPAWN;} 10 | exec(stage){ 11 | var enemyCount = stage.entities.filter(e => e.groupId === GroupId.ENEMY).length; 12 | if(enemyCount < 3) { 13 | _.range(5).forEach(() => { 14 | var enemy = new Enemy(); 15 | enemy.setPosition( 16 | Math.random() * stage.width, 17 | Math.random() * stage.height 18 | ); 19 | stage.addChild(enemy); 20 | }); 21 | } 22 | return true; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /domains/battlefield/src/tasks/task.ts: -------------------------------------------------------------------------------- 1 | import Stage = require('../stages/stage'); 2 | import Priority = require('../values/priority') 3 | 4 | interface Task { 5 | priority?: Priority; 6 | exec(stage?: Stage): void | boolean | Promise | Promise; 7 | } 8 | 9 | export = Task; 10 | -------------------------------------------------------------------------------- /domains/battlefield/src/types.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare class EventEmitter { 4 | on(name: string, fn: Function): void; 5 | off(name: string, fn?: Function): void; 6 | emit(name: string, ...args: any[]): void; 7 | } 8 | -------------------------------------------------------------------------------- /domains/battlefield/src/utils/serialize.ts: -------------------------------------------------------------------------------- 1 | import Stage = require('../stages/stage'); 2 | import Entity = require('../entities/entity'); 3 | 4 | function formatPhysicsBody(body: any){ 5 | var obj:any = { 6 | name: body.name, 7 | pos: body.state.pos.values(), 8 | angle: body.state.angular.pos 9 | } 10 | if(body.name === 'circle'){ 11 | obj.radius = body.radius; 12 | } else if(body.name === 'rectangle'){ 13 | obj.width = body.width; 14 | obj.height = body.height; 15 | obj.x = body.x; 16 | obj.y = body.y; 17 | } else if(body.name === 'convex-polygon'){ 18 | obj.vertices = body.geometry.vertices; 19 | } 20 | return obj; 21 | } 22 | 23 | function serializeEntity(entity: Entity){ 24 | return { 25 | id: entity.id, 26 | groupId: entity.groupId, 27 | x: entity.x, y: entity.y, 28 | dir: entity.dir, 29 | body: formatPhysicsBody(entity.physicsBody), 30 | type: (entity.constructor).type 31 | }; 32 | } 33 | 34 | //TODO: remove player camera controller from this function 35 | function serialize(game, stage: Stage, target: Entity){ 36 | var cx = target.x-320; 37 | var cy = target.y-240; 38 | return { 39 | cx: cx, 40 | cy: cy, 41 | cnt: stage.cnt, 42 | stage: { 43 | width: stage.width, 44 | height: stage.height 45 | }, 46 | entities: stage.entities.map(e => serializeEntity(e)), 47 | focus: game.inputBuffer.focus, 48 | /*running: game.running*/ 49 | /*bodies: formatBodies,*/ 50 | }; 51 | } 52 | export = serialize; 53 | -------------------------------------------------------------------------------- /domains/battlefield/src/values/group-id.ts: -------------------------------------------------------------------------------- 1 | enum GroupId { 2 | ALLY = 1, 3 | ENEMY = 2, 4 | UNDEFINED = 99, 5 | } 6 | 7 | export = GroupId; 8 | -------------------------------------------------------------------------------- /domains/battlefield/src/values/priority.ts: -------------------------------------------------------------------------------- 1 | enum Priority { 2 | // For System Event 3 | HIGH = 102, 4 | NORMAL = 101, 5 | LOW = 100, 6 | 7 | DEATH_CHECKER = 10, 8 | REMOVE_ENTITIES = 9, 9 | SPAWN = 5, 10 | } 11 | 12 | export = Priority; 13 | -------------------------------------------------------------------------------- /domains/battlefield/test/stages/stage-test.coffee: -------------------------------------------------------------------------------- 1 | Stage = require '../../../lib/game/stages/stage' 2 | sinon = require 'sinon' 3 | 4 | describe 'Stage', -> 5 | it 'should exec queues and consume', -> 6 | stage = new Stage 7 | 8 | spy1 = sinon.spy() 9 | spy2 = sinon.spy() 10 | 11 | task1 = exec: -> new Promise (done) -> 12 | setTimeout -> 13 | spy1() 14 | done() 15 | , 0 16 | 17 | task2 = exec: -> new Promise (done) -> 18 | setTimeout -> 19 | spy1() 20 | spy2() 21 | done() 22 | , 16 23 | 24 | stage.addTask(task1); 25 | stage.addTask(task2); 26 | equal stage.taskQueueCount, 2 27 | new Promise (done) -> 28 | stage.update().then -> 29 | ok spy1.calledTwice 30 | ok spy2.calledOnce 31 | equal stage.taskQueueCount, 0 32 | done() 33 | 34 | it 'should save task to next if it returns with true', -> 35 | stage = new Stage 36 | spy1 = sinon.spy() 37 | task1 = exec: -> new Promise (done) -> 38 | setTimeout -> 39 | spy1() 40 | done(true) 41 | 42 | stage.addTask(task1); 43 | equal stage.taskQueueCount, 1 44 | 45 | new Promise (done) -> 46 | stage.update().then -> 47 | equal stage.taskQueueCount, 1 48 | ok spy1.calledOnce 49 | 50 | stage.update().then -> 51 | equal stage.taskQueueCount, 1 52 | ok spy1.calledTwice 53 | done() 54 | 55 | it 'should do task by priority', (done) -> 56 | stage = new Stage 57 | spy1 = sinon.spy() 58 | spy2 = sinon.spy() 59 | 60 | task1 = 61 | exec: -> 62 | ok spy2.called 63 | spy1() 64 | done() 65 | priority: 0 66 | 67 | task2 = 68 | exec: -> 69 | ok not spy1.called 70 | spy2() 71 | priority: 1 72 | 73 | stage.addTask(task1); 74 | stage.addTask(task2); 75 | 76 | stage.update() 77 | -------------------------------------------------------------------------------- /domains/map-editor/debug.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /domains/map-editor/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mizchi-sandbox/ar2/308fe2096084b2cdf1aa03fcc7b609d62c651fc9/domains/map-editor/index.js -------------------------------------------------------------------------------- /domains/map-editor/src/component/actions.coffee: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | } 3 | -------------------------------------------------------------------------------- /domains/map-editor/src/component/index.coffee: -------------------------------------------------------------------------------- 1 | extend = require 'extend' 2 | template = require './template.jade' 3 | 4 | module.exports = React.createClass 5 | mixins: [Overworld.mixinFor(-> app), require './actions'] 6 | 7 | render: -> 8 | template extend {}, @, @props, @state 9 | -------------------------------------------------------------------------------- /domains/map-editor/src/component/template.jade: -------------------------------------------------------------------------------- 1 | mixin physicsBody(body) 2 | - var x = body.pos.x; 3 | - var y = body.pos.y; 4 | - var med = body.angle; 5 | g( 6 | key= body.uid 7 | transform= 'translate('+x+','+y+') rotate(' + med + ')' 8 | ) 9 | if body.name === "circle" 10 | circle( 11 | cx=0 12 | cy=0 13 | r= body.radius 14 | fill= 'none', 15 | stroke= 'green' 16 | ) 17 | else if body.name === 'rectangle' 18 | rect( 19 | width=body.width 20 | height=body.height 21 | x=-body.width/2 22 | y=-body.height/2 23 | fill='green' 24 | ) 25 | else if body.name === 'convex-polygon' 26 | - var vs = body.vertices; 27 | - var points = vs.map(function(v){return v.x+','+v.y;}).join(' ') 28 | polygon( 29 | points = points 30 | fill= 'green' 31 | ) 32 | 33 | mixin entityAvatar(entity) 34 | //- id, type, x, y, rad 35 | - var baseColor = entity.groupId === 1 ? 'white' : 'red'; 36 | g( 37 | transform='translate(' + entity.x + ', ' + entity.y + ') rotate(' + (entity.dir+90) + ')' 38 | key=entity.id 39 | id=entity.id 40 | ) 41 | if entity.type === 'player' 42 | circle(cx=0 cy=0 r=15 fill=baseColor stroke='black') 43 | line( 44 | x1=0 y1=0 45 | x2=0 y2=-15 46 | stroke='black') 47 | else if entity.type === 'enemy' 48 | circle(cx=0 cy=0 r=15 fill=baseColor stroke='black') 49 | line( 50 | x1=0 y1=0 51 | x2=0 y2=-15 52 | stroke='black') 53 | else if entity.type === 'wall' 54 | rect( 55 | width=entity.body.width 56 | height=entity.body.height 57 | x=-entity.body.width/2 58 | y=-entity.body.height/2 59 | fill='black' 60 | ) 61 | else if entity.type === 'polygon' 62 | - var vs = entity.body.vertices; 63 | - var points = vs.map(function(v){return v.x+','+v.y;}).join(' ') 64 | polygon( 65 | points = points 66 | fill='black' 67 | ) 68 | else 69 | //- maybe bullet now 70 | circle(cx=0 cy=0 r=5 fill=baseColor stroke='green') 71 | line( x1=0 y1=0 72 | x2=0 y2=-5 73 | stroke='white') 74 | 75 | mixin focus(x, y) 76 | g( 77 | transform='translate(' + x + ', ' + y + ')' 78 | ) 79 | circle(cx=0 cy=0 r=10 fill='transparent' stroke='blue') 80 | 81 | svg( 82 | key='main' 83 | className='main' 84 | width=640 height=480 85 | onMouseMove=onMouseMove 86 | onMouseDown=onMouseDown 87 | onMouseUp=onMouseUp 88 | onContextMenu=onContextMenu 89 | ) 90 | rect( 91 | x=0 y=0 92 | width=640 height=480 93 | fill='wheat' 94 | key='bg' 95 | ) 96 | g( 97 | transform='translate(' + -cx + ', ' + -cy + ')' 98 | key='field' 99 | ) 100 | //- background 101 | rect( 102 | x=0 y=0 103 | width=stage.width height=stage.height 104 | fill='gray' 105 | ) 106 | 107 | //- entities 108 | for entity in entities 109 | +entityAvatar(entity) 110 | +focus(focus.x, focus.y) 111 | 112 | //- g( 113 | //- transform='translate(' + -cx + ', ' + -cy + ')' 114 | //- key='physics' 115 | //- ) 116 | //- for body in bodies 117 | //- +physicsBody(body) 118 | -------------------------------------------------------------------------------- /domains/map-editor/src/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mizchi-sandbox/ar2/308fe2096084b2cdf1aa03fcc7b609d62c651fc9/domains/map-editor/src/index.js -------------------------------------------------------------------------------- /domains/map-editor/src/scene/aggregator.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | initState: (props) -> {} 3 | aggregate: (props, state) -> {} 4 | -------------------------------------------------------------------------------- /domains/map-editor/src/scene/index.coffee: -------------------------------------------------------------------------------- 1 | Overworld = require 'overworld' 2 | module.exports = 3 | class extends Overworld.World 4 | @component : require '../component' 5 | @aggregator: require './aggregator' 6 | @subscriber: require './subscriber' 7 | -------------------------------------------------------------------------------- /domains/map-editor/src/scene/subscriber.coffee: -------------------------------------------------------------------------------- 1 | module.exports = (subscribe) -> 2 | subscribe 'menu:close', (context) -> (args...) -> 3 | app.popWorld() 4 | -------------------------------------------------------------------------------- /dtsm.json: -------------------------------------------------------------------------------- 1 | { 2 | "repos": [ 3 | { 4 | "url": "https://github.com/borisyankov/DefinitelyTyped.git", 5 | "ref": "master" 6 | } 7 | ], 8 | "path": "typings", 9 | "bundle": "typings/bundle.d.ts", 10 | "dependencies": { 11 | "node/node.d.ts": { 12 | "ref": "ca32947b75a2b1203779dac7154ffbc232746e4d" 13 | }, 14 | "es6-promise/es6-promise.d.ts": { 15 | "ref": "ca32947b75a2b1203779dac7154ffbc232746e4d" 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /gulpfile.coffee: -------------------------------------------------------------------------------- 1 | gulp = require 'gulp' 2 | coffee = require 'gulp-coffee' 3 | webpack = require 'gulp-webpack' 4 | shell = require 'gulp-shell' 5 | sass = require 'gulp-sass' 6 | 7 | webpackConfig = require './webpack.config' 8 | 9 | gulp.task 'build:coffee', -> 10 | gulp.src('./src/**/*.coffee') 11 | .pipe(coffee()) 12 | .pipe(gulp.dest('./lib')) 13 | 14 | gulp.task 'build:ts', shell.task [ 15 | 'tsc -m commonjs --target es5 --outDir lib src/entry.ts' 16 | ] 17 | 18 | gulp.task 'webpack', -> 19 | gulp.src('lib/index.js') 20 | .pipe(webpack(webpackConfig)) 21 | .pipe(gulp.dest('.')) 22 | 23 | gulp.task 'build:jade', -> 24 | gulp.src('src/**/*.jade') 25 | .pipe(gulp.dest('lib')) 26 | 27 | gulp.task 'build:css', -> 28 | gulp 29 | .src('styles/style.scss') 30 | .pipe(sass()) 31 | .pipe(gulp.dest('public')) 32 | 33 | ## Watch tasks 34 | gulp.task 'watch', ['build'], -> 35 | gulp.watch 'src/**/*.coffee', ['build:coffee'] 36 | # gulp.watch 'src/**/*.ts', ['build:ts'] 37 | gulp.watch 'src/**/*.jade', ['build:jade'] 38 | gulp.watch 'lib/**/*', ['webpack'] 39 | gulp.watch 'domains/battlefield/lib/*.js', ['webpack'] 40 | gulp.watch 'styles/**/*.scss', ['build:css'] 41 | 42 | gulp.task 'build', ['clear', 'build:coffee', 'build:jade'] 43 | gulp.task 'default', ['build'] 44 | 45 | gulp.task 'clear', shell.task [ 46 | 'rm -r lib' 47 | ] 48 | 49 | ## Deploy tasks 50 | gulp.task 'prepare-deploy', -> 51 | gulp.src('public/**/*') 52 | .pipe(gulp.dest('deploy')) 53 | 54 | ## Deploy tasks 55 | gulp.task 'deploy', ['prepare-deploy'], shell.task [ 56 | 'git subtree push --prefix deploy/ origin gh-pages' 57 | ] 58 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ar2", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha --compilers coffee:coffee-script/register --recursive -t 200 --reporter spec" 8 | }, 9 | "author": "", 10 | "license": "MIT", 11 | "dependencies": { 12 | "bluebird": "^2.3.11", 13 | "change-case": "^2.1.6", 14 | "clone": "^0.2.0", 15 | "extend": "^2.0.0", 16 | "global": "^4.3.0", 17 | "gulp": "^3.8.10", 18 | "lodash": "^2.4.1", 19 | "minimongo": "^3.2.5", 20 | "mousetrap": "^1.4.6", 21 | "node-uuid": "^1.4.2", 22 | "overworld": "0.0.2", 23 | "react": "^0.12.1", 24 | "react-jade": "2.3.0", 25 | "sinon": "^1.12.2" 26 | }, 27 | "devDependencies": { 28 | "coffee-loader": "^0.7.2", 29 | "coffee-script": "^1.8.0", 30 | "dtsm": "^0.2.0", 31 | "font-awesome": "^4.2.0", 32 | "gulp-coffee": "^2.2.0", 33 | "gulp-sass": "^1.2.4", 34 | "gulp-shell": "^0.2.11", 35 | "gulp-webpack": "^1.1.2", 36 | "react-jade-loader": "mizchi/react-jade-loader#24b5cc8f34376c8d8c11ae96425b44de0d57f06c", 37 | "webpack": "^1.4.13" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | AR 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /scripts/build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # battlefield 4 | cd domains/battlefield 5 | npm install 6 | gulp 7 | 8 | # here 9 | cd ../.. 10 | ./node_modules/.bin/dtsm install 11 | npm install 12 | ./node_modules/.bin/gulp build 13 | ./node_modules/.bin/gulp webpack 14 | -------------------------------------------------------------------------------- /scripts/watch: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env coffee 2 | 3 | {spawn} = require 'child_process' 4 | 5 | startWatch = (cwd) -> 6 | name = cwd.replace process.cwd(), '' 7 | watchRoot = spawn('gulp', ['watch'], cwd: cwd) 8 | watchRoot.stdout.on 'data', (data) -> 9 | console.log name + ':' + data 10 | 11 | watchRoot.stderr.on 'data', (data) -> 12 | console.log name + ':' + data 13 | 14 | watchRoot.on 'close', (code) -> 15 | console.log name + ':' + data 16 | 17 | startWatch process.cwd() 18 | startWatch process.cwd() + '/domains/battlefield' 19 | -------------------------------------------------------------------------------- /src/components/field/actions.coffee: -------------------------------------------------------------------------------- 1 | getMainRect = do -> 2 | main = document.querySelector '.main' 3 | rect = null 4 | -> 5 | main ?= document.querySelector '.main' 6 | rect ?= main.getBoundingClientRect() 7 | 8 | MouseButtons = 9 | 0: 'mouseLeft' 10 | 2: 'mouseRight' 11 | 12 | module.exports = 13 | onMouseMove: (e) -> 14 | rect = getMainRect() 15 | x = e.clientX - rect.left 16 | y = e.clientY - rect.top 17 | game?.emit 'io:update-focus', {x, y} 18 | return 19 | 20 | onMouseDown: (e) -> 21 | game.emit 'io:update-key', MouseButtons[e.button], true 22 | e.stopPropagation() 23 | e.preventDefault() 24 | return 25 | 26 | onMouseUp: (e) -> 27 | game.emit 'io:update-key', MouseButtons[e.button], false 28 | e.stopPropagation() 29 | e.preventDefault() 30 | return 31 | 32 | onContextMenu: (e) -> 33 | e.stopPropagation() 34 | e.preventDefault() 35 | return 36 | 37 | onClickStop: (e) -> 38 | @emit 'field:stop' 39 | 40 | onClickRestart: (e) -> 41 | @emit 'field:restart' 42 | -------------------------------------------------------------------------------- /src/components/field/index.coffee: -------------------------------------------------------------------------------- 1 | extend = require 'extend' 2 | template = require './template.jade' 3 | 4 | module.exports = React.createClass 5 | mixins: [Overworld.mixinFor(-> app), require './actions'] 6 | 7 | render: -> 8 | template extend {}, @, @props, @state 9 | -------------------------------------------------------------------------------- /src/components/field/template.jade: -------------------------------------------------------------------------------- 1 | mixin physicsBody(body) 2 | - var x = body.pos.x; 3 | - var y = body.pos.y; 4 | - var med = body.angle; 5 | g( 6 | key= body.uid 7 | transform= 'translate('+x+','+y+') rotate(' + med + ')' 8 | ) 9 | if body.name === "circle" 10 | circle( 11 | cx=0 12 | cy=0 13 | r= body.radius 14 | fill= 'none', 15 | stroke= 'green' 16 | ) 17 | else if body.name === 'rectangle' 18 | rect( 19 | width=body.width 20 | height=body.height 21 | x=-body.width/2 22 | y=-body.height/2 23 | fill='green' 24 | ) 25 | else if body.name === 'convex-polygon' 26 | - var vs = body.vertices; 27 | - var points = vs.map(function(v){return v.x+','+v.y;}).join(' ') 28 | polygon( 29 | points = points 30 | fill= 'green' 31 | ) 32 | 33 | mixin entityAvatar(entity) 34 | //- id, type, x, y, rad 35 | - var baseColor = entity.groupId === 1 ? 'white' : 'red'; 36 | g( 37 | transform='translate(' + entity.x + ', ' + entity.y + ') rotate(' + (entity.dir+90) + ')' 38 | key=entity.id 39 | id=entity.id 40 | ) 41 | if entity.type === 'player' 42 | circle(cx=0 cy=0 r=15 fill=baseColor stroke='black') 43 | line( 44 | x1=0 y1=0 45 | x2=0 y2=-15 46 | stroke='black') 47 | else if entity.type === 'enemy' 48 | circle(cx=0 cy=0 r=15 fill=baseColor stroke='black') 49 | line( 50 | x1=0 y1=0 51 | x2=0 y2=-15 52 | stroke='black') 53 | else if entity.type === 'wall' 54 | rect( 55 | width=entity.body.width 56 | height=entity.body.height 57 | x=-entity.body.width/2 58 | y=-entity.body.height/2 59 | fill='black' 60 | ) 61 | else if entity.type === 'polygon' 62 | - var vs = entity.body.vertices; 63 | - var points = vs.map(function(v){return v.x+','+v.y;}).join(' ') 64 | polygon( 65 | points = points 66 | fill='black' 67 | ) 68 | else 69 | //- maybe bullet now 70 | circle(cx=0 cy=0 r=5 fill=baseColor stroke='green') 71 | line( x1=0 y1=0 72 | x2=0 y2=-5 73 | stroke='white') 74 | 75 | mixin focus(x, y) 76 | g( 77 | transform='translate(' + x + ', ' + y + ')' 78 | ) 79 | circle(cx=0 cy=0 r=10 fill='transparent' stroke='blue') 80 | 81 | svg( 82 | key='main' 83 | className='main' 84 | width=640 height=480 85 | onMouseMove=onMouseMove 86 | onMouseDown=onMouseDown 87 | onMouseUp=onMouseUp 88 | onContextMenu=onContextMenu 89 | ) 90 | rect( 91 | x=0 y=0 92 | width=640 height=480 93 | fill='wheat' 94 | key='bg' 95 | ) 96 | g( 97 | transform='translate(' + -cx + ', ' + -cy + ')' 98 | key='field' 99 | ) 100 | //- background 101 | rect( 102 | x=0 y=0 103 | width=stage.width height=stage.height 104 | fill='gray' 105 | ) 106 | //- entities 107 | for entity in entities 108 | +entityAvatar(entity) 109 | +focus(focus.x, focus.y) 110 | 111 | //- g( 112 | //- transform='translate(' + -cx + ', ' + -cy + ')' 113 | //- key='physics' 114 | //- ) 115 | //- for body in bodies 116 | //- +physicsBody(body) 117 | 118 | .controll 119 | if paused 120 | button(onClick=onClickRestart) restart 121 | else 122 | button(onClick=onClickStop) stop 123 | -------------------------------------------------------------------------------- /src/components/menu/actions.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | onClickBack: -> 3 | @emit 'menu:close', {} 4 | -------------------------------------------------------------------------------- /src/components/menu/index.coffee: -------------------------------------------------------------------------------- 1 | template = require './template.jade' 2 | actions = require './actions' 3 | extend = require 'extend' 4 | 5 | module.exports = React.createClass 6 | mixins: [Overworld.mixinFor(-> app), actions] 7 | render: -> 8 | template extend {}, @, @props, @state 9 | -------------------------------------------------------------------------------- /src/components/menu/template.jade: -------------------------------------------------------------------------------- 1 | .menu 2 | h1 menu 3 | button(onClick=onClickBack) Back 4 | -------------------------------------------------------------------------------- /src/components/opening/actions.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | onClick: -> 3 | app.pushWorld 'field', {} 4 | -------------------------------------------------------------------------------- /src/components/opening/index.coffee: -------------------------------------------------------------------------------- 1 | template = require './template.jade' 2 | actions = require './actions' 3 | extend = require 'extend' 4 | 5 | module.exports = React.createClass 6 | mixins: [Overworld.mixinFor(-> app), actions] 7 | render: -> 8 | template extend {}, @, @props, @state 9 | -------------------------------------------------------------------------------- /src/components/opening/template.jade: -------------------------------------------------------------------------------- 1 | .opening 2 | h1 Actrogue2 3 | 4 | p ~ under technical testings 5 | 6 | button(onClick=onClick) Game Start 7 | 8 | h2 操作方法 9 | p 10 | dl 11 | dt WASD or ↑←↓ 12 | dd プレイヤーの移動 13 | dt マウス 14 | dd 照準の移動 15 | dt 左クリック 16 | dd 発射 17 | dt 右クリック 18 | dd 弾を発射するトラップ 19 | -------------------------------------------------------------------------------- /src/index.coffee: -------------------------------------------------------------------------------- 1 | # requires 2 | window.app = null 3 | require './setup' 4 | require './router' 5 | 6 | Game = require '../domains/battlefield' 7 | window.game = Game.getInstance() 8 | 9 | window.addEventListener 'DOMContentLoaded', -> 10 | app.mount(document.body) 11 | app.transition('opening', {}) 12 | -------------------------------------------------------------------------------- /src/router.coffee: -------------------------------------------------------------------------------- 1 | app.link 'field', require './scenes/field' 2 | app.link 'opening', require './scenes/opening' 3 | app.link 'menu', require './scenes/menu' 4 | -------------------------------------------------------------------------------- /src/scenes/field/aggregator.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | initState: (props) -> 3 | entities: [] 4 | cx: 0 5 | cy: 0 6 | score: 0 7 | focus: 8 | x: -1000 9 | y: -1000 10 | stage: 11 | width: 0 12 | height: 0 13 | bodies: [] 14 | paused: false 15 | 16 | aggregate: (props, state) -> 17 | state 18 | -------------------------------------------------------------------------------- /src/scenes/field/index.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | class Field extends Overworld.World 3 | @component : require '../../components/field' 4 | @aggregator: require './aggregator' 5 | @subscriber: require './subscriber' 6 | -------------------------------------------------------------------------------- /src/scenes/field/subscriber.coffee: -------------------------------------------------------------------------------- 1 | clone = require 'clone' 2 | 3 | module.exports = (subscribe) -> 4 | subscribe 'stage:update', (context) -> (serialized) -> 5 | context.update(serialized) 6 | 7 | subscribe 'lifecycle:created', (context) -> () -> 8 | game.createNewStage() 9 | game.start() 10 | 11 | subscribe 'lifecycle:paused', (context) -> () -> 12 | game.pause() 13 | 14 | subscribe 'lifecycle:resumed', (context) -> () -> 15 | game.start() 16 | 17 | subscribe 'io:open-menu', (context) -> (serialized) -> 18 | console.log 'open menu' 19 | app.pushWorld 'menu', {} 20 | 21 | subscribe 'field:stop', (context) -> (serialized) -> 22 | game.pause() 23 | s = clone(context.state) 24 | s.paused = true 25 | context.update(s) 26 | 27 | subscribe 'field:restart', (context) -> (serialized) -> 28 | game.start() 29 | s = clone(context.state) 30 | s.paused = false 31 | context.update(s) 32 | -------------------------------------------------------------------------------- /src/scenes/menu/aggregator.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | initState: (props) -> {} 3 | aggregate: (props, state) -> {} 4 | -------------------------------------------------------------------------------- /src/scenes/menu/index.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | class extends Overworld.World 3 | @component : require '../../components/menu' 4 | @aggregator: require './aggregator' 5 | @subscriber: require './subscriber' 6 | -------------------------------------------------------------------------------- /src/scenes/menu/subscriber.coffee: -------------------------------------------------------------------------------- 1 | module.exports = (subscribe) -> 2 | subscribe 'menu:close', (context) -> (args...) -> 3 | app.popWorld() 4 | -------------------------------------------------------------------------------- /src/scenes/opening/aggregator.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | initState: (props) -> {} 3 | aggregate: (props, state) -> {} 4 | -------------------------------------------------------------------------------- /src/scenes/opening/index.coffee: -------------------------------------------------------------------------------- 1 | module.exports = 2 | class extends Overworld.World 3 | @component : require '../../components/opening' 4 | @aggregator: require './aggregator' 5 | @subscriber: require './subscriber' 6 | -------------------------------------------------------------------------------- /src/scenes/opening/subscriber.coffee: -------------------------------------------------------------------------------- 1 | module.exports = (subscribe) -> 2 | subscribe 'opening:update', (context) -> (args...) -> 3 | context.update {} 4 | -------------------------------------------------------------------------------- /src/setup.coffee: -------------------------------------------------------------------------------- 1 | # requires 2 | global = require 'global' 3 | global.Promise = require 'bluebird' 4 | global.React = require 'react' 5 | global.Overworld = require 'overworld' 6 | Overworld.setReact React 7 | 8 | global.app = new Overworld.Portal 9 | 10 | KeyMap = 11 | 37: 'left' 12 | 38: 'up' 13 | 39: 'right' 14 | 40: 'down' 15 | 87: 'w' 16 | 65: 'a' 17 | 83: 's' 18 | 68: 'd' 19 | 73: 'i' 20 | 21 | window.addEventListener 'keydown', (e) -> 22 | # console.log e.keyCode 23 | emitter = app.getActiveEmitter() 24 | return unless game 25 | switch KeyMap[e.keyCode] 26 | when 'left' , 'a' then game.emit 'io:update-key', 'left', true 27 | when 'up' , 'w' then game.emit 'io:update-key', 'up', true 28 | when 'right', 'd' then game.emit 'io:update-key', 'right',true 29 | when 'down' , 's' then game.emit 'io:update-key', 'down', true 30 | when 'i' then emitter.emit 'io:open-menu' 31 | 32 | window.addEventListener 'keyup', (e) -> 33 | return unless game 34 | switch KeyMap[e.keyCode] 35 | when 'left' , 'a' then game.emit 'io:update-key', 'left', false 36 | when 'up' , 'w' then game.emit 'io:update-key', 'up', false 37 | when 'right', 'd' then game.emit 'io:update-key', 'right',false 38 | when 'down' , 's' then game.emit 'io:update-key', 'down', false 39 | -------------------------------------------------------------------------------- /styles/mixins.scss: -------------------------------------------------------------------------------- 1 | @import "variables"; 2 | 3 | // Word break 4 | @mixin break-word($width: 0) { 5 | @if $width != 0 { 6 | width: $width; 7 | } 8 | word-wrap: break-word; 9 | word-break: break-all; 10 | } 11 | -------------------------------------------------------------------------------- /styles/style.scss: -------------------------------------------------------------------------------- 1 | $fa-font-path: "../node_modules/font-awesome/fonts"; 2 | @import "node_modules/font-awesome/scss/font-awesome"; 3 | @import "variables"; 4 | 5 | // Normalize (Should be replaced by normalize.css.) 6 | 7 | body { 8 | margin: 0; 9 | -webkit-font-smoothing: antialiased; 10 | } 11 | 12 | select { 13 | color: inherit; 14 | font: inherit; 15 | margin: 0; 16 | } 17 | 18 | ul { 19 | list-style-type: none; 20 | padding-left: 2px; 21 | } 22 | 23 | input { 24 | outline: 0; 25 | } 26 | 27 | // Styles 28 | 29 | // Common styles 30 | 31 | html,body { 32 | height: 100%; 33 | } 34 | 35 | body { 36 | width: 100%; 37 | overflow: hidden; 38 | font-family: "Helvetica Neue",Helvetica,'游ゴシック', YuGothic, "ヒラギノ角ゴ ProN W3","Hiragino Kaku Gothic ProN","メイリオ",Meiryo,sans-serif; 39 | } 40 | -------------------------------------------------------------------------------- /styles/variables.scss: -------------------------------------------------------------------------------- 1 | // Variables 2 | 3 | // Typography 4 | $text-color: #4a4a4a; 5 | $code-color: $text-color; 6 | $code-bg: #f7f7f7; 7 | $code-font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; 8 | $bullet: '\002022'; 9 | $italic-font-family: 'Helvetica Neue', Helvetica, 'ヒラギノ角ゴ ProN W3', 'Hiragino Kaku Gothic ProN', '游ゴシック', YuGothic, sans-serif; 10 | -------------------------------------------------------------------------------- /test/components/menu-test.coffee: -------------------------------------------------------------------------------- 1 | require '../test-helper' 2 | component = require '../../src/components/menu' 3 | element = React.createFactory component 4 | 5 | describe 'components/menu', -> 6 | describe '#render', -> 7 | it 'should validate output', -> 8 | rendered = React.renderToString(element {}) 9 | -------------------------------------------------------------------------------- /test/components/opening-test.coffee: -------------------------------------------------------------------------------- 1 | require '../test-helper' 2 | component = require '../../src/components/opening' 3 | element = React.createFactory component 4 | 5 | describe 'components/opening', -> 6 | describe '#render', -> 7 | it 'should validate output', -> 8 | rendered = React.renderToString(element {}) 9 | -------------------------------------------------------------------------------- /test/scenes/menu-test.coffee: -------------------------------------------------------------------------------- 1 | require '../test-helper' 2 | Scene = require '../../src/scenes/menu' 3 | 4 | describe 'scenes/menu', -> 5 | it 'should be written', -> 6 | new Scene 7 | -------------------------------------------------------------------------------- /test/scenes/opening-test.coffee: -------------------------------------------------------------------------------- 1 | require '../test-helper' 2 | Scene = require '../../src/scenes/opening' 3 | 4 | describe 'scenes/opening', -> 5 | it 'should be written', -> 6 | new Scene 7 | -------------------------------------------------------------------------------- /test/test-helper.coffee: -------------------------------------------------------------------------------- 1 | global = require 'global' 2 | global.Promise = require 'bluebird' 3 | global.React = require 'react' 4 | global.Overworld = require 'overworld' 5 | Overworld.setReact React 6 | 7 | # require jade 8 | jade = require('react-jade') 9 | require.extensions['.jade'] = (module, filename) -> 10 | module.exports = jade.compileFile(filename) 11 | 12 | global.assert = require 'assert' 13 | global.ok = assert.ok 14 | global.equal = assert.equal 15 | global.deepEqual = assert.deepEqual 16 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | var webpack = require("webpack"); 2 | var path = require('path'); 3 | 4 | module.exports = { 5 | // entry: './lib/index.js', 6 | 7 | output: { 8 | filename: 'public/bundle.js' 9 | }, 10 | 11 | module: { 12 | loaders: [ 13 | { test: /\.coffee$/, loader: "coffee" }, 14 | { test: /\.jade$/, loader: "react-jade-loader" } 15 | ] 16 | }, 17 | 18 | resolve: { 19 | root: [], 20 | extensions: ["", ".coffee", ".js"] 21 | } 22 | } 23 | --------------------------------------------------------------------------------