Browse Source

Deck building logic Done for the 3 modes (tournament, faction, draft)

jojo 4 years ago
parent
commit
0415b41cb5

+ 4 - 1
server/src/client-server-shared/const/constants.js

@@ -150,7 +150,10 @@ export const Constants = {
    * @default
    * @memberof Constants.PLAYER
    */
-  PLAYER_PASS: 'pass'
+  PLAYER_PASS: 'pass',
+
+  NB_CARDS_TO_PICK_DRAFT: 2,
+  NB_CARDS_TO_PICK_TOURNAMENT: 12
 };
 
 Constants.install = function(Vue) {

+ 4 - 2
server/src/client-server-shared/gameStates/0-initState.js

@@ -8,11 +8,13 @@ export default function initState(duelController) {
       duelCtrl.setData({
         bluePlayer: {
           name: payload.player1Name,
-          color: 'blue'
+          color: 'blue',
+          twelveHeroes: []
         },
         redPlayer: {
           name: payload.player2Name,
-          color: 'red'
+          color: 'red',
+          twelveHeroes: []
         },
         game: {
           allHeroes: duelCtrl.getAllHeroes(),

+ 9 - 2
server/src/client-server-shared/gameStates/1-selectTournamentState.js

@@ -7,6 +7,13 @@ export default function selectTournamentState(duelController) {
   this.start = () => {
     /** @type {import('type/game').TH_GameDataStore} */
     let gameData = duelCtrl.getGameData();
+
+    gameData.bluePlayer.draftHeroesIds = gameData.game.allHeroes.map(
+      hero => hero.id
+    );
+    gameData.redPlayer.draftHeroesIds = gameData.game.allHeroes.map(
+      hero => hero.id
+    );
     gameData.game['waitingFor/blue'] = true;
     gameData.game['waitingFor/red'] = true;
     gameData.game.currentPlayer = 'both';
@@ -19,10 +26,10 @@ export default function selectTournamentState(duelController) {
     /** @type {import('type/game').TH_GameDataStore} */
     let gameData = duelCtrl.getGameData();
     if (payload) {
-      let player = payload.color + 'player';
+      let player = payload.color + 'Player';
       /** @type {import('type/game').TH_HeroInGame[]} */
       let twelveHeroes = [];
-      payload.twelveHeroesIds.forEach(id => {
+      payload.chosenIds.forEach(id => {
         twelveHeroes.push({ id, position: 'pile', possibleActions: [] });
       });
       let randomHeroes = shuffleHeroes(twelveHeroes);

+ 2 - 2
server/src/client-server-shared/heroesHelper.js

@@ -52,8 +52,8 @@ export const getHeroesIdsByFaction = function(
   faction,
   popularityRule
 ) {
-  /** @type {import("type/game").Popularity} */
-  let popularity = 'ds';
+  /** @type {import("type/game").TH_Popularity} */
+  let popularity = 'without';
   if (popularityRule === true) {
     popularity = 'with';
   }

+ 1 - 1
server/src/client-server-shared/type/comm.js

@@ -35,7 +35,7 @@
  *
  * @typedef {object} TH_MessageTournamentDeckStep
  * @property {import('type/game').TH_Color} color - color of player
- * @property {number[]} twelveHeroesIds - The chosen 12 heroes IDs
+ * @property {number[]} chosenIds - The chosen 12 heroes IDs
  */
 /**
  * Message for chat from one player to others

+ 1 - 1
server/src/client-server-shared/type/game.js

@@ -118,7 +118,7 @@
  * @property {string} name - username of this player
  * @property {TH_Color} color - color of the player
  * @property {TH_Faction|''} faction - Chosen faction (empty if not playing faction mode)
- * @property {Array<number>} draftHeroesIds - Will contain the IDs of the heroes selectable for draft mode
+ * @property {Array<number>} draftHeroesIds - Will contain the IDs of the heroes selectable for draft and tournament mode
  * @property {Array<TH_HeroInGame>} twelveHeroes - The 12 Heroes used by player
  * @property {number} foodInCamp - Number of food in camp
  * @property {number} foodInBattle/left - Food on left battle field

+ 13 - 0
server/src/client-server-shared/type/global.js

@@ -0,0 +1,13 @@
+/**
+ * Main Store data
+ *
+ * @typedef {object} TH_MainStore
+ * @property {string} username - player username if connected
+ * @property {boolean} isConnected - If true, a user is currently connected
+ * @property {number} gameId - Game Id of current online game (-1 if no game played or local one)
+ * @property {boolean} isGameRunning - True of a game is running
+ * @property {import('type/comm').TH_MessageChatFrom} messages - chat messages to display in app
+ * @property {number[]} chosenIds - Array of IDs chosen by player
+ */
+
+exports.unused = {};

+ 0 - 6
server/src/game-server/games-manager.js

@@ -5,8 +5,6 @@ const path = require('path');
 export default function GamesManager(ioServer, mariadbConn) {
   let currentGames = new Map();
 
-  let count = 0;
-
   // We create Heroes data from JSON file, it will be needed by the duelController
   // DuelController has no dependency to browser env nor node env. So it cannot read the
   // file itself (not same mechanism, in node we use fs readFileSync)
@@ -43,8 +41,6 @@ export default function GamesManager(ioServer, mariadbConn) {
           allHeroesJson
         )
       );
-      count++;
-      console.log('create game : nb >> ', count);
       return true;
 
       // We are supposed to join a new game, but not there, must have been deleted in the meantime
@@ -69,8 +65,6 @@ export default function GamesManager(ioServer, mariadbConn) {
           mariadbConn.updateGameStatus(id, 'PAUSED');
         }
         currentGames.delete(id);
-        count--;
-        console.log('delete game : nb >> ', count);
       }
     }
 

+ 0 - 16
server/src/game-server/online-duel-sync.js

@@ -97,14 +97,9 @@ export default function OnlineDuelSync(
         chat(firstPlayer.playerName, message);
       })
       .on('end-turn', data => {
-        console.log(
-          '1st player, End turn called from :>> ',
-          firstPlayer.getSocket().id
-        );
         duelCtrlProxy.endTurn(data);
       });
 
-    console.log('first player is :>> ', firstPlayer.getSocket().id);
     // If not new game, set status to PLAYING right now
     // Otherwise we wait second player before doing so
     if (!isNew) {
@@ -144,13 +139,8 @@ export default function OnlineDuelSync(
             chat(player.playerName, message);
           })
           .on('end-turn', data => {
-            console.log(
-              '2nd  player, End turn called from :>> ',
-              player.getSocket().id
-            );
             duelCtrlProxy.endTurn(data);
           });
-        console.log('add player is :>> ', player.getSocket().id);
         // Both players connected to a new game, set status to PLAYING
         if (isNew) {
           isNew = false;
@@ -179,18 +169,12 @@ export default function OnlineDuelSync(
   // Handle player disconnection / reconnection without impacting other player
   // If no player left, this object will be destroyed by games-manager
   let playerLeft = function(player) {
-    console.log('player is leaving :>> ', player.playerName);
     let playerLeaving = players.get(player.playerName);
     if (playerLeaving) {
-      console.log('Leave room:>> ', gameId);
       playerLeaving.getSocket().leave(gameId);
 
       playerLeaving.getSocket().removeAllListeners('chat');
       playerLeaving.getSocket().removeAllListeners('end-turn');
-      console.log(
-        'remove listeners 1 by 1 for player id:>> ',
-        player.getSocket().id
-      );
 
       chat(player.playerName, player.playerName + ' has left the game');
       players.delete(player.playerName);

+ 33 - 14
src/common/heroes-display/HeroesDisplay.vue

@@ -1,5 +1,11 @@
 <template>
   <div>
+    <heroes-selector
+      v-if="selectHeroes"
+      :factionSelected="factionSelected"
+    ></heroes-selector>
+    <hr />
+    <h4 v-if="areHeroesSelectable">Select Heroes from below</h4>
     <button
       class="btn btn-primary"
       @click="showFilter = !showFilter"
@@ -19,21 +25,15 @@
       :filter-options="filterOptions"
       v-model="filter"
     ></heroes-filter>
-    <heroes-selector
-      v-if="selectHeroes"
-      :factionSelected="factionSelected"
-    ></heroes-selector>
-    <hr />
     <div
       class="row row-cols-2 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 row-cols-xl-6 mt-3"
     >
       <hero
-        v-for="(hero, index) in heroesToDisplay"
-        :key="index"
+        v-for="hero in heroesToDisplay"
+        :key="hero.id"
         :hero="hero"
-        :selectable="
-          selectHeroes === true && ['draft', 'tournament'].includes(deckMode)
-        "
+        :selectable="areHeroesSelectable"
+        :removable="false"
       ></hero>
       <!-- v-model="selected" -->
     </div>
@@ -45,6 +45,7 @@ import { mapGetters } from 'vuex';
 import Hero from './components/Hero';
 import HeroesFilter from './components/HeroesFilter';
 import HeroesSelector from './components/HeroesSelector';
+import { Constants } from 'const/constants';
 
 let initialFilter = function(filterOptions) {
   return {
@@ -75,7 +76,7 @@ export default {
   },
   data() {
     return {
-      showFilter: this.selectHeroes,
+      showFilter: this.selectHeroes === true && this.deckMode === 'faction',
       filter: {
         minPower: 0,
         maxPower: 6,
@@ -103,7 +104,9 @@ export default {
       isPopularityRule: 'game/isPopularityRule',
       myColor: 'game/myColor',
       getSelectableDraftIds: 'game/myDraftIds',
-      getTwelveHeroes: 'game/myTwelveHeroes'
+      getTwelveHeroes: 'game/myTwelveHeroes',
+      chosenIds: 'chosenIds',
+      isItMyTurn: 'game/isItMyTurn'
     }),
     filterOptions() {
       // init filter options will all
@@ -134,18 +137,35 @@ export default {
         byName: true
       };
     },
+    // TODO Here filter directly with heroesByIds ??
     heroesToDisplay() {
       if (this.display12heroesOnly === true) {
         return this.getTwelveHeroes;
       }
       if (this.selectHeroes === true) {
-        return this.heroesBy(this.filter);
+        return this.heroesBy(
+          this.filter,
+          this.deckMode === 'draft' || this.deckMode === 'tournament'
+        );
       } else {
         return this.allHeroes;
       }
     },
     factionSelected() {
       return this.filter.faction;
+    },
+    areHeroesSelectable() {
+      if (this.selectHeroes === true && this.isItMyTurn === true) {
+        if (
+          (this.deckMode === 'draft' &&
+            this.chosenIds.length < Constants.NB_CARDS_TO_PICK_DRAFT) ||
+          (this.deckMode === 'tournament' &&
+            this.chosenIds.length < Constants.NB_CARDS_TO_PICK_TOURNAMENT)
+        ) {
+          return true;
+        }
+      }
+      return false;
     }
   },
   methods: {
@@ -155,7 +175,6 @@ export default {
   },
   created() {
     Object.assign(this.filter, initialFilter(this.filterOptions));
-    this.filter.byId = this.getSelectableDraftIds;
   },
   watch: {
     'filter.byName'(newValue) {

+ 25 - 3
src/common/heroes-display/components/Hero.vue

@@ -17,18 +17,40 @@
         </p>
       </div>
       <div class="text-center mb-2" v-if="selectable">
-        <button class="btn btn-primary" style="width:50%;">
+        <button
+          class="btn btn-primary"
+          style="width:50%;"
+          @click="selectId(hero.id) && removeInMyDraftIds(hero.id)"
+        >
           Select
         </button>
       </div>
+      <div class="text-center mb-2" v-if="removable">
+        <button
+          class="btn btn-primary"
+          style="width:50%;"
+          @click="unselectId(hero.id) && addInMyDraftIds(hero.id)"
+        >
+          Remove
+        </button>
+      </div>
     </div>
   </div>
 </template>
 
 <script>
+import { mapActions } from 'vuex';
 export default {
-  props: ['hero', 'selectable'],
-  computed: {},
+  props: ['hero', 'selectable', 'removable'],
+  methods: {
+    ...mapActions({
+      selectId: 'selectId',
+      unselectId: 'unselectId',
+      clearChosenIds: 'clearChosenIds',
+      addInMyDraftIds: 'game/addInMyDraftIds',
+      removeInMyDraftIds: 'game/removeInMyDraftIds'
+    })
+  },
   filters: {
     draftText(value) {
       if (value === true) {

+ 95 - 8
src/common/heroes-display/components/HeroesSelector.vue

@@ -1,5 +1,8 @@
 <template>
   <div>
+    <div>
+      <h4>{{ instructions }}</h4>
+    </div>
     <button
       class="btn btn-primary"
       v-if="deckMode === 'faction'"
@@ -8,23 +11,57 @@
     >
       {{ factionButtonText }}
     </button>
-    <div v-if="deckMode === 'tournament'">
-      Here logic to select tournament deck
-    </div>
-    <div v-if="deckMode === 'draft'">
-      Here logic to select draft deck
+    <div v-if="deckMode === 'draft' || deckMode === 'tournament'">
+      <div v-if="myHeroes.length > 0 || heroesSelected.length > 0">
+        <hr />
+        <h3>My Heroes</h3>
+
+        <div
+          class="row row-cols-2 row-cols-sm-2 row-cols-md-3 row-cols-lg-4 row-cols-xl-6 mt-3"
+        >
+          <hero
+            v-for="hero in myHeroes"
+            :key="hero.id"
+            :hero="hero"
+            :selectable="false"
+            :removable="false"
+          ></hero>
+          <hero
+            v-for="hero in heroesSelected"
+            :key="hero.id"
+            :hero="hero"
+            :selectable="false"
+            :removable="isItMyTurn"
+          ></hero>
+        </div>
+      </div>
+      <button
+        class="btn btn-primary"
+        v-if="showValidSelectionBtn"
+        @click="submitChosenIds"
+      >
+        {{ validButtonText }}
+      </button>
     </div>
   </div>
 </template>
 
 <script>
+import Hero from './Hero';
 import { mapGetters, mapActions } from 'vuex';
+import { Constants } from 'const/constants';
 export default {
   props: ['factionSelected'],
+  components: {
+    Hero
+  },
   computed: {
     ...mapGetters({
       deckMode: 'game/deckMode',
-      isItMyTurn: 'game/isItMyTurn'
+      isItMyTurn: 'game/isItMyTurn',
+      myHeroes: 'game/myTwelveHeroes',
+      heroesByIds: 'game/heroesByIds',
+      chosenIds: 'chosenIds'
     }),
     factionButtonText() {
       if (this.isItMyTurn === true) {
@@ -32,14 +69,64 @@ export default {
       } else {
         return 'Wait other player...';
       }
+    },
+    heroesSelected() {
+      return this.heroesByIds(this.chosenIds);
+    },
+    showValidSelectionBtn() {
+      if (this.isItMyTurn === false) {
+        return false;
+      }
+      if (
+        (this.deckMode === 'draft' &&
+          this.chosenIds.length === Constants.NB_CARDS_TO_PICK_DRAFT) ||
+        (this.deckMode === 'tournament' &&
+          this.chosenIds.length === Constants.NB_CARDS_TO_PICK_TOURNAMENT)
+      ) {
+        return true;
+      } else {
+        return false;
+      }
+    },
+    validButtonText() {
+      if (this.isItMyTurn === false) {
+        return 'Waiting other player';
+      } else {
+        return 'Valid Selection';
+      }
+    },
+    instructions() {
+      let instructions = 'Waiting for other player ...';
+      if (this.isItMyTurn) {
+        switch (this.deckMode) {
+          case 'faction':
+            instructions = `Select a faction of 12 Heroes to play with`;
+            break;
+          case 'draft':
+            instructions = `Select 2 heroes below and Valid selection`;
+            break;
+          case 'tournament':
+            instructions = `Select 12 heroes below and Valid selection`;
+            break;
+
+          default:
+            break;
+        }
+      }
+      return instructions;
     }
   },
   methods: {
     ...mapActions({
-      submitFaction: 'game/submitMyFaction'
+      submitFaction: 'game/submitMyFaction',
+      submitChosenIds: 'game/submitMyChosenIds'
     })
   }
 };
 </script>
 
-<style></style>
+<style>
+p {
+  font-size: 20px;
+}
+</style>

+ 2 - 3
src/common/socket-service.js

@@ -138,9 +138,8 @@ export default function SocketService(socketEventBus, vuexStore) {
     return checkConnection(payload.username).then(
       () => {
         return new Promise(resolve => {
-          ioClient.emit('end-turn', payload, function(response) {
-            resolve(response);
-          });
+          ioClient.emit('end-turn', payload);
+          resolve();
         });
       },
       error => Promise.reject(error)

+ 22 - 6
src/store/game/game.js

@@ -54,8 +54,8 @@ const getters = {
   isDiscardRule(state) {
     return state.advRules.includes('discard');
   },
-  heroById: state => id => {
-    return state.allHeroes.find(hero => hero.id === id);
+  heroesByIds: state => ids => {
+    return state.allHeroes.filter(hero => ids.includes(hero.id));
   },
   myTwelveHeroes(state, getters) {
     let myHeroesIds = getters[getters.my('twelveHeroes')].map(hero => hero.id);
@@ -67,7 +67,7 @@ const getters = {
     return getters[getters.my('draftIds')];
   },
 
-  filterHeroes: state =>
+  filterHeroes: (state, getters) =>
     /**
      * Get a filtered array of heroes
      *
@@ -88,10 +88,10 @@ const getters = {
      * @param {boolean} filter.byName - Activate filter on Hero's name
      * @param {string} filter.name - Filter on Hero's name
      * @param {number[]} filter.byId - Filter on Heroes Id(s). Do not filter in Ids if array is empty
-     * @param state
+     * @param {boolean} draftMode - If true, only filter through the IDs in player/draftHeroesIds
      * @returns {object[]} Filtered array of heroes
      */
-    filter => {
+    (filter, draftMode = false) => {
       if (!filter.faction) filter.faction = 'all';
       if (!filter.popularity) filter.popularity = 'without';
       if (!filter.minPower) filter.minPower = 0;
@@ -112,7 +112,11 @@ const getters = {
           draftFilter = 'all';
           break;
       }
-      return state.allHeroes.filter(hero => {
+      let heroesToFilter = state.allHeroes;
+      if (draftMode === true) {
+        heroesToFilter = getters.heroesByIds(getters.myDraftIds);
+      }
+      return heroesToFilter.filter(hero => {
         if (filter.faction !== 'all' && hero.faction !== filter.faction)
           return false;
         if (
@@ -207,6 +211,8 @@ const actions = {
     commit('INIT_GAME_STATE');
     commit('bluePlayer/INIT_PLAYER_STATE');
     commit('redPlayer/INIT_PLAYER_STATE');
+    // Also clear chosenIds array if we leave game during deck building phase
+    commit('CLEAR_CHOSEN_IDS', null, { root: true });
   },
   /**
    * Submit chosen faction to the game master
@@ -217,6 +223,16 @@ const actions = {
    */
   submitMyFaction: ({ dispatch, getters }, payload) => {
     dispatch(getters.my('submitFaction'), payload);
+  },
+
+  submitMyChosenIds: ({ dispatch, getters }) => {
+    dispatch(getters.my('submitChosenIds'));
+  },
+  addInMyDraftIds: ({ dispatch, getters }, payload) => {
+    dispatch(getters.my('addInDraftIds'), payload);
+  },
+  removeInMyDraftIds: ({ dispatch, getters }, payload) => {
+    dispatch(getters.my('removeInDraftIds'), payload);
   }
 };
 const bluePlayer = new AnyPlayer();

+ 33 - 0
src/store/game/player/any-player.js

@@ -49,6 +49,12 @@ export default function AnyPlayer() {
     },
     INIT_PLAYER_STATE: state => {
       Object.assign(state, initPlayerState);
+    },
+    ADD_IN_DRAFT_IDS: (state, payload) => {
+      state.draftHeroesIds.push(payload);
+    },
+    REMOVE_IN_DRAFT_IDS: (state, payload) => {
+      state.draftHeroesIds = state.draftHeroesIds.filter(id => id !== payload);
     }
   };
 
@@ -70,6 +76,33 @@ export default function AnyPlayer() {
             { root: true }
           );
         });
+    },
+    submitChosenIds: ({ commit, state, rootGetters }) => {
+      commit(
+        'game/SET_WAITING_FOR',
+        { color: state.color, value: false },
+        { root: true }
+      );
+      socketService
+        .endTurn({ color: state.color, chosenIds: rootGetters.chosenIds })
+        .then(() => {
+          commit('CLEAR_CHOSEN_IDS', null, { root: true });
+        })
+        .catch(err => {
+          console.log('Error sending data : ', err);
+          //Unvalid submit
+          commit(
+            'game/SET_WAITING_FOR',
+            { color: state.color, value: true },
+            { root: true }
+          );
+        });
+    },
+    addInDraftIds: ({ commit }, payload) => {
+      commit('ADD_IN_DRAFT_IDS', payload);
+    },
+    removeInDraftIds: ({ commit }, payload) => {
+      commit('REMOVE_IN_DRAFT_IDS', payload);
     }
   };
   return {

+ 33 - 1
src/store/store.js

@@ -8,12 +8,14 @@ import menu from './menu/menu';
 import game from './game/game';
 import { socketService } from '../main';
 
+/** @type {import('type/global').TH_MainStore} */
 const state = {
   username: '',
   isConnected: false,
   gameId: -1,
   isGameRunning: false,
-  messages: []
+  messages: [],
+  chosenIds: []
 };
 
 // /**
@@ -40,6 +42,9 @@ const getters = {
   },
   isConnected(state) {
     return state.isConnected;
+  },
+  chosenIds(state) {
+    return state.chosenIds;
   }
 };
 
@@ -58,6 +63,23 @@ const mutations = {
   },
   SET_IS_CONNECTED: (state, payload) => {
     state.isConnected = payload;
+  },
+  ADD_CHOSEN_ID: (
+    /** @type {import('type/global').TH_MainStore} */ state,
+    payload
+  ) => {
+    state.chosenIds.push(payload);
+  },
+  REMOVE_CHOSEN_ID: (
+    /** @type {import('type/global').TH_MainStore} */ state,
+    payload
+  ) => {
+    state.chosenIds = state.chosenIds.filter(id => id !== payload);
+  },
+  CLEAR_CHOSEN_IDS: (
+    /** @type {import('type/global').TH_MainStore} */ state
+  ) => {
+    state.chosenIds = [];
   }
 };
 const actions = {
@@ -78,6 +100,16 @@ const actions = {
     commit('menu/RESET_ALL_STATUS');
     localStorage.removeItem('username');
     localStorage.removeItem('gameId');
+  },
+  selectId: ({ commit }, payload) => {
+    commit('ADD_CHOSEN_ID', payload);
+  },
+  unselectId: ({ commit }, payload) => {
+    console.log('unselectId :>> ', payload);
+    commit('REMOVE_CHOSEN_ID', payload);
+  },
+  clearChosenIds: ({ commit }) => {
+    commit('CLEAR_CHOSEN_IDS');
   }
 };