From 6f3ae033ace2f047842923d0209ef5fbd80d3f3d Mon Sep 17 00:00:00 2001 From: Nathan Date: Sat, 12 Oct 2024 23:25:04 +0100 Subject: [PATCH] Rewrite attack logic to ECS --- public/board.js | 331 +++++++++++++++++++++++++++--------------------- 1 file changed, 184 insertions(+), 147 deletions(-) diff --git a/public/board.js b/public/board.js index 542084a..48beb1f 100644 --- a/public/board.js +++ b/public/board.js @@ -30,9 +30,10 @@ let cardData = {}; let position = {}; let size = {}; //let shape = {}; -let cardStatus = {}; // tapped, attacking, inspected, untargettable +let cardStatus = {}; // tapped, attacking, inspected, untargettable (TODO:maybe used this instead of inEvent later) let player = {}; let listPosition = {}; +let inEvent = null; let playerHand = []; let opponentHand = []; @@ -58,7 +59,6 @@ const maxShield = 4; let inspectCard = null; let attackingCard = null; -let gameWin = 0; // Gonna need lots of refactoring, and sorting class Board{ @@ -88,13 +88,23 @@ class Board{ this.drawCardsECS(); // Atop most everything atm for testing - if(gameWin){ + if(this.checkGameWin == true){ this.drawWin(); } this.drawInspectedCard(); } + checkGameWin(){ + // TODO: Change logic to ECSey, and move into main loop (for other win-cons besides direct attack) + if(false){ + if(opponentShield.length <= 0){ + return true; + } + } + return false; + } + drawWin(){ var winBoard = new Shape({ name: name, @@ -165,6 +175,7 @@ class Board{ // Check status, and change border colour for display (for tapped atm) let border = null; if(cardStatus[itemKey] == 'tapped'){border = '#E0BC00';} + if(cardStatus[itemKey] == 'attacking'){border = '#C92D22';} // Set the card 'cardboard' colour based on the card colour type let colourId = cardData[itemKey].colour; @@ -472,7 +483,7 @@ class Board{ toPosition = getCurrentPositionAndLength(elementTo, playerTo)[0]+1 //console.log(toPosition); } - console.log('itemKey: '+itemKey+' fromPosition: '+fromPosition+' elementFrom: '+elementFrom+' elementTo: '+elementTo+' toPosition: '+toPosition+' playerFrom: '+playerFrom+' playerTo: '+playerTo); + //console.log('itemKey: '+itemKey+' fromPosition: '+fromPosition+' elementFrom: '+elementFrom+' elementTo: '+elementTo+' toPosition: '+toPosition+' playerFrom: '+playerFrom+' playerTo: '+playerTo); listPosition[itemKey] = toPosition; // Move from elementFrom to elementTo @@ -483,11 +494,11 @@ class Board{ //console.log(boardElement[itemKey]); // Move down(0) the positions of elementFrom, from fromPosition for player - console.log(JSON.stringify(boardElement)); - console.log(JSON.stringify(listPosition)); + //console.log(JSON.stringify(boardElement)); + //console.log(JSON.stringify(listPosition)); this.moveElementPositions(0, elementFrom, fromPosition, playerFrom); - console.log(JSON.stringify(boardElement)); - console.log(JSON.stringify(listPosition)); + //console.log(JSON.stringify(boardElement)); + //console.log(JSON.stringify(listPosition)); this.drawBoard(); return 1; // Got a loop that calls a loop, and checks the new values atm, so this keeps counting down @@ -675,60 +686,95 @@ class Board{ // Selects the card that will be attacking // Stop other actions besides selecting opponent/opponent unit // Can cancel, will do later - startAttack(index){ + // Rename to attack intent? + startAttack(itemAttacking){ // Can probably combine attacking/inspect, and set another array element to 'attacking', 'inspecting', etc. - attackingCard = [playerBoard[index], index]; + + // Do error/legal checks here + if(cardStatus[itemAttacking] == 'tapped'){ + alert('Cannot attack, as tapped'); + return false; + } + // Set event + inEvent = ['attack', itemAttacking]; + // Set the status of card to attacking for drawing. + // In future this may be what's used for check (or other in case you can attack with multiple units at once) + cardStatus[itemAttacking] = 'attacking'; + this.drawBoard(); } // Do the attack - makeAttack(index, array = null, name = null){ - if(array == null){ array = opponentBoard; name = 'opponentBoard' } - console.log(name); - // If card attacked - // Compare attackingCard and defendingCard - - let defendingCard = array[index]; - - // If hitting shield, don't calc combat damage - if(name == 'opponentShield'){ - // This should tap the card first, then after all shields - // are tapped, THEN attacking destroys them - if(array[index].tapped){ - // Untap - array[index].tapped = false; - // Add to hand - opponentHand.push(array[index]); - // Remove from shield zone - array.splice(index, 1); - }else{ - array[index].tapped = true; - } + makeAttack(itemDefending, itemAttacking = null){ - playerBoard[attackingCard[1]].tapped = true; - this.endAttack(); - return 1; + // If itemAttacking not defined, use the item from inEvent + if(itemAttacking == null){ + itemAttacking = inEvent[1]; } - if(defendingCard.atk <= attackingCard[0].atk){ - array.splice(index, 1); - // Need to push to grave, etc. here in future too + if(cardStatus[itemAttacking] == 'tapped'){ + alert('Cannot attack, as tapped'); + return false; } - if(attackingCard[0].atk <= defendingCard.atk){ - playerBoard.splice(attackingCard[1], 1); - }else{ - playerBoard[attackingCard[1]].tapped = true; + + switch (boardElement[itemDefending]) { + // If card on 'board' attacked + // Compare attackingCard and defendingCard + case 'board': + let atkAttacker = cardData[itemAttacking].attack; + let atkDefender = cardData[itemDefending].attack; + + // Does Attacker kill Defender + if(atkDefender <= atkAttacker){ + // TODO:Send the item to grave, boardElement = 'grave' + } + + // Does Defender kill Attacker + if(atkAttacker <= atkDefender){ + // TODO:Send the item to grave + this.endAttackFor(itemAttacking); + }else{ + // If not, end the attacker cards attack 'phase' + this.endAttackFor(itemAttacking); + } + + break; + + case 'shield': + // If the shield is tapped 'destroy' it + if(cardStatus[itemDefending] == 'tapped'){ + // Remove from shield, add to hand + // Untap, add to hand (when moving item between status, ANY ITEM. remove status) + this.destroyShield(itemDefending); + } + else{ + this.tapCard(itemDefending); + } + // End the attacker card attack 'phase' + this.endAttackFor(itemAttacking); + + break; + } + + + this.drawBoard(); + } + endAttackFor(itemAttacking){ + // Tap the card (this would check if after attack cards taps or not. + this.tapCard(itemAttacking); + + // If the item attacking was from the 'attack' event, remove the event + if(itemAttacking == inEvent[1]){ + inEvent = null; } - console.log('attacking'); - this.endAttack(); - - // If player attacked - // Compare life/remove life cards 5/10/15,1/2/3? } - endAttack(){ - attackingCard = null; - if(opponentShield.length <= 0){ - gameWin = 1; + cancelAttackFor(itemAttacking){ + if(itemAttacking != inEvent[1]){ + return false; } + + // If the card to attack is from inEvent of 'attack', remove it and its cardStatus + cardStatus[itemAttacking] = null; + inEvent = null; this.drawBoard(); } @@ -791,6 +837,23 @@ class Board{ this.drawBoard(); } } + + // HELPER METHODS, to simplify code-base for me in the future + destroyShield(itemKey){ + // Adds from 'shield' to 'hand' boardElement + // Sets the listPosition to the next in line for new boardElement + // Changes the listPosition of each item in previous boardElement + // All for same player, although below playerFrom/playerTo can be set to switch between players + // TODO: Removes any cardStatus + + //addFromBoardElement(playerFrom, fromPosition, elementFrom, elementTo, toPosition=null, playerTo=null) + this.addFromBoardElement(player[itemKey], listPosition[itemKey], boardElement[itemKey], 'hand', null, null); + } + tapCard(itemKey){ + // Set the cardStatus to tapped + cardStatus[itemKey] = 'tapped'; + // Do any other 'on tap' effects, etc. in the future + } } // TEMP!! @@ -816,14 +879,10 @@ board.drawBoard(true); //board.drawACard(3); +// Right Click, Rightclick, rightclick, right click canvas.addEventListener('contextmenu', function(event) { event.preventDefault(); - let specialEvent = false; - if(inspectCard !== null || attackingCard !== null || gameWin){ - specialEvent = true; - } - var x = event.pageX - canvasLeft, y = event.pageY - canvasTop; @@ -845,8 +904,15 @@ canvas.addEventListener('contextmenu', function(event) { switch(boardElement[itemKey]){ // Check item location, and trigger events based on it case 'board': - // Untap - cardStatus[itemKey] = null; + // TODO: If the current event is attack, right clicking same item in event + // will end the attack event + // If there's an attack event, target other cards + // If there is an event, the event is attack event, and the item currently loop + // is the same one as currently in the event + console.log(inEvent); + if(inEvent && inEvent[0] == 'attack' && itemKey == inEvent[1]){ + board.cancelAttackFor(itemKey); + } board.drawBoard(); break; case 'hand': @@ -860,21 +926,13 @@ canvas.addEventListener('contextmenu', function(event) { } } - // # PLAYER HAND - playerHand.forEach(function(card, index){ - - let clickable = card.clickable; - - if(clickableCheck(x,y,clickable) && !specialEvent){ - board.playMana(index); - board.drawBoard(); - } - }); - // Return false to prevent context opening: https://stackoverflow.com/a/4236294 + // Return false to prevent context opening: + // https://stackoverflow.com/a/4236294 return false; }, false); +// Left Click, Leftclick, leftclick, left click canvas.addEventListener('click', function(event) { console.log('EVENT LISTENER'); console.log(''); @@ -882,9 +940,23 @@ canvas.addEventListener('click', function(event) { // specialEvent used to prevent eventTriggers if something specific // is being done, attack needs to be made, inspecting card. // Prevents user from doing other actions until completed or cancelled event - let specialEvent = false; - if(inspectCard !== null || attackingCard !== null || gameWin){ - specialEvent = true; + + // If there is a 'specialEvent', and specialEvent is set to inspect + // TODO: Inspect event + if(false){ + if(inEvent && inEvent[0] == 'inspect'){ + clickable = inspectCard[0][inspectCard[1]].clickable; + + if(clickableCheck(x,y,clickable)){ + console.log('clicked inspect card'); + + }else{ + console.log('not inspected card'); + // Stop inspecting card if player clicks off it + inspectCard = null; + board.drawBoard(); + } + } } var x = event.pageX - canvasLeft, @@ -916,11 +988,33 @@ canvas.addEventListener('click', function(event) { // Check the location of element switch(boardElement[itemKey]){ // Check item location, and trigger events based on it + // TODO: Change inEvent locations, and checks elsewhere? + // TODO: Make existing mechanics ECSey case 'board': + // player/opponentBoard not seperated, as want to have + // each player able to have effects, etc. to make others attack + // etc. + + // playerBoard // Can attack - // board.startAttack(index); - // Get tapped - cardStatus[itemKey] = 'tapped'; + console.log(inEvent); + console.log(cardStatus[itemKey] != 'tapped'); + if(!inEvent && cardStatus[itemKey] != 'tapped'){ + board.startAttack(itemKey); + } + // opponentBoard + // If there's an attack event, target other cards + if(inEvent && inEvent[0] == 'attack' && inEvent[1] != itemKey){ + // Make attack on the card clicked, with card in inEvent[1] + board.makeAttack(itemKey); + } + + // If no event, and clicked an OPPONENT CARD (for now) + // inspect the card (zoom in on it) + if(!inEvent){ + board.inspectOpponentBoard(itemKey); + } + board.drawBoard(); break; case 'hand': @@ -930,96 +1024,39 @@ canvas.addEventListener('click', function(event) { board.playCardToBoard(listPosition[itemKey], 'hand', 'board', playerId, playerId, 1); board.drawBoard(); break; + case 'shield': + // TODO: + // If you have an attack, and click opponent shield, + // destroy (unless they can block, etc.) + if(inEvent && inEvent[0] == 'attack' && inEvent[1] != itemKey){ + board.makeAttack(itemKey); + } + break; } } } } - - - // Collision detection between clicked offset and clickableItems - // https://stackoverflow.com/a/9880302 - - if(inspectCard !== null){ - clickable = inspectCard[0][inspectCard[1]].clickable; - - if(clickableCheck(x,y,clickable)){ - console.log('clicked inspect card'); - - }else{ - console.log('not inspected card'); - // Stop inspecting card if player clicks off it - inspectCard = null; - board.drawBoard(); - } - } - + // Decks kept as-is for now + // TODO: boardElement realDeck, or smth and add these to the loop // # PLAYER DECK clickable = clickableItems['deckSprite']; - - // Want to loop clickables ideally, and call a function - // that's set to them for click, hover, etc. - // TODO: potentially loop all clickables, and do a check on clickable.name to differ event handling per-item - - // For now this will be fine, as it functions - if(clickableCheck(x,y,clickable) && !specialEvent){ + if(clickableCheck(x,y,clickable) && !inEvent){ board.drawACard(); } + // TODO: // # OPPONENT DECK clickable = clickableItems['deckOpponentSprite']; - - if(clickableCheck(x,y,clickable) && !specialEvent){ + if(clickableCheck(x,y,clickable) && !inEvent){ board.drawACardOpponent(); } - // # PLAYER BOARD - playerBoard.forEach(function(card, index){ - let clickable = card.clickable; - - if(clickableCheck(x,y,clickable)){ - if(attackingCard !== null && card == attackingCard[0]){ - board.endAttack(); - } - if(!specialEvent && card.tapped != true){ - board.startAttack(index); - } - board.drawBoard(); - } - }); - - // # OPPONENT BOARD - opponentBoard.forEach(function(card, index){ - let clickable = card.clickable; - - if(clickableCheck(x,y,clickable)){ - // Check if card if getting attacked - if(attackingCard !== null){ - board.makeAttack(index); - } - - if(!specialEvent){ - board.inspectOpponentBoard(index); - } - board.drawBoard(); - } - }); - // # OPPONENT SHIELD - opponentShield.forEach(function(card, index){ - let clickable = card.clickable; - - if(clickableCheck(x,y,clickable)){ - // Check if card if getting attacked - if(attackingCard !== null){ - board.makeAttack(index, opponentShield, 'opponentShield'); - } - board.drawBoard(); - } - }); - }, false); function clickableCheck(x,y,clickable=false,itemKey=false){ + // Collision detection between clicked offset and clickableItems + // https://stackoverflow.com/a/9880302 // Temp solution to add the check while the old way also exists // simultaneously. It works, so that's nice