├── .gitignore ├── LICENSE ├── Procfile ├── README.md ├── defold ├── assets │ ├── atlas │ │ └── game.atlas │ ├── fonts │ │ ├── JennaSue.ttf │ │ ├── jenna-large.font │ │ └── jenna-normal.font │ ├── gfx │ │ ├── background.jpg │ │ ├── board.png │ │ ├── clock-icon.png │ │ ├── colyseus.png │ │ └── logo.png │ └── gui │ │ ├── game.gui │ │ └── title.gui ├── game.project ├── input │ └── game.input_binding ├── scenes │ ├── container.collection │ ├── game.collection │ └── title.collection └── scripts │ ├── game_controller.script │ ├── gui │ ├── game.gui_script │ └── title.gui_script │ └── proxy_controller.script ├── javascript-pixi ├── package.json └── src │ ├── Application.js │ ├── components │ └── Board.js │ ├── core │ ├── LocalStorage.js │ └── SceneManager.js │ ├── favicon.ico │ ├── favicon.png │ ├── fonts │ ├── JennaSue.afm │ ├── JennaSue.eot │ ├── JennaSue.otf │ ├── JennaSue.svg │ ├── JennaSue.ttf │ ├── JennaSue.woff │ └── JennaSue.woff2 │ ├── images │ ├── background.jpg │ ├── board.png │ ├── clock-icon.png │ ├── colyseus.png │ └── logo.png │ ├── index.html │ ├── main.js │ └── screens │ ├── EndGameScreen.js │ ├── GameScreen.js │ └── TitleScreen.js ├── layout.psd ├── package.json └── server ├── index.ts ├── package.json ├── rooms └── tictactoe.ts └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | bundle.js 3 | .DS_Store 4 | package-lock.json 5 | 6 | /defold/.internal 7 | /defold/build 8 | /defold/.externalToolBuilders 9 | 10 | javascript-pixi/.parcel-cache/ 11 | javascript-pixi/dist/ 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016-2018 Endel Dreyer 2 | 3 | MIT License: 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: npm start 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tic-Tac-Toe Multiplayer 2 | 3 | Turn-based demonstration project using Colyseus. 4 | 5 | This repository has: 6 | - `server/`: The Colyseus server, with tic-tac-toe room implementation. 7 | - `javascript-pixi/`: A JavaScript/PixiJS client-side project. 8 | - `defold/`: A [Defold](https://defold.com/) client-side project. 9 | 10 | # Running the server locally 11 | 12 | You'll need two terminal windows open to be able to run this. One for the 13 | client, and one for the server: 14 | 15 | **Running the server** 16 | 17 | ``` 18 | cd server 19 | npm install 20 | npm start 21 | ``` 22 | 23 | **Important files to have look at** 24 | 25 | - [registering room](server/index.ts#L20) 26 | - [room and state implementation](server/rooms/tictactoe.ts) 27 | 28 | ## JavaScript / PixiJS 29 | 30 | The JavaScript/PixiJS example uses: 31 | 32 | - [colyseus](https://colyseus.io) 33 | - [pixi.js v4](http://npmjs.com/package/pixi.js) 34 | - [parcel](http://npmjs.com/package/parcel) 35 | 36 | **Running the JavaScript/PixiJS client** 37 | 38 | ``` 39 | cd javascript-pixi 40 | npm install 41 | npm start 42 | ``` 43 | 44 | **Important files to have look at** 45 | 46 | - [room connection](javascript-pixi/src/screens/GameScreen.js#L36) 47 | - [receiving state updates](javascript-pixi/src/screens/GameScreen.js#L39-L63) 48 | 49 | ## Defold Engine 50 | 51 | The Defold project was made by [Selim Anaç](https://github.com/selimanac/). 52 | 53 | ## License 54 | 55 | MIT 56 | -------------------------------------------------------------------------------- /defold/assets/atlas/game.atlas: -------------------------------------------------------------------------------- 1 | images { 2 | image: "/assets/gfx/background.jpg" 3 | } 4 | images { 5 | image: "/assets/gfx/board.png" 6 | } 7 | images { 8 | image: "/assets/gfx/clock-icon.png" 9 | } 10 | images { 11 | image: "/assets/gfx/colyseus.png" 12 | } 13 | images { 14 | image: "/assets/gfx/logo.png" 15 | } 16 | margin: 0 17 | extrude_borders: 1 18 | inner_padding: 0 19 | -------------------------------------------------------------------------------- /defold/assets/fonts/JennaSue.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endel/colyseus-tic-tac-toe/bf7b31f5e256bee056bc468dc27ed6fbd0c93cea/defold/assets/fonts/JennaSue.ttf -------------------------------------------------------------------------------- /defold/assets/fonts/jenna-large.font: -------------------------------------------------------------------------------- 1 | font: "/assets/fonts/JennaSue.ttf" 2 | material: "/builtins/fonts/font.material" 3 | size: 100 4 | antialias: 1 5 | alpha: 1.0 6 | outline_alpha: 0.0 7 | outline_width: 0.0 8 | shadow_alpha: 0.0 9 | shadow_blur: 0 10 | shadow_x: 0.0 11 | shadow_y: 0.0 12 | extra_characters: "" 13 | output_format: TYPE_BITMAP 14 | all_chars: false 15 | cache_width: 0 16 | cache_height: 0 17 | -------------------------------------------------------------------------------- /defold/assets/fonts/jenna-normal.font: -------------------------------------------------------------------------------- 1 | font: "/assets/fonts/JennaSue.ttf" 2 | material: "/builtins/fonts/font.material" 3 | size: 60 4 | antialias: 1 5 | alpha: 1.0 6 | outline_alpha: 0.0 7 | outline_width: 0.0 8 | shadow_alpha: 0.0 9 | shadow_blur: 0 10 | shadow_x: 0.0 11 | shadow_y: 0.0 12 | extra_characters: "" 13 | output_format: TYPE_BITMAP 14 | all_chars: false 15 | cache_width: 0 16 | cache_height: 0 17 | -------------------------------------------------------------------------------- /defold/assets/gfx/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endel/colyseus-tic-tac-toe/bf7b31f5e256bee056bc468dc27ed6fbd0c93cea/defold/assets/gfx/background.jpg -------------------------------------------------------------------------------- /defold/assets/gfx/board.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endel/colyseus-tic-tac-toe/bf7b31f5e256bee056bc468dc27ed6fbd0c93cea/defold/assets/gfx/board.png -------------------------------------------------------------------------------- /defold/assets/gfx/clock-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endel/colyseus-tic-tac-toe/bf7b31f5e256bee056bc468dc27ed6fbd0c93cea/defold/assets/gfx/clock-icon.png -------------------------------------------------------------------------------- /defold/assets/gfx/colyseus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endel/colyseus-tic-tac-toe/bf7b31f5e256bee056bc468dc27ed6fbd0c93cea/defold/assets/gfx/colyseus.png -------------------------------------------------------------------------------- /defold/assets/gfx/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endel/colyseus-tic-tac-toe/bf7b31f5e256bee056bc468dc27ed6fbd0c93cea/defold/assets/gfx/logo.png -------------------------------------------------------------------------------- /defold/assets/gui/game.gui: -------------------------------------------------------------------------------- 1 | script: "/scripts/gui/game.gui_script" 2 | fonts { 3 | name: "jenna-normal" 4 | font: "/assets/fonts/jenna-normal.font" 5 | } 6 | fonts { 7 | name: "jenna-large" 8 | font: "/assets/fonts/jenna-large.font" 9 | } 10 | textures { 11 | name: "game" 12 | texture: "/assets/atlas/game.atlas" 13 | } 14 | background_color { 15 | x: 0.0 16 | y: 0.0 17 | z: 0.0 18 | w: 0.0 19 | } 20 | nodes { 21 | position { 22 | x: 50.0 23 | y: 320.0 24 | z: 0.0 25 | w: 1.0 26 | } 27 | rotation { 28 | x: 0.0 29 | y: 0.0 30 | z: 0.0 31 | w: 1.0 32 | } 33 | scale { 34 | x: 1.0 35 | y: 1.0 36 | z: 1.0 37 | w: 1.0 38 | } 39 | size { 40 | x: 568.0 41 | y: 535.0 42 | z: 0.0 43 | w: 1.0 44 | } 45 | color { 46 | x: 1.0 47 | y: 1.0 48 | z: 1.0 49 | w: 1.0 50 | } 51 | type: TYPE_BOX 52 | blend_mode: BLEND_MODE_ALPHA 53 | texture: "game/board" 54 | id: "grid" 55 | xanchor: XANCHOR_LEFT 56 | yanchor: YANCHOR_NONE 57 | pivot: PIVOT_W 58 | adjust_mode: ADJUST_MODE_FIT 59 | layer: "static" 60 | inherit_alpha: true 61 | slice9 { 62 | x: 0.0 63 | y: 0.0 64 | z: 0.0 65 | w: 0.0 66 | } 67 | clipping_mode: CLIPPING_MODE_NONE 68 | clipping_visible: true 69 | clipping_inverted: false 70 | alpha: 1.0 71 | template_node_child: false 72 | size_mode: SIZE_MODE_AUTO 73 | } 74 | nodes { 75 | position { 76 | x: 90.0 77 | y: 180.0 78 | z: 0.0 79 | w: 1.0 80 | } 81 | rotation { 82 | x: 0.0 83 | y: 0.0 84 | z: 0.0 85 | w: 1.0 86 | } 87 | scale { 88 | x: 1.0 89 | y: 1.0 90 | z: 1.0 91 | w: 1.0 92 | } 93 | size { 94 | x: 150.0 95 | y: 150.0 96 | z: 0.0 97 | w: 1.0 98 | } 99 | color { 100 | x: 1.0 101 | y: 1.0 102 | z: 1.0 103 | w: 1.0 104 | } 105 | type: TYPE_BOX 106 | blend_mode: BLEND_MODE_ALPHA 107 | texture: "" 108 | id: "box-1" 109 | xanchor: XANCHOR_NONE 110 | yanchor: YANCHOR_NONE 111 | pivot: PIVOT_CENTER 112 | adjust_mode: ADJUST_MODE_FIT 113 | parent: "grid" 114 | layer: "alpha" 115 | inherit_alpha: true 116 | slice9 { 117 | x: 0.0 118 | y: 0.0 119 | z: 0.0 120 | w: 0.0 121 | } 122 | clipping_mode: CLIPPING_MODE_NONE 123 | clipping_visible: true 124 | clipping_inverted: false 125 | alpha: 0.0 126 | template_node_child: false 127 | size_mode: SIZE_MODE_MANUAL 128 | } 129 | nodes { 130 | position { 131 | x: 0.0 132 | y: 0.0 133 | z: 0.0 134 | w: 1.0 135 | } 136 | rotation { 137 | x: 0.0 138 | y: 0.0 139 | z: 0.0 140 | w: 1.0 141 | } 142 | scale { 143 | x: 1.0 144 | y: 1.0 145 | z: 1.0 146 | w: 1.0 147 | } 148 | size { 149 | x: 200.0 150 | y: 100.0 151 | z: 0.0 152 | w: 1.0 153 | } 154 | color { 155 | x: 0.0 156 | y: 0.0 157 | z: 0.0 158 | w: 1.0 159 | } 160 | type: TYPE_TEXT 161 | blend_mode: BLEND_MODE_ALPHA 162 | text: "0" 163 | font: "jenna-large" 164 | id: "text-1" 165 | xanchor: XANCHOR_NONE 166 | yanchor: YANCHOR_NONE 167 | pivot: PIVOT_CENTER 168 | outline { 169 | x: 1.0 170 | y: 1.0 171 | z: 1.0 172 | w: 1.0 173 | } 174 | shadow { 175 | x: 1.0 176 | y: 1.0 177 | z: 1.0 178 | w: 1.0 179 | } 180 | adjust_mode: ADJUST_MODE_FIT 181 | line_break: false 182 | parent: "box-1" 183 | layer: "text" 184 | inherit_alpha: false 185 | alpha: 1.0 186 | outline_alpha: 1.0 187 | shadow_alpha: 1.0 188 | template_node_child: false 189 | text_leading: 1.0 190 | text_tracking: 0.0 191 | } 192 | nodes { 193 | position { 194 | x: 280.0 195 | y: 180.0 196 | z: 0.0 197 | w: 1.0 198 | } 199 | rotation { 200 | x: 0.0 201 | y: 0.0 202 | z: 0.0 203 | w: 1.0 204 | } 205 | scale { 206 | x: 1.0 207 | y: 1.0 208 | z: 1.0 209 | w: 1.0 210 | } 211 | size { 212 | x: 150.0 213 | y: 150.0 214 | z: 0.0 215 | w: 1.0 216 | } 217 | color { 218 | x: 1.0 219 | y: 1.0 220 | z: 1.0 221 | w: 1.0 222 | } 223 | type: TYPE_BOX 224 | blend_mode: BLEND_MODE_ALPHA 225 | texture: "" 226 | id: "box-2" 227 | xanchor: XANCHOR_NONE 228 | yanchor: YANCHOR_NONE 229 | pivot: PIVOT_CENTER 230 | adjust_mode: ADJUST_MODE_FIT 231 | parent: "grid" 232 | layer: "alpha" 233 | inherit_alpha: true 234 | slice9 { 235 | x: 0.0 236 | y: 0.0 237 | z: 0.0 238 | w: 0.0 239 | } 240 | clipping_mode: CLIPPING_MODE_NONE 241 | clipping_visible: true 242 | clipping_inverted: false 243 | alpha: 0.0 244 | template_node_child: false 245 | size_mode: SIZE_MODE_MANUAL 246 | } 247 | nodes { 248 | position { 249 | x: 0.0 250 | y: 0.0 251 | z: 0.0 252 | w: 1.0 253 | } 254 | rotation { 255 | x: 0.0 256 | y: 0.0 257 | z: 0.0 258 | w: 1.0 259 | } 260 | scale { 261 | x: 1.0 262 | y: 1.0 263 | z: 1.0 264 | w: 1.0 265 | } 266 | size { 267 | x: 200.0 268 | y: 100.0 269 | z: 0.0 270 | w: 1.0 271 | } 272 | color { 273 | x: 0.0 274 | y: 0.0 275 | z: 0.0 276 | w: 1.0 277 | } 278 | type: TYPE_TEXT 279 | blend_mode: BLEND_MODE_ALPHA 280 | text: "0" 281 | font: "jenna-large" 282 | id: "text-2" 283 | xanchor: XANCHOR_NONE 284 | yanchor: YANCHOR_NONE 285 | pivot: PIVOT_CENTER 286 | outline { 287 | x: 1.0 288 | y: 1.0 289 | z: 1.0 290 | w: 1.0 291 | } 292 | shadow { 293 | x: 1.0 294 | y: 1.0 295 | z: 1.0 296 | w: 1.0 297 | } 298 | adjust_mode: ADJUST_MODE_FIT 299 | line_break: false 300 | parent: "box-2" 301 | layer: "text" 302 | inherit_alpha: false 303 | alpha: 1.0 304 | outline_alpha: 1.0 305 | shadow_alpha: 1.0 306 | template_node_child: false 307 | text_leading: 1.0 308 | text_tracking: 0.0 309 | } 310 | nodes { 311 | position { 312 | x: 475.0 313 | y: 180.0 314 | z: 0.0 315 | w: 1.0 316 | } 317 | rotation { 318 | x: 0.0 319 | y: 0.0 320 | z: 0.0 321 | w: 1.0 322 | } 323 | scale { 324 | x: 1.0 325 | y: 1.0 326 | z: 1.0 327 | w: 1.0 328 | } 329 | size { 330 | x: 150.0 331 | y: 150.0 332 | z: 0.0 333 | w: 1.0 334 | } 335 | color { 336 | x: 1.0 337 | y: 1.0 338 | z: 1.0 339 | w: 1.0 340 | } 341 | type: TYPE_BOX 342 | blend_mode: BLEND_MODE_ALPHA 343 | texture: "" 344 | id: "box-3" 345 | xanchor: XANCHOR_NONE 346 | yanchor: YANCHOR_NONE 347 | pivot: PIVOT_CENTER 348 | adjust_mode: ADJUST_MODE_FIT 349 | parent: "grid" 350 | layer: "alpha" 351 | inherit_alpha: true 352 | slice9 { 353 | x: 0.0 354 | y: 0.0 355 | z: 0.0 356 | w: 0.0 357 | } 358 | clipping_mode: CLIPPING_MODE_NONE 359 | clipping_visible: true 360 | clipping_inverted: false 361 | alpha: 0.0 362 | template_node_child: false 363 | size_mode: SIZE_MODE_MANUAL 364 | } 365 | nodes { 366 | position { 367 | x: 0.0 368 | y: 0.0 369 | z: 0.0 370 | w: 1.0 371 | } 372 | rotation { 373 | x: 0.0 374 | y: 0.0 375 | z: 0.0 376 | w: 1.0 377 | } 378 | scale { 379 | x: 1.0 380 | y: 1.0 381 | z: 1.0 382 | w: 1.0 383 | } 384 | size { 385 | x: 200.0 386 | y: 100.0 387 | z: 0.0 388 | w: 1.0 389 | } 390 | color { 391 | x: 0.0 392 | y: 0.0 393 | z: 0.0 394 | w: 1.0 395 | } 396 | type: TYPE_TEXT 397 | blend_mode: BLEND_MODE_ALPHA 398 | text: "0" 399 | font: "jenna-large" 400 | id: "text-3" 401 | xanchor: XANCHOR_NONE 402 | yanchor: YANCHOR_NONE 403 | pivot: PIVOT_CENTER 404 | outline { 405 | x: 1.0 406 | y: 1.0 407 | z: 1.0 408 | w: 1.0 409 | } 410 | shadow { 411 | x: 1.0 412 | y: 1.0 413 | z: 1.0 414 | w: 1.0 415 | } 416 | adjust_mode: ADJUST_MODE_FIT 417 | line_break: false 418 | parent: "box-3" 419 | layer: "text" 420 | inherit_alpha: false 421 | alpha: 1.0 422 | outline_alpha: 1.0 423 | shadow_alpha: 1.0 424 | template_node_child: false 425 | text_leading: 1.0 426 | text_tracking: 0.0 427 | } 428 | nodes { 429 | position { 430 | x: 90.0 431 | y: -5.0 432 | z: 0.0 433 | w: 1.0 434 | } 435 | rotation { 436 | x: 0.0 437 | y: 0.0 438 | z: 0.0 439 | w: 1.0 440 | } 441 | scale { 442 | x: 1.0 443 | y: 1.0 444 | z: 1.0 445 | w: 1.0 446 | } 447 | size { 448 | x: 150.0 449 | y: 150.0 450 | z: 0.0 451 | w: 1.0 452 | } 453 | color { 454 | x: 1.0 455 | y: 1.0 456 | z: 1.0 457 | w: 1.0 458 | } 459 | type: TYPE_BOX 460 | blend_mode: BLEND_MODE_ALPHA 461 | texture: "" 462 | id: "box-4" 463 | xanchor: XANCHOR_NONE 464 | yanchor: YANCHOR_NONE 465 | pivot: PIVOT_CENTER 466 | adjust_mode: ADJUST_MODE_FIT 467 | parent: "grid" 468 | layer: "alpha" 469 | inherit_alpha: true 470 | slice9 { 471 | x: 0.0 472 | y: 0.0 473 | z: 0.0 474 | w: 0.0 475 | } 476 | clipping_mode: CLIPPING_MODE_NONE 477 | clipping_visible: true 478 | clipping_inverted: false 479 | alpha: 0.0 480 | template_node_child: false 481 | size_mode: SIZE_MODE_MANUAL 482 | } 483 | nodes { 484 | position { 485 | x: 0.0 486 | y: 0.0 487 | z: 0.0 488 | w: 1.0 489 | } 490 | rotation { 491 | x: 0.0 492 | y: 0.0 493 | z: 0.0 494 | w: 1.0 495 | } 496 | scale { 497 | x: 1.0 498 | y: 1.0 499 | z: 1.0 500 | w: 1.0 501 | } 502 | size { 503 | x: 200.0 504 | y: 100.0 505 | z: 0.0 506 | w: 1.0 507 | } 508 | color { 509 | x: 0.0 510 | y: 0.0 511 | z: 0.0 512 | w: 1.0 513 | } 514 | type: TYPE_TEXT 515 | blend_mode: BLEND_MODE_ALPHA 516 | text: "0" 517 | font: "jenna-large" 518 | id: "text-4" 519 | xanchor: XANCHOR_NONE 520 | yanchor: YANCHOR_NONE 521 | pivot: PIVOT_CENTER 522 | outline { 523 | x: 1.0 524 | y: 1.0 525 | z: 1.0 526 | w: 1.0 527 | } 528 | shadow { 529 | x: 1.0 530 | y: 1.0 531 | z: 1.0 532 | w: 1.0 533 | } 534 | adjust_mode: ADJUST_MODE_FIT 535 | line_break: false 536 | parent: "box-4" 537 | layer: "text" 538 | inherit_alpha: false 539 | alpha: 1.0 540 | outline_alpha: 1.0 541 | shadow_alpha: 1.0 542 | template_node_child: false 543 | text_leading: 1.0 544 | text_tracking: 0.0 545 | } 546 | nodes { 547 | position { 548 | x: 280.0 549 | y: -5.0 550 | z: 0.0 551 | w: 1.0 552 | } 553 | rotation { 554 | x: 0.0 555 | y: 0.0 556 | z: 0.0 557 | w: 1.0 558 | } 559 | scale { 560 | x: 1.0 561 | y: 1.0 562 | z: 1.0 563 | w: 1.0 564 | } 565 | size { 566 | x: 150.0 567 | y: 150.0 568 | z: 0.0 569 | w: 1.0 570 | } 571 | color { 572 | x: 1.0 573 | y: 1.0 574 | z: 1.0 575 | w: 1.0 576 | } 577 | type: TYPE_BOX 578 | blend_mode: BLEND_MODE_ALPHA 579 | texture: "" 580 | id: "box-5" 581 | xanchor: XANCHOR_NONE 582 | yanchor: YANCHOR_NONE 583 | pivot: PIVOT_CENTER 584 | adjust_mode: ADJUST_MODE_FIT 585 | parent: "grid" 586 | layer: "alpha" 587 | inherit_alpha: true 588 | slice9 { 589 | x: 0.0 590 | y: 0.0 591 | z: 0.0 592 | w: 0.0 593 | } 594 | clipping_mode: CLIPPING_MODE_NONE 595 | clipping_visible: true 596 | clipping_inverted: false 597 | alpha: 0.0 598 | template_node_child: false 599 | size_mode: SIZE_MODE_MANUAL 600 | } 601 | nodes { 602 | position { 603 | x: 0.0 604 | y: 0.0 605 | z: 0.0 606 | w: 1.0 607 | } 608 | rotation { 609 | x: 0.0 610 | y: 0.0 611 | z: 0.0 612 | w: 1.0 613 | } 614 | scale { 615 | x: 1.0 616 | y: 1.0 617 | z: 1.0 618 | w: 1.0 619 | } 620 | size { 621 | x: 200.0 622 | y: 100.0 623 | z: 0.0 624 | w: 1.0 625 | } 626 | color { 627 | x: 0.0 628 | y: 0.0 629 | z: 0.0 630 | w: 1.0 631 | } 632 | type: TYPE_TEXT 633 | blend_mode: BLEND_MODE_ALPHA 634 | text: "0" 635 | font: "jenna-large" 636 | id: "text-5" 637 | xanchor: XANCHOR_NONE 638 | yanchor: YANCHOR_NONE 639 | pivot: PIVOT_CENTER 640 | outline { 641 | x: 1.0 642 | y: 1.0 643 | z: 1.0 644 | w: 1.0 645 | } 646 | shadow { 647 | x: 1.0 648 | y: 1.0 649 | z: 1.0 650 | w: 1.0 651 | } 652 | adjust_mode: ADJUST_MODE_FIT 653 | line_break: false 654 | parent: "box-5" 655 | layer: "text" 656 | inherit_alpha: false 657 | alpha: 1.0 658 | outline_alpha: 1.0 659 | shadow_alpha: 1.0 660 | template_node_child: false 661 | text_leading: 1.0 662 | text_tracking: 0.0 663 | } 664 | nodes { 665 | position { 666 | x: 475.0 667 | y: -5.0 668 | z: 0.0 669 | w: 1.0 670 | } 671 | rotation { 672 | x: 0.0 673 | y: 0.0 674 | z: 0.0 675 | w: 1.0 676 | } 677 | scale { 678 | x: 1.0 679 | y: 1.0 680 | z: 1.0 681 | w: 1.0 682 | } 683 | size { 684 | x: 150.0 685 | y: 150.0 686 | z: 0.0 687 | w: 1.0 688 | } 689 | color { 690 | x: 1.0 691 | y: 1.0 692 | z: 1.0 693 | w: 1.0 694 | } 695 | type: TYPE_BOX 696 | blend_mode: BLEND_MODE_ALPHA 697 | texture: "" 698 | id: "box-6" 699 | xanchor: XANCHOR_NONE 700 | yanchor: YANCHOR_NONE 701 | pivot: PIVOT_CENTER 702 | adjust_mode: ADJUST_MODE_FIT 703 | parent: "grid" 704 | layer: "alpha" 705 | inherit_alpha: true 706 | slice9 { 707 | x: 0.0 708 | y: 0.0 709 | z: 0.0 710 | w: 0.0 711 | } 712 | clipping_mode: CLIPPING_MODE_NONE 713 | clipping_visible: true 714 | clipping_inverted: false 715 | alpha: 0.0 716 | template_node_child: false 717 | size_mode: SIZE_MODE_MANUAL 718 | } 719 | nodes { 720 | position { 721 | x: 0.0 722 | y: 0.0 723 | z: 0.0 724 | w: 1.0 725 | } 726 | rotation { 727 | x: 0.0 728 | y: 0.0 729 | z: 0.0 730 | w: 1.0 731 | } 732 | scale { 733 | x: 1.0 734 | y: 1.0 735 | z: 1.0 736 | w: 1.0 737 | } 738 | size { 739 | x: 200.0 740 | y: 100.0 741 | z: 0.0 742 | w: 1.0 743 | } 744 | color { 745 | x: 0.0 746 | y: 0.0 747 | z: 0.0 748 | w: 1.0 749 | } 750 | type: TYPE_TEXT 751 | blend_mode: BLEND_MODE_ALPHA 752 | text: "0" 753 | font: "jenna-large" 754 | id: "text-6" 755 | xanchor: XANCHOR_NONE 756 | yanchor: YANCHOR_NONE 757 | pivot: PIVOT_CENTER 758 | outline { 759 | x: 1.0 760 | y: 1.0 761 | z: 1.0 762 | w: 1.0 763 | } 764 | shadow { 765 | x: 1.0 766 | y: 1.0 767 | z: 1.0 768 | w: 1.0 769 | } 770 | adjust_mode: ADJUST_MODE_FIT 771 | line_break: false 772 | parent: "box-6" 773 | layer: "text" 774 | inherit_alpha: false 775 | alpha: 1.0 776 | outline_alpha: 1.0 777 | shadow_alpha: 1.0 778 | template_node_child: false 779 | text_leading: 1.0 780 | text_tracking: 0.0 781 | } 782 | nodes { 783 | position { 784 | x: 90.0 785 | y: -187.0 786 | z: 0.0 787 | w: 1.0 788 | } 789 | rotation { 790 | x: 0.0 791 | y: 0.0 792 | z: 0.0 793 | w: 1.0 794 | } 795 | scale { 796 | x: 1.0 797 | y: 1.0 798 | z: 1.0 799 | w: 1.0 800 | } 801 | size { 802 | x: 150.0 803 | y: 150.0 804 | z: 0.0 805 | w: 1.0 806 | } 807 | color { 808 | x: 1.0 809 | y: 1.0 810 | z: 1.0 811 | w: 1.0 812 | } 813 | type: TYPE_BOX 814 | blend_mode: BLEND_MODE_ALPHA 815 | texture: "" 816 | id: "box-7" 817 | xanchor: XANCHOR_NONE 818 | yanchor: YANCHOR_NONE 819 | pivot: PIVOT_CENTER 820 | adjust_mode: ADJUST_MODE_FIT 821 | parent: "grid" 822 | layer: "alpha" 823 | inherit_alpha: true 824 | slice9 { 825 | x: 0.0 826 | y: 0.0 827 | z: 0.0 828 | w: 0.0 829 | } 830 | clipping_mode: CLIPPING_MODE_NONE 831 | clipping_visible: true 832 | clipping_inverted: false 833 | alpha: 0.0 834 | template_node_child: false 835 | size_mode: SIZE_MODE_MANUAL 836 | } 837 | nodes { 838 | position { 839 | x: 0.0 840 | y: 0.0 841 | z: 0.0 842 | w: 1.0 843 | } 844 | rotation { 845 | x: 0.0 846 | y: 0.0 847 | z: 0.0 848 | w: 1.0 849 | } 850 | scale { 851 | x: 1.0 852 | y: 1.0 853 | z: 1.0 854 | w: 1.0 855 | } 856 | size { 857 | x: 200.0 858 | y: 100.0 859 | z: 0.0 860 | w: 1.0 861 | } 862 | color { 863 | x: 0.0 864 | y: 0.0 865 | z: 0.0 866 | w: 1.0 867 | } 868 | type: TYPE_TEXT 869 | blend_mode: BLEND_MODE_ALPHA 870 | text: "0" 871 | font: "jenna-large" 872 | id: "text-7" 873 | xanchor: XANCHOR_NONE 874 | yanchor: YANCHOR_NONE 875 | pivot: PIVOT_CENTER 876 | outline { 877 | x: 1.0 878 | y: 1.0 879 | z: 1.0 880 | w: 1.0 881 | } 882 | shadow { 883 | x: 1.0 884 | y: 1.0 885 | z: 1.0 886 | w: 1.0 887 | } 888 | adjust_mode: ADJUST_MODE_FIT 889 | line_break: false 890 | parent: "box-7" 891 | layer: "text" 892 | inherit_alpha: false 893 | alpha: 1.0 894 | outline_alpha: 1.0 895 | shadow_alpha: 1.0 896 | template_node_child: false 897 | text_leading: 1.0 898 | text_tracking: 0.0 899 | } 900 | nodes { 901 | position { 902 | x: 280.0 903 | y: -187.0 904 | z: 0.0 905 | w: 1.0 906 | } 907 | rotation { 908 | x: 0.0 909 | y: 0.0 910 | z: 0.0 911 | w: 1.0 912 | } 913 | scale { 914 | x: 1.0 915 | y: 1.0 916 | z: 1.0 917 | w: 1.0 918 | } 919 | size { 920 | x: 150.0 921 | y: 150.0 922 | z: 0.0 923 | w: 1.0 924 | } 925 | color { 926 | x: 1.0 927 | y: 1.0 928 | z: 1.0 929 | w: 1.0 930 | } 931 | type: TYPE_BOX 932 | blend_mode: BLEND_MODE_ALPHA 933 | texture: "" 934 | id: "box-8" 935 | xanchor: XANCHOR_NONE 936 | yanchor: YANCHOR_NONE 937 | pivot: PIVOT_CENTER 938 | adjust_mode: ADJUST_MODE_FIT 939 | parent: "grid" 940 | layer: "alpha" 941 | inherit_alpha: true 942 | slice9 { 943 | x: 0.0 944 | y: 0.0 945 | z: 0.0 946 | w: 0.0 947 | } 948 | clipping_mode: CLIPPING_MODE_NONE 949 | clipping_visible: true 950 | clipping_inverted: false 951 | alpha: 0.0 952 | template_node_child: false 953 | size_mode: SIZE_MODE_MANUAL 954 | } 955 | nodes { 956 | position { 957 | x: 0.0 958 | y: 0.0 959 | z: 0.0 960 | w: 1.0 961 | } 962 | rotation { 963 | x: 0.0 964 | y: 0.0 965 | z: 0.0 966 | w: 1.0 967 | } 968 | scale { 969 | x: 1.0 970 | y: 1.0 971 | z: 1.0 972 | w: 1.0 973 | } 974 | size { 975 | x: 200.0 976 | y: 100.0 977 | z: 0.0 978 | w: 1.0 979 | } 980 | color { 981 | x: 0.0 982 | y: 0.0 983 | z: 0.0 984 | w: 1.0 985 | } 986 | type: TYPE_TEXT 987 | blend_mode: BLEND_MODE_ALPHA 988 | text: "0" 989 | font: "jenna-large" 990 | id: "text-8" 991 | xanchor: XANCHOR_NONE 992 | yanchor: YANCHOR_NONE 993 | pivot: PIVOT_CENTER 994 | outline { 995 | x: 1.0 996 | y: 1.0 997 | z: 1.0 998 | w: 1.0 999 | } 1000 | shadow { 1001 | x: 1.0 1002 | y: 1.0 1003 | z: 1.0 1004 | w: 1.0 1005 | } 1006 | adjust_mode: ADJUST_MODE_FIT 1007 | line_break: false 1008 | parent: "box-8" 1009 | layer: "text" 1010 | inherit_alpha: false 1011 | alpha: 1.0 1012 | outline_alpha: 1.0 1013 | shadow_alpha: 1.0 1014 | template_node_child: false 1015 | text_leading: 1.0 1016 | text_tracking: 0.0 1017 | } 1018 | nodes { 1019 | position { 1020 | x: 475.0 1021 | y: -187.0 1022 | z: 0.0 1023 | w: 1.0 1024 | } 1025 | rotation { 1026 | x: 0.0 1027 | y: 0.0 1028 | z: 0.0 1029 | w: 1.0 1030 | } 1031 | scale { 1032 | x: 1.0 1033 | y: 1.0 1034 | z: 1.0 1035 | w: 1.0 1036 | } 1037 | size { 1038 | x: 150.0 1039 | y: 150.0 1040 | z: 0.0 1041 | w: 1.0 1042 | } 1043 | color { 1044 | x: 1.0 1045 | y: 1.0 1046 | z: 1.0 1047 | w: 1.0 1048 | } 1049 | type: TYPE_BOX 1050 | blend_mode: BLEND_MODE_ALPHA 1051 | texture: "" 1052 | id: "box-9" 1053 | xanchor: XANCHOR_NONE 1054 | yanchor: YANCHOR_NONE 1055 | pivot: PIVOT_CENTER 1056 | adjust_mode: ADJUST_MODE_FIT 1057 | parent: "grid" 1058 | layer: "alpha" 1059 | inherit_alpha: true 1060 | slice9 { 1061 | x: 0.0 1062 | y: 0.0 1063 | z: 0.0 1064 | w: 0.0 1065 | } 1066 | clipping_mode: CLIPPING_MODE_NONE 1067 | clipping_visible: true 1068 | clipping_inverted: false 1069 | alpha: 0.0 1070 | template_node_child: false 1071 | size_mode: SIZE_MODE_MANUAL 1072 | } 1073 | nodes { 1074 | position { 1075 | x: 0.0 1076 | y: 0.0 1077 | z: 0.0 1078 | w: 1.0 1079 | } 1080 | rotation { 1081 | x: 0.0 1082 | y: 0.0 1083 | z: 0.0 1084 | w: 1.0 1085 | } 1086 | scale { 1087 | x: 1.0 1088 | y: 1.0 1089 | z: 1.0 1090 | w: 1.0 1091 | } 1092 | size { 1093 | x: 200.0 1094 | y: 100.0 1095 | z: 0.0 1096 | w: 1.0 1097 | } 1098 | color { 1099 | x: 0.0 1100 | y: 0.0 1101 | z: 0.0 1102 | w: 1.0 1103 | } 1104 | type: TYPE_TEXT 1105 | blend_mode: BLEND_MODE_ALPHA 1106 | text: "0" 1107 | font: "jenna-large" 1108 | id: "text-9" 1109 | xanchor: XANCHOR_NONE 1110 | yanchor: YANCHOR_NONE 1111 | pivot: PIVOT_CENTER 1112 | outline { 1113 | x: 1.0 1114 | y: 1.0 1115 | z: 1.0 1116 | w: 1.0 1117 | } 1118 | shadow { 1119 | x: 1.0 1120 | y: 1.0 1121 | z: 1.0 1122 | w: 1.0 1123 | } 1124 | adjust_mode: ADJUST_MODE_FIT 1125 | line_break: false 1126 | parent: "box-9" 1127 | layer: "text" 1128 | inherit_alpha: false 1129 | alpha: 1.0 1130 | outline_alpha: 1.0 1131 | shadow_alpha: 1.0 1132 | template_node_child: false 1133 | text_leading: 1.0 1134 | text_tracking: 0.0 1135 | } 1136 | nodes { 1137 | position { 1138 | x: 911.0 1139 | y: 550.0 1140 | z: 0.0 1141 | w: 1.0 1142 | } 1143 | rotation { 1144 | x: 0.0 1145 | y: 0.0 1146 | z: 0.0 1147 | w: 1.0 1148 | } 1149 | scale { 1150 | x: 1.0 1151 | y: 1.0 1152 | z: 1.0 1153 | w: 1.0 1154 | } 1155 | size { 1156 | x: 72.0 1157 | y: 72.0 1158 | z: 0.0 1159 | w: 1.0 1160 | } 1161 | color { 1162 | x: 1.0 1163 | y: 1.0 1164 | z: 1.0 1165 | w: 1.0 1166 | } 1167 | type: TYPE_BOX 1168 | blend_mode: BLEND_MODE_ALPHA 1169 | texture: "game/clock-icon" 1170 | id: "time" 1171 | xanchor: XANCHOR_RIGHT 1172 | yanchor: YANCHOR_NONE 1173 | pivot: PIVOT_E 1174 | adjust_mode: ADJUST_MODE_FIT 1175 | layer: "static" 1176 | inherit_alpha: true 1177 | slice9 { 1178 | x: 0.0 1179 | y: 0.0 1180 | z: 0.0 1181 | w: 0.0 1182 | } 1183 | clipping_mode: CLIPPING_MODE_NONE 1184 | clipping_visible: true 1185 | clipping_inverted: false 1186 | alpha: 1.0 1187 | template_node_child: false 1188 | size_mode: SIZE_MODE_AUTO 1189 | } 1190 | nodes { 1191 | position { 1192 | x: 830.0 1193 | y: 549.0 1194 | z: 0.0 1195 | w: 1.0 1196 | } 1197 | rotation { 1198 | x: 0.0 1199 | y: 0.0 1200 | z: 0.0 1201 | w: 1.0 1202 | } 1203 | scale { 1204 | x: 1.0 1205 | y: 1.0 1206 | z: 1.0 1207 | w: 1.0 1208 | } 1209 | size { 1210 | x: 200.0 1211 | y: 100.0 1212 | z: 0.0 1213 | w: 1.0 1214 | } 1215 | color { 1216 | x: 0.101960786 1217 | y: 0.101960786 1218 | z: 0.101960786 1219 | w: 1.0 1220 | } 1221 | type: TYPE_TEXT 1222 | blend_mode: BLEND_MODE_ALPHA 1223 | text: "10" 1224 | font: "jenna-normal" 1225 | id: "timer_txt" 1226 | xanchor: XANCHOR_RIGHT 1227 | yanchor: YANCHOR_NONE 1228 | pivot: PIVOT_E 1229 | outline { 1230 | x: 1.0 1231 | y: 1.0 1232 | z: 1.0 1233 | w: 1.0 1234 | } 1235 | shadow { 1236 | x: 1.0 1237 | y: 1.0 1238 | z: 1.0 1239 | w: 1.0 1240 | } 1241 | adjust_mode: ADJUST_MODE_FIT 1242 | line_break: false 1243 | layer: "text" 1244 | inherit_alpha: true 1245 | alpha: 1.0 1246 | outline_alpha: 1.0 1247 | shadow_alpha: 1.0 1248 | template_node_child: false 1249 | text_leading: 1.0 1250 | text_tracking: 0.0 1251 | } 1252 | nodes { 1253 | position { 1254 | x: 793.0 1255 | y: 320.0 1256 | z: 0.0 1257 | w: 1.0 1258 | } 1259 | rotation { 1260 | x: 0.0 1261 | y: 0.0 1262 | z: 0.0 1263 | w: 1.0 1264 | } 1265 | scale { 1266 | x: 1.0 1267 | y: 1.0 1268 | z: 1.0 1269 | w: 1.0 1270 | } 1271 | size { 1272 | x: 200.0 1273 | y: 100.0 1274 | z: 0.0 1275 | w: 1.0 1276 | } 1277 | color { 1278 | x: 0.101960786 1279 | y: 0.101960786 1280 | z: 0.101960786 1281 | w: 1.0 1282 | } 1283 | type: TYPE_TEXT 1284 | blend_mode: BLEND_MODE_ALPHA 1285 | text: "" 1286 | font: "jenna-normal" 1287 | id: "turn_txt" 1288 | xanchor: XANCHOR_NONE 1289 | yanchor: YANCHOR_NONE 1290 | pivot: PIVOT_CENTER 1291 | outline { 1292 | x: 1.0 1293 | y: 1.0 1294 | z: 1.0 1295 | w: 1.0 1296 | } 1297 | shadow { 1298 | x: 1.0 1299 | y: 1.0 1300 | z: 1.0 1301 | w: 1.0 1302 | } 1303 | adjust_mode: ADJUST_MODE_FIT 1304 | line_break: false 1305 | layer: "text" 1306 | inherit_alpha: true 1307 | alpha: 1.0 1308 | outline_alpha: 1.0 1309 | shadow_alpha: 1.0 1310 | template_node_child: false 1311 | text_leading: 1.0 1312 | text_tracking: 0.0 1313 | } 1314 | nodes { 1315 | position { 1316 | x: 475.0 1317 | y: 320.0 1318 | z: 0.0 1319 | w: 1.0 1320 | } 1321 | rotation { 1322 | x: 0.0 1323 | y: 0.0 1324 | z: 0.0 1325 | w: 1.0 1326 | } 1327 | scale { 1328 | x: 1.0 1329 | y: 1.0 1330 | z: 1.0 1331 | w: 1.0 1332 | } 1333 | size { 1334 | x: 1000.0 1335 | y: 900.0 1336 | z: 0.0 1337 | w: 1.0 1338 | } 1339 | color { 1340 | x: 1.0 1341 | y: 1.0 1342 | z: 1.0 1343 | w: 1.0 1344 | } 1345 | type: TYPE_BOX 1346 | blend_mode: BLEND_MODE_ALPHA 1347 | texture: "" 1348 | id: "waiting_box" 1349 | xanchor: XANCHOR_NONE 1350 | yanchor: YANCHOR_NONE 1351 | pivot: PIVOT_CENTER 1352 | adjust_mode: ADJUST_MODE_FIT 1353 | layer: "alpha" 1354 | inherit_alpha: false 1355 | slice9 { 1356 | x: 0.0 1357 | y: 0.0 1358 | z: 0.0 1359 | w: 0.0 1360 | } 1361 | clipping_mode: CLIPPING_MODE_NONE 1362 | clipping_visible: true 1363 | clipping_inverted: false 1364 | alpha: 1.0 1365 | template_node_child: false 1366 | size_mode: SIZE_MODE_MANUAL 1367 | } 1368 | nodes { 1369 | position { 1370 | x: 0.0 1371 | y: 0.0 1372 | z: 0.0 1373 | w: 1.0 1374 | } 1375 | rotation { 1376 | x: 0.0 1377 | y: 0.0 1378 | z: 0.0 1379 | w: 1.0 1380 | } 1381 | scale { 1382 | x: 1.2 1383 | y: 1.2 1384 | z: 1.2 1385 | w: 1.0 1386 | } 1387 | size { 1388 | x: 200.0 1389 | y: 100.0 1390 | z: 0.0 1391 | w: 1.0 1392 | } 1393 | color { 1394 | x: 0.101960786 1395 | y: 0.101960786 1396 | z: 0.101960786 1397 | w: 1.0 1398 | } 1399 | type: TYPE_TEXT 1400 | blend_mode: BLEND_MODE_ALPHA 1401 | text: "Waiting for opt..." 1402 | font: "jenna-normal" 1403 | id: "waiting_txt" 1404 | xanchor: XANCHOR_NONE 1405 | yanchor: YANCHOR_NONE 1406 | pivot: PIVOT_CENTER 1407 | outline { 1408 | x: 1.0 1409 | y: 1.0 1410 | z: 1.0 1411 | w: 1.0 1412 | } 1413 | shadow { 1414 | x: 1.0 1415 | y: 1.0 1416 | z: 1.0 1417 | w: 1.0 1418 | } 1419 | adjust_mode: ADJUST_MODE_FIT 1420 | line_break: false 1421 | parent: "waiting_box" 1422 | layer: "text" 1423 | inherit_alpha: false 1424 | alpha: 0.0 1425 | outline_alpha: 1.0 1426 | shadow_alpha: 1.0 1427 | template_node_child: false 1428 | text_leading: 1.0 1429 | text_tracking: 0.0 1430 | } 1431 | nodes { 1432 | position { 1433 | x: 0.0 1434 | y: -83.0 1435 | z: 0.0 1436 | w: 1.0 1437 | } 1438 | rotation { 1439 | x: 0.0 1440 | y: 0.0 1441 | z: 0.0 1442 | w: 1.0 1443 | } 1444 | scale { 1445 | x: 1.0 1446 | y: 1.0 1447 | z: 1.0 1448 | w: 1.0 1449 | } 1450 | size { 1451 | x: 200.0 1452 | y: 100.0 1453 | z: 0.0 1454 | w: 1.0 1455 | } 1456 | color { 1457 | x: 0.101960786 1458 | y: 0.101960786 1459 | z: 0.101960786 1460 | w: 1.0 1461 | } 1462 | type: TYPE_TEXT 1463 | blend_mode: BLEND_MODE_ALPHA 1464 | text: "Retry?" 1465 | font: "jenna-normal" 1466 | id: "retry_btn" 1467 | xanchor: XANCHOR_NONE 1468 | yanchor: YANCHOR_NONE 1469 | pivot: PIVOT_CENTER 1470 | outline { 1471 | x: 1.0 1472 | y: 1.0 1473 | z: 1.0 1474 | w: 1.0 1475 | } 1476 | shadow { 1477 | x: 1.0 1478 | y: 1.0 1479 | z: 1.0 1480 | w: 1.0 1481 | } 1482 | adjust_mode: ADJUST_MODE_FIT 1483 | line_break: false 1484 | parent: "waiting_box" 1485 | layer: "text" 1486 | inherit_alpha: false 1487 | alpha: 1.0 1488 | outline_alpha: 1.0 1489 | shadow_alpha: 1.0 1490 | template_node_child: false 1491 | text_leading: 1.0 1492 | text_tracking: 0.0 1493 | } 1494 | nodes { 1495 | position { 1496 | x: 475.0 1497 | y: 320.0 1498 | z: 0.0 1499 | w: 1.0 1500 | } 1501 | rotation { 1502 | x: 0.0 1503 | y: 0.0 1504 | z: 0.0 1505 | w: 1.0 1506 | } 1507 | scale { 1508 | x: 1.0 1509 | y: 1.0 1510 | z: 1.0 1511 | w: 1.0 1512 | } 1513 | size { 1514 | x: 1000.0 1515 | y: 900.0 1516 | z: 0.0 1517 | w: 1.0 1518 | } 1519 | color { 1520 | x: 1.0 1521 | y: 1.0 1522 | z: 1.0 1523 | w: 1.0 1524 | } 1525 | type: TYPE_BOX 1526 | blend_mode: BLEND_MODE_ALPHA 1527 | texture: "" 1528 | id: "result_box" 1529 | xanchor: XANCHOR_NONE 1530 | yanchor: YANCHOR_NONE 1531 | pivot: PIVOT_CENTER 1532 | adjust_mode: ADJUST_MODE_FIT 1533 | layer: "static" 1534 | inherit_alpha: false 1535 | slice9 { 1536 | x: 0.0 1537 | y: 0.0 1538 | z: 0.0 1539 | w: 0.0 1540 | } 1541 | clipping_mode: CLIPPING_MODE_NONE 1542 | clipping_visible: true 1543 | clipping_inverted: false 1544 | alpha: 1.0 1545 | template_node_child: false 1546 | size_mode: SIZE_MODE_MANUAL 1547 | } 1548 | nodes { 1549 | position { 1550 | x: 0.0 1551 | y: 98.0 1552 | z: 0.0 1553 | w: 1.0 1554 | } 1555 | rotation { 1556 | x: 0.0 1557 | y: 0.0 1558 | z: 0.0 1559 | w: 1.0 1560 | } 1561 | scale { 1562 | x: 1.2 1563 | y: 1.2 1564 | z: 1.2 1565 | w: 1.0 1566 | } 1567 | size { 1568 | x: 200.0 1569 | y: 100.0 1570 | z: 0.0 1571 | w: 1.0 1572 | } 1573 | color { 1574 | x: 0.101960786 1575 | y: 0.101960786 1576 | z: 0.101960786 1577 | w: 1.0 1578 | } 1579 | type: TYPE_TEXT 1580 | blend_mode: BLEND_MODE_ALPHA 1581 | text: "..." 1582 | font: "jenna-normal" 1583 | id: "result_txt" 1584 | xanchor: XANCHOR_NONE 1585 | yanchor: YANCHOR_NONE 1586 | pivot: PIVOT_CENTER 1587 | outline { 1588 | x: 1.0 1589 | y: 1.0 1590 | z: 1.0 1591 | w: 1.0 1592 | } 1593 | shadow { 1594 | x: 1.0 1595 | y: 1.0 1596 | z: 1.0 1597 | w: 1.0 1598 | } 1599 | adjust_mode: ADJUST_MODE_FIT 1600 | line_break: false 1601 | parent: "result_box" 1602 | layer: "text" 1603 | inherit_alpha: false 1604 | alpha: 1.0 1605 | outline_alpha: 1.0 1606 | shadow_alpha: 1.0 1607 | template_node_child: false 1608 | text_leading: 1.0 1609 | text_tracking: 0.0 1610 | } 1611 | nodes { 1612 | position { 1613 | x: 0.0 1614 | y: -69.0 1615 | z: 0.0 1616 | w: 1.0 1617 | } 1618 | rotation { 1619 | x: 0.0 1620 | y: 0.0 1621 | z: 0.0 1622 | w: 1.0 1623 | } 1624 | scale { 1625 | x: 1.0 1626 | y: 1.0 1627 | z: 1.0 1628 | w: 1.0 1629 | } 1630 | size { 1631 | x: 200.0 1632 | y: 100.0 1633 | z: 0.0 1634 | w: 1.0 1635 | } 1636 | color { 1637 | x: 0.101960786 1638 | y: 0.101960786 1639 | z: 0.101960786 1640 | w: 1.0 1641 | } 1642 | type: TYPE_TEXT 1643 | blend_mode: BLEND_MODE_ALPHA 1644 | text: "Replay?" 1645 | font: "jenna-normal" 1646 | id: "replay_btn" 1647 | xanchor: XANCHOR_NONE 1648 | yanchor: YANCHOR_NONE 1649 | pivot: PIVOT_CENTER 1650 | outline { 1651 | x: 1.0 1652 | y: 1.0 1653 | z: 1.0 1654 | w: 1.0 1655 | } 1656 | shadow { 1657 | x: 1.0 1658 | y: 1.0 1659 | z: 1.0 1660 | w: 1.0 1661 | } 1662 | adjust_mode: ADJUST_MODE_FIT 1663 | line_break: false 1664 | parent: "result_box" 1665 | layer: "text" 1666 | inherit_alpha: false 1667 | alpha: 1.0 1668 | outline_alpha: 1.0 1669 | shadow_alpha: 1.0 1670 | template_node_child: false 1671 | text_leading: 1.0 1672 | text_tracking: 0.0 1673 | } 1674 | layers { 1675 | name: "static" 1676 | } 1677 | layers { 1678 | name: "alpha" 1679 | } 1680 | layers { 1681 | name: "text" 1682 | } 1683 | material: "/builtins/materials/gui.material" 1684 | adjust_reference: ADJUST_REFERENCE_PARENT 1685 | max_nodes: 512 1686 | -------------------------------------------------------------------------------- /defold/assets/gui/title.gui: -------------------------------------------------------------------------------- 1 | script: "/scripts/gui/title.gui_script" 2 | fonts { 3 | name: "jenna-normal" 4 | font: "/assets/fonts/jenna-normal.font" 5 | } 6 | textures { 7 | name: "game" 8 | texture: "/assets/atlas/game.atlas" 9 | } 10 | background_color { 11 | x: 0.0 12 | y: 0.0 13 | z: 0.0 14 | w: 0.0 15 | } 16 | nodes { 17 | position { 18 | x: 475.0 19 | y: 320.0 20 | z: 0.0 21 | w: 1.0 22 | } 23 | rotation { 24 | x: 0.0 25 | y: 0.0 26 | z: 0.0 27 | w: 1.0 28 | } 29 | scale { 30 | x: 1.2 31 | y: 1.2 32 | z: 1.0 33 | w: 1.0 34 | } 35 | size { 36 | x: 200.0 37 | y: 100.0 38 | z: 0.0 39 | w: 1.0 40 | } 41 | color { 42 | x: 0.101960786 43 | y: 0.101960786 44 | z: 0.101960786 45 | w: 1.0 46 | } 47 | type: TYPE_TEXT 48 | blend_mode: BLEND_MODE_ALPHA 49 | text: "touch to start" 50 | font: "jenna-normal" 51 | id: "start_btn" 52 | xanchor: XANCHOR_NONE 53 | yanchor: YANCHOR_NONE 54 | pivot: PIVOT_CENTER 55 | outline { 56 | x: 1.0 57 | y: 1.0 58 | z: 1.0 59 | w: 1.0 60 | } 61 | shadow { 62 | x: 1.0 63 | y: 1.0 64 | z: 1.0 65 | w: 1.0 66 | } 67 | adjust_mode: ADJUST_MODE_FIT 68 | line_break: false 69 | layer: "text" 70 | inherit_alpha: true 71 | alpha: 0.0 72 | outline_alpha: 1.0 73 | shadow_alpha: 1.0 74 | template_node_child: false 75 | text_leading: 1.0 76 | text_tracking: 0.0 77 | } 78 | nodes { 79 | position { 80 | x: 475.0 81 | y: 585.0 82 | z: 0.0 83 | w: 1.0 84 | } 85 | rotation { 86 | x: 0.0 87 | y: 0.0 88 | z: 0.0 89 | w: 1.0 90 | } 91 | scale { 92 | x: 1.0 93 | y: 1.0 94 | z: 1.0 95 | w: 1.0 96 | } 97 | size { 98 | x: 397.0 99 | y: 101.0 100 | z: 0.0 101 | w: 1.0 102 | } 103 | color { 104 | x: 1.0 105 | y: 1.0 106 | z: 1.0 107 | w: 1.0 108 | } 109 | type: TYPE_BOX 110 | blend_mode: BLEND_MODE_ALPHA 111 | texture: "game/logo" 112 | id: "game_logo" 113 | xanchor: XANCHOR_NONE 114 | yanchor: YANCHOR_TOP 115 | pivot: PIVOT_N 116 | adjust_mode: ADJUST_MODE_FIT 117 | layer: "static" 118 | inherit_alpha: true 119 | slice9 { 120 | x: 0.0 121 | y: 0.0 122 | z: 0.0 123 | w: 0.0 124 | } 125 | clipping_mode: CLIPPING_MODE_NONE 126 | clipping_visible: false 127 | clipping_inverted: false 128 | alpha: 0.0 129 | template_node_child: false 130 | size_mode: SIZE_MODE_AUTO 131 | } 132 | nodes { 133 | position { 134 | x: 475.0 135 | y: 53.0 136 | z: 0.0 137 | w: 1.0 138 | } 139 | rotation { 140 | x: 0.0 141 | y: 0.0 142 | z: 0.0 143 | w: 1.0 144 | } 145 | scale { 146 | x: 1.0 147 | y: 1.0 148 | z: 1.0 149 | w: 1.0 150 | } 151 | size { 152 | x: 300.0 153 | y: 104.0 154 | z: 0.0 155 | w: 1.0 156 | } 157 | color { 158 | x: 1.0 159 | y: 1.0 160 | z: 1.0 161 | w: 1.0 162 | } 163 | type: TYPE_BOX 164 | blend_mode: BLEND_MODE_ALPHA 165 | texture: "game/colyseus" 166 | id: "coly_logo" 167 | xanchor: XANCHOR_NONE 168 | yanchor: YANCHOR_NONE 169 | pivot: PIVOT_S 170 | adjust_mode: ADJUST_MODE_FIT 171 | layer: "static" 172 | inherit_alpha: true 173 | slice9 { 174 | x: 0.0 175 | y: 0.0 176 | z: 0.0 177 | w: 0.0 178 | } 179 | clipping_mode: CLIPPING_MODE_NONE 180 | clipping_visible: true 181 | clipping_inverted: false 182 | alpha: 0.0 183 | template_node_child: false 184 | size_mode: SIZE_MODE_AUTO 185 | } 186 | layers { 187 | name: "static" 188 | } 189 | layers { 190 | name: "text" 191 | } 192 | material: "/builtins/materials/gui.material" 193 | adjust_reference: ADJUST_REFERENCE_PARENT 194 | max_nodes: 512 195 | -------------------------------------------------------------------------------- /defold/game.project: -------------------------------------------------------------------------------- 1 | [bootstrap] 2 | main_collection = /scenes/container.collectionc 3 | render = /orthographic/render/orthographic.renderc 4 | 5 | [script] 6 | shared_state = 1 7 | 8 | [display] 9 | width = 960 10 | height = 640 11 | 12 | [project] 13 | title = tic-tac-toe 14 | version = 0.15 15 | dependencies#0 = https://github.com/colyseus/colyseus-defold/archive/0.16.zip 16 | dependencies#1 = https://github.com/defold/extension-websocket/archive/master.zip 17 | dependencies#2 = https://github.com/britzl/defold-orthographic/archive/3.3.2.zip 18 | 19 | [library] 20 | include_dirs = 21 | 22 | [input] 23 | use_accelerometer = 0 24 | 25 | [collection] 26 | max_instances = 12 27 | 28 | [spine] 29 | max_count = 0 30 | 31 | [model] 32 | max_count = 0 33 | 34 | [collectionfactory] 35 | max_count = 4 36 | 37 | [gui] 38 | max_particle_count = 0 39 | 40 | [particle_fx] 41 | max_count = 0 42 | max_particle_count = 0 43 | 44 | [factory] 45 | max_count = 8 46 | 47 | -------------------------------------------------------------------------------- /defold/input/game.input_binding: -------------------------------------------------------------------------------- 1 | mouse_trigger { 2 | input: MOUSE_BUTTON_1 3 | action: "touch" 4 | } 5 | -------------------------------------------------------------------------------- /defold/scenes/container.collection: -------------------------------------------------------------------------------- 1 | name: "container" 2 | instances { 3 | id: "camera" 4 | prototype: "/orthographic/camera.go" 5 | position { 6 | x: 0.0 7 | y: 0.0 8 | z: 0.0 9 | } 10 | rotation { 11 | x: 0.0 12 | y: 0.0 13 | z: 0.0 14 | w: 1.0 15 | } 16 | scale3 { 17 | x: 1.0 18 | y: 1.0 19 | z: 1.0 20 | } 21 | } 22 | scale_along_z: 0 23 | embedded_instances { 24 | id: "background" 25 | data: "embedded_components {\n" 26 | " id: \"sprite\"\n" 27 | " type: \"sprite\"\n" 28 | " data: \"tile_set: \\\"/assets/atlas/game.atlas\\\"\\n" 29 | "default_animation: \\\"background\\\"\\n" 30 | "material: \\\"/builtins/materials/sprite.material\\\"\\n" 31 | "blend_mode: BLEND_MODE_ALPHA\\n" 32 | "\"\n" 33 | " position {\n" 34 | " x: 0.0\n" 35 | " y: 0.0\n" 36 | " z: 0.0\n" 37 | " }\n" 38 | " rotation {\n" 39 | " x: 0.0\n" 40 | " y: 0.0\n" 41 | " z: 0.0\n" 42 | " w: 1.0\n" 43 | " }\n" 44 | "}\n" 45 | "" 46 | position { 47 | x: 0.0 48 | y: 0.0 49 | z: -0.9 50 | } 51 | rotation { 52 | x: 0.0 53 | y: 0.0 54 | z: 0.0 55 | w: 1.0 56 | } 57 | scale3 { 58 | x: 1.0 59 | y: 1.0 60 | z: 1.0 61 | } 62 | } 63 | embedded_instances { 64 | id: "proxy" 65 | data: "components {\n" 66 | " id: \"proxy_controller\"\n" 67 | " component: \"/scripts/proxy_controller.script\"\n" 68 | " position {\n" 69 | " x: 0.0\n" 70 | " y: 0.0\n" 71 | " z: 0.0\n" 72 | " }\n" 73 | " rotation {\n" 74 | " x: 0.0\n" 75 | " y: 0.0\n" 76 | " z: 0.0\n" 77 | " w: 1.0\n" 78 | " }\n" 79 | "}\n" 80 | "embedded_components {\n" 81 | " id: \"title_proxy\"\n" 82 | " type: \"collectionproxy\"\n" 83 | " data: \"collection: \\\"/scenes/title.collection\\\"\\n" 84 | "exclude: false\\n" 85 | "\"\n" 86 | " position {\n" 87 | " x: 0.0\n" 88 | " y: 0.0\n" 89 | " z: 0.0\n" 90 | " }\n" 91 | " rotation {\n" 92 | " x: 0.0\n" 93 | " y: 0.0\n" 94 | " z: 0.0\n" 95 | " w: 1.0\n" 96 | " }\n" 97 | "}\n" 98 | "embedded_components {\n" 99 | " id: \"game_proxy\"\n" 100 | " type: \"collectionproxy\"\n" 101 | " data: \"collection: \\\"/scenes/game.collection\\\"\\n" 102 | "exclude: false\\n" 103 | "\"\n" 104 | " position {\n" 105 | " x: 0.0\n" 106 | " y: 0.0\n" 107 | " z: 0.0\n" 108 | " }\n" 109 | " rotation {\n" 110 | " x: 0.0\n" 111 | " y: 0.0\n" 112 | " z: 0.0\n" 113 | " w: 1.0\n" 114 | " }\n" 115 | "}\n" 116 | "" 117 | position { 118 | x: 0.0 119 | y: 0.0 120 | z: 0.0 121 | } 122 | rotation { 123 | x: 0.0 124 | y: 0.0 125 | z: 0.0 126 | w: 1.0 127 | } 128 | scale3 { 129 | x: 1.0 130 | y: 1.0 131 | z: 1.0 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /defold/scenes/game.collection: -------------------------------------------------------------------------------- 1 | name: "game" 2 | scale_along_z: 0 3 | embedded_instances { 4 | id: "gui" 5 | data: "components {\n" 6 | " id: \"game\"\n" 7 | " component: \"/assets/gui/game.gui\"\n" 8 | " position {\n" 9 | " x: 0.0\n" 10 | " y: 0.0\n" 11 | " z: 0.0\n" 12 | " }\n" 13 | " rotation {\n" 14 | " x: 0.0\n" 15 | " y: 0.0\n" 16 | " z: 0.0\n" 17 | " w: 1.0\n" 18 | " }\n" 19 | "}\n" 20 | "" 21 | position { 22 | x: 0.0 23 | y: 0.0 24 | z: 0.0 25 | } 26 | rotation { 27 | x: 0.0 28 | y: 0.0 29 | z: 0.0 30 | w: 1.0 31 | } 32 | scale3 { 33 | x: 1.0 34 | y: 1.0 35 | z: 1.0 36 | } 37 | } 38 | embedded_instances { 39 | id: "script" 40 | data: "components {\n" 41 | " id: \"game_controller\"\n" 42 | " component: \"/scripts/game_controller.script\"\n" 43 | " position {\n" 44 | " x: 0.0\n" 45 | " y: 0.0\n" 46 | " z: 0.0\n" 47 | " }\n" 48 | " rotation {\n" 49 | " x: 0.0\n" 50 | " y: 0.0\n" 51 | " z: 0.0\n" 52 | " w: 1.0\n" 53 | " }\n" 54 | "}\n" 55 | "" 56 | position { 57 | x: 0.0 58 | y: 0.0 59 | z: 0.0 60 | } 61 | rotation { 62 | x: 0.0 63 | y: 0.0 64 | z: 0.0 65 | w: 1.0 66 | } 67 | scale3 { 68 | x: 1.0 69 | y: 1.0 70 | z: 1.0 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /defold/scenes/title.collection: -------------------------------------------------------------------------------- 1 | name: "title" 2 | scale_along_z: 0 3 | embedded_instances { 4 | id: "gui" 5 | data: "components {\n" 6 | " id: \"title\"\n" 7 | " component: \"/assets/gui/title.gui\"\n" 8 | " position {\n" 9 | " x: 0.0\n" 10 | " y: 0.0\n" 11 | " z: 0.0\n" 12 | " }\n" 13 | " rotation {\n" 14 | " x: 0.0\n" 15 | " y: 0.0\n" 16 | " z: 0.0\n" 17 | " w: 1.0\n" 18 | " }\n" 19 | "}\n" 20 | "" 21 | position { 22 | x: 0.0 23 | y: 0.0 24 | z: 0.0 25 | } 26 | rotation { 27 | x: 0.0 28 | y: 0.0 29 | z: 0.0 30 | w: 1.0 31 | } 32 | scale3 { 33 | x: 1.0 34 | y: 1.0 35 | z: 1.0 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /defold/scripts/game_controller.script: -------------------------------------------------------------------------------- 1 | local ColyseusSDK = require "colyseus.sdk" 2 | 3 | -- SERVER VARS 4 | local server_adress = "localhost" 5 | local server_port = "2567" 6 | 7 | --VARS 8 | local client 9 | local room 10 | local numPlayers = 0 11 | local gui_controller = msg.url("game", hash("/gui"), "game") 12 | 13 | -- Show winner or loser on GUI 14 | local function showWinner(session_id) 15 | room:leave() 16 | local result = (room.session_id == session_id) and true or false 17 | msg.post(gui_controller, "Winners", {won = result}) 18 | end 19 | 20 | -- Show draw on GUI 21 | local function drawGame() 22 | room:leave() 23 | msg.post(gui_controller, "Draw") 24 | end 25 | 26 | -- Change the turn on GUI 27 | local function nextTurnOut(change_value) 28 | local result = (change_value == room.session_id) and 1 or 0 29 | msg.post(gui_controller, "ChangeTurn", {turn = result}) 30 | end 31 | 32 | -- When join completed start the game on GUI 33 | local function onJoin() 34 | msg.post(gui_controller, "start_game") 35 | end 36 | 37 | function init(self) 38 | -- Connect to WS 39 | client = ColyseusSDK.Client("ws://" .. server_adress .. ":" .. server_port, false) -- false: not to connect immediately 40 | 41 | -- EVENTS 42 | -- Join Room 43 | client:join_or_create("tictactoe", {}, function(err, room_instance) 44 | if err then 45 | print("ERROR JOINING ROOM") 46 | pprint(err) 47 | msg.post(gui_controller, "connection_fail") 48 | return 49 | end 50 | 51 | -- assign our roomm instance to global "room" variable 52 | room = room_instance 53 | callbacks = ColyseusSDK.callbacks(room) 54 | 55 | --Listen to room state changes from the room handler. 56 | --Use this method to synchronize the room state from the server with the clients. 57 | callbacks:on_add("players", function(player, session_id) 58 | numPlayers = numPlayers + 1 59 | if numPlayers == 2 then 60 | onJoin() 61 | nextTurnOut(session_id) -- Called second time here 62 | end 63 | end) 64 | 65 | callbacks:on_remove("players", function(player, session_id) 66 | numPlayers = numPlayers - 1 67 | end) 68 | 69 | callbacks:listen("currentTurn", function(value) 70 | nextTurnOut(value) 71 | end) 72 | 73 | callbacks:listen("draw", function() 74 | drawGame() 75 | end) 76 | 77 | callbacks:listen("winner", function(winnerSessionId) 78 | showWinner(winnerSessionId) 79 | end) 80 | 81 | callbacks:on_change("board", function(value, index) 82 | local i = index - 1 83 | local x = i % 3 84 | local y = math.floor(i / 3) 85 | msg.post(gui_controller, "ChangeTile", {change = {x=x, y=y, value=value}}) 86 | end) 87 | 88 | room:on("error", function(err) 89 | print("oops, error ocurred:") 90 | print(err) 91 | end) 92 | end); 93 | end 94 | 95 | function final(self) 96 | if room then 97 | -- If there is a active connection then clean it up 98 | room:leave() 99 | client = nil 100 | room = nil 101 | end 102 | end 103 | 104 | function update(self, dt) 105 | -- 106 | end 107 | 108 | function on_message(self, message_id, message, sender) 109 | if message_id == hash("SetTile") then 110 | -- Send server to selected tile's x, y 111 | room:send("action", { 112 | x = message.x, 113 | y = message.y 114 | }) 115 | end 116 | end 117 | -------------------------------------------------------------------------------- /defold/scripts/gui/game.gui_script: -------------------------------------------------------------------------------- 1 | -- VARS 2 | local timer_max = 11 3 | local current_time = 11 4 | local countdown_timer = 0 5 | local grid_lenght = 9 6 | local grid = {} 7 | local touch = hash("touch") 8 | local is_game_started = false 9 | 10 | -- NODES 11 | local waiting_txt_node 12 | local turn_txt_node 13 | local timer_node 14 | local waiting_box_node 15 | local retry_btn_node 16 | local result_box_node 17 | local result_txt_node 18 | local replay_btn_node 19 | 20 | -- URLS 21 | local game_controller = msg.url("game", hash("/script"), "game_controller") 22 | local proxy_controller = msg.url("container", hash("/proxy"), "proxy_controller") 23 | 24 | function init(self) 25 | waiting_txt_node = gui.get_node("waiting_txt") 26 | waiting_box_node = gui.get_node("waiting_box") 27 | timer_node = gui.get_node("timer_txt") 28 | turn_txt_node = gui.get_node("turn_txt") 29 | retry_btn_node = gui.get_node("retry_btn") 30 | result_box_node = gui.get_node("result_box") 31 | result_txt_node = gui.get_node("result_txt") 32 | replay_btn_node = gui.get_node("replay_btn") 33 | 34 | gui.set_enabled(retry_btn_node, false) 35 | gui.set_enabled(timer_node, false) 36 | gui.set_enabled(result_box_node, false) 37 | gui.set_enabled(replay_btn_node, false) 38 | 39 | gui.animate(waiting_box_node, "color.w", 0.7, gui.EASING_OUTSINE, 0.5) 40 | gui.animate(waiting_txt_node, "color.w", 1, gui.EASING_OUTSINE, 0.3, 0.2) 41 | gui.animate(waiting_txt_node, "scale", vmath.vector3(1, 1, 1), gui.EASING_OUTSINE, 0.3, 0.2) 42 | 43 | local grid_node 44 | local text_node 45 | local tile = {} 46 | 47 | -- clear grid 48 | grid = {} 49 | 50 | -- 3x3 Grid 51 | local i = 1 52 | for y = 0, 2 do 53 | for x = 0, 2 do 54 | grid_node = gui.get_node("box-" .. i) 55 | text_node = gui.get_node("text-" .. i) 56 | gui.set_enabled(text_node, false) 57 | tile = { 58 | tile_node = grid_node, 59 | text_node = text_node, 60 | tile_state = false, 61 | x = x, 62 | y = y 63 | } 64 | table.insert(grid, i, tile) 65 | i = i + 1 66 | end 67 | end 68 | end 69 | 70 | local function change_turn(turn) 71 | current_time = timer_max 72 | if turn == 1 then 73 | msg.post(".", "acquire_input_focus") 74 | gui.set_text(turn_txt_node, "Your move!") 75 | else 76 | msg.post(".", "release_input_focus") 77 | gui.set_text(turn_txt_node, "Opponent's turn...") 78 | end 79 | end 80 | 81 | local function start_game() 82 | gui.set_enabled(waiting_box_node, false) 83 | gui.set_enabled(waiting_txt_node, false) 84 | gui.set_text(turn_txt_node, "") 85 | is_game_started = true 86 | gui.set_enabled(timer_node, true) 87 | end 88 | 89 | local function restart_game() 90 | msg.post(proxy_controller, "reload_game") 91 | end 92 | 93 | local function connection_failed() 94 | msg.post(".", "acquire_input_focus") 95 | gui.set_text(waiting_txt_node, "Couldn't connect.") 96 | gui.set_enabled(retry_btn_node, true) 97 | end 98 | 99 | function update(self, dt) 100 | if is_game_started == false then 101 | return 102 | end 103 | current_time = current_time - 1 * dt 104 | countdown_timer = math.floor(current_time) 105 | gui.set_text(timer_node, countdown_timer) 106 | 107 | if countdown_timer == 0 then 108 | change_turn(nil) 109 | end 110 | end 111 | 112 | local function set_tile(id, value) 113 | local tile = grid[id] 114 | 115 | local text; 116 | if value == 0 then 117 | text = " " 118 | tile["tile_state"] = false 119 | elseif value == 1 then 120 | text = "x" 121 | tile["tile_state"] = true 122 | elseif value == 2 then 123 | text = "o" 124 | tile["tile_state"] = true 125 | end 126 | 127 | gui.set_enabled(tile["text_node"], true) 128 | gui.set_text(tile["text_node"], text) 129 | end 130 | 131 | local function change_tile(change) 132 | for i, v in ipairs(grid) do 133 | if v["x"] == change["x"] and v["y"] == change["y"] then 134 | set_tile(i, change["value"]) 135 | end 136 | end 137 | end 138 | 139 | local function show_result(won, draw) 140 | is_game_started = false 141 | 142 | local grid_node = gui.get_node("grid") 143 | local time_node = gui.get_node("time") 144 | gui.set_enabled(grid_node, false) 145 | gui.set_enabled(turn_txt_node, false) 146 | gui.set_enabled(timer_node, false) 147 | gui.set_enabled(time_node, false) 148 | 149 | local result = "" 150 | if draw then 151 | result = "Draw Game!" 152 | else 153 | result = won and "You win!" or "You Lose!" 154 | end 155 | msg.post(".", "acquire_input_focus") 156 | gui.set_text(result_txt_node, result) 157 | gui.set_enabled(result_box_node, true) 158 | gui.set_enabled(replay_btn_node, true) 159 | end 160 | 161 | function on_message(self, message_id, message, sender) 162 | if message_id == hash("start_game") then 163 | start_game() 164 | elseif message_id == hash("connection_fail") then 165 | connection_failed() 166 | elseif message_id == hash("ChangeTurn") then 167 | change_turn(message.turn) 168 | elseif message_id == hash("ChangeTile") then 169 | change_tile(message.change) 170 | elseif message_id == hash("Winners") then 171 | show_result(message.won, false) 172 | elseif message_id == hash("Draw") then 173 | show_result(nil, true) 174 | end 175 | end 176 | 177 | function on_input(self, action_id, action) 178 | for i, v in ipairs(grid) do 179 | local current_node = gui.pick_node(v["tile_node"], action.x, action.y) 180 | if action_id == touch and current_node and action.pressed and v["tile_state"] == false then 181 | msg.post(game_controller, "SetTile", {x = v["x"], y = v["y"]}) 182 | end 183 | end 184 | 185 | if action_id == touch and gui.is_enabled(replay_btn_node) and gui.pick_node(replay_btn_node, action.x, action.y) then 186 | restart_game() 187 | end 188 | 189 | if action_id == touch and gui.is_enabled(retry_btn_node) and gui.pick_node(retry_btn_node, action.x, action.y) then 190 | restart_game() 191 | end 192 | end 193 | -------------------------------------------------------------------------------- /defold/scripts/gui/title.gui_script: -------------------------------------------------------------------------------- 1 | -- NODES 2 | local start_btn_node 3 | local game_logo_node 4 | local coly_logo_node 5 | 6 | -- VARS 7 | local touch = hash("touch") 8 | local proxy_controller = msg.url("container", hash("/proxy"), "proxy_controller") 9 | 10 | local function on_fade_out() 11 | msg.post(proxy_controller, "load_game") 12 | end 13 | 14 | local function enable_gui() 15 | msg.post(".", "acquire_input_focus") 16 | end 17 | 18 | local function disable_gui() 19 | msg.post(".", "release_input_focus") 20 | 21 | gui.animate(start_btn_node, "color.w", 0, gui.EASING_OUTSINE, 0.2, 0.1) 22 | gui.animate(start_btn_node, "scale", vmath.vector3(1.2, 1.2, 1), gui.EASING_OUTSINE, 0.2) 23 | gui.animate(game_logo_node, "color.w", 0, gui.EASING_OUTSINE, 0.3, 0.1) 24 | gui.animate(coly_logo_node, "color.w", 0, gui.EASING_OUTSINE, 0.3, 0.1, on_fade_out) 25 | end 26 | 27 | function init(self) 28 | start_btn_node = gui.get_node("start_btn") 29 | game_logo_node = gui.get_node("game_logo") 30 | coly_logo_node = gui.get_node("coly_logo") 31 | 32 | gui.animate(game_logo_node, "color.w", 1, gui.EASING_OUTSINE, 0.2) 33 | gui.animate(coly_logo_node, "color.w", 1, gui.EASING_OUTSINE, 0.2) 34 | gui.animate(start_btn_node, "color.w", 1, gui.EASING_OUTSINE, 0.2, 0.5) 35 | gui.animate(start_btn_node, "scale", vmath.vector3(1, 1, 1), gui.EASING_OUTSINE, 0.2, 0.5, enable_gui) 36 | end 37 | 38 | function final(self) 39 | msg.post(".", "release_input_focus") 40 | end 41 | 42 | function on_input(self, action_id, action) 43 | if action_id == touch and gui.pick_node(start_btn_node, action.x, action.y) and action.pressed then 44 | gui.set_enabled(start_btn_node, false) 45 | disable_gui() 46 | end 47 | end 48 | 49 | -------------------------------------------------------------------------------- /defold/scripts/proxy_controller.script: -------------------------------------------------------------------------------- 1 | local function load_title() 2 | msg.post("/proxy#title_proxy", "load") 3 | end 4 | 5 | local function load_game() 6 | msg.post("/proxy#title_proxy", "disable") 7 | msg.post("/proxy#title_proxy", "final") 8 | msg.post("/proxy#title_proxy", "unload") 9 | end 10 | 11 | local function reload_game() 12 | msg.post("/proxy#game_proxy", "disable") 13 | msg.post("/proxy#game_proxy", "final") 14 | msg.post("/proxy#game_proxy", "unload") 15 | end 16 | 17 | function init(self) 18 | msg.post("@render:", "clear_color", { color = vmath.vector4(1, 1, 1, 1) } ) 19 | msg.post(".", "acquire_input_focus") 20 | load_title() 21 | end 22 | 23 | function on_message(self, message_id, message, sender) 24 | if message_id == hash("proxy_loaded") then 25 | msg.post(sender, "init") 26 | msg.post(sender, "enable") 27 | elseif message_id == hash("proxy_unloaded") then 28 | msg.post("/proxy#game_proxy", "load") 29 | end 30 | 31 | if message_id == hash("load_game") then 32 | load_game() 33 | elseif message_id == hash("reload_game") then 34 | reload_game() 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /javascript-pixi/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tictactoe-frontend", 3 | "version": "1.0.0", 4 | "description": "Tic Tac Toe powered by Colyseus", 5 | "scripts": { 6 | "start": "parcel src/index.html --port 8080", 7 | "build": "parcel build src/index.html", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "keywords": [ 11 | "tictactoe", 12 | "multiplayer", 13 | "game" 14 | ], 15 | "author": "Endel Dreyer", 16 | "license": "MIT", 17 | "devDependencies": { 18 | "buffer": "^6.0.3", 19 | "parcel": "^2.13.3", 20 | "path-browserify": "^1.0.1", 21 | "process": "^0.11.10", 22 | "url": "^0.11.4" 23 | }, 24 | "dependencies": { 25 | "@gamestdio/timer": "^1.1.9", 26 | "colyseus.js": "^0.16.0", 27 | "pixi.js": "^4.7.3", 28 | "tweener": "^0.1.3" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /javascript-pixi/src/Application.js: -------------------------------------------------------------------------------- 1 | import * as PIXI from 'pixi.js' 2 | import SceneManager from './core/SceneManager' 3 | 4 | import Clock from '@gamestdio/timer' 5 | window.clock = new Clock(); 6 | 7 | import Tweener from 'tweener' 8 | window.Tweener = Tweener 9 | window.tweener = new Tweener(); 10 | 11 | export default class Application { 12 | 13 | constructor () { 14 | this.background = new PIXI.Sprite.fromImage('background') 15 | this.background.pivot.x = this.background.width / 2 16 | this.background.pivot.y = this.background.height / 2 17 | 18 | this.width = 640; 19 | this.height = 640; 20 | 21 | this.scale = this.getMaxScale(); 22 | 23 | // canvas size 24 | this.screenWidth = window.innerWidth 25 | this.screenHeight = window.innerHeight 26 | 27 | this.scaledWidth = this.screenWidth / this.scale 28 | this.scaledHeight = this.screenHeight / this.scale 29 | 30 | // this.renderer = new PIXI.WebGLRenderer(width, height, { 31 | this.renderer = new PIXI.WebGLRenderer(this.screenWidth, this.screenHeight, { 32 | // resolution: window.devicePixelRatio, 33 | antialias: false 34 | }) 35 | this.renderer.backgroundColor = 0xffffff 36 | document.body.appendChild(this.renderer.view) 37 | 38 | this.stage = new SceneManager(Application.SCALE_RATIO) 39 | this.stage.scale.set(this.scale); 40 | 41 | this.container = new PIXI.Container() 42 | this.background.x = this.screenWidth / 2 43 | this.background.y = this.screenHeight / 2 44 | this.container.addChild(this.background) 45 | this.container.addChild(this.stage) 46 | 47 | tweener.add(this.background).from({ alpha: 0 }, 4000, Tweener.ease.quadOut) 48 | 49 | window.addEventListener('resize', this.onResize.bind(this)) 50 | this.onResize() 51 | 52 | // if (this.renderer.view.width > window.innerWidth) { 53 | // this.renderer.view.style.position = "absolute" 54 | // this.stage.x = (window.innerWidth - this.renderer.view.width) / 2 55 | // } 56 | } 57 | 58 | onResize (e) { 59 | this.background.x = window.innerWidth / 2 60 | this.background.y = window.innerHeight / 2 61 | 62 | this.scale = this.getMaxScale() 63 | 64 | this.screenWidth = window.innerWidth 65 | this.screenHeight = window.innerHeight 66 | 67 | this.scaledWidth = this.screenWidth / this.scale 68 | this.scaledHeight = this.screenHeight / this.scale 69 | 70 | this.renderer.resize(this.screenWidth, this.screenHeight) 71 | 72 | // this.stage.x = this.screenWidth / 2 73 | // this.stage.y = this.screenHeight / 2 74 | this.stage.scale.set(this.scale) 75 | 76 | Application.WIDTH = this.scaledWidth 77 | Application.HEIGHT = this.scaledHeight 78 | Application.MARGIN = (this.scaledHeight / 100) * 10 79 | 80 | } 81 | 82 | gotoScene (sceneClass) { 83 | this.stage.goTo(sceneClass) 84 | } 85 | 86 | getMaxScale () { 87 | return Math.min(window.innerWidth / this.width, 1) 88 | } 89 | 90 | update () { 91 | window.requestAnimationFrame( this.update.bind( this) ) 92 | clock.tick() 93 | 94 | tweener.update(clock.deltaTime) 95 | 96 | this.renderer.render(this.container) 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /javascript-pixi/src/components/Board.js: -------------------------------------------------------------------------------- 1 | import * as PIXI from 'pixi.js' 2 | 3 | export default class Board extends PIXI.Container { 4 | 5 | constructor () { 6 | super() 7 | 8 | this.size = 190 9 | 10 | this.background = new PIXI.Sprite.fromImage('board') 11 | this.addChild(this.background) 12 | 13 | let slot = new PIXI.Graphics() 14 | slot.beginFill(0x000000, 0.0) 15 | slot.drawRect(0, 0, this.size, this.size) 16 | 17 | for (let y=0; y<3; y++) { 18 | for (let x=0; x<3; x++) { 19 | let s = slot.clone() 20 | s.x = x * this.size 21 | s.y = y * this.size 22 | s.interactive = true 23 | s.on('click', this.onSelect.bind(this, x, y)) 24 | s.on('touchend', this.onSelect.bind(this, x, y)) 25 | this.addChild(s) 26 | } 27 | } 28 | } 29 | 30 | set (x, y, value) { 31 | var label = ""; 32 | 33 | if (value === 1) { 34 | label = "x"; 35 | 36 | } else if (value === 2) { 37 | label = "o" 38 | } 39 | 40 | var move = new PIXI.Text(label, { 41 | font: "150px JennaSue", 42 | fill: '#000', 43 | textAlign: 'center' 44 | }) 45 | move.pivot.x = move.width / 2 46 | move.pivot.y = move.height / 2 47 | move.x = (x * this.size) + (this.size / 2) 48 | move.y = (y * this.size) + (this.size / 2) - (move.height * 0.1) 49 | 50 | this.addChild(move) 51 | tweener.add(move).from({ alpha: 0 }, 300, Tweener.ease.quintOut) 52 | tweener.add(move.scale).from({x: 2, y: 2}, 500, Tweener.ease.quintOut) 53 | } 54 | 55 | onSelect (x, y) { 56 | console.log(x, y) 57 | this.emit('select', x, y) 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /javascript-pixi/src/core/LocalStorage.js: -------------------------------------------------------------------------------- 1 | export function get(name) { 2 | return parseInt(window.localStorage.getItem(name) || 0, 10) 3 | } 4 | 5 | export function set(name, value) { 6 | window.localStorage.setItem(name, value) 7 | } 8 | -------------------------------------------------------------------------------- /javascript-pixi/src/core/SceneManager.js: -------------------------------------------------------------------------------- 1 | export default class SceneManager extends PIXI.Container { 2 | 3 | constructor (ratio) { 4 | super() 5 | 6 | this.scale.x = ratio 7 | this.scale.y = ratio 8 | 9 | this.currentScene = null 10 | this.nextScene = null 11 | 12 | this.sceneInstanceMap = {} 13 | } 14 | 15 | goTo (screenClass, options = {}) { 16 | var screenName = screenClass.name 17 | 18 | if (!this.sceneInstanceMap[ screenName ]) { 19 | this.sceneInstanceMap[ screenName ] = new screenClass(options) 20 | this.sceneInstanceMap[ screenName ].__callbacks = {} 21 | 22 | this.bindEvents( this.sceneInstanceMap[ screenName ] ) 23 | } 24 | 25 | if (this.currentScene) { 26 | this.nextScene = this.sceneInstanceMap[ screenName ] 27 | 28 | let transitionOut = (!this.currentScene.transitionOut) 29 | ? this.defaultTransitionOut(this.currentScene) 30 | : this.currentScene.transitionOut() 31 | 32 | transitionOut.then(() => { 33 | this.transitionOutCallback(this.currentScene) 34 | 35 | let transitionIn = (!this.nextScene.transitionIn) 36 | ? this.defaultTransitionIn(this.nextScene) 37 | : this.nextScene.transitionIn() 38 | 39 | // add next scene to display list 40 | this.addChild(this.nextScene) 41 | this.currentScene = this.nextScene 42 | this.nextScene = null 43 | }) 44 | 45 | } else { 46 | this.currentScene = this.sceneInstanceMap[ screenName ] 47 | let transitionIn = (!this.currentScene.transitionIn) 48 | ? this.defaultTransitionIn(this.currentScene) 49 | : this.currentScene.transitionIn() 50 | 51 | // add next scene to display list 52 | this.addChild(this.currentScene) 53 | } 54 | } 55 | 56 | bindEvents (scene) { 57 | if (scene.onResize) { 58 | let resizeCallback = scene.onResize.bind(scene) 59 | window.addEventListener('resize', resizeCallback) 60 | this.sceneInstanceMap[ scene.constructor.name ].__callbacks['resize'] = resizeCallback 61 | resizeCallback() 62 | } 63 | 64 | scene.on('goto', (...args) => this.goTo.apply(this, args)) 65 | } 66 | 67 | defaultTransitionIn (scene) { 68 | return tweener.add(scene). 69 | from({ alpha: 0 }, 800, Tweener.ease.quintOut) 70 | } 71 | 72 | defaultTransitionOut (scene) { 73 | return tweener.add(scene). 74 | to({ alpha: 0 }, 800, Tweener.ease.quintOut) 75 | } 76 | 77 | transitionOutCallback (scene) { 78 | // dispose & remove all scene references on transition-out 79 | scene.emit('dispose') 80 | 81 | let instance = this.sceneInstanceMap[ scene.constructor.name ] 82 | if (instance) { 83 | // callbacks 84 | let callbacks = instance.__callbacks 85 | for (let event in callbacks) { 86 | window.removeEventListener(event, callbacks[event]) 87 | } 88 | delete this.sceneInstanceMap[ scene.constructor.name ] 89 | } 90 | 91 | scene.off() 92 | this.removeChild(scene) 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /javascript-pixi/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endel/colyseus-tic-tac-toe/bf7b31f5e256bee056bc468dc27ed6fbd0c93cea/javascript-pixi/src/favicon.ico -------------------------------------------------------------------------------- /javascript-pixi/src/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endel/colyseus-tic-tac-toe/bf7b31f5e256bee056bc468dc27ed6fbd0c93cea/javascript-pixi/src/favicon.png -------------------------------------------------------------------------------- /javascript-pixi/src/fonts/JennaSue.afm: -------------------------------------------------------------------------------- 1 | StartFontMetrics 2.0 2 | Comment Generated by FontForge 20120731 3 | Comment Creation Date: Sat Feb 27 16:46:40 2016 4 | FontName JennaSue 5 | FullName Jenna Sue 6 | FamilyName Jenna Sue 7 | Weight Book 8 | ItalicAngle 0 9 | IsFixedPitch false 10 | UnderlinePosition -50 11 | UnderlineThickness 50 12 | Version 1.0 13 | EncodingScheme ISO10646-1 14 | FontBBox -199 -387 607 867 15 | CapHeight 678 16 | XHeight 316 17 | Ascender 672 18 | Descender -363 19 | StartCharMetrics 117 20 | C 32 ; WX 160 ; N space ; B 0 0 0 0 ; 21 | C 33 ; WX 252 ; N exclam ; B 48 0 167 711 ; 22 | C 34 ; WX 171 ; N quotedbl ; B 22 497 141 697 ; 23 | C 35 ; WX 557 ; N numbersign ; B 61 76 496 559 ; 24 | C 36 ; WX 518 ; N dollar ; B 96 -6 478 610 ; 25 | C 37 ; WX 494 ; N percent ; B 16 -38 450 681 ; 26 | C 38 ; WX 418 ; N ampersand ; B 35 -60 395 667 ; 27 | C 39 ; WX 113 ; N quotesingle ; B 23 511 71 700 ; 28 | C 40 ; WX 183 ; N parenleft ; B 36 -95 194 649 ; 29 | C 41 ; WX 204 ; N parenright ; B 20 -86 167 658 ; 30 | C 42 ; WX 209 ; N asterisk ; B 23 447 182 622 ; 31 | C 43 ; WX 344 ; N plus ; B 23 108 264 360 ; 32 | C 44 ; WX 115 ; N comma ; B -35 -105 77 50 ; 33 | C 45 ; WX 250 ; N hyphen ; B 17 196 182 239 ; 34 | C 46 ; WX 96 ; N period ; B 17 4 76 54 ; 35 | C 47 ; WX 195 ; N slash ; B 24 -70 197 707 ; 36 | C 48 ; WX 302 ; N zero ; B 41 11 255 584 ; 37 | C 49 ; WX 270 ; N one ; B 97 24 173 591 ; 38 | C 50 ; WX 412 ; N two ; B -27 -20 361 590 ; 39 | C 51 ; WX 406 ; N three ; B 28 -23 376 576 ; 40 | C 52 ; WX 328 ; N four ; B 13 -24 298 604 ; 41 | C 53 ; WX 417 ; N five ; B 50 17 384 573 ; 42 | C 54 ; WX 354 ; N six ; B 42 -17 339 582 ; 43 | C 55 ; WX 271 ; N seven ; B -10 39 244 620 ; 44 | C 56 ; WX 288 ; N eight ; B 41 7 237 544 ; 45 | C 57 ; WX 297 ; N nine ; B 33 -18 253 598 ; 46 | C 58 ; WX 120 ; N colon ; B 29 21 93 256 ; 47 | C 59 ; WX 161 ; N semicolon ; B -12 -93 119 278 ; 48 | C 60 ; WX 268 ; N less ; B 61 90 217 369 ; 49 | C 61 ; WX 344 ; N equal ; B 54 145 267 246 ; 50 | C 62 ; WX 284 ; N greater ; B 71 73 228 351 ; 51 | C 63 ; WX 424 ; N question ; B 73 3 365 685 ; 52 | C 64 ; WX 461 ; N at ; B 42 25 397 496 ; 53 | C 65 ; WX 416 ; N A ; B -27 -61 416 665 ; 54 | C 66 ; WX 490 ; N B ; B -40 -30 500 647 ; 55 | C 67 ; WX 375 ; N C ; B 18 -7 422 698 ; 56 | C 68 ; WX 614 ; N D ; B 27 0 562 692 ; 57 | C 69 ; WX 495 ; N E ; B 19 4 498 701 ; 58 | C 70 ; WX 294 ; N F ; B 21 -1 357 691 ; 59 | C 71 ; WX 479 ; N G ; B 32 -51 521 642 ; 60 | C 72 ; WX 408 ; N H ; B -19 -56 448 710 ; 61 | C 73 ; WX 167 ; N I ; B -1 -9 72 708 ; 62 | C 74 ; WX 271 ; N J ; B 16 -235 294 696 ; 63 | C 75 ; WX 333 ; N K ; B 14 -11 372 660 ; 64 | C 76 ; WX 331 ; N L ; B 3 0 342 697 ; 65 | C 77 ; WX 623 ; N M ; B -44 -32 583 704 ; 66 | C 78 ; WX 556 ; N N ; B -58 -8 563 706 ; 67 | C 79 ; WX 345 ; N O ; B -32 1 350 662 ; 68 | C 80 ; WX 345 ; N P ; B -12 -8 438 699 ; 69 | C 81 ; WX 384 ; N Q ; B 4 -82 439 707 ; 70 | C 82 ; WX 553 ; N R ; B -51 -18 561 651 ; 71 | C 83 ; WX 459 ; N S ; B -9 -82 461 661 ; 72 | C 84 ; WX 255 ; N T ; B -199 0 397 702 ; 73 | C 85 ; WX 487 ; N U ; B 20 -1 494 663 ; 74 | C 86 ; WX 279 ; N V ; B -56 -10 312 725 ; 75 | C 87 ; WX 465 ; N W ; B 5 -10 471 629 ; 76 | C 88 ; WX 317 ; N X ; B -34 -89 315 602 ; 77 | C 89 ; WX 205 ; N Y ; B -46 -116 213 661 ; 78 | C 90 ; WX 395 ; N Z ; B -11 -5 436 660 ; 79 | C 91 ; WX 231 ; N bracketleft ; B 68 -65 182 738 ; 80 | C 92 ; WX 344 ; N backslash ; B 76 -54 295 716 ; 81 | C 93 ; WX 248 ; N bracketright ; B 63 -64 172 727 ; 82 | C 94 ; WX 641 ; N asciicircum ; B 108 -29 534 502 ; 83 | C 95 ; WX 398 ; N underscore ; B 31 -101 357 -69 ; 84 | C 96 ; WX 833 ; N grave ; B 0 0 0 0 ; 85 | C 97 ; WX 291 ; N a ; B -19 -1 310 296 ; 86 | C 98 ; WX 271 ; N b ; B -19 -8 232 647 ; 87 | C 99 ; WX 239 ; N c ; B 1 4 285 295 ; 88 | C 100 ; WX 312 ; N d ; B -21 -79 353 649 ; 89 | C 101 ; WX 250 ; N e ; B 6 2 265 292 ; 90 | C 102 ; WX 219 ; N f ; B -20 -70 309 643 ; 91 | C 103 ; WX 319 ; N g ; B -38 -387 316 300 ; 92 | C 104 ; WX 266 ; N h ; B 1 -18 273 722 ; 93 | C 105 ; WX 125 ; N i ; B 18 -2 87 441 ; 94 | C 106 ; WX 156 ; N j ; B -89 -338 200 433 ; 95 | C 107 ; WX 254 ; N k ; B 2 -5 261 675 ; 96 | C 108 ; WX 115 ; N l ; B -20 -141 141 697 ; 97 | C 109 ; WX 377 ; N m ; B 0 -8 389 349 ; 98 | C 110 ; WX 264 ; N n ; B 11 -2 274 321 ; 99 | C 111 ; WX 198 ; N o ; B -14 1 178 300 ; 100 | C 112 ; WX 295 ; N p ; B -9 -366 266 307 ; 101 | C 113 ; WX 304 ; N q ; B 29 -376 288 294 ; 102 | C 114 ; WX 210 ; N r ; B 26 -4 245 322 ; 103 | C 115 ; WX 266 ; N s ; B -20 -61 294 317 ; 104 | C 116 ; WX 280 ; N t ; B 4 -25 344 445 ; 105 | C 117 ; WX 271 ; N u ; B 14 -8 288 336 ; 106 | C 118 ; WX 240 ; N v ; B 11 -1 231 330 ; 107 | C 119 ; WX 328 ; N w ; B -4 -26 301 343 ; 108 | C 120 ; WX 240 ; N x ; B -15 -29 198 357 ; 109 | C 121 ; WX 306 ; N y ; B -2 -352 332 317 ; 110 | C 122 ; WX 360 ; N z ; B 8 7 366 304 ; 111 | C 123 ; WX 234 ; N braceleft ; B 18 -104 237 702 ; 112 | C 124 ; WX 241 ; N bar ; B 91 -204 140 589 ; 113 | C 125 ; WX 213 ; N braceright ; B -29 -113 183 692 ; 114 | C 126 ; WX 333 ; N asciitilde ; B 59 158 292 214 ; 115 | C 160 ; WX 160 ; N space ; B 0 0 0 0 ; 116 | C 162 ; WX 296 ; N cent ; B 32 85 273 498 ; 117 | C 169 ; WX 378 ; N copyright ; B 41 93 333 391 ; 118 | C 174 ; WX 464 ; N registered ; B 55 79 403 436 ; 119 | C 176 ; WX 127 ; N degree ; B 29 527 104 623 ; 120 | C 215 ; WX 361 ; N multiply ; B 92 138 311 352 ; 121 | C 247 ; WX 283 ; N divide ; B 22 122 251 299 ; 122 | C -1 ; WX 457 ; N tilde ; B 55 549 349 620 ; 123 | C -1 ; WX 421 ; N endash ; B 31 166 376 209 ; 124 | C -1 ; WX 652 ; N emdash ; B 41 156 607 199 ; 125 | C -1 ; WX 134 ; N quoteleft ; B 35 509 83 698 ; 126 | C -1 ; WX 150 ; N quoteright ; B 50 507 98 696 ; 127 | C -1 ; WX 202 ; N quotedblleft ; B 24 507 176 698 ; 128 | C -1 ; WX 185 ; N quotedblright ; B 38 491 178 702 ; 129 | C -1 ; WX 302 ; N bullet ; B 65 183 149 266 ; 130 | C -1 ; WX 134 ; N fraction ; B -138 171 272 867 ; 131 | C -1 ; WX 222 ; N trademark ; B -53 488 209 624 ; 132 | C -1 ; WX 299 ; N minus ; B 43 227 250 258 ; 133 | C -1 ; WX 134 ; N .notdef ; B 0 0 0 0 ; 134 | C -1 ; WX 0 ; N .null ; B 0 0 0 0 ; 135 | C -1 ; WX 134 ; N nonmarkingreturn ; B 0 0 0 0 ; 136 | C -1 ; WX 0 ; N glyph111 ; B 0 0 0 0 ; 137 | EndCharMetrics 138 | StartKernData 139 | StartKernPairs 1017 140 | KPX quotedbl A -42 141 | KPX quotedbl quotesingle -52 142 | KPX quotedbl Y 114 143 | KPX quotedbl X 93 144 | KPX quotedbl W 62 145 | KPX quotedbl V 114 146 | KPX quotedbl T 229 147 | KPX quotedbl S 52 148 | KPX quotedbl R 83 149 | KPX quotedbl l 83 150 | KPX quotedbl L 62 151 | KPX quotedbl k 83 152 | KPX quotedbl I 83 153 | KPX quotedbl h 73 154 | KPX quotedbl B 62 155 | KPX quotedbl b 73 156 | KPX quotesingle A -63 157 | KPX quotesingle Y 83 158 | KPX quotesingle V 83 159 | KPX quotesingle T 218 160 | KPX quotesingle R 93 161 | KPX quotesingle l 20 162 | KPX quotesingle k 20 163 | KPX quotesingle h 41 164 | KPX quotesingle b 52 165 | KPX parenleft h 73 166 | KPX zero five -73 167 | KPX one nine -31 168 | KPX one eight -31 169 | KPX one six -52 170 | KPX one five -94 171 | KPX one one -42 172 | KPX two nine -105 173 | KPX two eight -84 174 | KPX two seven -42 175 | KPX two six -63 176 | KPX two five -115 177 | KPX two four -63 178 | KPX two three -63 179 | KPX two two -32 180 | KPX two zero -52 181 | KPX two one -94 182 | KPX three nine -73 183 | KPX three seven -41 184 | KPX three five -62 185 | KPX three one -62 186 | KPX four nine -31 187 | KPX four five -41 188 | KPX four one -52 189 | KPX five five -94 190 | KPX five one -62 191 | KPX six nine -52 192 | KPX six seven -62 193 | KPX six six 11 194 | KPX six five -73 195 | KPX six one -52 196 | KPX seven seven 42 197 | KPX seven five -63 198 | KPX eight five -63 199 | KPX nine five -83 200 | KPX A quotedblright -32 201 | KPX A period -39 202 | KPX A backslash -94 203 | KPX A question -52 204 | KPX A z -63 205 | KPX A Z -42 206 | KPX A v -32 207 | KPX A u -32 208 | KPX A U -32 209 | KPX A t -55 210 | KPX A T 47 211 | KPX A S -42 212 | KPX A r -23 213 | KPX A R -63 214 | KPX A q -62 215 | KPX A P -105 216 | KPX A N -109 217 | KPX A M -115 218 | KPX A J -32 219 | KPX A H -8 220 | KPX A g -32 221 | KPX A e -32 222 | KPX A E -94 223 | KPX A d -23 224 | KPX A D -73 225 | KPX A c -23 226 | KPX B backslash -39 227 | KPX B comma -70 228 | KPX B question -55 229 | KPX B z 16 230 | KPX B Z 41 231 | KPX B y 39 232 | KPX B x 62 233 | KPX B w 62 234 | KPX B W 41 235 | KPX B v 52 236 | KPX B V 41 237 | KPX B u 31 238 | KPX B s 52 239 | KPX B r 16 240 | KPX B q -42 241 | KPX B Q 41 242 | KPX B p 47 243 | KPX B o 39 244 | KPX B O 47 245 | KPX B n 31 246 | KPX B N -136 247 | KPX B m 39 248 | KPX B M -115 249 | KPX B l 31 250 | KPX B k 41 251 | KPX B K 41 252 | KPX B j 20 253 | KPX B J -42 254 | KPX B i 31 255 | KPX B I 62 256 | KPX B h 62 257 | KPX B g -42 258 | KPX B d -8 259 | KPX B D -63 260 | KPX B C 24 261 | KPX B b 52 262 | KPX C braceright 62 263 | KPX C a 42 264 | KPX C parenright 42 265 | KPX C quotedbl 94 266 | KPX C backslash 8 267 | KPX C semicolon 47 268 | KPX C colon 55 269 | KPX C z 8 270 | KPX C y 62 271 | KPX C Y 52 272 | KPX C x 70 273 | KPX C w 47 274 | KPX C v 47 275 | KPX C V 52 276 | KPX C u 62 277 | KPX C t 16 278 | KPX C T 115 279 | KPX C s 39 280 | KPX C r 39 281 | KPX C q 8 282 | KPX C Q 52 283 | KPX C p 86 284 | KPX C o 94 285 | KPX C O 94 286 | KPX C n 55 287 | KPX C m 62 288 | KPX C M -73 289 | KPX C l 86 290 | KPX C k 86 291 | KPX C j 55 292 | KPX C i 70 293 | KPX C I 62 294 | KPX C h 70 295 | KPX C g 8 296 | KPX C G -31 297 | KPX C f 42 298 | KPX C e -52 299 | KPX C c 39 300 | KPX C b 52 301 | KPX D a -42 302 | KPX D A -53 303 | KPX D bracketright -62 304 | KPX D parenright -86 305 | KPX D quotedblright -84 306 | KPX D period -125 307 | KPX D backslash -109 308 | KPX D semicolon -55 309 | KPX D colon -47 310 | KPX D comma -101 311 | KPX D question -53 312 | KPX D z -55 313 | KPX D v -15 314 | KPX D t -42 315 | KPX D s 10 316 | KPX D S -32 317 | KPX D r -31 318 | KPX D R -73 319 | KPX D q -42 320 | KPX D P -105 321 | KPX D N -84 322 | KPX D M -105 323 | KPX D J -73 324 | KPX D i -11 325 | KPX D I 31 326 | KPX D g -42 327 | KPX D G -62 328 | KPX D e -42 329 | KPX D E -73 330 | KPX D D -94 331 | KPX D c -55 332 | KPX E a -31 333 | KPX E backslash -39 334 | KPX E z -31 335 | KPX E y -15 336 | KPX E v -23 337 | KPX E u -23 338 | KPX E t -52 339 | KPX E T 78 340 | KPX E r -31 341 | KPX E R -52 342 | KPX E q -78 343 | KPX E P -73 344 | KPX E n -21 345 | KPX E N 8 346 | KPX E m -15 347 | KPX E M -63 348 | KPX E I 21 349 | KPX E g -63 350 | KPX E G -42 351 | KPX E e -42 352 | KPX E d -63 353 | KPX E c -31 354 | KPX E C -31 355 | KPX E b 8 356 | KPX F braceright 86 357 | KPX F a -52 358 | KPX F bracketright 31 359 | KPX F parenright 83 360 | KPX F quotesingle 94 361 | KPX F quotedbl 109 362 | KPX F quotedblright 83 363 | KPX F period -94 364 | KPX F backslash 39 365 | KPX F semicolon -102 366 | KPX F comma -63 367 | KPX F z -63 368 | KPX F Z 83 369 | KPX F y -55 370 | KPX F Y 135 371 | KPX F x -31 372 | KPX F X 104 373 | KPX F w -24 374 | KPX F W 93 375 | KPX F v -55 376 | KPX F V 156 377 | KPX F u -47 378 | KPX F U 104 379 | KPX F t -86 380 | KPX F T 208 381 | KPX F s -70 382 | KPX F S 83 383 | KPX F r -78 384 | KPX F R 62 385 | KPX F q -102 386 | KPX F Q 104 387 | KPX F p -55 388 | KPX F P 62 389 | KPX F o -32 390 | KPX F O 114 391 | KPX F n -55 392 | KPX F m -52 393 | KPX F l 47 394 | KPX F L 104 395 | KPX F k 70 396 | KPX F K 104 397 | KPX F j 8 398 | KPX F J 73 399 | KPX F i -8 400 | KPX F I 156 401 | KPX F h 62 402 | KPX F H 52 403 | KPX F g -63 404 | KPX F f -24 405 | KPX F F 83 406 | KPX F e -63 407 | KPX F E 62 408 | KPX F B 125 409 | KPX F d -39 410 | KPX F c -42 411 | KPX F C 86 412 | KPX F b 104 413 | KPX G backslash 11 414 | KPX G Z 63 415 | KPX G y 31 416 | KPX G Y 73 417 | KPX G x 83 418 | KPX G X 83 419 | KPX G w 63 420 | KPX G W 63 421 | KPX G v 42 422 | KPX G V 104 423 | KPX G u 52 424 | KPX G U 52 425 | KPX G T 42 426 | KPX G s 42 427 | KPX G r 11 428 | KPX G q -42 429 | KPX G Q 83 430 | KPX G p 31 431 | KPX G P -31 432 | KPX G o 42 433 | KPX G O 31 434 | KPX G n 52 435 | KPX G N -73 436 | KPX G m 52 437 | KPX G M -104 438 | KPX G l 73 439 | KPX G k 83 440 | KPX G j 63 441 | KPX G i 63 442 | KPX G I 83 443 | KPX G h 83 444 | KPX G f 21 445 | KPX G D -52 446 | KPX G b 63 447 | KPX H backslash 104 448 | KPX H z -31 449 | KPX H V 31 450 | KPX H T 104 451 | KPX H p 63 452 | KPX H N -135 453 | KPX H M -156 454 | KPX H l 21 455 | KPX H k 31 456 | KPX H j 63 457 | KPX H i 21 458 | KPX H I 31 459 | KPX H g -31 460 | KPX H D -52 461 | KPX I A -32 462 | KPX I quotesingle -41 463 | KPX I quotedbl -32 464 | KPX I backslash 41 465 | KPX I question -84 466 | KPX I z -63 467 | KPX I v -42 468 | KPX I u -42 469 | KPX I t -47 470 | KPX I T 114 471 | KPX I P -52 472 | KPX I N -94 473 | KPX I M -73 474 | KPX I l -21 475 | KPX I J -21 476 | KPX I H -21 477 | KPX I g -21 478 | KPX I F -42 479 | KPX I E -63 480 | KPX I D -73 481 | KPX J A -21 482 | KPX J backslash -11 483 | KPX J T 83 484 | KPX J p 62 485 | KPX J P -63 486 | KPX J j 62 487 | KPX J I 83 488 | KPX J D -52 489 | KPX J b 21 490 | KPX K backslash 11 491 | KPX K colon 52 492 | KPX K y 21 493 | KPX K x 21 494 | KPX K w 32 495 | KPX K V 52 496 | KPX K u 21 497 | KPX K T 125 498 | KPX K s 42 499 | KPX K Q 63 500 | KPX K o 32 501 | KPX K O 63 502 | KPX K K 52 503 | KPX K I 94 504 | KPX K g -31 505 | KPX K d -41 506 | KPX K c -10 507 | KPX K b 63 508 | KPX L z -31 509 | KPX L u -21 510 | KPX L T 32 511 | KPX L P -31 512 | KPX L N -52 513 | KPX L m -10 514 | KPX L M -62 515 | KPX L I 42 516 | KPX L G -41 517 | KPX L d 11 518 | KPX L D -73 519 | KPX L c 11 520 | KPX L b 52 521 | KPX M backslash -94 522 | KPX M w 21 523 | KPX M S -42 524 | KPX M R -52 525 | KPX M q -31 526 | KPX M P -63 527 | KPX M N -73 528 | KPX M M -63 529 | KPX M I 42 530 | KPX M D -83 531 | KPX N comma 21 532 | KPX N question -52 533 | KPX N z -31 534 | KPX N U -42 535 | KPX N S -42 536 | KPX N R -73 537 | KPX N q -42 538 | KPX N P -83 539 | KPX N N -31 540 | KPX N J -10 541 | KPX N I 63 542 | KPX N G -31 543 | KPX N D -62 544 | KPX O backslash 93 545 | KPX O y 41 546 | KPX O Y 104 547 | KPX O x 52 548 | KPX O X 83 549 | KPX O w 52 550 | KPX O W 52 551 | KPX O v 52 552 | KPX O V 93 553 | KPX O u 41 554 | KPX O U 62 555 | KPX O T 114 556 | KPX O s 62 557 | KPX O S 52 558 | KPX O Q 62 559 | KPX O p 52 560 | KPX O o 62 561 | KPX O O 73 562 | KPX O n 41 563 | KPX O N 21 564 | KPX O l 52 565 | KPX O k 62 566 | KPX O K 31 567 | KPX O j 31 568 | KPX O i 93 569 | KPX O I 93 570 | KPX O h 52 571 | KPX O g -11 572 | KPX O f 52 573 | KPX O D -32 574 | KPX O C 52 575 | KPX O b 73 576 | KPX P braceright 104 577 | KPX P A 93 578 | KPX P bracketright 83 579 | KPX P bar 83 580 | KPX P parenright 93 581 | KPX P quotesingle 146 582 | KPX P quotedbl 125 583 | KPX P quotedblright 62 584 | KPX P slash 93 585 | KPX P backslash 62 586 | KPX P question 52 587 | KPX P Z 156 588 | KPX P Y 187 589 | KPX P X 177 590 | KPX P W 156 591 | KPX P V 198 592 | KPX P u 41 593 | KPX P U 135 594 | KPX P T 135 595 | KPX P S 146 596 | KPX P R 93 597 | KPX P q -42 598 | KPX P Q 146 599 | KPX P P 73 600 | KPX P O 187 601 | KPX P n 41 602 | KPX P N 83 603 | KPX P M 52 604 | KPX P l 114 605 | KPX P L 146 606 | KPX P k 125 607 | KPX P K 146 608 | KPX P j 114 609 | KPX P J 104 610 | KPX P i 93 611 | KPX P I 219 612 | KPX P h 125 613 | KPX P H 135 614 | KPX P G 93 615 | KPX P f 93 616 | KPX P F 135 617 | KPX P E 114 618 | KPX P B 135 619 | KPX P D 52 620 | KPX P C 166 621 | KPX P b 146 622 | KPX Q A 42 623 | KPX Q backslash 11 624 | KPX Q comma 101 625 | KPX Q Z 42 626 | KPX Q y 63 627 | KPX Q Y 84 628 | KPX Q x 84 629 | KPX Q X 73 630 | KPX Q w 73 631 | KPX Q W 84 632 | KPX Q v 73 633 | KPX Q V 105 634 | KPX Q u 42 635 | KPX Q U 32 636 | KPX Q T 125 637 | KPX Q s 52 638 | KPX Q S 84 639 | KPX Q r 73 640 | KPX Q Q 63 641 | KPX Q p 73 642 | KPX Q o 63 643 | KPX Q O 63 644 | KPX Q n 42 645 | KPX Q N 32 646 | KPX Q l 94 647 | KPX Q L 42 648 | KPX Q k 84 649 | KPX Q K 52 650 | KPX Q j 94 651 | KPX Q i 84 652 | KPX Q I 84 653 | KPX Q h 73 654 | KPX Q H 42 655 | KPX Q G 21 656 | KPX Q f 94 657 | KPX Q F 73 658 | KPX Q B 84 659 | KPX Q C 63 660 | KPX Q b 84 661 | KPX R z -31 662 | KPX R y -21 663 | KPX R t -52 664 | KPX R T 52 665 | KPX R q -42 666 | KPX R I 31 667 | KPX R D -73 668 | KPX S backslash -52 669 | KPX S question -41 670 | KPX S T 32 671 | KPX S S -52 672 | KPX S q -31 673 | KPX S P -52 674 | KPX S D -62 675 | KPX T braceright 219 676 | KPX T A 73 677 | KPX T bracketright 135 678 | KPX T parenright 146 679 | KPX T quotesingle 187 680 | KPX T quotedbl 187 681 | KPX T quotedblright 156 682 | KPX T slash 83 683 | KPX T backslash 146 684 | KPX T Z 52 685 | KPX T y -10 686 | KPX T Y 125 687 | KPX T X 94 688 | KPX T W 94 689 | KPX T V 115 690 | KPX T U 83 691 | KPX T T 187 692 | KPX T S 73 693 | KPX T r -21 694 | KPX T R 52 695 | KPX T q -42 696 | KPX T Q 42 697 | KPX T O 115 698 | KPX T L 62 699 | KPX T K 62 700 | KPX T I 250 701 | KPX T H 62 702 | KPX T F 83 703 | KPX T E 94 704 | KPX T B 73 705 | KPX T D 52 706 | KPX T C 73 707 | KPX T b 73 708 | KPX U a -32 709 | KPX U quotesingle -32 710 | KPX U quotedbl -63 711 | KPX U z -73 712 | KPX U Z -63 713 | KPX U y -21 714 | KPX U x -42 715 | KPX U v -42 716 | KPX U u -32 717 | KPX U t -84 718 | KPX U s -42 719 | KPX U S -53 720 | KPX U r -32 721 | KPX U R -105 722 | KPX U q -63 723 | KPX U Q -63 724 | KPX U P -125 725 | KPX U O -42 726 | KPX U n -11 727 | KPX U N -53 728 | KPX U m -32 729 | KPX U M -73 730 | KPX U L -42 731 | KPX U k -32 732 | KPX U j -42 733 | KPX U i -21 734 | KPX U h -32 735 | KPX U g -42 736 | KPX U G -42 737 | KPX U f -42 738 | KPX U F -63 739 | KPX U e -42 740 | KPX U E -94 741 | KPX U d -32 742 | KPX U D -146 743 | KPX U c -21 744 | KPX U C -32 745 | KPX V parenright 73 746 | KPX V quotesingle 63 747 | KPX V quotedbl 73 748 | KPX V quotedblright 63 749 | KPX V backslash 42 750 | KPX V Z 73 751 | KPX V Y 125 752 | KPX V x 73 753 | KPX V X 73 754 | KPX V w 52 755 | KPX V W 52 756 | KPX V v 31 757 | KPX V V 115 758 | KPX V U 73 759 | KPX V T 104 760 | KPX V S 52 761 | KPX V q -21 762 | KPX V Q 52 763 | KPX V o 42 764 | KPX V O 63 765 | KPX V n 31 766 | KPX V N -10 767 | KPX V M -10 768 | KPX V l 73 769 | KPX V L 73 770 | KPX V k 73 771 | KPX V K 52 772 | KPX V j 42 773 | KPX V i 63 774 | KPX V I 125 775 | KPX V h 73 776 | KPX V H 73 777 | KPX V F 52 778 | KPX V b 73 779 | KPX W braceright 52 780 | KPX W parenright 52 781 | KPX W quotesingle 52 782 | KPX W quotedbl 41 783 | KPX W quotedblright 62 784 | KPX W backslash 10 785 | KPX W comma -39 786 | KPX W Z 62 787 | KPX W Y 114 788 | KPX W X 83 789 | KPX W w 41 790 | KPX W W 52 791 | KPX W V 114 792 | KPX W U 41 793 | KPX W T 83 794 | KPX W S 62 795 | KPX W q -42 796 | KPX W Q 21 797 | KPX W p 31 798 | KPX W o 31 799 | KPX W O 83 800 | KPX W M -11 801 | KPX W l 52 802 | KPX W L 41 803 | KPX W k 62 804 | KPX W j 41 805 | KPX W i 41 806 | KPX W I 125 807 | KPX W h 73 808 | KPX W H 41 809 | KPX W f 31 810 | KPX W b 73 811 | KPX X a 63 812 | KPX X backslash -41 813 | KPX X Y 42 814 | KPX X P -52 815 | KPX X o 53 816 | KPX X k 73 817 | KPX X I 32 818 | KPX X e 32 819 | KPX Y braceright 73 820 | KPX Y parenright 94 821 | KPX Y quotesingle 84 822 | KPX Y quotedbl 73 823 | KPX Y quotedblright 63 824 | KPX Y backslash 21 825 | KPX Y y 42 826 | KPX Y Y 104 827 | KPX Y x 63 828 | KPX Y X 73 829 | KPX Y w 52 830 | KPX Y W 73 831 | KPX Y V 115 832 | KPX Y U 63 833 | KPX Y T 188 834 | KPX Y s 63 835 | KPX Y S 84 836 | KPX Y q -31 837 | KPX Y Q 63 838 | KPX Y p 52 839 | KPX Y o 42 840 | KPX Y O 73 841 | KPX Y l 63 842 | KPX Y L 52 843 | KPX Y k 73 844 | KPX Y K 63 845 | KPX Y j 63 846 | KPX Y i 63 847 | KPX Y I 104 848 | KPX Y h 63 849 | KPX Y H 42 850 | KPX Y f 21 851 | KPX Y F 63 852 | KPX Y E 42 853 | KPX Y B 73 854 | KPX Y C 32 855 | KPX Y b 84 856 | KPX Z z -84 857 | KPX Z Y 31 858 | KPX Z T 94 859 | KPX Z q -136 860 | KPX Z I 52 861 | KPX Z g -73 862 | KPX Z e -31 863 | KPX Z d -11 864 | KPX Z D -52 865 | KPX Z c -42 866 | KPX a backslash -84 867 | KPX a question -52 868 | KPX a z -10 869 | KPX a t -47 870 | KPX a r -16 871 | KPX a q -32 872 | KPX a i -21 873 | KPX a f -15 874 | KPX a D -94 875 | KPX b a -21 876 | KPX b backslash -94 877 | KPX b comma -70 878 | KPX b question -73 879 | KPX b z -31 880 | KPX b w 10 881 | KPX b r -11 882 | KPX b q -52 883 | KPX b l 10 884 | KPX b g -21 885 | KPX b e -11 886 | KPX b c -10 887 | KPX c a -21 888 | KPX c question -42 889 | KPX c w 31 890 | KPX c s 31 891 | KPX c o 31 892 | KPX c l 20 893 | KPX c g -32 894 | KPX c d -11 895 | KPX d braceright 84 896 | KPX d a -16 897 | KPX d parenright 73 898 | KPX d quotesingle 84 899 | KPX d quotedbl 73 900 | KPX d quotedblright 63 901 | KPX d z -62 902 | KPX d q -52 903 | KPX d l 83 904 | KPX d k 63 905 | KPX d j 21 906 | KPX d i 15 907 | KPX d h 73 908 | KPX d g -39 909 | KPX d e -20 910 | KPX d b 105 911 | KPX e a -10 912 | KPX e question -63 913 | KPX e t -52 914 | KPX e r -24 915 | KPX e q -52 916 | KPX e l 23 917 | KPX e g -31 918 | KPX e d -23 919 | KPX f braceright 114 920 | KPX f parenright 93 921 | KPX f quotesingle 135 922 | KPX f quotedbl 104 923 | KPX f quotedblright 114 924 | KPX f backslash 83 925 | KPX f question 83 926 | KPX f y 52 927 | KPX f x 41 928 | KPX f w 31 929 | KPX f q -21 930 | KPX f p 62 931 | KPX f o 31 932 | KPX f l 104 933 | KPX f k 104 934 | KPX f j 41 935 | KPX f i 52 936 | KPX f h 125 937 | KPX f f 52 938 | KPX f b 146 939 | KPX g backslash -104 940 | KPX g question -104 941 | KPX g z -41 942 | KPX g t -39 943 | KPX g q -52 944 | KPX g h -15 945 | KPX g g -39 946 | KPX g e -24 947 | KPX h backslash -84 948 | KPX h z -31 949 | KPX h u -31 950 | KPX h t -52 951 | KPX h r -21 952 | KPX h q -42 953 | KPX h g -21 954 | KPX h e -10 955 | KPX h c -11 956 | KPX i a 21 957 | KPX i backslash -52 958 | KPX i question -52 959 | KPX i z -31 960 | KPX i t -24 961 | KPX i s 11 962 | KPX i q -10 963 | KPX i p 31 964 | KPX i n 21 965 | KPX i l 21 966 | KPX i g -8 967 | KPX i d 21 968 | KPX i b 31 969 | KPX j backslash -62 970 | KPX j question -62 971 | KPX j q -31 972 | KPX j p 42 973 | KPX j o 11 974 | KPX j j 52 975 | KPX k backslash -42 976 | KPX k question -52 977 | KPX k q -42 978 | KPX l braceright 104 979 | KPX l a 24 980 | KPX l quotesingle 72 981 | KPX l quotedbl 83 982 | KPX l quotedblright 62 983 | KPX l backslash 52 984 | KPX l comma 39 985 | KPX l y 31 986 | KPX l x 41 987 | KPX l w 41 988 | KPX l u 10 989 | KPX l q -32 990 | KPX l p 31 991 | KPX l o 31 992 | KPX l n 31 993 | KPX l m 31 994 | KPX l l 62 995 | KPX l k 62 996 | KPX l j 93 997 | KPX l i 31 998 | KPX l h 83 999 | KPX l f 31 1000 | KPX l b 93 1001 | KPX m backslash -104 1002 | KPX m question -94 1003 | KPX m z -31 1004 | KPX m t -42 1005 | KPX m r -21 1006 | KPX m q -42 1007 | KPX m g -21 1008 | KPX m e -21 1009 | KPX n backslash -83 1010 | KPX n question -83 1011 | KPX n t -71 1012 | KPX n o 21 1013 | KPX n l 31 1014 | KPX n i -16 1015 | KPX n g -23 1016 | KPX o a 11 1017 | KPX o w 24 1018 | KPX o t -10 1019 | KPX o s 21 1020 | KPX o q -42 1021 | KPX o p 31 1022 | KPX o o 21 1023 | KPX o l 31 1024 | KPX p backslash -73 1025 | KPX p comma -70 1026 | KPX p question -83 1027 | KPX p y 31 1028 | KPX p w 42 1029 | KPX p t -42 1030 | KPX p s 24 1031 | KPX p r -21 1032 | KPX p q -42 1033 | KPX p p 8 1034 | KPX p o 23 1035 | KPX p k 42 1036 | KPX p e -8 1037 | KPX p b 31 1038 | KPX q backslash -62 1039 | KPX q question -83 1040 | KPX q w 42 1041 | KPX q s 42 1042 | KPX q q -21 1043 | KPX q p 42 1044 | KPX q o 10 1045 | KPX q j 63 1046 | KPX q i 21 1047 | KPX q g 10 1048 | KPX r four 42 1049 | KPX r backslash -41 1050 | KPX r comma -31 1051 | KPX r question -41 1052 | KPX r y 42 1053 | KPX r x 42 1054 | KPX r w 53 1055 | KPX r v 32 1056 | KPX r u 21 1057 | KPX r t 16 1058 | KPX r r 31 1059 | KPX r q -41 1060 | KPX r p 53 1061 | KPX r o 52 1062 | KPX r n 42 1063 | KPX r m 39 1064 | KPX r l 42 1065 | KPX r k 53 1066 | KPX r i 31 1067 | KPX r h 73 1068 | KPX r g -40 1069 | KPX r f 53 1070 | KPX r d -15 1071 | KPX r b 63 1072 | KPX s question -84 1073 | KPX s t -31 1074 | KPX t braceright 73 1075 | KPX t slash 84 1076 | KPX t backslash 73 1077 | KPX t semicolon 52 1078 | KPX t colon 63 1079 | KPX t comma 55 1080 | KPX t y 42 1081 | KPX t x 42 1082 | KPX t w 42 1083 | KPX t u 39 1084 | KPX t p 73 1085 | KPX t o 52 1086 | KPX t l 42 1087 | KPX t k 52 1088 | KPX t j 21 1089 | KPX t i 31 1090 | KPX t h 52 1091 | KPX t g -21 1092 | KPX t b 42 1093 | KPX u exclam -47 1094 | KPX u backslash -84 1095 | KPX u question -94 1096 | KPX u z -42 1097 | KPX u v -21 1098 | KPX u t -47 1099 | KPX u r -24 1100 | KPX u q -42 1101 | KPX v backslash -73 1102 | KPX v comma -47 1103 | KPX v question -32 1104 | KPX v y 42 1105 | KPX v x 52 1106 | KPX v w 62 1107 | KPX v v 20 1108 | KPX v u 41 1109 | KPX v s 52 1110 | KPX v r 31 1111 | KPX v q -32 1112 | KPX v p 52 1113 | KPX v o 52 1114 | KPX v n 31 1115 | KPX v m 31 1116 | KPX v l 41 1117 | KPX v k 52 1118 | KPX v j 41 1119 | KPX v i 8 1120 | KPX v h 42 1121 | KPX v f 10 1122 | KPX v b 62 1123 | KPX w parenright -41 1124 | KPX w backslash -94 1125 | KPX w comma -39 1126 | KPX w question -104 1127 | KPX w y 10 1128 | KPX w w 21 1129 | KPX w u 21 1130 | KPX w t -42 1131 | KPX w s 21 1132 | KPX w q -52 1133 | KPX w p 21 1134 | KPX w o 21 1135 | KPX w k 10 1136 | KPX w i 8 1137 | KPX w h 31 1138 | KPX w e -16 1139 | KPX w b 31 1140 | KPX x backslash -94 1141 | KPX x question -94 1142 | KPX x z -53 1143 | KPX x t -32 1144 | KPX x q -32 1145 | KPX x i -23 1146 | KPX y backslash -94 1147 | KPX y question -73 1148 | KPX y z -52 1149 | KPX y t -63 1150 | KPX y q -31 1151 | KPX y j 21 1152 | KPX y i -11 1153 | KPX z backslash -105 1154 | KPX z comma -15 1155 | KPX z question -94 1156 | KPX z z -52 1157 | EndKernPairs 1158 | EndKernData 1159 | EndFontMetrics 1160 | -------------------------------------------------------------------------------- /javascript-pixi/src/fonts/JennaSue.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endel/colyseus-tic-tac-toe/bf7b31f5e256bee056bc468dc27ed6fbd0c93cea/javascript-pixi/src/fonts/JennaSue.eot -------------------------------------------------------------------------------- /javascript-pixi/src/fonts/JennaSue.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endel/colyseus-tic-tac-toe/bf7b31f5e256bee056bc468dc27ed6fbd0c93cea/javascript-pixi/src/fonts/JennaSue.otf -------------------------------------------------------------------------------- /javascript-pixi/src/fonts/JennaSue.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Created by FontForge 20120731 at Sat Feb 27 16:46:40 2016 6 | By Endel 7 | 8 | 9 | 10 | 25 | 27 | 29 | 31 | 33 | 35 | 37 | 41 | 44 | 47 | 51 | 56 | 61 | 65 | 68 | 73 | 77 | 81 | 87 | 92 | 95 | 97 | 99 | 103 | 107 | 111 | 114 | 117 | 120 | 126 | 130 | 134 | 137 | 141 | 144 | 148 | 152 | 157 | 160 | 165 | 168 | 172 | 176 | 179 | 182 | 186 | 189 | 192 | 195 | 199 | 202 | 205 | 208 | 211 | 215 | 218 | 221 | 224 | 226 | 228 | 230 | 232 | 234 | 236 | 239 | 242 | 249 | 254 | 257 | 262 | 267 | 269 | 273 | 275 | 278 | 280 | 283 | 286 | 289 | 292 | 296 | 299 | 302 | 306 | 309 | 311 | 314 | 316 | 318 | 323 | 326 | 339 | 346 | 350 | 352 | 354 | 356 | 358 | 360 | 362 | 364 | 367 | 370 | 373 | 376 | 380 | 382 | 385 | 388 | 390 | 392 | 395 | 399 | 402 | 404 | 406 | 409 | 412 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | 695 | 696 | 697 | 698 | 699 | 700 | 701 | 702 | 703 | 704 | 705 | 706 | 707 | 708 | 709 | 710 | 711 | 712 | 713 | 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 737 | 738 | 739 | 740 | 741 | 742 | 743 | 744 | 745 | 746 | 747 | 748 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 763 | 764 | 765 | 766 | 767 | 768 | 769 | 770 | 771 | 772 | 773 | 774 | 775 | 776 | 777 | 778 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 816 | 817 | 818 | 819 | 820 | 821 | 822 | 823 | 824 | 825 | 826 | 827 | 828 | 829 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 955 | 956 | 957 | 958 | 959 | 960 | 961 | 962 | 963 | 964 | 965 | 966 | 967 | 968 | 969 | 970 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 978 | 979 | 980 | 981 | 982 | 983 | 984 | 985 | 986 | 987 | 988 | 989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 997 | 998 | 999 | 1000 | 1001 | 1002 | 1003 | 1004 | 1005 | 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | 1012 | 1013 | 1014 | 1015 | 1016 | 1017 | 1018 | 1019 | 1020 | 1021 | 1022 | 1023 | 1024 | 1025 | 1026 | 1027 | 1028 | 1029 | 1030 | 1031 | 1032 | 1033 | 1034 | 1035 | 1036 | 1037 | 1038 | 1039 | 1040 | 1041 | 1042 | 1043 | 1044 | 1045 | 1046 | 1047 | 1048 | 1049 | 1050 | 1051 | 1052 | 1053 | 1054 | 1055 | 1056 | 1057 | 1058 | 1059 | 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | 1066 | 1067 | 1068 | 1069 | 1070 | 1071 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | 1086 | 1087 | 1088 | 1089 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | 1096 | 1097 | 1098 | 1099 | 1100 | 1101 | 1102 | 1103 | 1104 | 1105 | 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 1112 | 1113 | 1114 | 1115 | 1116 | 1117 | 1118 | 1119 | 1120 | 1121 | 1122 | 1123 | 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 1130 | 1131 | 1132 | 1133 | 1134 | 1135 | 1136 | 1137 | 1138 | 1139 | 1140 | 1141 | 1142 | 1143 | 1144 | 1145 | 1146 | 1147 | 1148 | 1149 | 1150 | 1151 | 1152 | 1153 | 1154 | 1155 | 1156 | 1157 | 1158 | 1159 | 1160 | 1161 | 1162 | 1163 | 1164 | 1165 | 1166 | 1167 | 1168 | 1169 | 1170 | 1171 | 1172 | 1173 | 1174 | 1175 | 1176 | 1177 | 1178 | 1179 | 1180 | 1181 | 1182 | 1183 | 1184 | 1185 | 1186 | 1187 | 1188 | 1189 | 1190 | 1191 | 1192 | 1193 | 1194 | 1195 | 1196 | 1197 | 1198 | 1199 | 1200 | 1201 | 1202 | 1203 | 1204 | 1205 | 1206 | 1207 | 1208 | 1209 | 1210 | 1211 | 1212 | 1213 | 1214 | 1215 | 1216 | 1217 | 1218 | 1219 | 1220 | 1221 | 1222 | 1223 | 1224 | 1225 | 1226 | 1227 | 1228 | 1229 | 1230 | 1231 | 1232 | 1233 | 1234 | 1235 | 1236 | 1237 | 1238 | 1239 | 1240 | 1241 | 1242 | 1243 | 1244 | 1245 | 1246 | 1247 | 1248 | 1249 | 1250 | 1251 | 1252 | 1253 | 1254 | 1255 | 1256 | 1257 | 1258 | 1259 | 1260 | 1261 | 1262 | 1263 | 1264 | 1265 | 1266 | 1267 | 1268 | 1269 | 1270 | 1271 | 1272 | 1273 | 1274 | 1275 | 1276 | 1277 | 1278 | 1279 | 1280 | 1281 | 1282 | 1283 | 1284 | 1285 | 1286 | 1287 | 1288 | 1289 | 1290 | 1291 | 1292 | 1293 | 1294 | 1295 | 1296 | 1297 | 1298 | 1299 | 1300 | 1301 | 1302 | 1303 | 1304 | 1305 | 1306 | 1307 | 1308 | 1309 | 1310 | 1311 | 1312 | 1313 | 1314 | 1315 | 1316 | 1317 | 1318 | 1319 | 1320 | 1321 | 1322 | 1323 | 1324 | 1325 | 1326 | 1327 | 1328 | 1329 | 1330 | 1331 | 1332 | 1333 | 1334 | 1335 | 1336 | 1337 | 1338 | 1339 | 1340 | 1341 | 1342 | 1343 | 1344 | 1345 | 1346 | 1347 | 1348 | 1349 | 1350 | 1351 | 1352 | 1353 | 1354 | 1355 | 1356 | 1357 | 1358 | 1359 | 1360 | 1361 | 1362 | 1363 | 1364 | 1365 | 1366 | 1367 | 1368 | 1369 | 1370 | 1371 | 1372 | 1373 | 1374 | 1375 | 1376 | 1377 | 1378 | 1379 | 1380 | 1381 | 1382 | 1383 | 1384 | 1385 | 1386 | 1387 | 1388 | 1389 | 1390 | 1391 | 1392 | 1393 | 1394 | 1395 | 1396 | 1397 | 1398 | 1399 | 1400 | 1401 | 1402 | 1403 | 1404 | 1405 | 1406 | 1407 | 1408 | 1409 | 1410 | 1411 | 1412 | 1413 | 1414 | 1415 | 1416 | 1417 | 1418 | 1419 | 1420 | 1421 | 1422 | 1423 | 1424 | 1425 | 1426 | 1427 | 1428 | 1429 | 1430 | 1431 | 1432 | 1433 | 1434 | -------------------------------------------------------------------------------- /javascript-pixi/src/fonts/JennaSue.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endel/colyseus-tic-tac-toe/bf7b31f5e256bee056bc468dc27ed6fbd0c93cea/javascript-pixi/src/fonts/JennaSue.ttf -------------------------------------------------------------------------------- /javascript-pixi/src/fonts/JennaSue.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endel/colyseus-tic-tac-toe/bf7b31f5e256bee056bc468dc27ed6fbd0c93cea/javascript-pixi/src/fonts/JennaSue.woff -------------------------------------------------------------------------------- /javascript-pixi/src/fonts/JennaSue.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endel/colyseus-tic-tac-toe/bf7b31f5e256bee056bc468dc27ed6fbd0c93cea/javascript-pixi/src/fonts/JennaSue.woff2 -------------------------------------------------------------------------------- /javascript-pixi/src/images/background.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endel/colyseus-tic-tac-toe/bf7b31f5e256bee056bc468dc27ed6fbd0c93cea/javascript-pixi/src/images/background.jpg -------------------------------------------------------------------------------- /javascript-pixi/src/images/board.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endel/colyseus-tic-tac-toe/bf7b31f5e256bee056bc468dc27ed6fbd0c93cea/javascript-pixi/src/images/board.png -------------------------------------------------------------------------------- /javascript-pixi/src/images/clock-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endel/colyseus-tic-tac-toe/bf7b31f5e256bee056bc468dc27ed6fbd0c93cea/javascript-pixi/src/images/clock-icon.png -------------------------------------------------------------------------------- /javascript-pixi/src/images/colyseus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endel/colyseus-tic-tac-toe/bf7b31f5e256bee056bc468dc27ed6fbd0c93cea/javascript-pixi/src/images/colyseus.png -------------------------------------------------------------------------------- /javascript-pixi/src/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endel/colyseus-tic-tac-toe/bf7b31f5e256bee056bc468dc27ed6fbd0c93cea/javascript-pixi/src/images/logo.png -------------------------------------------------------------------------------- /javascript-pixi/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Tic-Tac-Toe Multiplayer Online - Colyseus Example 5 | 6 | 7 | 8 | 9 | 10 | 11 | 41 | 42 | 43 |

Please wait...

44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /javascript-pixi/src/main.js: -------------------------------------------------------------------------------- 1 | import * as PIXI from 'pixi.js' 2 | import Application from './Application'; 3 | import TitleScreen from './screens/TitleScreen' 4 | 5 | // Pre-load all assets before starting the Application. 6 | var loader = new PIXI.loaders.Loader(); 7 | 8 | // 9 | // ATTENTION: We use "require()" here because ParcelJS takes care of it. 10 | // This is not the case if you're using other bundler such as Webpack, etc. 11 | // 12 | loader.add('logo', require('./images/logo.png')) 13 | loader.add('background', require('./images/background.jpg')); 14 | loader.add('colyseus', require('./images/colyseus.png')); 15 | loader.add('clock-icon', require('./images/clock-icon.png')); 16 | loader.add('board', require('./images/board.png')); 17 | 18 | loader.on('complete', () => { 19 | var loading = document.querySelector('.loading'); 20 | document.body.removeChild(loading); 21 | 22 | var app = new Application() 23 | app.gotoScene (TitleScreen) 24 | app.update() 25 | }) 26 | 27 | loader.load(); 28 | -------------------------------------------------------------------------------- /javascript-pixi/src/screens/EndGameScreen.js: -------------------------------------------------------------------------------- 1 | import * as PIXI from 'pixi.js' 2 | 3 | import Application from '../Application' 4 | import GameScreen from './GameScreen' 5 | 6 | import * as LocalStorage from '../core/LocalStorage' 7 | 8 | export default class EndGameScreen extends PIXI.Container { 9 | 10 | constructor (options = {}) { 11 | super() 12 | 13 | let titleText = "Draw game!" 14 | , incrementAttribute = null 15 | 16 | if (options.draw) { 17 | titleText = "Draw game!" 18 | incrementAttribute = 'draw' 19 | 20 | } else if (options.won) { 21 | titleText = "You win!" 22 | incrementAttribute = 'win' 23 | 24 | } else { 25 | titleText = "You lose" 26 | incrementAttribute = 'loss' 27 | } 28 | 29 | // increment statistics 30 | LocalStorage.set( incrementAttribute, LocalStorage.get(incrementAttribute)+1 ) 31 | 32 | this.title = new PIXI.Text(titleText, { 33 | font: "132px JennaSue", 34 | fill: 0x000, 35 | textAlign: 'center' 36 | }) 37 | this.title.pivot.x = this.title.width / 2 38 | this.addChild(this.title) 39 | 40 | this.instructionText = new PIXI.Text("touch to play again", { 41 | font: "52px JennaSue", 42 | fill: 0x000, 43 | textAlign: 'center' 44 | }) 45 | this.instructionText.pivot.x = this.instructionText.width / 2 46 | this.instructionText.pivot.y = this.instructionText.height / 2 47 | this.addChild(this.instructionText) 48 | 49 | let statuses = `Win ${ LocalStorage.get('win') } | Draw ${ LocalStorage.get('draw') } | Loss ${ LocalStorage.get('loss') } ` 50 | this.statusesText = new PIXI.Text(statuses, { 51 | font: "52px JennaSue", 52 | fill: 0x000, 53 | textAlign: 'center' 54 | }) 55 | this.statusesText.pivot.x = this.statusesText.width / 2 56 | this.statusesText.pivot.y = this.statusesText.height / 2 57 | this.addChild(this.statusesText) 58 | 59 | this.colyseus = new PIXI.Sprite.fromImage('colyseus') 60 | this.colyseus.pivot.x = this.colyseus.width / 2 61 | this.addChild(this.colyseus) 62 | 63 | this.interactive = true 64 | this.once('click', this.startGame.bind(this)) 65 | this.once('touchstart', this.startGame.bind(this)) 66 | 67 | this.on('dispose', this.onDispose.bind(this)) 68 | } 69 | 70 | transitionIn () { 71 | tweener.add(this.title).from({y: this.title.y - 10, alpha: 0}, 300, Tweener.ease.quintOut) 72 | tweener.add(this.colyseus).from({ y: this.colyseus.y + 10, alpha: 0 }, 300, Tweener.ease.quintOut) 73 | tweener.add(this.statusesText).from({ alpha: 0 }, 300, Tweener.ease.quintOut) 74 | return tweener.add(this.instructionText).from({ alpha: 0 }, 300, Tweener.ease.quintOut) 75 | } 76 | 77 | transitionOut () { 78 | tweener.remove(this.title) 79 | tweener.remove(this.colyseus) 80 | tweener.remove(this.statusesText) 81 | tweener.remove(this.instructionText) 82 | 83 | tweener.add(this.title).to({y: this.title.y - 10, alpha: 0}, 300, Tweener.ease.quintOut) 84 | tweener.add(this.colyseus).to({ y: this.colyseus.y + 10, alpha: 0 }, 300, Tweener.ease.quintOut) 85 | tweener.add(this.statusesText).to({ alpha: 0 }, 300, Tweener.ease.quintOut) 86 | return tweener.add(this.instructionText).to({ alpha: 0 }, 300, Tweener.ease.quintOut) 87 | } 88 | 89 | startGame () { 90 | this.emit('goto', GameScreen) 91 | } 92 | 93 | onResize () { 94 | this.MARGIN = (Application.WIDTH / 100) * 8 // 5% 95 | 96 | this.title.x = Application.WIDTH / 2; 97 | this.title.y = this.MARGIN 98 | 99 | this.instructionText.x = Application.WIDTH / 2 100 | this.instructionText.y = Application.HEIGHT / 2 - this.instructionText.height / 3.8 101 | 102 | this.statusesText.x = Application.WIDTH / 2 103 | this.statusesText.y = this.instructionText.y + this.instructionText.height + 10 104 | 105 | this.colyseus.x = Application.WIDTH / 2 106 | this.colyseus.y = Application.HEIGHT - this.colyseus.height - this.MARGIN 107 | } 108 | 109 | onDispose () { 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /javascript-pixi/src/screens/GameScreen.js: -------------------------------------------------------------------------------- 1 | import * as PIXI from 'pixi.js' 2 | 3 | import Application from '../Application' 4 | import TitleScreen from './TitleScreen' 5 | import EndGameScreen from './EndGameScreen' 6 | 7 | import Board from '../components/Board' 8 | 9 | import { Client, getStateCallbacks } from 'colyseus.js' 10 | 11 | // define endpoint based on environment 12 | const colyseusSDK = new Client("http://localhost:2567"); 13 | 14 | export default class GameScreen extends PIXI.Container { 15 | 16 | constructor () { 17 | super() 18 | 19 | this.waitingText = new PIXI.Text("Waiting for an opponent...", { 20 | font: "100px JennaSue", 21 | fill: '#000', 22 | textAlign: 'center' 23 | }) 24 | this.waitingText.pivot.x = this.waitingText.width / 2 25 | this.waitingText.pivot.y = this.waitingText.height / 2 26 | this.addChild(this.waitingText) 27 | 28 | this.on('dispose', this.onDispose.bind(this)) 29 | 30 | this.board = new Board() 31 | this.connect(); 32 | 33 | this.onResize() 34 | } 35 | 36 | async connect () { 37 | this.room = await colyseusSDK.joinOrCreate('tictactoe'); 38 | 39 | const $ = getStateCallbacks(this.room); 40 | 41 | let numPlayers = 0; 42 | $(this.room.state).players.onAdd(() => { 43 | numPlayers++; 44 | 45 | if (numPlayers === 2) { 46 | this.onJoin(); 47 | } 48 | }); 49 | 50 | $(this.room.state).board.onChange((value, index) => { 51 | const x = index % 3; 52 | const y = Math.floor(index / 3); 53 | this.board.set(x, y, value); 54 | }) 55 | 56 | $(this.room.state).listen("currentTurn", (sessionId) => { 57 | // go to next turn after a little delay, to ensure "onJoin" gets called before this. 58 | setTimeout(() => this.nextTurn(sessionId), 10); 59 | }); 60 | 61 | $(this.room.state).listen("draw", () => this.drawGame()); 62 | $(this.room.state).listen("winner", (sessionId) => this.showWinner(sessionId)); 63 | 64 | $(this.room.state).onChange((changes) => { 65 | console.log("state.onChange =>", changes); 66 | }); 67 | 68 | this.room.onError.once(() => this.emit('goto', TitleScreen)); 69 | } 70 | 71 | transitionIn () { 72 | tweener.add(this.waitingText).from({ alpha: 0 }, 300, Tweener.ease.quintOut) 73 | return tweener.add(this.waitingText.scale).from({x: 1.5, y: 1.5}, 300, Tweener.ease.quintOut) 74 | } 75 | 76 | transitionOut () { 77 | if (this.timeIcon) { 78 | tweener.add(this.timeIcon).to({y: this.timeIcon.y - 10, alpha: 0}, 300, Tweener.ease.quintOut) 79 | tweener.add(this.timeRemaining).to({y: this.timeRemaining.y - 10, alpha: 0}, 300, Tweener.ease.quintOut) 80 | tweener.add(this.board).to({ alpha: 0 }, 300, Tweener.ease.quintOut) 81 | return tweener.add(this.statusText).to({ y: this.statusText.y + 10, alpha: 0 }, 300, Tweener.ease.quintOut) 82 | 83 | } else { 84 | return tweener.add(this.waitingText).to({ alpha: 0 }, 300, Tweener.ease.quintOut) 85 | } 86 | } 87 | 88 | onJoin () { 89 | // not waiting anymore! 90 | this.removeChild(this.waitingText) 91 | 92 | this.timeIcon = new PIXI.Sprite.fromImage('clock-icon') 93 | this.timeIcon.pivot.x = this.timeIcon.width / 2 94 | this.addChild(this.timeIcon) 95 | 96 | this.timeRemaining = new PIXI.Text("10", { 97 | font: "100px JennaSue", 98 | fill: 0x000000, 99 | textAlign: 'center' 100 | }) 101 | this.timeRemaining.pivot.x = this.timeRemaining.width / 2 102 | this.addChild(this.timeRemaining) 103 | 104 | this.board.pivot.x = this.board.width / 2 105 | this.board.pivot.y = this.board.height / 2 106 | this.board.on('select', this.onSelect.bind(this)) 107 | this.addChild(this.board) 108 | 109 | this.statusText = new PIXI.Text("Your move!", { 110 | font: "100px JennaSue", 111 | fill: 0x000, 112 | textAlign: 'center' 113 | }) 114 | this.statusText.pivot.y = this.statusText.height / 2 115 | this.addChild(this.statusText) 116 | 117 | this.countdownInterval = clock.setInterval(this.turnCountdown.bind(this), 1000) 118 | 119 | this.onResize() 120 | } 121 | 122 | onSelect (x, y) { 123 | this.room.send("action", { x: x, y: y }) 124 | } 125 | 126 | nextTurn (playerId) { 127 | tweener.add(this.statusText).to({ 128 | y: Application.HEIGHT - Application.MARGIN + 10, 129 | alpha: 0 130 | }, 200, Tweener.ease.quintOut).then(() => { 131 | 132 | if (playerId == this.room.sessionId) { 133 | this.statusText.text = "Your move!" 134 | 135 | } else { 136 | this.statusText.text = "Opponent's turn..." 137 | } 138 | 139 | this.statusText.x = Application.WIDTH / 2 - this.statusText.width / 2 140 | 141 | tweener.add(this.statusText).to({ 142 | y: Application.HEIGHT - Application.MARGIN, 143 | alpha: 1 144 | }, 200, Tweener.ease.quintOut) 145 | 146 | }) 147 | 148 | this.timeRemaining.style.fill = '#000000'; 149 | this.timeRemaining.text = "10" 150 | this.countdownInterval.reset() 151 | } 152 | 153 | turnCountdown () { 154 | var currentNumber = parseInt(this.timeRemaining.text, 10) - 1 155 | 156 | if (currentNumber >= 0) { 157 | this.timeRemaining.text = currentNumber.toString() 158 | } 159 | 160 | if (currentNumber <= 3) { 161 | this.timeRemaining.style.fill = '#934e60'; 162 | } else { 163 | this.timeRemaining.style.fill = '#000000'; 164 | } 165 | 166 | } 167 | 168 | drawGame () { 169 | this.room.leave() 170 | this.emit('goto', EndGameScreen, { draw: true }) 171 | } 172 | 173 | showWinner (sessionId) { 174 | this.room.leave() 175 | this.emit('goto', EndGameScreen, { 176 | won: (this.room.sessionId == sessionId) 177 | }) 178 | } 179 | 180 | onResize () { 181 | this.waitingText.x = Application.WIDTH / 2 182 | this.waitingText.y = Application.HEIGHT / 2 183 | 184 | if (this.timeIcon) { 185 | var margin = Application.HEIGHT / 100 * 6 186 | 187 | this.timeIcon.x = Application.WIDTH / 2 - this.timeIcon.pivot.x 188 | this.timeIcon.y = margin 189 | 190 | this.timeRemaining.x = Application.WIDTH / 2 + this.timeIcon.pivot.x + 20 191 | this.timeRemaining.y = margin - 20 192 | 193 | this.board.x = Application.WIDTH / 2 194 | this.board.y = Application.HEIGHT / 2 195 | 196 | this.statusText.x = Application.WIDTH / 2 - this.statusText.width / 2 197 | this.statusText.y = Application.HEIGHT - margin 198 | } 199 | } 200 | 201 | onDispose () { 202 | } 203 | 204 | } 205 | -------------------------------------------------------------------------------- /javascript-pixi/src/screens/TitleScreen.js: -------------------------------------------------------------------------------- 1 | import * as PIXI from 'pixi.js' 2 | 3 | import Application from '../Application' 4 | import GameScreen from './GameScreen' 5 | 6 | export default class TitleScreen extends PIXI.Container { 7 | 8 | constructor () { 9 | super() 10 | 11 | this.title = new PIXI.Sprite.fromImage("logo") 12 | this.title.pivot.x = this.title.width / 2 13 | this.addChild(this.title) 14 | 15 | this.instructionText = new PIXI.Text("touch to start", { 16 | font: "62px JennaSue", 17 | fill: 0x000, 18 | textAlign: 'center' 19 | }) 20 | this.instructionText.pivot.x = this.instructionText.width / 2 21 | this.instructionText.pivot.y = this.instructionText.height / 2 22 | this.addChild(this.instructionText) 23 | 24 | this.colyseusLogo = new PIXI.Sprite.fromImage('colyseus') 25 | this.colyseusLogo.pivot.x = this.colyseusLogo.width / 2 26 | this.addChild(this.colyseusLogo) 27 | 28 | this.interactive = true 29 | this.once('click', this.startGame.bind(this)) 30 | this.once('touchstart', this.startGame.bind(this)) 31 | 32 | this.on('dispose', this.onDispose.bind(this)) 33 | } 34 | 35 | transitionIn () { 36 | tweener.add(this.title).from({y: this.title.y - 10, alpha: 0}, 300, Tweener.ease.quadOut) 37 | tweener.add(this.colyseusLogo).from({ y: this.colyseusLogo.y + 10, alpha: 0 }, 300, Tweener.ease.quadOut) 38 | return tweener.add(this.instructionText).from({ alpha: 0 }, 300, Tweener.ease.quadOut) 39 | } 40 | 41 | transitionOut () { 42 | tweener.remove(this.title) 43 | tweener.remove(this.colyseusLogo) 44 | tweener.remove(this.instructionText) 45 | 46 | tweener.add(this.title).to({y: this.title.y - 10, alpha: 0}, 300, Tweener.ease.quintOut) 47 | tweener.add(this.colyseusLogo).to({ y: this.colyseusLogo.y + 10, alpha: 0 }, 300, Tweener.ease.quintOut) 48 | return tweener.add(this.instructionText).to({ alpha: 0 }, 300, Tweener.ease.quintOut) 49 | } 50 | 51 | startGame () { 52 | this.emit('goto', GameScreen) 53 | } 54 | 55 | onResize () { 56 | this.title.x = Application.WIDTH / 2; 57 | this.title.y = Application.MARGIN 58 | 59 | this.instructionText.x = Application.WIDTH / 2 60 | this.instructionText.y = Application.HEIGHT / 2 - this.instructionText.height / 3.8 61 | 62 | this.colyseusLogo.x = Application.WIDTH / 2 63 | this.colyseusLogo.y = Application.HEIGHT - this.colyseusLogo.height - Application.MARGIN 64 | } 65 | 66 | onDispose () { 67 | window.removeEventListener('resize', this.onResizeCallback) 68 | } 69 | 70 | } 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /layout.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/endel/colyseus-tic-tac-toe/bf7b31f5e256bee056bc468dc27ed6fbd0c93cea/layout.psd -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tictactoe-colyseus", 3 | "version": "1.0.0", 4 | "description": "HTML5 Tic-Tac-Toe Game powered by Colyseus", 5 | "scripts": { 6 | "install": "npm install --prefix ./server && npm install --prefix ./javascript-pixi", 7 | "start": "npm start --prefix ./server" 8 | }, 9 | "author": "Endel Dreyer", 10 | "dependencies": { 11 | "css-loader": "^0.28.11", 12 | "json-loader": "^0.5.7", 13 | "style-loader": "^0.20.3", 14 | "webpack": "^4.5.0", 15 | "webpack-cli": "^2.0.14", 16 | "webpack-dev-server": "^3.1.3" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /server/index.ts: -------------------------------------------------------------------------------- 1 | import http from 'http'; 2 | import express from 'express'; 3 | import cors from "cors"; 4 | import { Server } from 'colyseus'; 5 | import { monitor } from "@colyseus/monitor"; 6 | import { playground } from "@colyseus/playground"; 7 | import { WebSocketTransport } from '@colyseus/ws-transport'; 8 | 9 | import { TicTacToe } from "./rooms/tictactoe" 10 | 11 | const app = express(); 12 | const port = Number(process.env.PORT || 2567); 13 | 14 | app.use(cors()); 15 | app.use(express.json()); 16 | 17 | app.use("/monitor", monitor()); 18 | app.use("/", playground()); 19 | app.use(express.static(__dirname + "/../frontend/public")); 20 | 21 | const server = http.createServer(app); 22 | const gameServer = new Server({ 23 | transport: new WebSocketTransport({ server: server }) 24 | }); 25 | 26 | gameServer.define('tictactoe', TicTacToe); 27 | gameServer.listen(port). 28 | then(() => console.log(`Listening on http://localhost:${port}`)); 29 | -------------------------------------------------------------------------------- /server/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "colyseus-starter", 3 | "version": "1.0.0", 4 | "description": "Colyseus Server - Minimal example", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "tsx ./index.ts" 8 | }, 9 | "author": "", 10 | "license": "MIT", 11 | "dependencies": { 12 | "@colyseus/monitor": "^0.16.6", 13 | "@colyseus/playground": "^0.16.3", 14 | "@colyseus/ws-transport": "^0.16.0", 15 | "colyseus": "^0.16.0", 16 | "cors": "^2.8.5", 17 | "express": "^4.17.1" 18 | }, 19 | "devDependencies": { 20 | "@types/express": "^4.17.1", 21 | "tsx": "^4.19.3", 22 | "typescript": "^5.8.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /server/rooms/tictactoe.ts: -------------------------------------------------------------------------------- 1 | import { Room, Delayed, Client } from 'colyseus'; 2 | import { type, Schema, MapSchema, ArraySchema } from '@colyseus/schema'; 3 | 4 | const TURN_TIMEOUT = 10 5 | const BOARD_WIDTH = 3; 6 | 7 | class State extends Schema { 8 | @type("string") currentTurn: string; 9 | @type({ map: "boolean" }) players = new MapSchema(); 10 | @type(["number"]) board: ArraySchema = new ArraySchema(0, 0, 0, 0, 0, 0, 0, 0, 0); 11 | @type("string") winner: string; 12 | @type("boolean") draw: boolean; 13 | } 14 | 15 | export class TicTacToe extends Room { 16 | state = new State(); 17 | maxClients = 2; 18 | randomMoveTimeout: Delayed; 19 | 20 | onCreate () { 21 | this.onMessage("action", (client, message) => this.playerAction(client, message)); 22 | } 23 | 24 | onJoin (client: Client) { 25 | this.state.players.set(client.sessionId, true); 26 | 27 | if (this.state.players.size === 2) { 28 | this.state.currentTurn = client.sessionId; 29 | this.setAutoMoveTimeout(); 30 | 31 | // lock this room for new users 32 | this.lock(); 33 | } 34 | } 35 | 36 | playerAction (client: Client, data: any) { 37 | if (this.state.winner || this.state.draw) { 38 | return false; 39 | } 40 | 41 | if (client.sessionId === this.state.currentTurn) { 42 | const playerIds = Array.from(this.state.players.keys()); 43 | 44 | const index = data.x + BOARD_WIDTH * data.y; 45 | 46 | if (this.state.board[index] === 0) { 47 | const move = (client.sessionId === playerIds[0]) ? 1 : 2; 48 | this.state.board[index] = move; 49 | 50 | if (this.checkWin(data.x, data.y, move)) { 51 | this.state.winner = client.sessionId; 52 | 53 | } else if (this.checkBoardComplete()) { 54 | this.state.draw = true; 55 | 56 | } else { 57 | // switch turn 58 | const otherPlayerSessionId = (client.sessionId === playerIds[0]) ? playerIds[1] : playerIds[0]; 59 | 60 | this.state.currentTurn = otherPlayerSessionId; 61 | 62 | this.setAutoMoveTimeout(); 63 | } 64 | 65 | } 66 | } 67 | } 68 | 69 | setAutoMoveTimeout() { 70 | if (this.randomMoveTimeout) { 71 | this.randomMoveTimeout.clear(); 72 | } 73 | 74 | this.randomMoveTimeout = this.clock.setTimeout(() => this.doRandomMove(), TURN_TIMEOUT * 1000); 75 | } 76 | 77 | checkBoardComplete () { 78 | return this.state.board 79 | .filter(item => item === 0) 80 | .length === 0; 81 | } 82 | 83 | doRandomMove () { 84 | const sessionId = this.state.currentTurn; 85 | for (let x=0; x 0) { 151 | this.state.winner = remainingPlayerIds[0] 152 | } 153 | } 154 | 155 | } 156 | 157 | -------------------------------------------------------------------------------- /server/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "lib", 4 | "module": "commonjs", 5 | "lib": ["es6"], 6 | "target": "es2016", 7 | "declaration": true, 8 | "noImplicitAny": false, 9 | "experimentalDecorators": true, 10 | "sourceMap": false, 11 | "esModuleInterop": true 12 | }, 13 | "include": [ 14 | "**/*.ts" 15 | ] 16 | } 17 | --------------------------------------------------------------------------------