Selaa lähdekoodia

Merge branch 'test'

Jojo 5 vuotta sitten
vanhempi
commit
08f553a2e6
41 muutettua tiedostoa jossa 2071 lisäystä ja 360 poistoa
  1. 3 0
      .eslintrc.json
  2. 8 3
      .gitignore
  3. 0 42
      buildScripts/srcServer.js
  4. 0 5
      buildScripts/startMessage.js
  5. 0 7
      buildScripts/testSetup.js
  6. 26 0
      config.xml
  7. 1247 57
      package-lock.json
  8. 37 19
      package.json
  9. 7 0
      server/game-server/duel-controller.js
  10. 33 0
      server/main.js
  11. 16 0
      server/players/player-id.js
  12. 0 18
      src/GameCreator.js
  13. 37 37
      src/assets/all-heroes.json
  14. 20 0
      src/common/game-params.js
  15. 35 0
      src/common/socket-service.js
  16. 10 0
      src/common/utils/const/faction-enum.js
  17. 7 0
      src/common/utils/const/game-deck-mode-enum.js
  18. 6 0
      src/common/utils/const/game-type-enum.js
  19. 7 0
      src/common/utils/const/phaser-scene-enum.js
  20. 9 0
      src/common/utils/prompt-renderer.js
  21. 16 0
      src/game/control/behaviors/deck-building-steps.js
  22. 65 0
      src/game/control/game-controller-pnp.js
  23. 10 0
      src/game/control/player-controller.js
  24. 0 0
      src/game/game-model/game-global-state.js
  25. 0 0
      src/game/game-model/heroes/hero-ability.js
  26. 22 11
      src/game/game-model/heroes/hero-factory.js
  27. 0 0
      src/game/game-model/heroes/hero.js
  28. 18 0
      src/game/game-model/player.js
  29. 10 0
      src/game/views/deck-building-scene.js
  30. 36 0
      src/game/views/faction-deck-building-scene.js
  31. 69 0
      src/game/views/game-world-scene.js
  32. 50 5
      src/index.html
  33. 57 53
      src/main.js
  34. 15 0
      src/main.test.js
  35. 41 0
      src/menu/control/menu-controller.js
  36. 55 0
      src/menu/views/main-menu-scene.js
  37. 2 0
      src/polyfills.js
  38. 0 62
      src/view/PhaserGame.js
  39. 0 41
      webpack.config.dev.js
  40. 93 0
      webpack.config.js
  41. 4 0
      www/.gitignore

+ 3 - 0
.eslintrc.json

@@ -20,5 +20,8 @@
     "semi": [2, "always"],
     "eqeqeq": [2,"always"],
     "no-unused-vars": 1
+  },
+  "settings": {
+    "import/core-modules": ["phaser"]
   }
 }

+ 8 - 3
.gitignore

@@ -60,8 +60,13 @@ typings/
 # next.js build output
 .next
 
-#prod build
-dist
-
 #mock db
 db.json
+
+#webpack dev build
+assets
+webpack
+
+#cordova build
+platforms/
+plugins/

+ 0 - 42
buildScripts/srcServer.js

@@ -1,42 +0,0 @@
-import express from 'express';
-import path from 'path';
-import open from 'open';
-import webpack from 'webpack';
-import config from '../webpack.config.dev';
-
-/* eslint-disable no-console */
-
-const port = 3000;
-const app = express();
-const compiler = webpack(config);
-
-app.use(require('webpack-dev-middleware')(compiler, {
-  noInfo: true,
-  publicPath: config.output.publicPath
-}));
-
-app.get('/', function (req, res) {
-  // bind path
-  res.sendFile(path.join(__dirname, '../src/index.html'));
-});
-
-
-// The onle below is let's say the real prod DB, the test one will be json server
-// Use same express for test and for serving API to web app :
-app.get('/users', function (req, res) {
-  //Hard coding here but ler's pretend it is a real DB
-
-  res.json([
-    { "id": 1, "firstName": "Jojo", "lastName": "LB", "email": "jojo@gmail.com" },
-    { "id": 2, "firstName": "Lily", "lastName": "Ma", "email": "lily@gmail.com" },
-    { "id": 3, "firstName": "Bob", "lastName": "Smith", "email": "bob@gmail.com" }
-  ]);
-});
-
-app.listen(port, function (err) {
-  if (err) {
-    console.log(err);
-  } else {
-    open('http://localhost:' + port);
-  }
-});

+ 0 - 5
buildScripts/startMessage.js

@@ -1,5 +0,0 @@
-import chalk from 'chalk';
-
-//var chalk = require('chalk');
-
-console.log(chalk.green('Starting app in dev mode...')); // eslint-disable-line no-console

+ 0 - 7
buildScripts/testSetup.js

@@ -1,7 +0,0 @@
-// This file isn't transpiled, so must use CommonJS and ES5
-
-// Register babel to transpile before our tests run.
-require('@babel/register')();
-
-// Disable webpack features that Mocha doesn't understand.
-require.extensions['.css'] = function() {};

+ 26 - 0
config.xml

@@ -0,0 +1,26 @@
+<?xml version='1.0' encoding='utf-8'?>
+<widget id="com.jojo.twelve-heroes" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
+    <name>twelve-heroes</name>
+    <description>
+        A sample Apache Cordova application that responds to the deviceready event.
+    </description>
+    <author email="dev@cordova.apache.org" href="http://cordova.io">
+        Apache Cordova Team
+    </author>
+    <content src="index.html" />
+    <plugin name="cordova-plugin-whitelist" spec="1" />
+    <access origin="*" />
+    <allow-intent href="http://*/*" />
+    <allow-intent href="https://*/*" />
+    <allow-intent href="tel:*" />
+    <allow-intent href="sms:*" />
+    <allow-intent href="mailto:*" />
+    <allow-intent href="geo:*" />
+    <platform name="android">
+        <allow-intent href="market:*" />
+    </platform>
+    <platform name="ios">
+        <allow-intent href="itms:*" />
+        <allow-intent href="itms-apps:*" />
+    </platform>
+</widget>

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 1247 - 57
package-lock.json


+ 37 - 19
package.json

@@ -1,46 +1,52 @@
 {
-  "name": "javascript-development-environment",
+  "name": "twelve-heroes",
   "version": "1.0.0",
-  "description": "JavaScript development environment Pluralsight course by Cory House",
+  "description": "twelve heroes game",
+  "keywords": [
+    "ecosystem:cordova"
+  ],
   "scripts": {
-    "prestart": "babel-node buildScripts/startMessage.js",
-    "start": "npm-run-all --parallel open:src lint:watch test:watch",
-    "open:src": "babel-node buildScripts/srcServer.js",
-    "lint": "esw webpack.config.* src buildScripts --color",
+    "server": "babel-node server/main.js",
+    "dev": "webpack --watch",
+    "precor-build": "cordova prepare",
+    "cor-build": "CORDOVA=true cordova build -- --webpackConfig webpack.config.js",
+    "cordova": "CORDOVA=true cordova run -- -w webpack.config.js",
+    "start": "npm-run-all --parallel server dev lint:watch",
+    "lint": "esw webpack.config.* src --color",
     "lint:watch": "npm run lint -- --watch",
     "localtunnel": "lt --port 3000",
     "share": "npm-run-all --parallel open:src localtunnel",
-    "test": "mocha --reporter progress buildScripts/testSetup.js \"src/**/*.test.js\"",
+    "test": "mocha --reporter progress \"src/**/*.test.js\"",
     "test:watch": "npm run test -- --watch",
-    "generate-mock-data": "babel-node buildScripts/generateMockData.js",
-    "prestart-mockapi": "npm run generate-mock-data",
-    "start-mockapi": "json-server --watch src/api/db.json --port 3001",
-    "clean-dist": "rimraf ./dist && mkdir dist",
-    "prebuild": "npm-run-all clean-dist test lint",
-    "build": "babel-node buildScripts/build.js",
-    "postbuild": "babel-node buildScripts/distServer.js",
-    "deploy": "surge ./dist"
+    "clean": "git clean -fdx -e node_modules"
   },
-  "author": "Cory House",
+  "author": "JOJO",
   "license": "MIT",
   "dependencies": {
     "@babel/node": "7.6.2",
     "@babel/polyfill": "7.6.0",
+    "express": "^4.17.1",
     "phaser": "3.19.0"
   },
   "devDependencies": {
     "@babel/cli": "7.6.2",
-    "@babel/core": "7.6.2",
+    "@babel/core": "^7.6.2",
+    "@babel/plugin-proposal-object-rest-spread": "^7.6.2",
     "@babel/preset-env": "7.6.2",
-    "@babel/register": "7.6.2",
+    "@babel/register": "^7.6.2",
     "babel-loader": "8.0.6",
+    "browser-sync": "2.26.7",
+    "browser-sync-webpack-plugin": "2.2.2",
     "chai": "4.2.0",
     "chalk": "2.4.2",
     "clean-webpack-plugin": "3.0.0",
+    "copy-webpack-plugin": "^5.1.0",
+    "cordova-browser": "^6.0.0",
+    "cordova-plugin-webpack": "^0.4.7",
+    "cordova-plugin-whitelist": "^1.3.4",
     "eslint": "6.4.0",
     "eslint-plugin-import": "2.18.2",
     "eslint-watch": "6.0.1",
-    "express": "4.17.1",
     "file-loader": "4.2.0",
     "html-webpack-plugin": "3.2.0",
     "mocha": "6.2.0",
@@ -48,9 +54,21 @@
     "open": "6.4.0",
     "path": "0.12.7",
     "raw-loader": "3.1.0",
+    "readline-sync": "1.4.10",
+    "socket.io": "^2.3.0",
+    "socket.io-client": "^2.3.0",
     "webpack": "4.41.0",
     "webpack-cli": "3.3.9",
     "webpack-dev-middleware": "3.7.2",
     "webpack-md5-hash": "0.0.6"
+  },
+  "cordova": {
+    "plugins": {
+      "cordova-plugin-whitelist": {},
+      "cordova-plugin-webpack": {}
+    },
+    "platforms": [
+      "browser"
+    ]
   }
 }

+ 7 - 0
server/game-server/duel-controller.js

@@ -0,0 +1,7 @@
+'use strict';
+
+export default class DuelController {
+  constructor(playerOne, playerTwo) {
+    console.log('DuelCtrl constructor : ' + playerOne.playerName + ' and : ' + playerTwo.playerName);
+  }
+}

+ 33 - 0
server/main.js

@@ -0,0 +1,33 @@
+'use strict';
+import PlayerId from './players/player-id';
+import DuelController from './game-server/duel-controller';
+
+const io = require('socket.io'); //(http)
+const server = io.listen(4000);
+
+let players = [];
+
+let addNewPlayer = function (playerSocket, playerName) {
+  console.log('players length : ' + players.length + ' : ' + players);
+  if (players.length < 2) {
+    let newPlayer = new PlayerId(playerSocket,playerName);
+    console.log('push player : ' + newPlayer);
+    players.push(newPlayer);
+  }
+  if (players.length === 2) {
+    let duelController = new DuelController(players[0], players[1]);
+  }
+}
+
+server.on('connection', function (socket) {
+  console.log("A player connected with id : " + socket.id);
+  socket.on ('disconnect', () => {
+    console.log("A player disconnected with id : " + socket.id);
+  });
+
+  socket.on('auth', (playerName) => {
+    console.log(' Received auth message, player name : ' + playerName);
+    addNewPlayer(socket, playerName);
+  });
+
+})

+ 16 - 0
server/players/player-id.js

@@ -0,0 +1,16 @@
+'use strict';
+
+export default class PlayerId {
+  constructor(playerSocket, playerName = '', playerColor = 'unk') {
+    this.playerSocket = playerSocket;
+    this.playerName = playerName;
+    this.playerColor = playerColor;
+  }
+
+  setPlayerName(playerName) {
+    this.playerName = playerName;
+  }
+  setPlayerColor(playerColor) {
+    this.playerColor = playerColor;
+  }
+}

+ 0 - 18
src/GameCreator.js

@@ -1,18 +0,0 @@
-'use strict';
-import HeroFactory from './heroes/HeroFactory';
-import PhaserGame from './view/PhaserGame';
-
-export default class GameCreator {
-  constructor() {
-    let heroFactory = new HeroFactory();
-
-    let heroesSet = heroFactory.getHeroesSet();
-
-    console.log('Heroes Set : ', heroesSet);
-
-    let phaser = new PhaserGame(heroesSet);
-
-
-
-  }
-}

+ 37 - 37
src/assets/all-heroes.json

@@ -5,7 +5,7 @@
       "name" : "Paysan",
       "cost" : 0,
       "power" : 1,
-      "faction": "Humans",
+      "faction": "humans",
       "draftMode" : true,
       "ability": "PaysanAbility",
       "nbInDeck": 2
@@ -14,7 +14,7 @@
       "name" : "Dragon",
       "cost" : 7,
       "power" : 6,
-      "faction": "Humans",
+      "faction": "humans",
       "draftMode" : true,
       "ability": "DragonAbility",
       "nbInDeck": 1
@@ -23,7 +23,7 @@
       "name" : "Archange",
       "cost" : 4,
       "power" : 3,
-      "faction": "Humans",
+      "faction": "humans",
       "draftMode" : true,
       "ability": "ArchangeAbility",
       "nbInDeck": 1
@@ -32,7 +32,7 @@
       "name" : "Chevalier",
       "cost" : 3,
       "power" : 4,
-      "faction": "Humans",
+      "faction": "humans",
       "draftMode" : true,
       "ability": "ChevalierAbility",
       "nbInDeck": 2
@@ -41,7 +41,7 @@
       "name" : "Faucon Geant",
       "cost" : 1,
       "power" : 2,
-      "faction": "Humans",
+      "faction": "humans",
       "draftMode" : false,
       "ability": "FauconAbility",
       "nbInDeck": 1
@@ -50,7 +50,7 @@
       "name" : "Intendant",
       "cost" : 2,
       "power" : 2,
-      "faction": "Humans",
+      "faction": "humans",
       "draftMode" : false,
       "ability": "IntendantAbility",
       "nbInDeck": 1
@@ -59,7 +59,7 @@
       "name" : "Canon",
       "cost" : 1,
       "power" : 4,
-      "faction": "Humans",
+      "faction": "humans",
       "draftMode" : false,
       "ability": "CanonAbility",
       "nbInDeck": 1
@@ -68,7 +68,7 @@
       "name" : "Char",
       "cost" : 3,
       "power" : 3,
-      "faction": "Humans",
+      "faction": "humans",
       "draftMode" : false,
       "ability": "CharAbility",
       "nbInDeck": 1
@@ -77,7 +77,7 @@
       "name" : "Chevre",
       "cost" : 1,
       "power" : 2,
-      "faction": "Humans",
+      "faction": "humans",
       "draftMode" : false,
       "ability": "ChevreAbility",
       "nbInDeck": 1
@@ -86,7 +86,7 @@
       "name" : "Stratege",
       "cost" : 1,
       "power" : 2,
-      "faction": "Humans",
+      "faction": "humans",
       "draftMode" : false,
       "ability": "StrategeAbility",
       "nbInDeck": 1
@@ -95,7 +95,7 @@
       "name" : "Homme-Arbre",
       "cost" : 4,
       "power" : 4,
-      "faction": "Elves",
+      "faction": "elves",
       "draftMode" : true,
       "ability": "HommeArbreAbility",
       "nbInDeck": 2
@@ -104,7 +104,7 @@
       "name" : "Elf",
       "cost" : 2,
       "power" : 2,
-      "faction": "Elves",
+      "faction": "elves",
       "draftMode" : true,
       "ability": "ElfAbility",
       "nbInDeck": 2
@@ -113,7 +113,7 @@
       "name" : "Stratege",
       "cost" : 1,
       "power" : 2,
-      "faction": "Elves",
+      "faction": "elves",
       "draftMode" : true,
       "ability": "StrategeAbility",
       "nbInDeck": 1
@@ -122,7 +122,7 @@
       "name" : "Intendant",
       "cost" : 2,
       "power" : 2,
-      "faction": "Elves",
+      "faction": "elves",
       "draftMode" : true,
       "ability": "IntendantAbility",
       "nbInDeck": 1
@@ -131,7 +131,7 @@
       "name" : "Faucon Geant",
       "cost" : 1,
       "power" : 2,
-      "faction": "Elves",
+      "faction": "elves",
       "draftMode" : true,
       "ability": "FauconAbility",
       "nbInDeck": 2
@@ -140,7 +140,7 @@
       "name" : "Paysan",
       "cost" : 0,
       "power" : 1,
-      "faction": "Elves",
+      "faction": "elves",
       "draftMode" : false,
       "ability": "PaysanAbility",
       "nbInDeck": 1
@@ -149,7 +149,7 @@
       "name" : "Archange",
       "cost" : 4,
       "power" : 3,
-      "faction": "Elves",
+      "faction": "elves",
       "draftMode" : true,
       "ability": "ArchangeAbility",
       "nbInDeck": 1
@@ -158,7 +158,7 @@
       "name" : "Chevre",
       "cost" : 1,
       "power" : 2,
-      "faction": "Elves",
+      "faction": "elves",
       "draftMode" : false,
       "ability": "ChevreAbility",
       "nbInDeck": 1
@@ -167,7 +167,7 @@
       "name" : "Voilier Celeste",
       "cost" : 1,
       "power" : 2,
-      "faction": "Elves",
+      "faction": "elves",
       "draftMode" : false,
       "ability": "VoilierAbility",
       "nbInDeck": 1
@@ -176,16 +176,16 @@
       "name" : "Orc",
       "cost" : 3,
       "power" : 3,
-      "faction": "Orcs",
+      "faction": "orcs",
       "draftMode" : true,
-      "ability": "OrcAbility",
+      "ability": "orcsAbility",
       "nbInDeck": 2
     },
     {
       "name" : "Behemoth",
       "cost" : 5,
       "power" : 5,
-      "faction": "Orcs",
+      "faction": "orcs",
       "draftMode" : true,
       "ability": "BehemothAbility",
       "nbInDeck": 2
@@ -194,7 +194,7 @@
       "name" : "Gobelin",
       "cost" : 0,
       "power" : 2,
-      "faction": "Orcs",
+      "faction": "orcs",
       "draftMode" : true,
       "ability": "GobelinAbility",
       "nbInDeck": 2
@@ -203,7 +203,7 @@
       "name" : "Char",
       "cost" : 3,
       "power" : 3,
-      "faction": "Orcs",
+      "faction": "orcs",
       "draftMode" : true,
       "ability": "CharAbility",
       "nbInDeck": 2
@@ -212,7 +212,7 @@
       "name" : "Chevre",
       "cost" : 1,
       "power" : 2,
-      "faction": "Orcs",
+      "faction": "orcs",
       "draftMode" : true,
       "ability": "ChevreAbility",
       "nbInDeck": 2
@@ -221,7 +221,7 @@
       "name" : "Paysan",
       "cost" : 0,
       "power" : 1,
-      "faction": "Orcs",
+      "faction": "orcs",
       "draftMode" : false,
       "ability": "PaysanAbility",
       "nbInDeck": 1
@@ -230,7 +230,7 @@
       "name" : "Intendant",
       "cost" : 2,
       "power" : 2,
-      "faction": "Orcs",
+      "faction": "orcs",
       "draftMode" : true,
       "ability": "IntendantAbility",
       "nbInDeck": 1
@@ -239,7 +239,7 @@
       "name" : "Geant de Fer",
       "cost" : 2,
       "power" : 3,
-      "faction": "Meca",
+      "faction": "meca",
       "draftMode" : true,
       "ability": "GeantAbility",
       "nbInDeck": 2
@@ -248,7 +248,7 @@
       "name" : "Golem",
       "cost" : 4,
       "power" : 4,
-      "faction": "Meca",
+      "faction": "meca",
       "draftMode" : true,
       "ability": "GolemAbility",
       "nbInDeck": 2
@@ -257,7 +257,7 @@
       "name" : "Canon",
       "cost" : 1,
       "power" : 4,
-      "faction": "Meca",
+      "faction": "meca",
       "draftMode" : true,
       "ability": "CanonAbility",
       "nbInDeck": 2
@@ -266,7 +266,7 @@
       "name" : "Char",
       "cost" : 3,
       "power" : 3,
-      "faction": "Meca",
+      "faction": "meca",
       "draftMode" : false,
       "ability": "CharAbility",
       "nbInDeck": 1
@@ -275,7 +275,7 @@
       "name" : "Voilier Celeste",
       "cost" : 1,
       "power" : 2,
-      "faction": "Meca",
+      "faction": "meca",
       "draftMode" : true,
       "ability": "VoilierAbility",
       "nbInDeck": 2
@@ -284,7 +284,7 @@
       "name" : "Intendant",
       "cost" : 2,
       "power" : 2,
-      "faction": "Meca",
+      "faction": "meca",
       "draftMode" : false,
       "ability": "IntendantAbility",
       "nbInDeck": 1
@@ -293,7 +293,7 @@
       "name" : "Gobelin",
       "cost" : 0,
       "power" : 2,
-      "faction": "Meca",
+      "faction": "meca",
       "draftMode" : false,
       "ability": "GobelinAbility",
       "nbInDeck": 1
@@ -302,7 +302,7 @@
       "name" : "Stratege",
       "cost" : 1,
       "power" : 2,
-      "faction": "Meca",
+      "faction": "meca",
       "draftMode" : true,
       "ability": "StrategeAbility",
       "nbInDeck": 1
@@ -397,10 +397,10 @@
       "abilityDesc-FR" : "Quand le Voilier Celeste est deploye, vous pouvez ajouter gratuitement avec lui un Heros de cout 0 ou 1 de votre main. Si ce Heros a un effet quand il est recrute, appliquez-le."
     },
     {
-      "abilityName" : "OrcAbility",
+      "abilityName" : "orcsAbility",
       "abilityHook": "AfterDeploy",
       "optionnal": "false",
-      "abilityDesc-FR" : "Quand l'Orc est deploye, chaque joueur doit defausser 1 Nourriture dans cette region, s'il le peut."
+      "abilityDesc-FR" : "Quand l'orcs est deploye, chaque joueur doit defausser 1 Nourriture dans cette region, s'il le peut."
     },
     {
       "abilityName" : "BehemothAbility",

+ 20 - 0
src/common/game-params.js

@@ -0,0 +1,20 @@
+'use strict';
+
+export default class GameParams {
+  constructor(playerBlueName, playerRedName, gameDeckMode, advancedRules = []) {
+    this.playerBlueName = playerBlueName;
+    this.playerRedName = playerRedName;
+    this.gameDeckMode = gameDeckMode;
+    this.advancedRules = advancedRules;
+  }
+
+  setCurrentPlayer(playerController) {
+    this.currentPlayer = playerController;
+  }
+
+  getCurrentPlayer() {
+    return this.currentPlayer;
+  }
+
+
+}

+ 35 - 0
src/common/socket-service.js

@@ -0,0 +1,35 @@
+'use strict';
+import io from 'socket.io-client';
+
+/*
+  Messages :
+    - disconnect
+    - reconnecting
+    - reconnect
+    - reconnect_failed
+    -
+*/
+const SERVER_URL = process.env.SERVER_URL || 'http://localhost';
+const SERVER_PORT = process.env.SERVER_PORT || 4000;
+
+export default class SocketService {
+  constructor(namespace) {
+
+    this.ioClient = io.connect(SERVER_URL + ":" + SERVER_PORT + '/');
+  }
+
+  connect(serverUrl, serverPort) {
+    this.disconnected=false;
+    this.ioClient = io.connect(serverUrl + ":" + serverPort);
+
+    console.log("is connected : " + this.ioClient.connected);
+    this.ioClient.on('disconnect', () => {
+      console.log("Connection with server lost");
+
+    });
+  }
+  auth(name) {
+    console.log("Authenticate to server player name : " + name);
+    this.ioClient.emit('auth', name);
+  }
+}

+ 10 - 0
src/common/utils/const/faction-enum.js

@@ -0,0 +1,10 @@
+'use strict';
+
+export const Faction = Object.freeze({
+    HUMANS : "humans",
+    ORCS: "orcs",
+    ELVES: "elves",
+    MECA: "meca",
+    NONE:"none"
+
+  });

+ 7 - 0
src/common/utils/const/game-deck-mode-enum.js

@@ -0,0 +1,7 @@
+'use strict';
+
+export const GameDeckMode = Object.freeze({
+    FACTIONS : "factions",
+    DRAFT: "draft",
+    TOURNAMENT: "tournament"
+  });

+ 6 - 0
src/common/utils/const/game-type-enum.js

@@ -0,0 +1,6 @@
+'use strict';
+
+export const GameType = Object.freeze({
+    PASSNPLAY : "PassNPlay",
+    MULTI: "Multi"
+  });

+ 7 - 0
src/common/utils/const/phaser-scene-enum.js

@@ -0,0 +1,7 @@
+'use strict';
+
+export const PhaserScene = Object.freeze({
+    MAIN_MENU : "MainMenuScene",
+    GAME_WORLD: "GameWorldScene",
+    DECK_BUILDING: "DeckBuildingScene"
+  });

+ 9 - 0
src/common/utils/prompt-renderer.js

@@ -0,0 +1,9 @@
+'use strict';
+
+export function promptSelectAmongOptions(message, options) {
+  let selection = "";
+  while (options.includes(selection) === false) {
+    selection = prompt(`${message} (${options}) :`);
+  }
+  return selection;
+}

+ 16 - 0
src/game/control/behaviors/deck-building-steps.js

@@ -0,0 +1,16 @@
+'use strict';
+
+
+export const DeckBuildingSteps = self => ({
+  selectFactionDeck: (renderer,heroFactory) => {
+    self.player.faction = renderer.getInput(self.player.name + ' to choose faction');
+    self.player.setHeroesDeck(heroFactory.getFactionRandom(self.player.faction));
+  },
+  selectDraftHeroes: (heroesDraftBurst) => {
+    console.log('Choose 2 heroes among : ', heroesDraftBurst);
+  },
+  selectTournamentDeck: (allHeroes) => {
+    console.log('Choose 12 heroes among : ', allHeroes);
+  }
+
+});

+ 65 - 0
src/game/control/game-controller-pnp.js

@@ -0,0 +1,65 @@
+'use strict';
+import HeroFactory from '../game-model/heroes/hero-factory';
+import { PhaserScene } from '../../common/utils/const/phaser-scene-enum';
+import GameWorldScene from '../views/game-world-scene';
+import { GameDeckMode } from '../../common/utils/const/game-deck-mode-enum';
+import FactionDeckBuildingScene from '../views/faction-deck-building-scene';
+import Player from '../game-model/player';
+
+export default class GameControllerPnp {
+  constructor(phaserEngine, gameParams) {
+    this.phaserEngine = phaserEngine;
+    console.log("In constructor" + this.phaserEngine);
+    this.gameParams = gameParams;
+
+    this.playerBlue = new Player(gameParams.playerBlueName, "Blue");
+    this.playerRed = new Player(gameParams.playerResName, "Red");
+
+    console.log();
+    switch (this.gameParams.gameDeckMode) {
+      case GameDeckMode.FACTIONS:
+        this.deckBuildingScene = new FactionDeckBuildingScene(this.gameEventListener());
+        break;
+
+      default:
+        // ONLY Factions mode
+        this.deckBuildingScene = new FactionDeckBuildingScene(this.gameEventListener());
+        break;
+    }
+
+    // Instantiates Heroes
+    this.heroFactory = new HeroFactory();
+  }
+
+  displayDeckBuilding() {
+    console.log("In displayDeckBuilding " + this.phaserEngine);
+    this.phaserEngine.scene.add(PhaserScene.DECK_BUILDING, this.deckBuildingScene);
+    this.phaserEngine.scene.start(PhaserScene.DECK_BUILDING);
+    this.phaserEngine.scene.remove(PhaserScene.MAIN_MENU);
+  }
+  displayGameWorld() {
+    this.phaserEngine.scene.add(PhaserScene.GAME_WORLD, this.gameWorldScene);
+    this.phaserEngine.scene.start (PhaserScene.GAME_WORLD);
+    this.phaserEngine.scene.remove(PhaserScene.DECK_BUILDING);
+  }
+
+  gameEventListener() {
+    return {
+      onFactionsSelected: this.onFactionsSelected.bind(this)
+    };
+  }
+
+  onFactionsSelected(playerBlueFaction, playerRedFaction ) {
+    this.playerBlue.setFaction(playerBlueFaction);
+    this.playerRed.setFaction(playerRedFaction);
+
+    this.playerBlue.setHeroesDeck(this.heroFactory.getFactionRandom(this.playerBlue.faction));
+    this.playerRed.setHeroesDeck(this.heroFactory.getFactionRandom(this.playerRed.faction));
+
+    console.log('player Blue deck : ', this.playerBlue.getHeroesDeck());
+    console.log('player Red deck : ', this.playerRed.getHeroesDeck());
+    this.gameWorldScene = new GameWorldScene(this.playerBlue, this.playerRed);
+    this.displayGameWorld();
+  }
+
+}

+ 10 - 0
src/game/control/player-controller.js

@@ -0,0 +1,10 @@
+'use-strict';
+import {DeckBuildingSteps} from './behaviors/deck-building-steps';
+
+export const PlayerControllerLocal = function(player) {
+  const self = {
+    player
+  };
+
+  return Object.assign(self, DeckBuildingSteps(self));
+};

+ 0 - 0
src/game/game-model/game-global-state.js


+ 0 - 0
src/heroes/HeroAbility.js → src/game/game-model/heroes/hero-ability.js


+ 22 - 11
src/heroes/HeroFactory.js → src/game/game-model/heroes/hero-factory.js

@@ -1,15 +1,10 @@
 'use strict';
-import allHeroesJson from "../assets/all-heroes.json";
-import Hero from './Hero';
-import HeroAbility from './HeroAbility';
-
+import allHeroesJson from "../../../assets/all-heroes.json";
+import Hero from './hero';
+import HeroAbility from './hero-ability';
 
 export default class HeroFactory {
   constructor() {
-
-  }
-
-  getHeroesSet() {
     let abilitiesMap = new Map();
     allHeroesJson.abilities.forEach(ability => {
       let abilityObj = new HeroAbility(ability.abilityName,
@@ -19,7 +14,7 @@ export default class HeroFactory {
       abilitiesMap.set(ability.abilityName, abilityObj);
     });
 
-    let heroesSet = new Set();
+    this.heroesSet = new Set();
     allHeroesJson.heroes.forEach(hero => {
       let i = 0;
       while (i < hero.nbInDeck) {
@@ -31,11 +26,27 @@ export default class HeroFactory {
           abilitiesMap.get(hero.ability),
           hero.draftMode
         );
-        heroesSet.add(heroObj);
+        this.heroesSet.add(heroObj);
         i++;
       }
     });
-    return heroesSet;
   }
 
+  getAllHeroesSet() {
+    return this.heroesSet;
+  }
+
+  getFactionRandom(faction) {
+    let factionHeroes = shuffleArray(Array.from(this.heroesSet).filter(hero => hero.faction === faction));
+    return factionHeroes;
+  }
+
+}
+
+function shuffleArray(a) {
+  for (let i = a.length - 1; i > 0; i--) {
+      const j = Math.floor(Math.random() * (i + 1));
+      [a[i], a[j]] = [a[j], a[i]];
+  }
+  return a;
 }

+ 0 - 0
src/heroes/Hero.js → src/game/game-model/heroes/hero.js


+ 18 - 0
src/game/game-model/player.js

@@ -0,0 +1,18 @@
+'use strict';
+
+export default class Player {
+  constructor(name, color, faction = 'None') {
+    this.name = name;
+    this.color = color;
+    this.faction = faction;
+  }
+  setFaction(faction) {
+    this.faction = faction;
+  }
+  setHeroesDeck(heroesDeck) {
+    this.heroesDeck = heroesDeck;
+  }
+  getHeroesDeck(){
+    return this.heroesDeck;
+  }
+}

+ 10 - 0
src/game/views/deck-building-scene.js

@@ -0,0 +1,10 @@
+'use strict';
+import { PhaserScene } from '../../common/utils/const/phaser-scene-enum';
+import Phaser from 'phaser';
+
+export default class DeckBuildingScene extends Phaser.Scene {
+
+  constructor() {
+    super({ key: PhaserScene.DECK_BUILDING, active: false });
+  }
+}

+ 36 - 0
src/game/views/faction-deck-building-scene.js

@@ -0,0 +1,36 @@
+'use strict';
+import DeckBuildingScene from './deck-building-scene';
+import { Faction } from '../../common/utils/const/faction-enum';
+import * as Utils from '../../common/utils/prompt-renderer';
+
+export default class FactionDeckBuildingScene extends DeckBuildingScene {
+
+  constructor(gameEventListener) {
+    super();
+    this.gameEventListener = gameEventListener;
+  }
+
+  preload() {
+    console.log('Preload');
+
+  }
+
+  create() {
+    console.log('create');
+
+
+    setTimeout(() => {
+      let factionOptions = Object.values(Faction).filter(faction => faction !== Faction.NONE);
+      let pBlueFaction = Utils.promptSelectAmongOptions("Select Player Blue Faction", factionOptions);
+
+      factionOptions = factionOptions.filter(faction => faction !== pBlueFaction);
+      let pRedFaction = Utils.promptSelectAmongOptions("Select Player Red Faction", factionOptions);
+      this.gameEventListener.onFactionsSelected(pBlueFaction, pRedFaction );
+    }, 100);
+  }
+
+  update() {
+
+  }
+}
+

+ 69 - 0
src/game/views/game-world-scene.js

@@ -0,0 +1,69 @@
+'use strict';
+import { PhaserScene } from '../../common/utils/const/phaser-scene-enum';
+import Phaser from 'phaser';
+
+export default class GameWorldScene extends Phaser.Scene {
+
+  constructor(playerBlue, playerRed) {
+    super({ key: PhaserScene.GAME_WORLD, active: false });
+    this.playerBlue = playerBlue;
+    this.playerRed = playerRed;
+  }
+
+  preload() {
+    console.log('Preload');
+
+  }
+
+  create() {
+    console.log('create');
+    render(this, this.playerBlue.getHeroesDeck(),0 );
+    render(this, this.playerRed.getHeroesDeck(),300 );
+  }
+
+  update() {
+
+  }
+}
+
+function addCard(x, y, phaser, heroName, heroCost, heroPower, heroDesc) {
+  let textStyle = {
+    font: "normal 12px Arial",
+    fill: '#000000',
+    align: 'center',
+    boundsAlignH: "center", // bounds center align horizontally
+    boundsAlignV: "middle" // bounds center align vertically
+  };
+  let graphics = phaser.add.graphics(0, 0);
+  let color = 0x654321;
+  let thickness = 2;
+  let alpha = 1;
+
+  graphics.lineStyle(thickness, color, alpha);
+  graphics.strokeRect(0, 0, 80, 120);
+  let text = `${heroName}\ncost : ${heroCost}\npower : ${heroPower}`;
+  let label = phaser.add.text(3, 3, text, textStyle);
+  label.setOrigin(0, 0);
+  let container = phaser.add.container(x, y, [graphics, label]).setSize(5, 5);
+  // container.setOrigin(0, 0);
+}
+
+
+  function render(game, heroesSet, offset) {
+
+    let i = 0;
+    let j = 0;
+    heroesSet.forEach(hero => {
+      let x = 10 + (i * 80) + 5;
+      let y = 10 + offset + (j * 120) + 5;
+      addCard(x, y, game, hero.name, hero.cost, hero.power, hero.ability.description);
+      if (x >= 1040) {
+        i = 0;
+        j++;
+      } else {
+
+        i++;
+      }
+
+    });
+  }

+ 50 - 5
src/index.html

@@ -1,9 +1,54 @@
 <!DOCTYPE html>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+     KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
 <html>
-    <head>
-        <title>Twelve Heroes Game</title>
-    </head>
-    <body>
 
-    </body>
+<head>
+  <!--
+        Customize this policy to fit your own app's needs. For more guidance, see:
+            https://github.com/apache/cordova-plugin-whitelist/blob/master/README.md#content-security-policy
+        Some notes:
+            * gap: is required only on iOS (when using UIWebView) and is needed for JS->native communication
+            * https://ssl.gstatic.com is required only on Android and is needed for TalkBack to function properly
+            * Disables use of inline scripts in order to mitigate risk of XSS vulnerabilities. To change this:
+                * Enable inline JS: add 'unsafe-inline' to default-src
+        -->
+  <!-- <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content: blob:;"> -->
+  <meta name="format-detection" content="telephone=no">
+  <meta name="msapplication-tap-highlight" content="no">
+  <meta name="viewport" content="initial-scale=1, width=device-width, viewport-fit=cover">
+  <!-- <link rel="stylesheet" type="text/css" href="css/index.css"> -->
+  <title>Hello World</title>
+</head>
+
+<body>
+  <% if (process.env.CORDOVA) { %>
+
+  <div class="app">
+    <h1>Twelve Heroes</h1>
+    <div id="deviceready" class="blink">
+      <p class="event listening"></p>
+      <p class="event received"></p>
+    </div>
+  </div>
+  <script type="text/javascript" src="cordova.js"></script>
+  <% } %>
+</body>
+
 </html>

+ 57 - 53
src/main.js

@@ -1,57 +1,61 @@
 'use strict';
 
 /* eslint-disable no-unused-vars */
-/* eslint-disable no-console */
-import Phaser from "phaser";
-import logoImg from "./assets/twelveHeroes_cover.png";
-
-import '@babel/polyfill';
-
-import GameCreator from './GameCreator';
-
-// const Faction = {
-//   HUMANS: 'Humans',
-//   ELVES: 'Elves',
-//   ORCS: 'Orcs',
-//   MECA: 'Meca',
-//   NONE: 'None',
-
-// };
-// Object.freeze(Faction);
-
-
-
-let gameCreator = new GameCreator();
-
-
-// const config = {
-//   type: Phaser.AUTO,
-//   parent: "phaser-example",
-//   width: 800,
-//   height: 600,
-//   scene: {
-//     preload: preload,
-//     create: create
-//   }
-// };
-
-// const game = new Phaser.Game(config);
-
-// function preload() {
-//   this.load.image("logo", logoImg);
-// }
-
-// function create() {
-//   const logo = this.add.image(400, 150, "logo");
-
-//   this.tweens.add({
-//     targets: logo,
-//     y: 350,
-//     duration: 2000,
-//     ease: "Power2",
-//     yoyo: true,
-//     loop: -1
-//   });
-// }
-
 
+import MenuController from './menu/control/menu-controller';
+import Phaser from 'phaser';
+
+function startGame() {
+  console.log('start Game');
+
+  let phaserConfig = {
+    type: Phaser.CANVAS,
+    width: 1200,
+    height: 500,
+    scene: [],
+    backgroundColor: 'rgba(34,139,34 ,1 )'
+  };
+  let phaserEngine = new Phaser.Game(phaserConfig);
+  let menuController = new MenuController(phaserEngine);
+
+  menuController.displayMainMenu();
+
+}
+
+let app = {
+  // Application Constructor
+  initialize: function () {
+    console.log('app initialize');
+    document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);
+  },
+
+  // deviceready Event Handler
+  //
+  // Bind any cordova events here. Common events are:
+  // 'pause', 'resume', etc.
+  onDeviceReady: function () {
+    this.receivedEvent('deviceready');
+    startGame();
+  },
+
+  // Update DOM on a Received Event
+  receivedEvent: function (id) {
+    var parentElement = document.getElementById(id);
+    var listeningElement = parentElement.querySelector('.listening');
+    var receivedElement = parentElement.querySelector('.received');
+
+    listeningElement.setAttribute('style', 'display:none;');
+    receivedElement.setAttribute('style', 'display:block;');
+
+    console.log('Received Event: ' + id);
+  }
+};
+
+console.log('cordova  :', process.env.CORDOVA );
+
+if (process.env.CORDOVA) {
+
+  app.initialize();
+} else {
+  startGame();
+}

+ 15 - 0
src/main.test.js

@@ -0,0 +1,15 @@
+'use strict';
+
+// import * as chai from 'chai';
+
+// let assert = chai.assert;
+// let should = chai.should();
+// let expect = chai.expect;
+
+
+describe('Main', () => {
+  it('Should start cordova game', (done) => {
+    process.env.CORDOVA = null;
+    done();
+  });
+});

+ 41 - 0
src/menu/control/menu-controller.js

@@ -0,0 +1,41 @@
+'use strict';
+
+import MainMenuScene from '../views/main-menu-scene';
+import { PhaserScene } from '../../common/utils/const/phaser-scene-enum';
+import GameParams from '../../common/game-params';
+import GameControllerPnp from '../../game/control/game-controller-pnp';
+import SocketService from '../../common/socket-service';
+
+export default class MenuController {
+
+  constructor(phaserEngine) {
+    this.phaserEngine = phaserEngine;
+    console.log("In constructor MenuController " + this.phaserEngine);
+
+    this.mainMenuScene = new MainMenuScene(this.menuEventListener());
+
+    //this.socketService = new SocketService();
+    //this.socketService.connect('http://localhost', 4000);
+    //this.socketService.auth('Jojo');
+  }
+
+  displayMainMenu() {
+
+    this.phaserEngine.scene.add(PhaserScene.MAIN_MENU,this.mainMenuScene);
+    this.phaserEngine.scene.start(PhaserScene.MAIN_MENU);
+  }
+
+  menuEventListener() {
+    return {
+      onNewPnpGame: this.onNewPnpGame.bind(this)
+    };
+  }
+
+  onNewPnpGame(pBlueName, pRedName, gameMode, advancedRules) {
+    console.log("onNewPnpGame this  : " + this);
+      let gameParams = new GameParams(pBlueName,pRedName,gameMode,advancedRules);
+      let gameControllerPnp = new GameControllerPnp(this.phaserEngine, gameParams);
+      gameControllerPnp.displayDeckBuilding();
+  }
+
+}

+ 55 - 0
src/menu/views/main-menu-scene.js

@@ -0,0 +1,55 @@
+'use strict';
+import {GameDeckMode} from '../../common/utils/const/game-deck-mode-enum';
+import { PhaserScene } from '../../common/utils/const/phaser-scene-enum';
+import Phaser from 'phaser';
+import * as Utils from '../../common/utils/prompt-renderer';
+
+export default class MainMenuScene extends Phaser.Scene {
+
+  constructor(menuEventListener) {
+    super({ key: PhaserScene.MAIN_MENU, active: false });
+    this.menuEventListener = menuEventListener;
+
+  }
+
+  preload() {
+    console.log('Preload');
+    this.load.image('game-cover', '../../assets/twelveHeroes_cover.png');
+  }
+
+  create() {
+    console.log('create');
+    let logo = this.add.image(0,0,'game-cover');
+    logo.setOrigin(0,0);
+
+
+    //this.add.dom
+
+
+
+    // TODO : Display main menu
+    // Create new scenes : Local player room / Multi player room
+    // Pass to them the event listener
+    // On local player room, user can enter player names, selected color, game deck mode & rules
+
+    // FOR TESTING
+    this.fakeNewPnpGameSelected();
+  }
+
+  update() {
+
+  }
+
+  fakeNewPnpGameSelected() {
+    setTimeout(() => {
+      let pBlueName = prompt("Player blue name : ");
+      let pRedName = prompt("Player red name : ");
+
+      let gameDeckModeOptions = Object.values(GameDeckMode);
+      let gameDeckMode = Utils.promptSelectAmongOptions("Select Deck Mode", gameDeckModeOptions);
+      let advancedRules = [];
+      console.log("Calling listener");
+      this.menuEventListener.onNewPnpGame(pBlueName, pRedName, gameDeckMode, advancedRules);
+    }, 1000);
+  }
+}

+ 2 - 0
src/polyfills.js

@@ -0,0 +1,2 @@
+
+import '@babel/polyfill';

+ 0 - 62
src/view/PhaserGame.js

@@ -1,62 +0,0 @@
-import Phaser from "phaser";
-import logoImg from "../assets/twelveHeroes_cover.png";
-
-function addCard(x, y, game,heroName,heroCost,heroPower,heroDesc) {
-  let textStyle = {
-    font: "normal 12px Arial",
-    fill: '#000000',
-    align: 'center',
-    boundsAlignH: "center", // bounds center align horizontally
-    boundsAlignV: "middle" // bounds center align vertically
-  };
-  let graphics = game.add.graphics(0, 0);
-  let color = 0xffff00;
-  let thickness = 2;
-  let alpha = 1;
-
-  graphics.lineStyle(thickness, color, alpha);
-  graphics.strokeRect(0, 0, 100, 160);
-  let text = `${heroName}
-  cost : ${heroCost}
-  power : ${heroPower}
-  ${heroDesc}`;
-  let label = game.add.text(3, 3, text, textStyle);
-  label.setOrigin(0, 0);
-  let container = game.add.container(x,y, [graphics, label]).setSize(5, 5);
-  // container.setOrigin(0, 0);
-}
-
-export default class PhaserGameScene {
-  constructor(heroesSet) {
-    this.heroesSet = heroesSet;
-    // create a new scene named "Game"
-    this.gameScene = new Phaser.Scene('Game');
-
-    this.gameScene.preload = function () {
-      this.load.image("logo", logoImg);
-
-    };
-
-    this.gameScene.create = function () {
-      let i = 0;
-      heroesSet.forEach(hero => {
-
-        addCard(10+(i*100),10,this,hero.name,hero.cost,hero.power,hero.ability.description);
-        i++;
-      });
-    };
-
-    // our game's configuration
-    let config = {
-      type: Phaser.AUTO,  //Phaser will decide how to render our game (WebGL or Canvas)
-      width: 1200, // game width
-      height: 550, // game height
-      scene: this.gameScene, // our newly created scene
-      backgroundColor: 'rgba(173,216,230 ,1 )'
-    };
-
-    // create the game, and pass it the configuration
-    this.game = new Phaser.Game(config);
-  }
-
-}

+ 0 - 41
webpack.config.dev.js

@@ -1,41 +0,0 @@
-import path from 'path';
-import HtmlWebpackPlugin from 'html-webpack-plugin';
-import webpack from 'webpack';
-
-export default {
-  devtool: 'inline-source-map',
-  mode: 'development',
-  entry: [
-    path.resolve(__dirname, 'src/main.js')
-  ],
-  target: 'web',
-  output: {
-    path: path.resolve(__dirname, 'src'),
-    publicPath: '/',
-    filename: 'bundle.js'
-  },
-  plugins: [
-    new webpack.DefinePlugin({
-      CANVAS_RENDERER: JSON.stringify(true),
-      WEBGL_RENDERER: JSON.stringify(true)
-    }),
-    // Create HTML file that includes reference to bundled JS.
-    new HtmlWebpackPlugin({
-      template: 'src/index.html',
-      inject: true
-    })
-  ],
-  module: {
-    rules: [
-      { test: /\.js$/, exclude: /node_modules/, loaders: "babel-loader" },
-      {
-        test: /\.(gif|png|jpe?g|svg|xml)$/i,
-        use: "file-loader"
-      },
-      {
-        test: [/\.vert$/, /\.frag$/],
-        use: "raw-loader"
-      }
-    ]
-  }
-};

+ 93 - 0
webpack.config.js

@@ -0,0 +1,93 @@
+var path = require('path');
+var webpack = require('webpack');
+var HtmlWebpackPlugin = require('html-webpack-plugin');
+var BrowserSyncPlugin = require('browser-sync-webpack-plugin');
+var CopyWebpackPlugin = require('copy-webpack-plugin');
+var { CleanWebpackPlugin } = require('clean-webpack-plugin');
+
+var definePlugin = new webpack.DefinePlugin({
+  'process.env.CORDOVA': JSON.stringify(JSON.parse(process.env.CORDOVA || 'false'))
+});
+
+var outpath = './webpack/';
+
+if (process.env.CORDOVA) {
+  outpath = 'www/';
+}
+
+module.exports = {
+  devtool: 'cheap-source-map',
+  mode: 'development',
+  entry: {
+    polyfills: './src/polyfills.js',
+    app: path.resolve(__dirname, 'src/main.js'),
+  },
+  target: 'web',
+  watch: true,
+  output: {
+    pathinfo: true,
+    filename: '[name].bundle.js',
+    chunkFilename: '[name].bundle.js',
+    path: path.resolve(__dirname, outpath + 'dist')
+  },
+  optimization: {
+    splitChunks: {
+      cacheGroups: {
+        commons: { test: /[\\/]node_modules[\\/]/, name: "vendors", chunks: "all" }
+      }
+    }
+  },
+  plugins: [
+    definePlugin,
+    new CleanWebpackPlugin(),
+    new CopyWebpackPlugin([
+      {
+        context: path.resolve(__dirname, 'src', 'assets'),
+        from: '**/*',
+        to: path.resolve(__dirname, outpath, 'assets')
+      }
+    ]),
+    new HtmlWebpackPlugin({
+      filename: '../index.html',
+      template: './src/index.html',
+      chunksSortMode: "manual",
+      chunks: ['polyfills', 'vendors', 'app'],
+      minify: {
+        removeAttributeQuotes: false,
+        collapseWhitespace: false,
+        html5: false,
+        minifyCSS: false,
+        minifyJS: false,
+        minifyURLs: false,
+        removeComments: false,
+        removeEmptyAttributes: false
+      },
+      hash: false,
+      inject: true
+    }),
+    new BrowserSyncPlugin({
+      host: process.env.IP || 'localhost',
+      port: process.env.PORT || 3000,
+      server: {
+        baseDir: [outpath, './build']
+      }
+    }),
+    new webpack.DefinePlugin({
+      CANVAS_RENDERER: JSON.stringify(true),
+      WEBGL_RENDERER: JSON.stringify(true)
+    })
+  ],
+  module: {
+    rules: [
+      { test: /\.js$/, exclude: /node_modules/, loaders: "babel-loader" },
+      {
+        test: /\.(gif|png|jpe?g|svg|xml)$/i,
+        use: "file-loader"
+      },
+      {
+        test: [/\.vert$/, /\.frag$/],
+        use: "raw-loader"
+      }
+    ]
+  }
+};

+ 4 - 0
www/.gitignore

@@ -0,0 +1,4 @@
+# Ignore everything in this directory
+*
+# Except this file
+!.gitignore

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä