diff --git a/.gitignore b/.gitignore index 96b57de..c8ef4c5 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ client/node_modules/ server/node_modules/ *.log *.env -.turns-debug.json +turns-debug.json diff --git a/README.md b/README.md index 3a4b162..f889cf6 100644 --- a/README.md +++ b/README.md @@ -34,12 +34,17 @@ or just use environment variables ### Bugs and Issues +Prettymuch all of the bugs i'm aware of occur when a user reconnects to a game that's taking place. ESPECIALLY if that user is the host of the game + ### Contributing -To contribute a translation +To contribute a translation there's a few scripts that need to be run + +To see what needs to be completed code-wise, take a look at `TODO`, there you will find tasks that need to be completed as well as known bugs. There's also a lot of TODOs in the code :) ### Acknowledgements Express.js - HTTP Routing and Management Socket.io - Socket Routing and Management + Inês Filipa Baiõa Antunes - Tranlations (Portuguese, Spanish) diff --git a/TODO b/TODO index 493981a..e7c03f2 100644 --- a/TODO +++ b/TODO @@ -15,8 +15,8 @@ → Update with new locales ✔ Spanish (Ines) @done(21-04-11 01:03) → Update with new locales - → French (Alexendro) - → Czech (Mikdore) + ✘ French (Alexendro) @cancelled(21-05-10 18:22) + ✘ Czech (Mikdore) @cancelled(21-05-10 18:22) → Go through code again once finished and pick out locales ☐ Lobbying logic @@ -28,14 +28,14 @@ → Find appropriate scrabble dictionary for ✔ Portuagese @done(21-04-19 02:10) ✔ Spanish @done(21-04-19 02:10) - ✔ French @done(21-04-19 02:10) - → Czech + ✔ French @done(21-05-10 18:22) + ✘ Czech @cancelled(21-05-10 18:22) → Optimise! n-ary tree ☐ Game networking - → Figure out game reconnection procedure - → Perhaps players skip a turn if they disconnect on their turn - → Game should go on until 1 person remains + ✔ Figure out game reconnection procedure @done(21-05-10 18:22) + ✘ Perhaps players skip a turn if they disconnect on their turn @cancelled(21-05-10 18:22) + ✘ Game should go on until 1 person remains @cancelled(21-05-10 18:22) → Game chat ☐ Game logic @@ -52,13 +52,13 @@ ✔ Update dynamically @done(21-05-09 22:44) ☐ Code - → Refactor to code portsoc eslint - → Refactor game client + ✘ Refactor to code portsoc eslint @cancelled(21-05-10 18:22) + ✘ Refactor game client @cancelled(21-05-10 18:22) → WHY THE HELL ARE THEY CALLED USERS IN SOME PLACES AND PLAYERS IN OTHERS → Why in some places is it user.name and others user.username + → Should lobbies persist once game starts? ☐ Bugfixes - → If a user leaves their current game then makes a new one, it's corrupted + ✔ If a user leaves their current game then makes a new one, it's corrupted @done(21-05-10 17:56) → If a user disconnects during a game, the game is irrevokably corrupted - - + → Usually, if a player leaves, it's fine, but if the host leaves. The lobbby is destructed and the logic creates a new game on the spot and they're the only player diff --git a/client/public/game/lobbies.js b/client/public/game/lobbies.js index cb9c095..40e262c 100644 --- a/client/public/game/lobbies.js +++ b/client/public/game/lobbies.js @@ -162,10 +162,10 @@ socket.on('lobby-create-error', obj => { const errorDiv = document.createElement('div'); errorDiv.id = 'lobby-error'; - errorDiv.innerHTML = localeString('error-bold') + ' ' + localeString('error-creating-lobby') + ' ' + JSON.stringify(args); + errorDiv.innerHTML = localeString('error-bold') + ' ' + localeString('error-creating-lobby') + ' ' + JSON.stringify(obj); errorDiv.classList.add('red'); CreateLobbyBlock.appendChild(errorDiv); - pageLog(localeString('error-bold') + ' ' + localeString('error-creating-lobby') + ' ' + JSON.stringify(args)); + pageLog(localeString('error-bold') + ' ' + localeString('error-creating-lobby') + ' ' + JSON.stringify(obj)); }); diff --git a/client/public/scrabble/game.js b/client/public/scrabble/game.js index eceea47..89e1a34 100644 --- a/client/public/scrabble/game.js +++ b/client/public/scrabble/game.js @@ -20,6 +20,7 @@ NOTES let Users = []; // just shorthand, so long as i remember to keep it updated lmao let MyTurn = false; +let TileSet = []; let pastTurns = []; @@ -27,6 +28,8 @@ function initGame(boardstates, tileset, myplayer, players) { pastTurns.push(...boardstates); + TileSet = tileset; + // construct piece array // structure [{letter: '', score: int}] let drawerStructure = []; diff --git a/client/public/scrabble/index.html b/client/public/scrabble/index.html index b98b4aa..0a446c8 100644 --- a/client/public/scrabble/index.html +++ b/client/public/scrabble/index.html @@ -341,12 +341,12 @@ - + \ No newline at end of file diff --git a/client/public/scrabble/network.js b/client/public/scrabble/network.js index 1997bba..7822d2d 100644 --- a/client/public/scrabble/network.js +++ b/client/public/scrabble/network.js @@ -129,6 +129,16 @@ function onIdentifyError(socket, args) ConnectionState.forEach(e => { e.innerHTML = JSON.stringify(args); }); + removePiecesFromDrawer('*'); + addPiecesToDrawer([ + {letter: 'H', score: 1}, + {letter: 'E', score: 2}, + {letter: 'L', score: 3}, + {letter: 'L', score: 4}, + {letter: 'O', score: 5}, + {letter: 'O', score: 6}, + {letter: 'O', score: 7} + ]); onDisconnect(); } @@ -299,6 +309,15 @@ if (isSingleplayer) e.innerHTML = localeString('no-connection-singleplayer'); }); alert('Singleplayer is not implemented yet! a practice board will be set up to demonstrate tech and tile stuff'); + addPiecesToDrawer([ + {letter: 'H', score: 1}, + {letter: 'E', score: 2}, + {letter: 'L', score: 3}, + {letter: 'L', score: 4}, + {letter: 'O', score: 5}, + {letter: 'O', score: 6}, + {letter: 'O', score: 7} + ]); } else { initMultiplayer(); diff --git a/client/public/scrabble/pieces.js b/client/public/scrabble/pieces.js index 010bfaa..75a74da 100644 --- a/client/public/scrabble/pieces.js +++ b/client/public/scrabble/pieces.js @@ -52,6 +52,11 @@ function addPiecesToDrawer(pieces) // Removes regardless of vadility function removePiecesFromDrawer(pieces) { + if (pieces === '*') + { + Drawer.innerHTML = ''; + return; + } for (const piece of pieces) { piece.remove(); @@ -216,11 +221,48 @@ function piecePlaced(piece) BoardSounds[0].play(); placePieceOnBoard(piece, coords.x, coords.y); - + piece.classList.remove('unplayed-piece'); piece.classList.add('staged-piece'); piece.dataset.coords = JSON.stringify(coords); + // Wildcard! (done clientside because of laziness) + let letter = piece.dataset.letter; + if (letter === '_') + { + // TODO: this would be a pretty good function for elsewhere.. + const tilesetincludes = (l) => { + for (const range of TileSet) + if (range.letters.includes(l)) return true; + return false; + }; + + letter = prompt('You placed a wildcard! what would you like the letter to be?').toUpperCase(); + if (letter === null || !tilesetincludes(letter)) + { + DrawerSounds[Math.floor(Math.random() * 3)].play(); + + piece.classList.remove('staged-piece'); + piece.classList.remove('small-piece'); + piece.classList.add('unplayed-piece'); + delete piece.dataset.coords; + + setupPieces(); + return; + } + + piece.dataset.letter = letter; + // no way to work this out here + + let score = -1; + for (const range in TileSet) + if (TileSet[range].letters.includes(letter)) + score = TileSet[range].points; + + piece.dataset.score = score; + piece.innerHTML = `${piece.dataset.letter}${piece.dataset.score}`; + } + setupPieces(); } else { diff --git a/server/src/game-logic.js b/server/src/game-logic.js index 592eaab..aae6252 100644 --- a/server/src/game-logic.js +++ b/server/src/game-logic.js @@ -149,6 +149,15 @@ function GetGameByUserUID(useruid) return false; } +function GetGameUserByUserUID(useruid) +{ + for (const game in ActiveGames) + for (const player of ActiveGames[game].players) + if (player.uid === useruid) return player; + + return false; +} + function GetTurnUser(gameuid) { if (!ActiveGames[gameuid]) return false; @@ -277,14 +286,32 @@ function PlayTurn(gameuid, playeruid, turn) } // process outcome + const temptiles = turn.oldboardtiles.concat(turn.boardtiles) // check if user is allowed to make that move + const gameplayer = GetGameUserByUserUID(playeruid); + console.log(gameplayer); // process turn and allocate scores // give user new tiles - - turn.boardtiles = turn.oldboardtiles.append(turn.boardtiles); + + // update tiles with scores + turn.boardtiles = turn.oldboardtiles.concat(turn.boardtiles); + for (const tile in turn.boardtiles) + { + let score = 0; + for (const pointband of Dist.GetDist(game.locale).dist) + { + if (pointband.letters.includes(turn.boardtiles[tile].letter)) + { + score = pointband.points; + break; + } + } + turn.boardtiles[tile].score = score; + } + ActiveGames[gameuid].gamestates.push(turn); ActiveGames[gameuid].turn = turninfo.newTurn; ActiveGames[gameuid].turntotal = turninfo.newTotalTurn; @@ -327,9 +354,9 @@ function gameNextTurn(gameuid) } // same as how the -function EndGame() +function EndGame(gameuid) { - + delete ActiveGames[gameuid]; } @@ -359,6 +386,7 @@ module.exports = { // Get game exports GetGameByUserUID: GetGameByUserUID, + GetGameUserByUserUID: GetGameUserByUserUID, GetTurnUser: GetTurnUser, // Change game state exports diff --git a/server/src/socketserver.js b/server/src/socketserver.js index 828f69d..e989f5a 100644 --- a/server/src/socketserver.js +++ b/server/src/socketserver.js @@ -458,9 +458,16 @@ function HandleDisconnect(socket, args) // if user is in a game, notify the game logic // if the user is the last user in a game - delete it // if the user is leaving, change their status so reconnect is allowed - // TODO: VERY IMPORTANTTTT THAT^^^ + // quick fix, prevents game destruction + // if (user.intent === 'GAME') + // { + // Game.Registrar.UserDisconnect(user.uid); + // Logger.info(`SOCKET ${socket.id} DISCONNECTED FROM GAME`); + // return; + // } + // if user is in a lobby, leave and if user own's a lobby, destruct // leave lobby before user is disconnected if (user.intent !== 'GAMETRANSITION') @@ -526,6 +533,8 @@ function EmitGameBegin(game) const userturnstartconnection = Game.Registrar.GetConnectionByUser(userturnstart); io.to(userturnstartconnection).emit('game-your-turn'); + + Logger.debug(JSON.stringify(game.players, null, 2)); } function EmitGameReconnect(user, game) @@ -543,12 +552,17 @@ function EmitGameReconnect(user, game) // NOTE it shouldn't ever be their turn on a reconnect // as the game logic should pass control to next player // as the game order is changed + if (!Game.Logic.GetTurnUser(game.uid)) return; const userturnstart = Game.Logic.GetTurnUser(game.uid).uid; if (userturnstart === user.uid) + { + io.to(gameuserconnection).emit('game-your-turn'); + } else { - const userturnstartconnection = Game.Registrar.GetConnectionByUser(userturnstart); - - io.to(userturnstartconnection).emit('game-your-turn'); + // TODO: this needs to send game heuristics + io.to(gameuserconnection).emit('game-turn-start'); } + + Logger.debug(JSON.stringify(game.players, null, 2)); }