function drawGameBoard(){ // Reset board ctx.clearRect(0, 0, canvas.width, canvas.height); drawPlayerNames(); calculateDeckPositions(); calculateHandPositions(); calculateBoardPositions(); calculateShieldPositions(); calculateManaPositions(); 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(){ let playerWeight = 'normal'; if(gameData.playerId == gameData.playerTurn){ playerWeight = 'bold'; } let opponentWeight = 'normal'; if(gameData.opponentId == gameData.playerTurn){ opponentWeight = 'bold'; } // Player Name printText(gameData.playerId, 50, canvas.height - 70, 'left', 'alphabetic', 'normal', playerWeight, '10', 'Arial', '#000' ); printText(gameData.players[gameData.playerId][1].playerId, 50, canvas.height - 50, 'left', 'alphabetic', 'normal', playerWeight, '10', 'Arial', '#000' ); // Opponent Name printText(gameData.opponentId, canvas.width - (ctx.measureText(gameData.opponentId).width + 50), 50, 'left', 'alphabetic', 'normal', opponentWeight, '10', 'Arial', '#000' ); printText(gameData.players[gameData.opponentId][1].playerId, canvas.width - (ctx.measureText(gameData.players[gameData.opponentId][1].playerId).width + 50), 70, 'left', 'alphabetic', 'normal', opponentWeight, '10', 'Arial', '#000' ); } // TODO: Move from draw into somewhere else function calculateDeckPositions(){ for (const [key, value] of Object.entries(gameData.deck)) { // If the deckItem (key is the entityId), // belongs to the player (matches this sockets player) switch(gameData.player[key]){ // Set position for player deck case gameData.playerId: gameData.position[key] = [canvas.width-cardWidth-40, canvas.height-cardHeight-60]; // X/Y gameData.size[key] = [cardWidth, cardHeight]; break; // Same for opponent. This will need redoing when 2v2,4v1,etc. are added case gameData.opponentId: gameData.position[key] = [40, 60]; // X/Y gameData.size[key] = [cardWidth, cardHeight]; break; } } } // 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 * handScale) * (cardsInHand - (positionInHand+1)) - (cardMargin * (positionInHand+1))) ,canvas.height-(cardHeight * handScale)-20 ]; gameData.size[key] = [cardWidth * handScale, cardHeight * handScale]; break; // Opponent, currently done in fakeHand } } } // TODO: Move from draw into somewhere else function calculateBoardPositions(){ for (const [key, value] of Object.entries(gameData.board)) { // key is entity Id here let cardsOnBoard = 0; let position = 0; switch(gameData.player[key]){ // Set position for player hand (all the time at current) case gameData.playerId: cardsOnBoard = gameData.cardCount.board[gameData.playerId]; position = gameData.listPosition[key]; gameData.position[key] = [ canvas.width/2 - (cardWidth * (cardsOnBoard - (position+1)) - (cardMargin * (position+1))) ,canvas.height-((cardHeight*2) * handScale)-30 ]; gameData.size[key] = [cardWidth * handScale, cardHeight * handScale]; break; // Opponent case gameData.opponentId: cardsOnBoard = gameData.cardCount.board[gameData.opponentId]; position = gameData.listPosition[key]; gameData.position[key] = [ canvas.width/2 - (cardWidth * (cardsOnBoard - (position+1)) - (cardMargin * (position+1))) ,(cardHeight * handScale)+30 ]; gameData.size[key] = [cardWidth * handScale, cardHeight * handScale]; break; } } } function calculateShieldPositions(){ for (const [key, value] of Object.entries(gameData.shield)) { // key is entity Id here let cardsOnBoard = 0; let position = 0; let fromX = 0; let fromY = 0; let split = 0; let shieldScale = .5; // TODO: Make global (like handScale) switch(gameData.player[key]){ // Set position for player hand (all the time at current) case gameData.playerId: position = gameData.listPosition[key]; fromX = 60; fromY = 300; // i-1 here as it's based on 0 being start, like array. // TODO: Not sure if I want to start elements at 1 (for clienty) or 0 (for programmy) if(position-1>=2){ split = 1; } gameData.position[key] = [ (fromX+((position%2)*cardMargin)) +(position%2*(cardWidth*shieldScale)) ,canvas.height-fromY+(split*(cardHeight*shieldScale)+(cardMargin*split)) ]; gameData.size[key] = [cardWidth * shieldScale, cardHeight * shieldScale]; break; // Opponent case gameData.opponentId: position = gameData.listPosition[key]; fromX = canvas.width-60; fromY = 300; if(position-1>=2){ split = 1; } // i%2 0 = 0, 1 = 1, 2 = 0, 3 = 1 to prevent margin from X/Y axis, and just between cards gameData.position[key] = [ (fromX+((position%2)*cardMargin)) +(position%2*(cardWidth*shieldScale)-(cardWidth*2*shieldScale)) ,fromY+(split*(cardHeight*shieldScale)+(cardMargin*split)-((cardHeight*2*shieldScale) + cardMargin)) ]; gameData.size[key] = [cardWidth * shieldScale, cardHeight * shieldScale]; break; } } } function calculateManaPositions(){ for (const [key, value] of Object.entries(gameData.mana)) { // key is entity Id here let cardsOnBoard = 0; let position = 0; let fromX = 0; let fromY = 0; let manaScale = .3; switch(gameData.player[key]){ // Set position for player hand (all the time at current) case gameData.playerId: position = gameData.listPosition[key] - 1; // Position starts at 1 fromX = 60; fromY = 60 + cardHeight*manaScale; gameData.position[key] = [ (fromX)+(position*(cardWidth*manaScale)+cardMargin*position) ,canvas.height-fromY ]; gameData.size[key] = [cardWidth * manaScale, cardHeight * manaScale]; break; // Opponent case gameData.opponentId: position = gameData.listPosition[key] - 1; // Position starts at 1 fromX = 60; fromY = 60;// + cardHeight*manaScale; gameData.position[key] = [ // TODO: correct (then again all position need correcting tbf) canvas.width - fromX-cardWidth*manaScale-(position*(cardWidth*manaScale))-cardMargin*position ,fromY ]; gameData.size[key] = [cardWidth * manaScale, cardHeight * manaScale]; break; } } } // TODO: Move this function elsewhere, not really a draw function function calculateCardSpacing(positionInt, size, standardSize){ let scaleMultiplier = size/standardSize; return positionInt * scaleMultiplier; } function drawEntities(){ // Loop position component for entities with a position for (const [key, value] of Object.entries(gameData.position)) { // Key is the entityId here // If the entity has a position AND a size, we can print it if(gameData.size[key] !== undefined){ // Should prolly switch from ifs somehow, but it works for now // If the entity is a deck if(key in gameData.deck){ drawDeck(key); } // If card in (player) hand if(key in gameData.hand){ drawCardInHand(key); } // If card on board if(key in gameData.board){ drawCardOnBoard(key); } // If shield if(key in gameData.shield){ drawShield(key); } // Mana if(key in gameData.mana){ drawMana(key); } if(key in gameData.inInteractionMenu){ // Add the menu with 'play', 'activate effect', 'inspect', etc. drawInteractionMenu(key); } } } } function drawDeck(entity){ // Draw the deck shape drawCardBack(entity); // Draw circle for deck count to sit in let deckCounterSprite = new Shape({ shape: 'circle', x: gameData.position[entity][0], y: gameData.position[entity][1], width: gameData.size[entity][0]*.25, height: gameData.size[entity][1]*.25, fillStyle: '#DCDCDC' }); deckCounterSprite.draw(); // Draw deck count text ctx.fillStyle = '#000'; // Deck count for the deck belonging to player let deckCount=gameData.cardCount.deck[gameData.player[entity]]; let textX=gameData.position[entity][0];//-(ctx.measureText(deckCount).width); let textY=gameData.position[entity][1]+(ctx.measureText(deckCount).width/2); printText(deckCount, textX, textY, 'center', 'bottom', 'normal', 'bold', '10', 'Arial', '#000'); } function drawCardInHand(entity){ // TODO: Change card colour based on its colours // Draw the card 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(); drawCardDetails(entity); } function drawCardOnBoard(entity){ // TODO: Tapped, Attacking, Targettable, Activatable borders // TODO: Passives, flight, etc. effects let strokeStyle = '#AAA'; if(entity in gameData.cardStatus.tapped){ strokeStyle = '#555'; } // Draw the card 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: strokeStyle, }); shape.draw(); drawCardDetails(entity); } function drawShield(entity){ // TODO: Tapped drawCardBack(entity); let strokeStyle = '#AAA'; if(entity in gameData.cardStatus.tapped){ strokeStyle = '#555'; } // Draw the card 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: false, strokeStyle: strokeStyle, }); shape.draw(); } function drawMana(entity){ // TODO: Show the colour/Icon of the mana for ease when tapping, etc. console.log('drawMana'); console.log(entity); // TODO: Tapped drawCardBack(entity); let strokeStyle = '#AAA'; if(entity in gameData.cardStatus.tapped){ strokeStyle = '#555'; } // Draw the card 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: false, strokeStyle: strokeStyle, }); shape.draw(); } // The draw all the card data, name, colour, etc. function drawCardDetails(entity){ //console.log(gameData.cardData[entity]); drawCardImage(entity); // TODO: Use a sprite per card, not just temp. drawCardText(entity); /* this.printColourRequirements(entity); this.printCardPassives(entity); */ } function drawCardImage(entity){ let positionX = gameData.position[entity][0]; let positionY = gameData.position[entity][1]; let width = gameData.size[entity][0]; let height = gameData.size[entity][1]; // Create the clipping shape let cardImageContainer = new Shape({ shape: 'unit', x: positionX+width*.5, // Center (probably change to an int in future) y: positionY+calculateCardSpacing(65, height, cardHeight), width: calculateCardSpacing(100, width, cardWidth), height: calculateCardSpacing(150, height, cardHeight) }); // Save canvas drawing, start the clip cardImageContainer.startClip(); // Draw the image into the clipping mask // image, dx,dy,dw,dh // image, sx,sy, sw,sh,dx,dy,dw,dh // TODO: give cards/cardData a sprite position and use it [0,0], [1,0], [1,4], etc... let spriteSheetX = 80;//*cardSprite[entity][0]; let spriteSheetY = 120;//*cardSprite[entity][1]; ctx.drawImage(cardArt, spriteSheetX,spriteSheetY ,80 ,120 ,positionX ,positionY ,width ,height ); // Restore the canvas draw post clip applied, to get everything else back too cardImageContainer.endClip(); } function drawCardText(entity){ let positionX = gameData.position[entity][0]; let positionY = gameData.position[entity][1]; let width = gameData.size[entity][0]; let height = gameData.size[entity][1]; // Calculate the fontsize for the card at different 'zooms'/sizes let fontSize = width/cardWidth*8; // 8pt. 10 = baseFontSize of 10pt //ctx.font = "bold "+fontSize+"pt Arial"; let fillStyle = '#000'; let strokeStyle = '#FFF'; let strokeSize = fontSize/12; // TODO: // 5px on regular 80px = 80/16, so width/16 = the same spacing. Probbaly just another // function to calc the spacings like cardSize/actualSizeOfSpace (80/5) = 16, then. sizeOfThisCard/16 = RETURN // This is fow the positionX + Xs as the Xs will not be that size if the card size is smaller/bigger // so TODO gotta make it scaled % wise with the 80/16 I wrote above // NAME if(gameData.cardData[entity] !== undefined && gameData.cardData[entity]['name'] !== undefined){ printText(gameData.cardData[entity]['name'] , positionX + 5 + 10 // + 5 (spacing for cost) + 10 for more spacing , positionY + 10 + 5 // + 10pt + 5 as it's the spacing I actually want , 'left', 'alphabetic', 'normal', 'normal', fontSize, 'Arial', fillStyle, strokeStyle, strokeSize, false ); } // COST if(gameData.cardData[entity] !== undefined && gameData.cardData[entity]['cost'] !== undefined){ printText(gameData.cardData[entity]['cost'] , positionX + 5 , positionY + 10 + 5 // + 10pt + 5 as it's the spacing I actually want , 'left', 'alphabetic', 'normal', 'normal', fontSize, 'Arial', fillStyle, strokeStyle, strokeSize, false ); } // TYPE if(gameData.cardData[entity] !== undefined && gameData.cardData[entity]['type'] !== undefined){ printText( CLASS[gameData.cardData[entity]['type']] , positionX + 5 , positionY + width // To be slightly over bottom of image (which uses width to position) , 'left', 'alphabetic', 'normal', 'normal', fontSize, 'Arial', fillStyle, strokeStyle, strokeSize, false ); } // CLASS(ES) if(gameData.cardData[entity] !== undefined && gameData.cardData[entity]['class'] !== undefined){ let classes = ''; // Loop the classes, and add each of them to the card for(let i = 0; i < gameData.cardData[entity]['class'].length; i++){ // Add a comma to seperate if it's not the first class if(i > 0){ classes += ', '; } // Add the class name to the list to print classes += CLASS[gameData.cardData[entity]['class'][i]]; } printText( classes , positionX + 5 + 5 + ctx.measureText(CLASS[gameData.cardData[entity]['type']]).width // + another 5 and width of type for spacing , positionY + width // To be slightly over bottom of image (which uses width to position) , 'left', 'alphabetic', 'normal', 'normal', fontSize, 'Arial', fillStyle, strokeStyle, strokeSize, false ); } // EFFECT(S) if(gameData.cardData[entity] !== undefined && gameData.cardData[entity]['effect'] !== undefined){ // TODO: Split the text onto lines based on character count (but not splitting words) so they fit into the // bounds of the card element let effects = ''; // Loop the classes, and add each of them to the card for(let i = 0; i < gameData.cardData[entity]['effect'].length; i++){ // Split effects with an additonal break (any more than 1st effect) if(i > 0){ classes += '\n'; } // Add the class name to the list to print effects += gameData.cardData[entity]['effect'][i]; } printText( effects , positionX + 5 , positionY + width + 10 // To be beneath image, and type + classes , 'left', 'alphabetic', 'normal', 'normal', fontSize, 'Arial', fillStyle, strokeStyle, strokeSize, false ); } // ATTACK if(gameData.cardData[entity] !== undefined && gameData.cardData[entity]['attack'] !== undefined){ printText(gameData.cardData[entity]['attack'] , positionX + width/2 - ctx.measureText(gameData.cardData[entity]['attack']).width/2 // Should be centred , positionY + 10 + (height - 10) // + 10pt + 5 as it's the spacing I actually want , 'left', 'alphabetic', 'normal', 'normal', fontSize, 'Arial', fillStyle, strokeStyle, strokeSize, false ); } // Reset font for other draws // TODO: Probably just use a save/restore for the font draws in here ctx.font = "10pt Arial"; } function drawCardBack(entity){ ctx.drawImage( cardBackArt ,0 // X Position From in Spritesheet ,0 // Y Position From in Spritesheet ,80 // Width of image selection from Spritesheet ,120 // Height of image selection from Spritesheet ,gameData.position[entity][0] // X ,gameData.position[entity][1] // Y ,gameData.size[entity][0] // Width ,gameData.size[entity][1] // Height ); } // 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(){ // 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++){ ctx.drawImage( cardBackArt ,0 // X Position From in Spritesheet ,0 // Y Position From in Spritesheet ,80 // Width of image selection from Spritesheet ,120 // Height of image selection from Spritesheet ,canvas.width/2 - ((cardWidth*handScale) * (itemListLength - (i+1)) - (cardMargin * (i+1))) // X ,20 // Y ,cardWidth*handScale // Width ,cardHeight*handScale // Height ); } break; } } } function drawInteractionMenu(entity){ // Draws the interactable options availabe for an entity // TODO: Draw atop/below depening on position, etc. for (const [key, value] of Object.entries(gameData.interactionOption)) { // Draw the interaction box (TODO: make much better); let menuItem = new Shape({ x: value.x, y: value.y, width: value.width, height: value.height, fillStyle: '#DDD', strokeStyle: '#666' }); menuItem.draw(); // Add the text printText( key ,value.x + value.width/2 ,value.y + value.height/2 , 'center', 'middle', 'normal', 'normal', 8, 'Arial', '#333', false, false, false ); } } // TODO: Put the stack into the UI function drawStackResolve(){ } // TODO: Draw stack, all cards in the stack in order (use the cardId for display) // make inspectable, and display which part of the effect will be triggered in it's // stack position