Commit | Line | Data |
---|---|---|
b4d619d1 BA |
1 | <!-- Main playing hall: online players + current challenges + button "new game" --> |
2 | ||
ccd4a2b7 | 3 | <template lang="pug"> |
9d58ef95 | 4 | main |
5b020e73 BA |
5 | input#modalNewgame.modal(type="checkbox") |
6 | div(role="dialog" aria-labelledby="titleFenedit") | |
7 | .card.smallpad | |
8 | label#closeNewgame.modal-close(for="modalNewgame") | |
9 | fieldset | |
10 | label(for="selectVariant") {{ st.tr["Variant"] }} | |
9d58ef95 | 11 | select#selectVariant(v-model="newchallenge.vid") |
85e5b5c1 | 12 | option(v-for="v in st.variants" :value="v.id") {{ v.name }} |
5b020e73 BA |
13 | fieldset |
14 | label(for="selectNbPlayers") {{ st.tr["Number of players"] }} | |
9d58ef95 | 15 | select#selectNbPlayers(v-model="newchallenge.nbPlayers") |
5b020e73 BA |
16 | option(v-show="possibleNbplayers(2)" value="2") 2 |
17 | option(v-show="possibleNbplayers(3)" value="3") 3 | |
18 | option(v-show="possibleNbplayers(4)" value="4") 4 | |
19 | fieldset | |
b4d619d1 | 20 | label(for="timeControl") {{ st.tr["Time control"] }} |
9d58ef95 | 21 | input#timeControl(type="text" v-model="newchallenge.timeControl" |
b4d619d1 BA |
22 | placeholder="3m+2s, 1h+30s, 7d+1d ...") |
23 | fieldset(v-if="st.user.id > 0") | |
9d58ef95 | 24 | label(for="selectPlayers") {{ st.tr["Play with? (optional)"] }} |
5b020e73 | 25 | #selectPlayers |
03608482 | 26 | input(type="text" v-model="newchallenge.to[0].name") |
9d58ef95 | 27 | input(v-show="newchallenge.nbPlayers>=3" type="text" |
03608482 | 28 | v-model="newchallenge.to[1].name") |
9d58ef95 | 29 | input(v-show="newchallenge.nbPlayers==4" type="text" |
03608482 | 30 | v-model="newchallenge.to[2].name") |
b4d619d1 | 31 | fieldset(v-if="st.user.id > 0") |
9d58ef95 BA |
32 | label(for="inputFen") {{ st.tr["FEN (optional)"] }} |
33 | input#inputFen(type="text" v-model="newchallenge.fen") | |
b4d619d1 | 34 | button(@click="newChallenge") {{ st.tr["Send challenge"] }} |
9d58ef95 BA |
35 | .row |
36 | .col-sm-12.col-md-5.col-md-offset-1.col-lg-4.col-lg-offset-2 | |
03608482 BA |
37 | .button-group |
38 | button(@click="cpdisplay='challenges'") Challenges | |
39 | button(@click="cpdisplay='players'") Players | |
40 | ChallengeList(v-show="cpdisplay=='challenges'" | |
41 | :challenges="challenges" @click-challenge="clickChallenge") | |
42 | #players(v-show="cpdisplay=='players'") | |
9d58ef95 | 43 | h3 Online players |
b4d619d1 BA |
44 | div(v-for="p in uniquePlayers" @click="tryChallenge(p)") |
45 | | {{ p.name + (!!p.count ? " ("+p.count+")" : "") }} | |
9d58ef95 BA |
46 | .row |
47 | .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2 | |
48 | button(onClick="doClick('modalNewgame')") New game | |
49 | .row | |
50 | .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2 | |
51 | .button-group | |
52 | button(@click="gdisplay='live'") Live games | |
53 | button(@click="gdisplay='corr'") Correspondance games | |
54 | GameList(v-show="gdisplay=='live'" :games="liveGames" | |
55 | @show-game="showGame") | |
56 | GameList(v-show="gdisplay=='corr'" :games="corrGames" | |
57 | @show-game="showGame") | |
625022fd BA |
58 | </template> |
59 | ||
60 | <script> | |
5b020e73 | 61 | import { store } from "@/store"; |
85e5b5c1 | 62 | import { NbPlayers } from "@/data/nbPlayers"; |
9d58ef95 BA |
63 | import { checkChallenge } from "@/data/challengeCheck"; |
64 | import { ArrayFun } from "@/utils/array"; | |
03608482 | 65 | import { ajax } from "@/utils/ajax"; |
5b020e73 BA |
66 | import GameList from "@/components/GameList.vue"; |
67 | import ChallengeList from "@/components/ChallengeList.vue"; | |
625022fd | 68 | export default { |
cf2343ce | 69 | name: "my-hall", |
5b020e73 BA |
70 | components: { |
71 | GameList, | |
72 | ChallengeList, | |
73 | }, | |
fb54f098 BA |
74 | data: function () { |
75 | return { | |
5b020e73 | 76 | st: store.state, |
b4d619d1 | 77 | cpdisplay: "challenges", |
fb54f098 BA |
78 | gdisplay: "live", |
79 | liveGames: [], | |
80 | corrGames: [], | |
b4d619d1 | 81 | challenges: [], |
fb54f098 | 82 | players: [], //online players |
9d58ef95 | 83 | newchallenge: { |
fb54f098 BA |
84 | fen: "", |
85 | vid: 0, | |
86 | nbPlayers: 0, | |
03608482 BA |
87 | // NOTE: id (server DB) and sid (socket ID). |
88 | // Anonymous players just have a socket ID. | |
89 | to: [ | |
90 | {id:0, sid:"", name:""}, | |
91 | {id:0, sid:"", name:""}, | |
92 | {id:0, sid:"", name:""} | |
93 | ], | |
9d58ef95 | 94 | timeControl: "", |
fb54f098 BA |
95 | }, |
96 | }; | |
97 | }, | |
b4d619d1 BA |
98 | computed: { |
99 | uniquePlayers: function() { | |
100 | // Show e.g. "5 @nonymous", and do nothing on click on anonymous | |
101 | let playerList = [{id:0, name:"@nonymous", count:0}]; | |
102 | this.players.forEach(p => { | |
103 | if (p.id > 0) | |
104 | playerList.push(p); | |
105 | else | |
106 | playerList[0].count++; | |
107 | }); | |
108 | return playerList; | |
109 | }, | |
110 | }, | |
111 | // TODO: this looks ugly... (use VueX ?!) | |
85e5b5c1 BA |
112 | watch: { |
113 | "st.conn": function() { | |
9d58ef95 BA |
114 | this.st.conn.onmessage = this.socketMessageListener; |
115 | this.st.conn.onclose = this.socketCloseListener; | |
85e5b5c1 BA |
116 | }, |
117 | }, | |
9d58ef95 BA |
118 | created: function() { |
119 | // TODO: ask server for current corr games (all but mines: names, ID, time control) | |
b4d619d1 BA |
120 | // also ask for corr challenges |
121 | // TODO: add myself to players | |
122 | // --> when sending something, send to all players but NOT me ! | |
9d58ef95 BA |
123 | if (!!this.st.conn) |
124 | { | |
125 | this.st.conn.onmessage = this.socketMessageListener; | |
126 | this.st.conn.onclose = this.socketCloseListener; | |
127 | } | |
b4d619d1 | 128 | this.players.push(this.st.user); |
9d58ef95 | 129 | }, |
fb54f098 | 130 | methods: { |
9d58ef95 BA |
131 | socketMessageListener: function(msg) { |
132 | const data = JSON.parse(msg.data); | |
133 | switch (data.code) | |
134 | { | |
b4d619d1 BA |
135 | // * - receive "new game": if live, store locally + redirect to game |
136 | // * If corr: notify "new game has started", give link, but do not redirect | |
9d58ef95 BA |
137 | case "newgame": |
138 | // TODO: new game just started: data contain all informations | |
139 | // (id, players, time control, fenStart ...) | |
b4d619d1 BA |
140 | // + cid to remove challenge from list |
141 | break; | |
142 | // * - receive "playergame": a live game by some connected player (NO corr) | |
143 | case "playergame": | |
144 | // TODO: receive live game summary (update, count moves) | |
145 | // (just players names, time control, and ID + player ID) | |
146 | break; | |
147 | // * - receive "playerchallenges": list of challenges (sent) by some online player (NO corr) | |
148 | case "playerchallenges": | |
149 | // TODO: receive challenge + challenge updates | |
9d58ef95 | 150 | break; |
b4d619d1 BA |
151 | case "newmove": //live or corr |
152 | // TODO: name conflict ? (game "newmove" event) | |
153 | break; | |
154 | // * - receive new challenge: if targeted, replace our name with sender name | |
155 | case "newchallenge": | |
156 | // receive live or corr challenge | |
157 | break; | |
158 | // * - receive "accept/withdraw/cancel challenge": apply action to challenges list | |
9d58ef95 BA |
159 | case "acceptchallenge": |
160 | if (true) //TODO: if challenge is full | |
161 | this.newGame(data.challenge, data.user); //user.id et user.name | |
162 | break; | |
163 | case "withdrawchallenge": | |
164 | const cIdx = this.challenges.findIndex(c => c.id == data.cid); | |
165 | let chall = this.challenges[cIdx] | |
166 | ArrayFun.remove(chall.players, p => p.id == data.uid); | |
167 | chall.players.push({id:0, name:""}); | |
168 | break; | |
169 | case "cancelchallenge": | |
170 | ArrayFun.remove(this.challenges, c => c.id == data.cid); | |
171 | break; | |
b4d619d1 BA |
172 | // NOTE: finally only one connect / disconnect couple of events |
173 | // (because on server side we wouldn't know which to choose) | |
174 | case "connect": | |
175 | // * - receive "player connect": send all our current challenges (to him or global) | |
176 | // * Also send all our games (live - max 1 - and corr) [in web worker ?] | |
177 | // * + all our sent challenges. | |
9d58ef95 | 178 | this.players.push({name:data.name, id:data.uid}); |
b4d619d1 BA |
179 | // TODO: si on est en train de jouer une partie, le notifier au nouveau connecté |
180 | // envoyer aussi nos défis | |
9d58ef95 | 181 | break; |
b4d619d1 BA |
182 | // * - receive "player disconnect": remove from players list |
183 | case "disconnect": | |
9d58ef95 | 184 | ArrayFun.remove(this.players, p => p.id == data.uid); |
03608482 BA |
185 | // TODO: also remove all challenges sent by this player, |
186 | // and all live games where he plays and no other opponent is online | |
9d58ef95 BA |
187 | break; |
188 | } | |
189 | }, | |
190 | socketCloseListener: function() { | |
b4d619d1 BA |
191 | // connexion is reinitialized in store.js |
192 | this.st.conn.addEventListener('message', this.socketMessageListener); | |
193 | this.st.conn.addEventListener('close', this.socketCloseListener); | |
9d58ef95 | 194 | }, |
fb54f098 | 195 | showGame: function(game) { |
5b020e73 BA |
196 | // NOTE: if we are an observer, the game will be found in main games list |
197 | // (sent by connected remote players) | |
b4d619d1 | 198 | // TODO: game path ? /vname/gameId seems better |
5b020e73 | 199 | this.$router.push("/" + game.id) |
fb54f098 | 200 | }, |
b4d619d1 BA |
201 | tryChallenge: function(player) { |
202 | if (player.id == 0) | |
203 | return; //anonymous players cannot be challenged | |
204 | this.newchallenge.players[0] = { | |
205 | name: player.name, | |
206 | id: player.id, | |
207 | sid: player.sid, | |
208 | }; | |
209 | doClick("modalNewgame"); | |
fb54f098 | 210 | }, |
b4d619d1 BA |
211 | // * - accept challenge (corr or live) --> send info to all concerned players |
212 | // * - cancel challenge (click on sent challenge) --> send info to all concerned players | |
213 | // * - withdraw from challenge (if >= 3 players and previously accepted) | |
214 | // * --> send info to all concerned players | |
215 | // * - refuse challenge (or receive refusal): send to all challenge players (from + to) | |
216 | // * except us ; graphics: modal again ? (inline ?) | |
217 | // * - prepare and start new game (if challenge is full after acceptation) | |
218 | // * --> include challenge ID (so that opponents can delete the challenge too) | |
219 | // * Also send to all connected players (only from me) | |
fb54f098 BA |
220 | clickChallenge: function(challenge) { |
221 | const index = this.challenges.findIndex(c => c.id == challenge.id); | |
222 | const toIdx = challenge.to.findIndex(p => p.id == user.id); | |
223 | const me = {name:user.name,id:user.id}; | |
224 | if (toIdx >= 0) | |
225 | { | |
226 | // It's a multiplayer challenge I accepted: withdraw | |
227 | this.st.conn.send(JSON.stringify({code:"withdrawchallenge", | |
228 | cid:challenge.id, user:me})); | |
229 | this.challenges.to.splice(toIdx, 1); | |
230 | } | |
231 | else if (challenge.from.id == user.id) //it's my challenge: cancel it | |
232 | { | |
233 | this.st.conn.send(JSON.stringify({code:"cancelchallenge", cid:challenge.id})); | |
234 | this.challenges.splice(index, 1); | |
235 | } | |
236 | else //accept a challenge | |
237 | { | |
238 | this.st.conn.send(JSON.stringify({code:"acceptchallenge", | |
239 | cid:challenge.id, user:me})); | |
240 | this.challenges[index].to.push(me); | |
241 | } | |
242 | // TODO: accepter un challenge peut lancer une partie, il | |
243 | // faut alors supprimer challenge + creer partie + la retourner et l'ajouter ici | |
fb54f098 BA |
244 | // si pas le mien et FEN speciale :: (charger code variante et) |
245 | // montrer diagramme + couleur (orienté) | |
246 | }, | |
b4d619d1 BA |
247 | // user: last person to accept the challenge (TODO: revoir ça) |
248 | // newGame: function(chall, user) { | |
249 | // const fen = chall.fen || V.GenRandInitFen(); | |
250 | // const game = {}; //TODO: fen, players, time ... | |
251 | // //setStorage(game); //TODO | |
252 | // game.players.forEach(p => { //...even if game is by corr (could be played live, why not...) | |
253 | // this.conn.send( | |
254 | // JSON.stringify({code:"newgame", oppid:p.id, game:game})); | |
255 | // }); | |
256 | // if (this.settings.sound >= 1) | |
257 | // new Audio("/sounds/newgame.mp3").play().catch(err => {}); | |
258 | // }, | |
259 | // Send new challenge (corr or live, cf. time control), with button or click on player | |
9d58ef95 BA |
260 | newChallenge: async function() { |
261 | const idxInVariants = | |
262 | this.st.variants.findIndex(v => v.id == this.newchallenge.vid); | |
03608482 | 263 | const vname = this.st.variants[idxInVariants].name; |
9d58ef95 BA |
264 | const vModule = await import("@/variants/" + vname + ".js"); |
265 | window.V = vModule.VariantRules; | |
03608482 | 266 | // checkChallenge side-effect = set FEN, and mainTime + increment in seconds |
9d58ef95 BA |
267 | const error = checkChallenge(this.newchallenge); |
268 | if (!!error) | |
269 | return alert(error); | |
03608482 BA |
270 | // Less than 3 days ==> live game (TODO: heuristic... 40 moves also) |
271 | const liveGame = | |
272 | this.newchallenge.mainTime + 40 * this.newchallenge.increment < 3*24*60*60; | |
273 | // Check that the players (if any indicated) are online | |
274 | for (let p of this.newchallenge.to) | |
9d58ef95 | 275 | { |
03608482 BA |
276 | if (p.name != "") |
277 | { | |
278 | const pIdx = this.players.findIndex(pl => pl.name == p.name); | |
279 | if (pIdx === -1) | |
280 | return alert(p.name + " is not connected"); | |
281 | p.id = this.players[pIdx].id; | |
282 | p.sid = this.players[pIdx].sid; | |
283 | } | |
284 | } | |
b4d619d1 | 285 | // TODO: clarify challenge format (too many fields for now :/ ) |
03608482 BA |
286 | const finishAddChallenge = (cid) => { |
287 | const chall = Object.assign( | |
288 | {}, | |
9d58ef95 | 289 | this.newchallenge, |
03608482 BA |
290 | { |
291 | id: cid, | |
292 | from: this.st.user, | |
293 | added: Date.now(), | |
294 | vname: vname, | |
fb54f098 BA |
295 | } |
296 | ); | |
03608482 | 297 | this.challenges.push(chall); |
b4d619d1 | 298 | // Send challenge to peers |
03608482 | 299 | const chall = JSON.stringify({ |
b4d619d1 | 300 | code: "newchallenge", |
03608482 BA |
301 | sender: {name:this.st.user.name, id:this.st.user.id, sid:this.st.user.sid}, |
302 | }); | |
303 | if (this.newchallenge.to[0].id > 0) | |
9d58ef95 | 304 | { |
03608482 BA |
305 | // Challenge with targeted players |
306 | this.newchallenge.to.forEach(p => { | |
307 | if (p.id > 0) | |
308 | this.st.conn.send(Object.assign({}, chall, {receiver: p.sid})); | |
9d58ef95 BA |
309 | }); |
310 | } | |
311 | else | |
312 | { | |
313 | // Open challenge: send to all connected players | |
03608482 | 314 | this.players.forEach(p => { this.st.conn.send(chall); }); |
9d58ef95 | 315 | } |
b4d619d1 BA |
316 | document.getElementById("modalNewgame").checked = false; |
317 | }; | |
318 | if (liveGame) | |
319 | { | |
03608482 BA |
320 | // Live challenges have cid = 0 |
321 | finishAddChallenge(0); | |
322 | } | |
b4d619d1 | 323 | else |
03608482 | 324 | { |
b4d619d1 | 325 | // Correspondance game: send challenge to server |
03608482 BA |
326 | ajax( |
327 | "/challenges/" + this.newchallenge.vid, | |
328 | "POST", | |
329 | this.newchallenge, | |
330 | response => { finishAddChallenge(cid); } | |
331 | ); | |
9d58ef95 | 332 | } |
fb54f098 BA |
333 | }, |
334 | possibleNbplayers: function(nbp) { | |
9d58ef95 | 335 | if (this.newchallenge.vid == 0) |
fb54f098 | 336 | return false; |
85e5b5c1 | 337 | const variants = this.st.variants; |
fb54f098 | 338 | const idxInVariants = |
9d58ef95 | 339 | variants.findIndex(v => v.id == this.newchallenge.vid); |
fb54f098 BA |
340 | return NbPlayers[variants[idxInVariants].name].includes(nbp); |
341 | }, | |
342 | }, | |
85e5b5c1 | 343 | }; |
ccd4a2b7 | 344 | </script> |
85e5b5c1 BA |
345 | |
346 | <style lang="sass"> | |
347 | // TODO | |
348 | </style> |