Commit | Line | Data |
---|---|---|
ccd4a2b7 | 1 | <template lang="pug"> |
9d58ef95 | 2 | main |
5b020e73 BA |
3 | input#modalNewgame.modal(type="checkbox") |
4 | div(role="dialog" aria-labelledby="titleFenedit") | |
5 | .card.smallpad | |
6 | label#closeNewgame.modal-close(for="modalNewgame") | |
7 | fieldset | |
8 | label(for="selectVariant") {{ st.tr["Variant"] }} | |
9d58ef95 | 9 | select#selectVariant(v-model="newchallenge.vid") |
85e5b5c1 | 10 | option(v-for="v in st.variants" :value="v.id") {{ v.name }} |
5b020e73 BA |
11 | fieldset |
12 | label(for="selectNbPlayers") {{ st.tr["Number of players"] }} | |
9d58ef95 | 13 | select#selectNbPlayers(v-model="newchallenge.nbPlayers") |
81d9ce72 | 14 | option(v-show="possibleNbplayers(2)" value="2" selected) 2 |
5b020e73 BA |
15 | option(v-show="possibleNbplayers(3)" value="3") 3 |
16 | option(v-show="possibleNbplayers(4)" value="4") 4 | |
17 | fieldset | |
b4d619d1 | 18 | label(for="timeControl") {{ st.tr["Time control"] }} |
9d58ef95 | 19 | input#timeControl(type="text" v-model="newchallenge.timeControl" |
b4d619d1 BA |
20 | placeholder="3m+2s, 1h+30s, 7d+1d ...") |
21 | fieldset(v-if="st.user.id > 0") | |
9d58ef95 | 22 | label(for="selectPlayers") {{ st.tr["Play with? (optional)"] }} |
5b020e73 | 23 | #selectPlayers |
81d9ce72 | 24 | input(type="text" v-model="newchallenge.to[0]") |
9d58ef95 | 25 | input(v-show="newchallenge.nbPlayers>=3" type="text" |
81d9ce72 | 26 | v-model="newchallenge.to[1]") |
9d58ef95 | 27 | input(v-show="newchallenge.nbPlayers==4" type="text" |
81d9ce72 | 28 | v-model="newchallenge.to[2]") |
b4d619d1 | 29 | fieldset(v-if="st.user.id > 0") |
9d58ef95 BA |
30 | label(for="inputFen") {{ st.tr["FEN (optional)"] }} |
31 | input#inputFen(type="text" v-model="newchallenge.fen") | |
b4d619d1 | 32 | button(@click="newChallenge") {{ st.tr["Send challenge"] }} |
9d58ef95 BA |
33 | .row |
34 | .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2 | |
35 | button(onClick="doClick('modalNewgame')") New game | |
36 | .row | |
6855163c BA |
37 | .col-sm-12.col-md-5.col-md-offset-1.col-lg-4.col-lg-offset-2 |
38 | .collapse | |
39 | input#challengeSection(type="radio" checked aria-hidden="true" name="accordion") | |
40 | label(for="challengeSection" aria-hidden="true") Challenges | |
41 | div | |
42 | .button-group | |
43 | button(@click="cdisplay='live'") Live Challenges | |
44 | button(@click="cdisplay='corr'") Correspondance challenges | |
45 | ChallengeList(v-show="cdisplay=='live'" | |
46 | :challenges="filterChallenges('live')" @click-challenge="clickChallenge") | |
47 | ChallengeList(v-show="cdisplay=='corr'" | |
48 | :challenges="filterChallenges('corr')" @click-challenge="clickChallenge") | |
49 | input#peopleSection(type="radio" checked aria-hidden="true" name="accordion") | |
50 | label(for="peopleSection" aria-hidden="true") People | |
51 | div | |
52 | #players(v-show="pdisplay=='players'") | |
53 | h3 Online players | |
54 | .player(v-for="p in uniquePlayers" @click="tryChallenge(p)" | |
55 | :class="{anonymous: !!p.count}" | |
56 | ) | |
57 | | {{ p.name + (!!p.count ? " ("+p.count+")" : "") }} | |
58 | #chat(v-show="pdisplay=='chat'") | |
59 | h3 Chat (TODO) | |
60 | input#gameSection(type="radio" checked aria-hidden="true" name="accordion") | |
61 | label(for="gameSection" aria-hidden="true") Games | |
62 | div | |
63 | .button-group | |
64 | button(@click="gdisplay='live'") Live games | |
65 | button(@click="gdisplay='corr'") Correspondance games | |
66 | GameList(v-show="gdisplay=='live'" :games="filterGames('live')" | |
67 | @show-game="showGame") | |
68 | GameList(v-show="gdisplay=='corr'" :games="filterGames('corr')" | |
69 | @show-game="showGame") | |
625022fd BA |
70 | </template> |
71 | ||
72 | <script> | |
5b020e73 | 73 | import { store } from "@/store"; |
85e5b5c1 | 74 | import { NbPlayers } from "@/data/nbPlayers"; |
9d58ef95 BA |
75 | import { checkChallenge } from "@/data/challengeCheck"; |
76 | import { ArrayFun } from "@/utils/array"; | |
03608482 | 77 | import { ajax } from "@/utils/ajax"; |
052d17ea | 78 | import { getRandString } from "@/utils/alea"; |
5b020e73 BA |
79 | import GameList from "@/components/GameList.vue"; |
80 | import ChallengeList from "@/components/ChallengeList.vue"; | |
625022fd | 81 | export default { |
cf2343ce | 82 | name: "my-hall", |
5b020e73 BA |
83 | components: { |
84 | GameList, | |
85 | ChallengeList, | |
86 | }, | |
fb54f098 BA |
87 | data: function () { |
88 | return { | |
5b020e73 | 89 | st: store.state, |
6855163c BA |
90 | cdisplay: "live", //or corr |
91 | pdisplay: "players", //or chat | |
fb54f098 | 92 | gdisplay: "live", |
6855163c | 93 | games: [], |
b4d619d1 | 94 | challenges: [], |
fb54f098 | 95 | players: [], //online players |
9d58ef95 | 96 | newchallenge: { |
fb54f098 BA |
97 | fen: "", |
98 | vid: 0, | |
99 | nbPlayers: 0, | |
6faa92f2 BA |
100 | to: ["", "", ""], //name of challenged players |
101 | timeControl: "", //"2m+2s" ...etc | |
fb54f098 BA |
102 | }, |
103 | }; | |
104 | }, | |
b4d619d1 BA |
105 | computed: { |
106 | uniquePlayers: function() { | |
6855163c | 107 | // Show e.g. "@nonymous (5)", and do nothing on click on anonymous |
4d64881e BA |
108 | let anonymous = {id:0, name:"@nonymous", count:0}; |
109 | let playerList = []; | |
b4d619d1 BA |
110 | this.players.forEach(p => { |
111 | if (p.id > 0) | |
112 | playerList.push(p); | |
113 | else | |
4d64881e | 114 | anonymous.count++; |
b4d619d1 | 115 | }); |
4d64881e BA |
116 | if (anonymous.count > 0) |
117 | playerList.push(anonymous); | |
b4d619d1 BA |
118 | return playerList; |
119 | }, | |
120 | }, | |
9d58ef95 | 121 | created: function() { |
4d64881e BA |
122 | // Always add myself to players' list |
123 | this.players.push(this.st.user); | |
f4f4c03c | 124 | // Ask server for current corr games (all but mines) |
052d17ea BA |
125 | // ajax( |
126 | // "", | |
127 | // "GET", | |
128 | // response => { | |
129 | // | |
130 | // } | |
131 | // ); | |
132 | // // Also ask for corr challenges (all) | |
133 | // ajax( | |
134 | // "", | |
135 | // "GET", | |
136 | // response => { | |
137 | // | |
138 | // } | |
139 | // ); | |
f4f4c03c | 140 | // 0.1] Ask server for for room composition: |
4d64881e | 141 | const socketOpenListener = () => { |
81d9ce72 | 142 | this.st.conn.send(JSON.stringify({code:"pollclients"})); |
4d64881e BA |
143 | }; |
144 | this.st.conn.onopen = socketOpenListener; | |
81d9ce72 BA |
145 | // TODO: is this required here? |
146 | this.oldOnmessage = this.st.conn.onmessage || Function.prototype; | |
4d64881e | 147 | this.st.conn.onmessage = this.socketMessageListener; |
052d17ea | 148 | const oldOnclose = this.st.conn.onclose; |
4d64881e | 149 | const socketCloseListener = () => { |
052d17ea | 150 | oldOnclose(); //reinitialize connexion (in store.js) |
4d64881e BA |
151 | this.st.conn.addEventListener('message', this.socketMessageListener); |
152 | this.st.conn.addEventListener('close', socketCloseListener); | |
153 | }; | |
154 | this.st.conn.onclose = socketCloseListener; | |
9d58ef95 | 155 | }, |
fb54f098 | 156 | methods: { |
6855163c BA |
157 | filterChallenges: function(type) { |
158 | return this.challenges.filter(c => c.type == type); | |
159 | }, | |
160 | filterGames: function(type) { | |
161 | return this.games.filter(c => c.type == type); | |
162 | }, | |
163 | classifyChallenge: function(c) { | |
164 | // Heuristic: should work for most cases... (TODO) | |
165 | return (c.timeControl.indexOf('d') === -1 ? "live" : "corr"); | |
166 | }, | |
9d58ef95 | 167 | socketMessageListener: function(msg) { |
052d17ea BA |
168 | // Save and call current st.conn.onmessage if one was already defined |
169 | // --> also needed in future Game.vue (also in Chat.vue component) | |
052d17ea | 170 | this.oldOnmessage(msg); |
9d58ef95 BA |
171 | const data = JSON.parse(msg.data); |
172 | switch (data.code) | |
173 | { | |
f4f4c03c | 174 | // 0.2] Receive clients list (just socket IDs) |
81d9ce72 | 175 | case "pollclients": |
5a3da968 BA |
176 | data.sockIds.forEach(sid => { |
177 | this.players.push({sid:sid, id:0, name:""}); | |
81d9ce72 | 178 | // Ask identity, challenges and game(s) |
5a3da968 | 179 | this.st.conn.send(JSON.stringify({code:"askidentity", target:sid})); |
81d9ce72 BA |
180 | this.st.conn.send(JSON.stringify({code:"askchallenges", target:sid})); |
181 | this.st.conn.send(JSON.stringify({code:"askgame", target:sid})); | |
5a3da968 BA |
182 | }); |
183 | break; | |
81d9ce72 | 184 | case "askidentity": |
6855163c BA |
185 | // Request for identification: reply if I'm not anonymous |
186 | if (this.st.user.id > 0) | |
187 | { | |
188 | this.st.conn.send(JSON.stringify( | |
189 | {code:"identity", user:this.st.user, target:data.from})); | |
190 | } | |
5a3da968 | 191 | break; |
dd75774d | 192 | case "askchallenge": |
6855163c | 193 | // Send my current live challenge (if any) |
dd75774d | 194 | const cIdx = this.challenges |
6855163c | 195 | .findIndex(c => c.from.sid == this.st.user.sid && c.type == "live"); |
dd75774d BA |
196 | if (cIdx >= 0) |
197 | { | |
198 | const c = this.challenges[cIdx]; | |
199 | const myChallenge = | |
200 | { | |
81d9ce72 BA |
201 | // Minimal challenge informations: (from not required) |
202 | to: c.to, | |
203 | fen: c.fen, | |
204 | vid: c.vid, | |
205 | timeControl: c.timeControl | |
dd75774d BA |
206 | }; |
207 | this.st.conn.send(JSON.stringify({code:"challenge", | |
208 | challenge:myChallenge, target:data.from}) | |
81d9ce72 BA |
209 | } |
210 | break; | |
211 | case "askgame": | |
dd75774d | 212 | // TODO: Send my current live game (if any): variant, players, movesCount |
81d9ce72 | 213 | break; |
5a3da968 | 214 | case "identity": |
6855163c BA |
215 | const pIdx = this.players.findIndex(p => p.sid == data.user.sid); |
216 | this.players[pIdx].id = data.user.id; | |
217 | this.players[pIdx].name = data.user.name; | |
5a3da968 | 218 | break; |
dd75774d BA |
219 | case "challenge": |
220 | // Receive challenge from some player (+sid) | |
6855163c BA |
221 | let newChall = data.chall; |
222 | newChall.type = classifyChallenge(data.chall); | |
223 | const pIdx = this.players.findIndex(p => p.sid == data.sid); | |
224 | newChall.from = this.players[pIdx]; //may be anonymous | |
225 | this.challenges.push(newChall); | |
81d9ce72 | 226 | break; |
dd75774d | 227 | case "game": |
6855163c BA |
228 | // Receive game from some player (+sid) |
229 | // TODO: receive game summary (update, count moves) | |
230 | // (just players names, time control, and ID + player ID) | |
231 | // NOTE: it may be correspondance (if newgame while we are connected) | |
81d9ce72 | 232 | break; |
b4d619d1 BA |
233 | // * - receive "new game": if live, store locally + redirect to game |
234 | // * If corr: notify "new game has started", give link, but do not redirect | |
9d58ef95 BA |
235 | case "newgame": |
236 | // TODO: new game just started: data contain all informations | |
237 | // (id, players, time control, fenStart ...) | |
b4d619d1 BA |
238 | // + cid to remove challenge from list |
239 | break; | |
b4d619d1 | 240 | // * - receive "accept/withdraw/cancel challenge": apply action to challenges list |
9d58ef95 | 241 | case "acceptchallenge": |
6855163c BA |
242 | // someone accept an open (or targeted) challenge |
243 | // ==> if (open and) full and I don't play, delete from list | |
244 | // If I play: just add player. Then, if full send a "newgame" | |
245 | // and (if full) in any case, remove challenge from list. | |
246 | if (this.challenges.some(c => c.id == data.cid) //.............TODO | |
9d58ef95 BA |
247 | this.newGame(data.challenge, data.user); //user.id et user.name |
248 | break; | |
249 | case "withdrawchallenge": | |
250 | const cIdx = this.challenges.findIndex(c => c.id == data.cid); | |
251 | let chall = this.challenges[cIdx] | |
252 | ArrayFun.remove(chall.players, p => p.id == data.uid); | |
253 | chall.players.push({id:0, name:""}); | |
254 | break; | |
255 | case "cancelchallenge": | |
256 | ArrayFun.remove(this.challenges, c => c.id == data.cid); | |
257 | break; | |
6855163c BA |
258 | // TODO: distinguish hallConnect and gameConnect ? |
259 | // Or global variable players | |
260 | // + game variable: "observers" | |
b4d619d1 | 261 | case "connect": |
6855163c | 262 | // * - receive "player connect": send our current challenge (to him or global) |
b4d619d1 | 263 | // * Also send all our games (live - max 1 - and corr) [in web worker ?] |
5a3da968 BA |
264 | this.players.push({name:"", id:0, sid:data.sid}); |
265 | this.st.conn.send(JSON.stringify({code:"askidentity", target:data.sid})); | |
9d58ef95 | 266 | break; |
b4d619d1 BA |
267 | // * - receive "player disconnect": remove from players list |
268 | case "disconnect": | |
5a3da968 | 269 | ArrayFun.remove(this.players, p => p.sid == data.sid); |
03608482 BA |
270 | // TODO: also remove all challenges sent by this player, |
271 | // and all live games where he plays and no other opponent is online | |
9d58ef95 BA |
272 | break; |
273 | } | |
274 | }, | |
fb54f098 | 275 | showGame: function(game) { |
5b020e73 BA |
276 | // NOTE: if we are an observer, the game will be found in main games list |
277 | // (sent by connected remote players) | |
b4d619d1 | 278 | // TODO: game path ? /vname/gameId seems better |
6855163c | 279 | this.$router.push("/" + game.id); |
fb54f098 | 280 | }, |
b4d619d1 BA |
281 | tryChallenge: function(player) { |
282 | if (player.id == 0) | |
283 | return; //anonymous players cannot be challenged | |
81d9ce72 | 284 | this.newchallenge.to[0] = player.name; |
b4d619d1 | 285 | doClick("modalNewgame"); |
fb54f098 | 286 | }, |
b4d619d1 BA |
287 | // * - accept challenge (corr or live) --> send info to all concerned players |
288 | // * - cancel challenge (click on sent challenge) --> send info to all concerned players | |
289 | // * - withdraw from challenge (if >= 3 players and previously accepted) | |
290 | // * --> send info to all concerned players | |
291 | // * - refuse challenge (or receive refusal): send to all challenge players (from + to) | |
292 | // * except us ; graphics: modal again ? (inline ?) | |
293 | // * - prepare and start new game (if challenge is full after acceptation) | |
294 | // * --> include challenge ID (so that opponents can delete the challenge too) | |
295 | // * Also send to all connected players (only from me) | |
fb54f098 | 296 | clickChallenge: function(challenge) { |
81d9ce72 | 297 | // TODO: also correspondance case (send to server) |
fb54f098 | 298 | const index = this.challenges.findIndex(c => c.id == challenge.id); |
81d9ce72 | 299 | const toIdx = challenge.to.findIndex(name => name == this.st.user.name); |
fb54f098 BA |
300 | if (toIdx >= 0) |
301 | { | |
302 | // It's a multiplayer challenge I accepted: withdraw | |
303 | this.st.conn.send(JSON.stringify({code:"withdrawchallenge", | |
81d9ce72 | 304 | cid:challenge.id, user:this.st.user.sid})); |
fb54f098 BA |
305 | this.challenges.to.splice(toIdx, 1); |
306 | } | |
307 | else if (challenge.from.id == user.id) //it's my challenge: cancel it | |
308 | { | |
309 | this.st.conn.send(JSON.stringify({code:"cancelchallenge", cid:challenge.id})); | |
310 | this.challenges.splice(index, 1); | |
311 | } | |
312 | else //accept a challenge | |
313 | { | |
314 | this.st.conn.send(JSON.stringify({code:"acceptchallenge", | |
315 | cid:challenge.id, user:me})); | |
316 | this.challenges[index].to.push(me); | |
317 | } | |
318 | // TODO: accepter un challenge peut lancer une partie, il | |
319 | // faut alors supprimer challenge + creer partie + la retourner et l'ajouter ici | |
fb54f098 BA |
320 | // si pas le mien et FEN speciale :: (charger code variante et) |
321 | // montrer diagramme + couleur (orienté) | |
322 | }, | |
b4d619d1 BA |
323 | // user: last person to accept the challenge (TODO: revoir ça) |
324 | // newGame: function(chall, user) { | |
325 | // const fen = chall.fen || V.GenRandInitFen(); | |
326 | // const game = {}; //TODO: fen, players, time ... | |
327 | // //setStorage(game); //TODO | |
328 | // game.players.forEach(p => { //...even if game is by corr (could be played live, why not...) | |
329 | // this.conn.send( | |
330 | // JSON.stringify({code:"newgame", oppid:p.id, game:game})); | |
331 | // }); | |
332 | // if (this.settings.sound >= 1) | |
333 | // new Audio("/sounds/newgame.mp3").play().catch(err => {}); | |
334 | // }, | |
8ef2edfa BA |
335 | // Load a variant file (TODO: should probably be global) |
336 | loadVariant: async function(vid, variantArray) { | |
337 | const idxInVariants = variantArray.findIndex(v => v.id == vid); | |
338 | const vname = variantArray[idxInVariants].name; | |
339 | const vModule = await import("@/variants/" + vname + ".js"); | |
340 | window.V = vModule.VariantRules; | |
341 | return vname; | |
342 | }, | |
b4d619d1 | 343 | // Send new challenge (corr or live, cf. time control), with button or click on player |
9d58ef95 | 344 | newChallenge: async function() { |
8ef2edfa BA |
345 | // TODO: put this "load variant" block elsewhere |
346 | const vname = this.loadVariant(this.newchallenge.vid, this.st.variants); | |
dd75774d | 347 | // checkChallenge side-effect = , and mainTime + increment in seconds |
f4f4c03c | 348 | // TODO: should not be a side-effect but set here ; for received server challenges we do not have mainTime+increment |
9d58ef95 BA |
349 | const error = checkChallenge(this.newchallenge); |
350 | if (!!error) | |
351 | return alert(error); | |
dd75774d BA |
352 | if (this.challenges.some(c => c.from.sid == this.st.user.sid && c.liveGame)) |
353 | { | |
354 | document.getElementById("modalNewgame").checked = false; | |
355 | return alert("You already have a pending live challenge"); | |
356 | // TODO: better to just replace current challenge | |
6855163c | 357 | // --> also for corr challenges |
dd75774d | 358 | } |
03608482 | 359 | // Check that the players (if any indicated) are online |
81d9ce72 BA |
360 | let chall = Object.Assign( |
361 | {}, | |
362 | this.newchallenge, | |
363 | { | |
364 | from: this.st.user, | |
365 | added: Date.now(), | |
dd75774d | 366 | fen: this.newchallenge.fen || V.GenRandInitFen(), |
5578a7bf BA |
367 | variant: {id: this.newchallenge.vid, name: vname}, |
368 | nbPlayers: this.newchallenge.nbPlayers, | |
369 | to: [ | |
370 | {id: 0, name: this.newchallenge.to[0], sid: ""}, | |
371 | {id: 0, name: this.newchallenge.to[1], sid: ""}, | |
372 | {id: 0, name: this.newchallenge.to[2], sid: ""}, | |
373 | ], | |
374 | timeControl: this.newchallenge.timeControl, | |
5578a7bf BA |
375 | }; |
376 | for (let p of chall.to) | |
9d58ef95 | 377 | { |
03608482 BA |
378 | if (p.name != "") |
379 | { | |
380 | const pIdx = this.players.findIndex(pl => pl.name == p.name); | |
6faa92f2 BA |
381 | // NOTE: id (server DB) and sid (socket ID). |
382 | // Anonymous players just have a socket ID. | |
5578a7bf BA |
383 | // NOTE: for correspondance play we don't require players to be online |
384 | // (==> we don't have IDs, and no sid) | |
385 | if (liveGame && pIdx === -1) | |
03608482 BA |
386 | return alert(p.name + " is not connected"); |
387 | p.id = this.players[pIdx].id; | |
388 | p.sid = this.players[pIdx].sid; | |
389 | } | |
390 | } | |
8ef2edfa | 391 | const finishAddChallenge = (cid) => { |
052d17ea | 392 | chall.id = cid || "c" + getRandString(); |
03608482 | 393 | this.challenges.push(chall); |
b4d619d1 | 394 | // Send challenge to peers |
5a3da968 | 395 | let challSock = |
5578a7bf | 396 | { |
b4d619d1 | 397 | code: "newchallenge", |
5578a7bf | 398 | chall: chall, |
5a3da968 BA |
399 | target: "", |
400 | }; | |
401 | const sendChallengeTo = (sid) => { | |
402 | challSock.target = sid; | |
403 | this.st.conn.send(JSON.stringify(challSock)); | |
5578a7bf BA |
404 | }; |
405 | if (chall.to[0].id > 0) | |
9d58ef95 | 406 | { |
03608482 | 407 | // Challenge with targeted players |
5578a7bf | 408 | chall.to.forEach(p => { |
03608482 | 409 | if (p.id > 0) |
5a3da968 | 410 | sendChallengeTo(p.sid); |
9d58ef95 BA |
411 | }); |
412 | } | |
413 | else | |
414 | { | |
5578a7bf | 415 | // Open challenge: send to all connected players (except us) |
5578a7bf BA |
416 | this.players.forEach(p => { |
417 | if (p.sid != this.st.user.sid) //only sid is always set | |
5a3da968 | 418 | sendChallengeTo(p.sid); |
5578a7bf | 419 | }); |
9d58ef95 | 420 | } |
b4d619d1 BA |
421 | document.getElementById("modalNewgame").checked = false; |
422 | }; | |
dd75774d | 423 | if (this.newchallenge.liveGame) |
b4d619d1 | 424 | { |
03608482 | 425 | // Live challenges have cid = 0 |
5578a7bf | 426 | finishAddChallenge(); |
03608482 | 427 | } |
b4d619d1 | 428 | else |
03608482 | 429 | { |
f4f4c03c BA |
430 | const chall = { |
431 | uid: req.body["from"], | |
432 | vid: req.body["vid"], | |
433 | fen: req.body["fen"], | |
434 | timeControl: req.body["timeControl"], | |
435 | nbPlayers: req.body["nbPlayers"], | |
436 | to: req.body["to"], //array of IDs | |
437 | }; | |
b4d619d1 | 438 | // Correspondance game: send challenge to server |
03608482 BA |
439 | ajax( |
440 | "/challenges/" + this.newchallenge.vid, | |
441 | "POST", | |
052d17ea | 442 | chall, |
5578a7bf BA |
443 | response => { |
444 | chall.id = response.cid; | |
445 | finishAddChallenge(); | |
446 | } | |
03608482 | 447 | ); |
9d58ef95 | 448 | } |
fb54f098 BA |
449 | }, |
450 | possibleNbplayers: function(nbp) { | |
9d58ef95 | 451 | if (this.newchallenge.vid == 0) |
fb54f098 | 452 | return false; |
85e5b5c1 | 453 | const variants = this.st.variants; |
fb54f098 | 454 | const idxInVariants = |
9d58ef95 | 455 | variants.findIndex(v => v.id == this.newchallenge.vid); |
fb54f098 BA |
456 | return NbPlayers[variants[idxInVariants].name].includes(nbp); |
457 | }, | |
458 | }, | |
85e5b5c1 | 459 | }; |
ccd4a2b7 | 460 | </script> |
85e5b5c1 BA |
461 | |
462 | <style lang="sass"> | |
463 | // TODO | |
464 | </style> |