diff --git a/cardGen.js b/cardGen.js index 160cb1c..2eccb69 100644 --- a/cardGen.js +++ b/cardGen.js @@ -490,7 +490,7 @@ function requestDeck(itemData = null){ // Add the entity into 'inDeck' component // also add a listPosition based on current deckLength itemData.component.inDeck[itemCount] = true; // Not sure what to store here - itemData.component.listPosition[itemCount] = itemData.component.cardCount.deck[forPlayer]; + itemData.component.listPosition[itemCount] = itemData.component.cardCount.deck[forPlayer] + 1; // Associate the card with the deck // TODO: Change deckIn to something more sensical diff --git a/components.js b/components.js index 6d789fe..8b59d6a 100644 --- a/components.js +++ b/components.js @@ -10,6 +10,7 @@ const component = { roomId : null, turn : 0, playerTurn : 0, + player : {}, cardCount : { deck : {}, diff --git a/gameHelper.js b/gameHelper.js new file mode 100644 index 0000000..949a1ed --- /dev/null +++ b/gameHelper.js @@ -0,0 +1,53 @@ +// Moves card positions down/up for the old/new element for player in room +function setCardPosition(roomId, player, card, newPosition, newElement, oldPosition, oldElement){ + + // Move anything in the old boardElement after the old listPosition down by one + moveElementPositions(roomId, player, 0, oldElement, oldPosition); + + // Move anything in the new boardElement after (including) the new listPosition up by one + moveElementPositions(roomId, player, 1, newElement, newPosition); + + // Then fit the card into the new gap that's opened up + listPosition[card] = newPosition; + + // Remove from oldElement + delete oldElement[card] + // Add to newElement + newElement[card] = newPosition; + +} + +// direction 0 up, 1 down +function moveElementPositions(roomId, player, direction, element, position){ + + position = position; + listPosition = global.roomData[roomId].itemData.component.listPosition; + + for (const [key, value] of Object.entries(element)) { + + if(global.roomData[roomId].itemData.component.player[key] != player){ + continue; + } + + + // Move down from (not including) the position passed + if(direction == 0 && listPosition[key] > position){ + listPosition[key]--; + } + + // Move everything from (including) the new position up + if(direction == 1 && listPosition[key] >= position){ + console.log('hit'); + console.log(listPosition[key]); + console.log(position); + listPosition[key]++; + } + + } + +} + +module.exports = { + setCardPosition +}; + diff --git a/gameMod.js b/gameMod.js index 611f375..d7d3c8d 100644 --- a/gameMod.js +++ b/gameMod.js @@ -1,3 +1,5 @@ +const gameHelper = require('./gameHelper'); + // For anything related to the actual game itself (kinda) // this will be split into different bits, but should be what manages a rooms // game states, and alladat @@ -17,7 +19,7 @@ function passTurn(roomId, playerId){ //global.socketAlert(roomData[roomId].playerData[playerId].socketId, playerTurn, 'log'); if(playerTurn != playerId){ - global.socketAlert(roomData[roomId].playerData[playerId].socketId, 'Not your turn', 'alert'); + global.socketAlert(global.getPlayerSocketFromRoom(playerId, roomId), 'Not your turn', 'alert'); return false; }; @@ -32,30 +34,14 @@ function passTurn(roomId, playerId){ } // Send turn data to each player - // Probably via the sockets in room (as could be spectators in future) - let clients = global.io.sockets.adapter.rooms.get(roomId); - for (const clientId of clients) { - - const clientSocket = global.io.sockets.sockets.get(clientId); - console.log('>> '+clientSocket.playerId+' passed turn'); - - // Send the data back - let turnData = { - 'playerTurn': newPlayerTurn, - 'turn': global.roomData[roomId].itemData.component.turn, - }; - - global.io.to(clientSocket.id).emit('responsePassTurn', turnData); - - } + global.socketResponsePassTurn(roomId); // Let the player know it's their turn via alert too (in case tabbed out) + // TODO: This could probably be done front-end from the newPlayerTurn in socketResponsePassTurn global.socketAlert(roomData[roomId].playerData[newPlayerTurn].socketId, 'Your turn', 'alert'); - // Start of the new players turn, draw a card drawACard(roomId, newPlayerTurn); - } @@ -65,49 +51,104 @@ function drawACard(roomId, playerId){ global.socketAlert(roomData[roomId].playerData[playerId].socketId, 'Hand full; cannot draw card', 'alert'); return false; } + if(global.roomData[roomId].itemData.component.cardCount.deck[playerId] <= 0){ + global.socketAlert(roomData[roomId].playerData[playerId].socketId, 'Deck empty; cannot draw card', 'alert'); + return false; + } + + // TODO: Check no card event/trigger occured that prevents/change draw card + // Change position to last position available in hand - let fromPosition = global.roomData[roomId].itemData.component.cardCount.deck[playerId]; // 'Bottom' of deck - let toPosition = global.roomData[roomId].itemData.component.cardCount.hand[playerId]; // Rightmost hand pos + let fromPosition = global.roomData[roomId].itemData.component.cardCount.deck[playerId]; // 'top' of deck + let toPosition = global.roomData[roomId].itemData.component.cardCount.hand[playerId] + 1; // Rightmost hand pos (starting at 1) + + // ECSey att2, there's surely a better way of getting playerX top card within inDeck? + // Tried unions but it messes up the object data. Maybe need to have no data in each object + // and have it literally just be keys? + + // Get each card from the deck + for (const [key, value] of Object.entries(global.roomData[roomId].itemData.component.inDeck)) { + // Key is the entity here + + // If the card inDeck does not belongs to the player, skip over it + if(global.roomData[roomId].itemData.component.player[key] != playerId){ + continue; + } + + // If the card isn't the last (bottom) card of deck, skip over it + // TODO: -1 is jank, sort so listPositions all start from 1..x + if(global.roomData[roomId].itemData.component.listPosition[key] != fromPosition){ + continue; + } + + // The main man + // Move positions in hand/deck, and put the item from the deck into the hand + gameHelper.setCardPosition(roomId, playerId, key, toPosition, global.roomData[roomId].itemData.component.hand, fromPosition, global.roomData[roomId].itemData.component.inDeck); + + } - - // from inDeck to hand - // change the listPositions of both (only return listPosition of hand ids?) - // Reduce deckSize by 1 for the player that drew global.roomData[roomId].itemData.component.cardCount.deck[playerId]--; // And increase the hand size by 1 global.roomData[roomId].itemData.component.cardCount.hand[playerId]++; + + // Then emit the deckSize and hand size to all the player's sockets + global.socketResponseDrawCard(roomId, playerId); - // TODO: Move card from deck to hand for the player. Dupe what I've got in board for - // this, but tidy it up - //global.roomData[roomId].itemData.component.inDeck = newPlayerTurn; - //global.roomData[roomId].itemData.component.hand = newPlayerTurn; + // Emit the 'hand' and related cardData for cards in the players hand + // Could merge this with the top? + global.socketResponsePlayerDrewCard(roomId, playerId); +} - // Then emit the deckSize and hand size to all the player's sockets - let clients = global.io.sockets.adapter.rooms.get(roomId); - for (const clientId of clients) { - const clientSocket = global.io.sockets.sockets.get(clientId); - console.log('>> '+clientSocket.playerId+' drew card'); +// DATA RETURNER DUDES +// TODO: Where to put this? Kind of a helper, kind of functionality. Hmmmmm +// maybe do a dataHelper? then anything to return data can be included there? +// TODO: May get all the data from hand, board, grave, etc. in functions +// like this, then union all the data and return that in one swoomp +// Probably better to just get all the keys from the boardlemenets and do +// the loop once though... +function getPlayerHandData(roomId, playerId){ + + let handEntities = {}; + let handPositions = {}; + let handCardData = {}; + + for (const [key, value] of Object.entries(global.roomData[roomId].itemData.component.hand)) { + + // Key is entity ID here + + // If the entity in hand belongs to the player, then they are allowed its data + if(global.roomData[roomId].itemData.component.player[key] == playerId){ - // Send the data back - global.io.to(clientSocket.id).emit( - 'responseDrawCard' - ,global.roomData[roomId].itemData.component.cardCount - ); + // Get entity of items in the hand + handEntities[key] = global.roomData[roomId].itemData.component.hand[key]; + // Get listPosition of just items in the hand + handPositions[key] = global.roomData[roomId].itemData.component.listPosition[key]; + // Same for cardData + handCardData[key] = global.roomData[roomId].itemData.component.cardData[key]; // TODO: Nothing on client side? + + // Leaving other bits for now + + } } - - // Emit the 'hand' and related cardData for cards in the players hand - - + + return { + 'handEntities': handEntities, + 'handPositions': handPositions, + 'handCardData': handCardData + }; + } module.exports = { passTurn + ,getPlayerHandData + ,drawACard }; diff --git a/public/index.html b/public/index.html index dfa5155..ea0eb6f 100644 --- a/public/index.html +++ b/public/index.html @@ -19,6 +19,7 @@
+


diff --git a/public/js/canvas/draw.js b/public/js/canvas/draw.js index 33c0767..fa6bd7c 100644 --- a/public/js/canvas/draw.js +++ b/public/js/canvas/draw.js @@ -5,9 +5,14 @@ function drawGameBoard(){ drawPlayerNames(); calculateDeckPositions(); + calculateHandPositions(); drawEntities(); + // TEMP. Likely want to pass entities, but if you don't have cardData for them + // then don't draw the card face up? + drawFakeHand(); + } function drawPlayerNames(){ @@ -68,6 +73,35 @@ function calculateDeckPositions(){ } } +// TODO: Move from draw into somewhere else +function calculateHandPositions(){ + + for (const [key, value] of Object.entries(gameData.hand)) { + + // key is entity Id here + + switch(gameData.player[key]){ + + // Set position for player hand (all the time at current) + case gameData.playerId: + + let cardsInHand = gameData.cardCount.hand[gameData.playerId]; + let positionInHand = gameData.listPosition[key]; + + gameData.position[key] = [ + canvas.width/2 - (cardWidth * (cardsInHand - (positionInHand+1)) - (cardMargin * (positionInHand+1))) + ,canvas.height-cardWidth*1.5-20 + ]; + gameData.size[key] = [cardWidth, cardHeight]; + break; + + // Opponent, currently done in fakeHand + + } + } + +} + function drawEntities(){ @@ -79,10 +113,15 @@ function drawEntities(){ if(gameData.size[key] !== undefined){ // If the entity is a deck - if(gameData.deck[key]){ + if(key in gameData.deck){ drawDeck(key); } + // If card in (player) hand + if(key in gameData.hand){ + drawCardInHand(key); + } + } } @@ -123,4 +162,71 @@ function drawDeck(entity){ printText(deckCount, textX, textY, 'center', 'bottom', 'normal', 'bold', '10', 'Arial', '#000'); } +function drawCardInHand(entity){ + + console.log('Draw card in hand'); + console.log(gameData.position[entity]); + console.log(gameData.size[entity]); + + // Draw the deck shape + let shape = new Shape({ + x: gameData.position[entity][0], + y: gameData.position[entity][1], + width: gameData.size[entity][0], + height: gameData.size[entity][1], + fillStyle: '#EEE', + strokeStyle: '#AAA', + }); + shape.draw(); + +} + + +// TEMP, or maybe not for opponent. Not 100% how to draw opponent hand without passing the data +// avoiding passing the data (for now) to prevent cheating by checking their cardIds etc. in the +// console. An issue that comes with making this in JS... +function drawFakeHand(){ + + console.log('Draw FAKE hand'); + + // itemList length is the count (/highest listPosition) in the hand in this case + // i is the listPosition of the entity (which since this isn't using the entities at the mo...) + + // All jank temp kinda vars + // Needed as i substitute to track the fake hand drawn count + let opponentHandItem = 0; + let itemListLength = 0; // Believe value should just be the count of current cardCount.hand + let i = 0; // Believe value should just be the count of current cardCount.hand + + // Loop each 'hand' not actual hand, but count of cards in hand + for (const [key, value] of Object.entries(gameData.cardCount.hand)) { + + // key is the playerId here + switch(gameData.player[key]){ + + // Set position for opponents deck + case gameData.opponentId: + + // Then loop the size of the hand + itemListLength = value; // Believe value should just be the count of current cardCount.hand + i = opponentHandItem; // Believe value should just be the count of current cardCount.hand + + for(i; i < itemListLength; i++){ + let shape = new Shape({ + x: canvas.width/2 - (cardWidth * (itemListLength - (i+1)) - (cardMargin * (i+1))), + y: 20, + width: cardWidth, + height: cardHeight, + fillStyle: '#EEE', + strokeStyle: '#AAA', + }); + shape.draw(); + } + break; + + } + + } + +} diff --git a/public/js/game/components.js b/public/js/game/components.js index df92a49..f368fe7 100644 --- a/public/js/game/components.js +++ b/public/js/game/components.js @@ -19,8 +19,11 @@ let gameData = { // Real components from here? + listPosition : {}, + cardData : {}, player : {}, deck : {}, + hand : {}, // Local components, not done on serverside // calculated, etc. by client diff --git a/public/js/game/dataUpdate.js b/public/js/game/dataUpdate.js index 8aabd47..60508c1 100644 --- a/public/js/game/dataUpdate.js +++ b/public/js/game/dataUpdate.js @@ -11,7 +11,7 @@ function updateGameData(data){ updateTurn(data.component.turn, data.component.playerTurn); updateItems(data.component.item, data.component.itemCount); - updatePlayerComponent(data.player); + updatePlayerComponent(data.component.player); updateDecks(data.component.deck); updateCardCount(data.component.cardCount); @@ -52,3 +52,15 @@ function updateCardCount(cardCount = null){ gameData.cardCount = cardCount; } +// Cards in hand +function updatePlayerHand(data){ + console.log('Update player hand'); + // TODO: These will likely not be = as will overwrite. + // Likely to union all the data server-side and pass as one + // function such as updateEntities() or something + gameData.hand = data.handEntities; + gameData.listPosition = data.handPositions; + gameData.cardData = data.handCardData; + console.log(gameData); +} + diff --git a/public/js/game/socket.js b/public/js/game/socket.js index bda521b..04eab94 100644 --- a/public/js/game/socket.js +++ b/public/js/game/socket.js @@ -10,6 +10,10 @@ socket.on('responsePassTurn', function (data) { updateTurn(data.turn, data.playerTurn); drawGameBoard(); }); +function requestDrawACard(){ + console.log('>> drawACard'); + socket.emit('drawACard', gameData.roomId, gameData.playerId); +} // Functions like this would be elsewhere, do client-side // validation THEN request stuff from the server? @@ -19,9 +23,19 @@ function passTurn(){ } // Draw Card +// Both players get new hand + deck counts updated socket.on('responseDrawCard', function (data) { console.log('<< drawCard'); updateCardCount(data); drawGameBoard(); }); +// Player drew card +// Player that drew the card (atm) gets the cardData, listPosition +// TODO: related attack, cost, effects, etc. +socket.on('responsePlayerDrewCard', function (data) { + console.log('<< playerDrewCard'); + updatePlayerHand(data); + drawGameBoard(); +}); + diff --git a/public/js/global.js b/public/js/global.js index cbcbaa9..c70bd98 100644 --- a/public/js/global.js +++ b/public/js/global.js @@ -20,3 +20,5 @@ const maxHandSize = 4; const maxBoardSize = 3; const maxShield = 2; +const cardMargin = 10; + diff --git a/roomMod.js b/roomMod.js index be7b19f..384f2af 100644 --- a/roomMod.js +++ b/roomMod.js @@ -77,6 +77,7 @@ function roomGeneration(roomId){ // Add all the empty components to the room itemData itemData.component = components.component; + itemData.component.player = itemData.player; // TODO: do this in setPlayers // Generate the decks, and card within the deck cardLists [itemData] = await Promise.all([ cardGen.requestDeck(itemData) ]); diff --git a/server.js b/server.js index 255de91..beab350 100644 --- a/server.js +++ b/server.js @@ -55,6 +55,16 @@ function onConnection(socket){ gameMod.passTurn(roomId, playerId); }); + socket.on('drawACard', function(roomId, playerId) { + gameMod.drawACard(roomId, playerId); + }); + +} + +global.getPlayerSocketFromRoom = function(playerId, roomId){ + + return roomData[roomId].playerData[playerId].socketId; + } // Globals for easier clientside alerts/logs, etc. @@ -64,3 +74,58 @@ global.socketAlert = function(socket, message, type = 'alert'){ ); } +// passTurn response for all clients in room +// TODO: include spectators when added +global.socketResponsePassTurn = function(roomId){ + + let clients = global.io.sockets.adapter.rooms.get(roomId); + for (const clientId of clients) { + + const clientSocket = global.io.sockets.sockets.get(clientId); + console.log('>> '+clientSocket.playerId+' passed turn'); + + // Send the data back. Whose turn it is, and what turn + let turnData = { + 'playerTurn': global.roomData[roomId].itemData.component.playerTurn, + 'turn': global.roomData[roomId].itemData.component.turn, + }; + + global.io.to(clientSocket.id).emit('responsePassTurn', turnData); + + } + +} + +// Then emit the deckSize and hand size to all the player's sockets +// TODO: spectators in future, won't comment about this from now on +global.socketResponseDrawCard = function(roomId, playerId){ + + let clients = global.io.sockets.adapter.rooms.get(roomId); + for (const clientId of clients) { + + const clientSocket = global.io.sockets.sockets.get(clientId); + console.log('>> '+roomData[roomId].playerData[playerId].playerId+' drew card'); + + // Send the data back + global.io.to(clientSocket.id).emit( + 'responseDrawCard' + ,global.roomData[roomId].itemData.component.cardCount + ); + + } + +} + +global.socketResponsePlayerDrewCard = function(roomId, playerId){ + + // This function may be redundant fast, as hand, board, grave, void + // anywhere that's visible to one (or both) players will need all the data + // sending, so will likely be one function that returns it all? + // Could do each thing as their own, and union the data too? + global.io.to(global.getPlayerSocketFromRoom(playerId, roomId)).emit( + 'responsePlayerDrewCard' + ,gameMod.getPlayerHandData(roomId, playerId) + ); + +} +