diff --git a/cardGen.js b/cardGen.js new file mode 100644 index 0000000..17a8b98 --- /dev/null +++ b/cardGen.js @@ -0,0 +1,450 @@ +// Need to require DB again. Apparently this is ok, as it'll +// still only have one require cross project (?) +const database = require('./database'); + + +// cardClass, cardColourRequirement +// may want to be seperate too (as well as in cardItem), so that +// during match they can be altered by effects while keeping the OG card +// for inspecting (and compare against new stats,reqs,etc.) +// same with attack, cost, etc. things that will can be visually shown as +// changed in game + +// Just grabbing everything from DB for now, as it's quite small at current +// then will rejig when needed. +// Should all cards, effects, classes etc. be loaded in on server start +// then just load decks and decklists when needed? +function getDecks(deckIds = false){ + // Await promise, once it's done get the data, if errors send err + const dPromise = new Promise((resolve, reject) => { + + database.dbGetDecks().then(data => { + + let decks = []; + + data.forEach((deck) => { + //let itemId = item.length; + //item.push(itemId); // Add the next available item + // Add the deck info to deckItem + // deckItem[itemId] = {}; + decks.push({ + 'deckId': deck.deckId, + 'playerId': deck.playerId, + 'deckName': deck.deckName, + }); + }); + + // Resolve the decks pulled + resolve(decks); + + //console.log(item); + //console.log(deckItem); + //console.log(decks); + }) + .catch(err => { throw err; }) + + }); + return dPromise; +} +//getDecks(); +function getDeckList(){ + const dPromise = new Promise((resolve, reject) => { + + database.dbGetDeckList().then(data => { + + let deckList = []; + + data.forEach((listItem) => { + // Add the deck info to deckItem + deckList.push({ + 'deckId': listItem.deckId, + 'playerId': listItem.playerId, + 'cardId': listItem.cardId, + 'cardCount': listItem.cardCount, + }); + resolve(deckList); + }); + }) + .catch(err => { throw err; }) + }); + return dPromise; +} +//getDeckList(); +function getCards(){ + const dPromise = new Promise((resolve, reject) => { + + database.dbGetCards().then(data => { + + let cards = []; + + data.forEach((card) => { + cards.push({ + 'id': card.id, + 'cardName': card.cardName, + 'cardCost': card.cardCost, + 'cardType': card.cardType, + 'cardAttack': card.cardAttack, + 'cardRarity': card.cardRarity, + 'cardClass': [], + 'cardColourRequirement': [], + }); + }); + resolve(cards); + }) + .catch(err => { throw err; }) + + }); + return dPromise; +} +//getCards(); +function getCardClasses(){ + const dPromise = new Promise((resolve, reject) => { + database.dbGetCardClasses().then(data => { + + let cardClasses = []; + + data.forEach((cardClass) => { + + cardClasses.push({ + 'cardId': cardClass.cardId, + 'classId': cardClass.classId, + }); + + }); + resolve(cardClasses); + }) + .catch(err => { throw err; }) + }); + return dPromise; +} +//getCardClasses(); +function getCardColourRequirement(){ + const dPromise = new Promise((resolve, reject) => { + database.dbGetCardColourRequirement().then(data => { + + let colourRequirements = []; + + data.forEach((cardColourReq) => { + + colourRequirements.push({ + 'cardId': cardColourReq.cardId, + 'colourId': cardColourReq.colourId, + 'cost': cardColourReq.cost, + }); + + }); + resolve(colourRequirements); + + }) + .catch(err => { throw err; }) + }); + return dPromise; +} +//getCardColourRequirement(); +// Then effects which will have effects with parent triggers, and unit type checks +// colour checks, all sorts. So will be more difficult. Basic (flight, etc) +// shouldn't be too bad +// something like effect_basic, card_effect, effect_trigger, effect_requirement, effect_option +// effect_stage, effect_advanced, with advanced being an id with x triggers, triggers have +// x req, advanced as with x options, x stages to be able to fine-tune fancy stuff +// combining all other effects, units, cards, colours, etc. Will be a lot of though, +// but better than hard coding anything more than basic effects and effect check logic +// TODO: effect (as above) + +// request a deck in the format CURRENTLY used in the game +// decks will likely be changed around + +// https://www.geeksforgeeks.org/how-to-wait-for-multiple-promises-in-javascript/ +// https://medium.com/@nikolozz/using-socket-io-with-async-await-13fa8c2dc9d9 +// using last example +// TODO: When this is functionally working, need to split up into other func/modules +// such as: playerMod, cardMod, deckMod, miscMod ? +function requestDeck(itemData = null){ + return new Promise((resolve, reject) => { + (async () => { + + // Get the deck(s) requested. + // Not 100% on how to do two differening atm + // Besides all of playerId, and all of deckId. But 1,2/2,3 for instance + const [decks, deckList] = + await Promise.all([ + getDecks(), + getDeckList() + ]); + + //console.log(decks); + //console.log(deckList); + // Now loop the deckList for the cardIds + let deckCardIds = []; + deckList.forEach((deckItem) => { + deckCardIds.push(deckItem.cardId); + + }); + //console.log(deckCardIds); + + // Next, get the cards in the deck by their ID + // TODO: https://stackoverflow.com/a/65510676 + // Change SQL to accept for just the cards passed + + // Get each cards data, colourReqs, and classes + const [cards, cardClasses, cardColourRequirements] = + await Promise.all([ + getCards(), + getCardClasses(), + getCardColourRequirement(), + ]); + // ^^^^ Classes async? Can pass the cardsIds, then loop classes, if the class cardId + // matches the cardId in cards[] then push the class to cards[x].classes + + // TODO: Build the card so far async for it's done alongside getting effects? + // Then when both done add the effects to the existing cardObjects? Yep good idea me + //console.log(cards); + //console.info(cardClasses); + //console.log(cardColourRequirements); + + const [builtCards] = + await Promise.all([ + buildCards(cards, cardClasses, cardColourRequirements), + // TODO: builtEffects + ]); + + //console.log(builtCards); + // TODO: addEffectsToCards + // TODO: Finally do the effects for each card. This will be the complicater + // since it's not 100% thought out yet... ( + + // Add the cards (x of how many cardObjects with cardId are in count in decklist) + // to a deck array to pass through. Or decks array with deckId specified? + //console.log(deckList); + // These are the four basic fellas for this + // from public/board.js where current game testing/logic is applied + // So creating as a test for a 'room' to see if it's simple enough to + // send and play with + // BUILD THE DATA TO SEND TO CLIENTS! (Kinda) + + // To continue from previous item/itemCount from other funcs, something like this + let item = []; + let itemCount = 0; + let player = {}; + let players = []; + + // Jank check to allow current client call, and new roomMod.roomGeneration call to + // both work (will replace client call with roomGeneration when closer) + if(itemData !== null){ + if(itemData['item'] !== undefined){ + item = itemData['item']; + } + if(itemData['itemCount'] !== undefined){ + itemCount = itemData['itemCount']; + } + if(itemData['player'] !== undefined){ + player = itemData['player']; + } + if(itemData['players'] !== undefined){ + players = itemData['players']; + } + }else{ + itemData = {}; + } + + // TODO: Deck data/deckIn should be in deckGen, other stuff probably in a cardGen + // function + let deckData = {}; // New but may be useful + let deckIn = {}; // Which deck the item is in? Kinda the player/boardelement thing? + let cardData = {}; + let boardElement = {}; + // New(in here) + let cardStatus = {}; // Tapped, etc + // player + let listPosition = {}; + let cardFace = {}; + // REALLY NEW + let cardColours = {}; // Array of req. + let cardAttack = {}; // Base, Current + let cardSprite = {}; // Maybe from DB? + + // TODO: Set the player. For now will do this in front-end as testing currently + + let forPlayer = 0; // TODO: Change to actually have each player select a deck + // Loop and create the deck first + decks.forEach((deck) => { + item.push(itemCount); // Add new item to add stuff for + deckData[itemCount] = {'deckId':deck.deckId, 'playerId':deck.playerId, 'deckName':deck.deckName, 'maxLength': 0}; // Max length for listPositioning later in cardBuild + boardElement[itemCount] = 'realDeck'; + cardFace[itemCount] = 0; // Face down for deck + player[itemCount] = forPlayer; + itemCount++; // Needed/good + forPlayer++; // Jank/bad + }) + //console.log(deckData); + //console.log(deckList); + + // Loop each item in the deckList + // Loop inside it X times where X is cardCount + // Add the builtCard with same cardId as deckList item X times + + // and load that deck for them. This just sets first deck to player0 to players.length + deckList.forEach((deckListItem) => { + + let deckItem = null; + // Loop each deck, if the deck/playerIds match, add association + for(key in deckData){ + //Object.keys(deckData).forEach(function(key) { // Less efficient than for + // Needs to check deck AND player id, as that's the primary key (together) + if(deckData[key].deckId == deckListItem.deckId && deckData[key].playerId == deckListItem.playerId){ + deckItem = key; // Key is the `item` key + // Now add cards to the player that this deck belongs to + forPlayer = player[key]; + } + + }; + + // For each new card, loop to the cardCount (how many cards in deck) + // and add to the deck + for(let i = 0; i < deckListItem.cardCount; i++){ + item.push(itemCount); // Add new item to add stuff for + // ^ not using item.length incase anything in future gets deleted + // from item for instance + cardData[itemCount] = builtCards[deckListItem.cardId]; // builtCards id set to cardId from DB, so adding the builtCard object is based on the deckList's cardId (from DB) + boardElement[itemCount] = 'deck'; // Add all cards to deck at match start + // Associate the card with the deck + // TODO: Change deckIn to something more sensical + deckIn[itemCount] = deckItem; + + // Attempt to set the listPosition of each card in related deck + // Increase the length of the deck in deckItem + listPosition[itemCount] = deckData[deckItem].maxLength; // TODO: better + deckData[deckItem].maxLength++; + + // Adding to get everything sorted in one! + cardStatus[itemCount] = null; + cardFace[itemCount] = 0; // Face down by default (in deck) + cardColours[itemCount] = cardData[itemCount].colourRequirements; // TODO; + // From cardData set the base attack, and current (same when deckbuild) + let atk = cardData[itemCount].atk; + cardAttack[itemCount] = [atk, atk]; // Base, Current + player[itemCount] = forPlayer; // Jank TODO: actually set to correct player + + + itemCount++; // Increment item to not overwrite + } + + }); + + // ADD all new elements, and updated data into itemData + itemData.item = item; + itemData.itemCount = itemCount; + itemData.player = player; + itemData.players = players; + itemData.deckData = deckData; + itemData.deckIn = deckIn; + itemData.cardData = cardData; + itemData.boardElement = boardElement; + itemData.cardStatus = cardStatus; + itemData.listPosition = listPosition; + itemData.cardFace = cardFace; + itemData.cardColours = cardColours; + itemData.cardAttack = cardAttack; + itemData.cardSprite = cardSprite; + + // item, itemCount, deckData, cardData, boardElement + //console.log(cardData); + + // Returning everything to be looped in the front-end + // This won't be looped as it will at final, instead just for deck generation + // Returned as object over array, as easier to disect when gets to front-end + let dataReturn = itemData; + return resolve(dataReturn); + //return resolve(cardData); + + // Promise stuff testing + })() + }); + + // TODO: In future the item inc. all the decks, cards, locations, and other attributes + // will come from here + + // TODO: unrelated, but thought. If lots of players on, generating cards each time + // will be a big hit, as well as all the DB hits, so may need to load all cardObjects + // into memory/array when server starts. This then prevents any DB changes to alter + // things, but outside of testing that won't occur(?), may need to test this at some + // point to see. For now DB, and generating is ok, as still working on it +} + +function buildCards(cards, cardClasses, cardColourRequirements){ + + console.log(cardColourRequirements); + const dPromise = new Promise((resolve, reject) => { + + builtCards = {}; + + //console.log(cards); + // Loop the cards and build the base + cards.forEach((card) => { + + let builtCard = { + id: card.id, + name: card.cardName, + colour: [], + cost: card.cardCost, + colourRequirements: [], + type: card.cardType, + atk: card.cardAttack, + rarity: card.cardRarity, + effect: [], + cardClass: [], + }; + // Give the card an easily accessible Id for compares + // and to add to the cardItem being built + builtCards[card.id] = builtCard; + + }); + //console.log(builtCards); + + // Next loop each class, and add to the new builtCard (if cardIds match) + cardClasses.forEach((cardClass) => { + + // Check the card exists (it should always, but don't want jank) + if(cardClass.cardId in builtCards){ + // It's the Id for now + // TODO: Send the text too (from join) but don't use for compares + //console.log(cardClass.cardId); + //console.log(cardClass.classId); + //console.log('---'); + //console.log(builtCards[cardClass.cardId]); + + // Add the class to the class array (cards can have multiple) + builtCards[cardClass.cardId].cardClass.push(cardClass.classId); + + // TODO: As an array [classId, className] then + // card.classes[x][0] can be compared as numeric in code + } + + }); + + // Do the same for cardColourRequirements (if cardIds match) + cardColourRequirements.forEach((colourReq) => { + + // Check the card exists (it should always, but don't want jank) + if(colourReq.cardId in builtCards){ + // Add the colours to the class array (cards can have multiple) + builtCards[colourReq.cardId].colourRequirements.push([colourReq.colourId, colourReq.cost]); + + // TODO: As an array [classId, className] then + // card.classes[x][0] can be compared as numeric in code + } + + }); + + resolve(builtCards); + + }); + return dPromise; +} +// TODO: Add the effects onto the cards passed through +// Takes cards array (of obj), and array of effects (from DB or built, probabaly pre-built?) +function addCardsEffects(){} + +module.exports = { + requestDeck +}; diff --git a/database.js b/database.js index 5f5d6a7..723dba7 100644 --- a/database.js +++ b/database.js @@ -16,162 +16,7 @@ function disconnect(){ con.end(); } -// Is calling a promise in a promise the best way to do this? -// Keeping because this is how I've figured to call other funcs from database -function getCardById(cardId){ - - const dPromise = new Promise((resolve, reject) => { - - getCards(' card.id = '+cardId).then(cards => { - resolve(cards); - }) - .catch(err => { - throw err; reject(new Error(err)); - }); - - }); - return dPromise; -} - -function getCards(whereClause){ - const cPromise = new Promise((resolve, reject) => { - // Get main card info - let cards = []; - let sql = `SELECT - card.id AS cardId -- TEMP UNTIL UID - ,cardName - ,cardCost - ,typeName - ,cardAttack - ,rarityName - FROM card - LEFT JOIN type ON type.id = card.id - LEFT JOIN rarity ON rarity.id = card.cardRarity - `; - // TODO: Rewrite this so it's not so insecure!!!! - if(whereClause){ sql = sql + " WHERE "+whereClause; } - - con.query(sql, function (err, result, fields) { - if (err) { throw err; reject(new Error(err)); } - cards = dbResultToCards(result); - //console.log(cards); - resolve(cards); - }); - - // Now get the - // class - // colour requirements - // card effects - }); - return cPromise; -} - -// May want a FromDecks to get the SQL done in one blast -// then filter the results according? -function getCardsFromDeck(playerId, deckId){ - const cPromise = new Promise((resolve, reject) => { - if(playerId == null || deckId == null){ reject(new Error('Player/Deck Id not provided')); } - let cards = []; - // TODO: Change rarity, and maybe cardName to be the id (prolly card name for fancy effects) - // Then have a SQL loop at start of game that adds [name, id], [rariry, id] etc. - let sql = `SELECT - cardName - ,cardCost - ,typeName - ,cardAttack - ,rarityName - ,cardCount - FROM deck - INNER JOIN deck_cards ON deck_cards.deckId = deck.deckId AND deck_cards.playerId = deck.playerId - INNER JOIN card ON card.id = deck_cards.cardId - LEFT JOIN type ON type.id = card.id - LEFT JOIN rarity ON rarity.id = card.cardRarity`; - // TODO: Make more secure!! - sql += ' WHERE deck.deckId ='+deckId+' AND deck.playerId = '+playerId; - sql += ';'; - - con.query(sql, function (err, result, fields) { - if (err) { throw err; reject(new Error(err)); } - cards = dbResultToCards(result); - resolve(cards); - }); - - // Card list has been built - // Resolve/return the data of the cards Array/deck - - }); - return cPromise; -} - -// This may be more jank than I'd like. Was going procedure route, or JOINs -// but thinking of future effects that could have 4-5 effects, that check 2-3 colours -// and X amounts of cards/classes, to finally trigger something, those feel like they -// wouldn't work. So this is it for now, and when I think of something better I'll rewrite -function dbGetCardClasses(cardId){ - // Maybe this isn't too bad as async? - // But I imagine for lets say 100 1v1s 200 loops of 35cards each, eekers - - const cPromise = new Promise((resolve, reject) => { - if(cardId == null){ reject(new Error('cardId not provided')); } - let classes = []; - // Just getting the class IDs, as intend to load all the class data [id, name] into - // an array or file on game load. This way just the ID is needed, and no text compares - let sql = `SELECT classId - FROM card_class - WHERE cardId = `+cardId - ; // TODO: As all these SQL, need to make more secure - - con.query(sql, function (err, result, fields) { - if (err) { throw err; reject(new Error(err)); } - result.forEach((card) => { - // Add the classId to array to be used in card buildery doodad - classes.push(result[0]); - }); - }); - - // Card list has been built - // Resolve/return the data of the cards Array/deck - resolve(classes); - - }); - return cPromise; -} - -// Don't really want this in DB layer, but it wouldn't play -// in the main server file, so it's here until I figure out why -function dbResultToCards(result){ - let cards = []; - result.forEach((card) => { - let tempCard = {}; - tempCard.id = card.cardId; - tempCard.name = card.cardName; - tempCard.colour = null; - tempCard.cost = card.cardCost; - tempCard.type = card.typeName; - tempCard.atk = card.cardAttack; - tempCard.rarity = card.rarityName; - tempCard.effect = null; - // TODO: Will need more SQL statements, or some function/procedure - // class - // colour requirements - // card effects - - // Add the 'completed' cards into the deck - if(card.cardCount){ - // Add as many cards into the deck as is in cardCount - for(let i = 0; i < card.cardCount; i++){ - cards.push(tempCard); - } - }else{ - // Or just one - cards.push(tempCard); - } - }); - return cards; -} - - -// Testing, and trialing rewrites +// My DB stuffs function dbGetDecks(){ const dPromise = new Promise((resolve, reject) => { let cards = []; @@ -265,9 +110,6 @@ function dbGetCardColourRequirement(){ module.exports = { connect, disconnect - , getCards - , getCardById - , getCardsFromDeck // Testing, and trailing , dbGetDecks , dbGetDeckList diff --git a/public/board.js b/public/board.js index 30dcc15..9433fbe 100644 --- a/public/board.js +++ b/public/board.js @@ -8,6 +8,13 @@ const cardHeight = 120; const cardArt = new Image(); const cardBackArt = new Image(); +// Colours +const COLOUR = { + 'white': 1, + 'blue': 2, + 'red': 3, +}; + // Counters to keep track of players, and boardElements, may be changed in future // But once game starts, will be const anyway, so shouldn't need passing let players = 2; // Player, Opponent for now, but will be up to 6 players for 5v1 boss raids? @@ -28,6 +35,10 @@ let player = {}; let listPosition = {}; let cardFace = {}; let cardSprite = {}; +let deckIn = {}; // NEW, may be used, for now player substitute +let deckData = {}; +let cardAttack = {}; // TODO: add to the logic +let cardColours = {}; let inEvent = null; // To disable drawing each time something changes @@ -141,9 +152,9 @@ class Board{ let fill = null; if(boardElement[itemKey] != 'realDeck'){ // TODO: Change these to isset checks instead... let colourId = cardData[itemKey].colour; - if(colourId == 0){ fill = '#EEE'; } - else if(colourId == 1){ fill = '#0033EE'; } - else if(colourId == 2){ fill = '#ED344A'; } + if(colourId == COLOUR.white){ fill = '#EEE'; } + else if(colourId == COLOUR.blue){ fill = '#0033EE'; } + else if(colourId == COLOUR.red){ fill = '#ED344A'; } }else{ fill = '#B0B0B0'; } @@ -807,14 +818,13 @@ let board = new Board; // At least I think, for now. May need more stuff in future // Request the deck(s) from server/db -requestDeck(); +// When the page loads (for now) start the game +requestStartGame(); // This will trigger an emit to and on from the server will trigger loadBoard(); -// public/main.js -// socket.on('responseGetDeck', function (data) { +// public/main.js // socket.on('responseStartGame', function (data) { function loadBoard(data) { - console.log('DATA'); console.log(data); // Built the original boardstate using the data passed from server @@ -826,91 +836,57 @@ function loadBoard(data) { cardData = data.cardData; // position; // Not to do, as calculated per client (and will be per screen-size) // size; // Like position, calculated per client. - //cardStatus; // TODO: ? Statuses on load?, TODO: OR if player DCs and needs to reload! - player = {}; // TODO: Set each item to correct player - listPosition = {}; // TODO: - cardFace = {}; // TODO: Like status, for DCs and reloads + cardStatus = data.cardStatus; + player = data.player; // Set each item to correct player + listPosition = data.listPosition; + cardFace = data.cardFace; // Like status, for DCs and reloads + cardSprite = {}; // TODO: ? Maybe, or this could be done clientside based // ^ on card id? - cardFace = {}; // TODO: For DB/Reconnect more-so - let deckIn = data.deckIn; // NEW, may be used, for now player substitute - let deckData = data.deckData; - + deckIn = data.deckIn; // NEW, may be used, for now player substitute + deckData = data.deckData; + cardAttack = data.cardAttack; // TODO: add to the logic + cardColours = data.cardColours; - // Stuff not currently in the getDecks TODO: To be stripped down and - // rewritten/made into generateItems or something, for each match - // Decks can be as-is for getting deckLists for deckselection, but - // TODO: for matches need something to generate and return every item, and attribute - - console.log(cardData[0]); - - // Temp solution - // Loop all items, and set their related missing attributes - // TODO: some more jank to get it 'playable' with DB entries - let player0DeckCount = 0; - let player1DeckCount = 0; + // TODO: JANK IN, CHANGE CODE TO USE NEW ARRAY!! + // Temp jank, set colour to first colour req. for(let i = 0; i < itemCount; i++){ - // Set player for decks (to just be their array index for now) TODO: Properly - if(boardElement[i] == 'realDeck'){ - player[i] = i; // Jank... - //player[i] = deckData[i]; - //console.log(deckData[i]); - }else{ - // Everything else missing in a jank fashion - player[i] = deckIn[i]; // 1-1 here, but likely not in future - } - - cardStatus[i] = null; // Just building on first load, as deck gen - // substite at the mo, so all cards are free of status - cardFace[i] = 0; // As above, deck gen sub. Everything starts facedown - - // Set position for cards in each deck... - // TODO: JANK, tidy, remove, make good!! - // using the new player set just above, which in itself is jank - if(player[i] == 0 && boardElement[i] != 'realDeck'){ - listPosition[i] = player0DeckCount+1; - player0DeckCount++; - } - // Just an else, as in DB/for this test only 2 decks - // TODO: MAKE BETTER! - else{ - listPosition[i] = player0DeckCount+1; - player0DeckCount++; - } - - // TODO: JANK IN, CHANGE CODE TO USE NEW ARRAY!! - // Temp jank, set colour to first colour req. - if(cardData[i] !== undefined){ // i may not have carddata, as realDeck - cardData[i].colour = 0;//cardData[itemCount].colour[0]; + console.log(itemCount); + console.log(cardColours[itemCount]); + // after && to check if there is a colourReq record in cardColours + if(cardData[i] !== undefined && i in cardColours){ // i may not have carddata, as realDeck + console.log(i); + cardData[i].colour = cardColours[i][0][0]; // Colour Id of first req // Set the artwork (this would be done front-end I think) cardSprite[i] = [0,0]; // Temp sprite set based on colour TODO: Change to set correct sprite from DB switch (cardData[i].colour){ - case 0: // White + case COLOUR.white: // White cardSprite[i] = [0,0]; break; - case 1: // Blue + case COLOUR.blue: // Blue cardSprite[i] = [0,1]; break; - case 2: // Red + case COLOUR.red: // Red cardSprite[i] = [1,0]; break; - case 3: // Green + case COLOUR.green: // Green cardSprite[i] = [1,1]; break; default: break; } } - } + // Stuff not currently in the getDecks TODO: To be stripped down and + // rewritten/made into generateItems or something, for each match + // Decks can be as-is for getting deckLists for deckselection, but + // TODO: for matches need something to generate and return every item, and attribute + + // TODO: Loop these for the amount of players in players (and use the correct itemKey) shuffleDeck(0); // Shuffle player 0 deck shuffleDeck(1); // Shuffle player 1 deck - //return false; - // Fill each players deck with their cards - //createDecks(); - // Play shield from top of each players deck to the maximum allowed (4 typically) for(let currentPlayer = 0; currentPlayer <= players-1; currentPlayer++){ board.playShield(1, 'deck', currentPlayer, maxShield); @@ -1003,6 +979,8 @@ canvas.addEventListener('click', function(event) { if(boardElement[itemKey] == 'deck'){ continue; }else{ + // Will print everything that's clickable (or should be clickable) + // any undefined attributes and such should display with this too //printECSData([itemKey]); } @@ -1113,79 +1091,6 @@ function clickableCheck(cursorX,cursorY,itemKey){ } -function createDecks(){ - // Create a 'realDeck' element for each player - for(let i = 0; i < players; i++){ - item.push(itemCount); - boardElement[itemCount] = 'realDeck'; - player[itemCount] = i; - // TODO: Added these in to prevent error - // In future want to remove, and add isset checks for non-used data - cardStatus[itemCount] = null; - listPosition[itemCount] = null; - cardFace[itemCount] = 0; // Deck is facedown, as there's no cardArt - itemCount++; - } - - // Loop again and create the deckLists - for(let i = 0; i < players; i++){ - createDeckList(i); - } - -} -// TODO: USE DATABASE FOR THIS!!! -function createDeckList(playerId){ - // TODO:Create the deckList by loading the deckDB - // For now pulling from a deckList array that uses a cardArray - let deckList = null; - if(playerId == 0){ deckList = deckListPlayer; } - if(playerId == 1){ deckList = deckListOpponent; } - - for(let deckItem = 0; deckItem < deckList.length; deckItem++){ - // Create new item for ECS - item.push(itemCount); - // Set card data for new item - // Use the ID from the deckList of player, and return the card of that ID from cardList/DB - // For now, needs to be -1, as ID1 in decklist refers to ID 1 of carlist which is INDEX 0 - // TODO: Make it use the cardList ID, not the index - cardData[itemCount] = cardArray[deckList[deckItem]-1]; - // Set to base position of 'deck' - boardElement[itemCount] = 'deck'; - // Set Attack, ManaCost, ManaColours TODO: When these are implemented seperately - - // Set the player - player[itemCount] = playerId; - // Set the position in the deck (as was added), will be shuffled on game start - listPosition[itemCount] = deckItem+1; - cardFace[itemCount] = 0; // Start with all cards face down - cardSprite[itemCount] = [0,0]; - // Temp sprite set based on colour TODO: Change to set correct sprite from DB - console.log(cardData[itemCount].colour); - switch (cardData[itemCount].colour){ - case 0: // White - cardSprite[itemCount] = [0,0]; - break; - case 1: // Blue - cardSprite[itemCount] = [0,1]; - break; - case 2: // Red - cardSprite[itemCount] = [1,0]; - break; - case 3: // Green - cardSprite[itemCount] = [1,1]; - break; - default: - break; - } - - // Increment the itemCount to prevent overwriting stuff - itemCount++; - } - let cardsInDeck = board.getItems(null, playerId); - - shuffleDeck(playerId); -} - function shuffleDeck(playerId){ // Create a tempDeck array of same length of the player deck @@ -1271,21 +1176,18 @@ function calculateItemSizePosition(itemKey){ let i = listPosition[itemKey]; //console.log('cardName: '+cardData[itemKey].name+' listPosition/i: '+i); - + if(itemPlayer == 1 && itemElement == 'realDeck'){ positionX = 40; positionY = 60; width = cardWidth*1.5; height = cardHeight*1.5; - console.log('HELLO 1'); - } if(itemPlayer == 0 && itemElement == 'realDeck'){ positionX = canvas.width-cardWidth*1.5-40; positionY = canvas.height-cardHeight*1.5-60; width = cardWidth*1.5; height = cardHeight*1.5; - console.log('HELLO 0'); } if(itemPlayer == 1 && itemElement == 'board'){ diff --git a/public/index.html b/public/index.html index 265c1de..d0ccafc 100644 --- a/public/index.html +++ b/public/index.html @@ -16,6 +16,8 @@ + +
@@ -58,7 +60,8 @@
- + +
diff --git a/public/main.js b/public/main.js index 433553f..413e56d 100644 --- a/public/main.js +++ b/public/main.js @@ -165,24 +165,44 @@ socket.on('responseGetCards', function (data) { }); // Testing getting cards from server/DB -function requestDeck(){ - console.log('+ requestDeck'); - socket.emit('requestDeck'); +function requestStartGame(){ + console.log('+ requestStartGame'); + socket.emit('requestStartGame'); } -function responseGetDeck(data){ - console.log(data); +function responseStartGame(data){ + if(!data.success){ + alert(data.message); + return false; + } + return data; +} +socket.on('responseStartGame', function (data) { + console.log('<< responseStartGame'); + responseStartGame(data); + if(data.success !== true){ + alert('Err with responseStartGame. '+data.message); + } + // Pass only the data to loadBoard + loadBoard(data.message); +}); + +// More experimenty +function requestRoomGeneration(){ + console.log('+ requestRoomGeneration'); + socket.emit('requestRoomGeneration'); +} +function responseRoomGeneration(data){ if(!data.success){ alert(data.message); return false; } return data; } -socket.on('responseGetDeck', function (data) { - console.log('<< responseGetDeck'); - responseGetDeck(data); - console.log('Load board?'); +socket.on('responseRoomGeneration', function (data) { + console.log('<< responseRoomGeneration'); + responseStartGame(data); if(data.success !== true){ - alert('Err with responseGetDeck. '+data.message); + alert('Err with responseRoomGeneration. '+data.message); } // Pass only the data to loadBoard loadBoard(data.message); diff --git a/roomMod.js b/roomMod.js new file mode 100644 index 0000000..8223345 --- /dev/null +++ b/roomMod.js @@ -0,0 +1,71 @@ +// Build a room, fill will players, etc. +const cardGen = require('./cardGen'); + + +// Room should, setPlayers, add them to correct team (TODO), build their decks, and first shuffle + +function startItemCount(){ + let item = []; + let itemCount = 0; + + returns = {'item': item, 'itemCount': itemCount}; + return(returns); +} +function setPlayers(playerCount = 2, itemData){ + // Add new item attribute for 'players' + // TODO: Maybe check if exists, and add to in that case (for replacing people?) + // Doubt that would ever make it into the game, but could still be worth + itemData.player = {}; //let player = {}; // Player item belongs to + itemData.players = []; // List of the players (an associated playerItem?) + //itemData. + + // Can be done with just referring to itemData[x], but less readable + // Will likely redefine vars each new function. For now will keep this as-is + + let playerNo = 0 + itemData['itemCount']; // Start loop from current itemCount + playerCount = playerCount + itemData['itemCount']; // End at playerCount diff from itemCount + for(playerNo; playerNo < playerCount; playerNo++){ + // REMOVED PLAYERS AS ITEM, BECAUSE THEY WERE BORKING + // TODO: Add back at some point, or don't bother + //itemData['item'].push(playerNo); + //itemData['player'][itemData['itemCount']] = playerNo; // The player belongs to itself + //itemData['itemCount']++; + itemData['players'].push(playerNo); // Add player no to array so can be looped + } + // Return related item and item attributes + //returns = {'item': item, 'itemCount': itemCount, 'player': player}; + return itemData; + //return([item, itemCount, player]); +} + +// For future, when 2v2s, and 5v1 Raids, etc. +function setTeams(){ + +} + +function roomGeneration(playerCount, teamCount = null, playerTeams = null){ + return new Promise((resolve, reject) => { + (async () => { + let itemData = startItemCount(); + + // Create 2 players for the room + itemData = setPlayers(2, itemData); + + // TODO: Get their selected decks (will need to pass somewhere) + + // Generate the decks, and card within the deck cardLists + [itemData] = await Promise.all([ cardGen.requestDeck(itemData) ]); + + //console.log('deckData'); + //console.log(deckData); + return resolve(itemData); + })() + }); +} + +module.exports = { + startItemCount + ,setPlayers + ,roomGeneration +}; + diff --git a/server.js b/server.js index cf3e629..70889cb 100644 --- a/server.js +++ b/server.js @@ -1,6 +1,8 @@ const express = require('express'); const database = require('./database'); +const cardGen = require('./cardGen'); +const roomMod = require('./roomMod'); const app = express(); const http = require('http').Server(app); const port = process.env.PORT || 3000; @@ -39,425 +41,50 @@ for (let roomId = 1; roomId <= numRoomsToPreGen; roomId++) { createRoom(roomId); } +// For testing to see console logs +roomMod.roomGeneration(2); +//cardGen.requestDeck(); -// For testing DB/makeshift ORM/building my objects -// Maybe if this works, put it into a deck and card module -// Doing similar to I have to other stuff, that will need to be -// migrated serverside anyways. - -/* -let item = []; -let deckItem = {}; -let deckList = {}; -let cardItem = {}; -*/ - -// cardClass, cardColourRequirement -// may want to be seperate too (as well as in cardItem), so that -// during match they can be altered by effects while keeping the OG card -// for inspecting (and compare against new stats,reqs,etc.) -// same with attack, cost, etc. things that will can be visually shown as -// changed in game - -// Just grabbing everything from DB for now, as it's quite small at current -// then will rejig when needed. -// Should all cards, effects, classes etc. be loaded in on server start -// then just load decks and decklists when needed? -function getDecks(deckId, playerId, item = [], deckItem = {}){ - // Await promise, once it's done get the data, if errors send err - const dPromise = new Promise((resolve, reject) => { - - database.dbGetDecks().then(data => { - - let decks = []; - - data.forEach((deck) => { - //let itemId = item.length; - //item.push(itemId); // Add the next available item - // Add the deck info to deckItem - // deckItem[itemId] = {}; - decks.push({ - 'deckId': deck.deckId, - 'playerId': deck.playerId, - 'deckName': deck.deckName, - }); - }); - - // Resolve the decks pulled - resolve(decks); - - //console.log(item); - //console.log(deckItem); - //console.log(decks); - }) - .catch(err => { throw err; }) - - }); - return dPromise; -} -//getDecks(); -function getDeckList(){ - const dPromise = new Promise((resolve, reject) => { - - database.dbGetDeckList().then(data => { - - let deckList = []; - - data.forEach((listItem) => { - // Add the deck info to deckItem - deckList.push({ - 'deckId': listItem.deckId, - 'playerId': listItem.playerId, - 'cardId': listItem.cardId, - 'cardCount': listItem.cardCount, - }); - resolve(deckList); - }); - }) - .catch(err => { throw err; }) - }); - return dPromise; -} -//getDeckList(); -function getCards(){ - const dPromise = new Promise((resolve, reject) => { - - database.dbGetCards().then(data => { - - let cards = []; - - data.forEach((card) => { - cards.push({ - 'id': card.id, - 'cardName': card.cardName, - 'cardCost': card.cardCost, - 'cardType': card.cardType, - 'cardAttack': card.cardAttack, - 'cardRarity': card.cardRarity, - 'cardClass': [], - 'cardColourRequirement': [], - }); - }); - resolve(cards); - }) - .catch(err => { throw err; }) - - }); - return dPromise; -} -//getCards(); -function getCardClasses(){ - const dPromise = new Promise((resolve, reject) => { - database.dbGetCardClasses().then(data => { +function requestStartGame(socket){ + response = {success: false, message: 'Failed requestStartGame() server.js'}; + cardGen.requestDeck().then(data => { + response.success = true; + response.message = data; + io.to(socket.id).emit('responseStartGame', response); - let cardClasses = []; - - data.forEach((cardClass) => { - - cardClasses.push({ - 'cardId': cardClass.cardId, - 'classId': cardClass.classId, - }); - - }); - resolve(cardClasses); - }) - .catch(err => { throw err; }) - }); - return dPromise; -} -//getCardClasses(); -function getCardColourRequirement(){ - const dPromise = new Promise((resolve, reject) => { - database.dbGetCardColourRequirement().then(data => { - - let colourRequirements = []; - - data.forEach((cardColourReq) => { - - colourRequirements.push({ - 'cardId': cardColourReq.cardId, - 'colourId': cardColourReq.colourId, - 'cost': cardColourReq.cost, - }); - - }); - resolve(colourRequirements); - - }) - .catch(err => { throw err; }) - }); - return dPromise; -} -//getCardColourRequirement(); -// Then effects which will have effects with parent triggers, and unit type checks -// colour checks, all sorts. So will be more difficult. Basic (flight, etc) -// shouldn't be too bad -// something like effect_basic, card_effect, effect_trigger, effect_requirement, effect_option -// effect_stage, effect_advanced, with advanced being an id with x triggers, triggers have -// x req, advanced as with x options, x stages to be able to fine-tune fancy stuff -// combining all other effects, units, cards, colours, etc. Will be a lot of though, -// but better than hard coding anything more than basic effects and effect check logic -// TODO: effect (as above) - -// request a deck in the format CURRENTLY used in the game -// decks will likely be changed around - -// https://www.geeksforgeeks.org/how-to-wait-for-multiple-promises-in-javascript/ -// https://medium.com/@nikolozz/using-socket-io-with-async-await-13fa8c2dc9d9 -// using last example -function requestDeck(socket, playerId, deckId){ - return new Promise((resolve, reject) => { - (async () => { - - // Get the deck(s) requested. - // Not 100% on how to do two differening atm - // Besides all of playerId, and all of deckId. But 1,2/2,3 for instance - const [decks, deckList] = - await Promise.all([ - getDecks(), - getDeckList() - // Don't need to do this for each when awaiting all - // Instead it'll wait until each is done, set the data to the const - // Then do the stuff after the variable's are set!! - /* - getDecks().then(data => { - //console.log(data); - }).catch(err => { throw err; }), - */ - - ]); - - //console.log(decks); - //console.log(deckList); - // Now loop the deckList for the cardIds - let deckCardIds = []; - deckList.forEach((deckItem) => { - deckCardIds.push(deckItem.cardId); - - }); - //console.log(deckCardIds); - - // Next, get the cards in the deck by their ID - // TODO: https://stackoverflow.com/a/65510676 - // Change SQL to accept for just the cards passed - - // Get each cards data, colourReqs, and classes - const [cards, cardClasses, cardColourRequirements] = - await Promise.all([ - getCards(), - getCardClasses(), - getCardColourRequirement(), - ]); - // ^^^^ Classes async? Can pass the cardsIds, then loop classes, if the class cardId - // matches the cardId in cards[] then push the class to cards[x].classes - - // TODO: Build the card so far async for it's done alongside getting effects? - // Then when both done add the effects to the existing cardObjects? Yep good idea me - //console.log(cards); - //console.info(cardClasses); - //console.log(cardColourRequirements); - - const [builtCards] = - await Promise.all([ - buildCards(cards, cardClasses, cardColourRequirements), - // TODO: builtEffects - ]); - - //console.log(builtCards); - // TODO: addEffectsToCards - // TODO: Finally do the effects for each card. This will be the complicater - // since it's not 100% thought out yet... ( - - // Add the cards (x of how many cardObjects with cardId are in count in decklist) - // to a deck array to pass through. Or decks array with deckId specified? - //console.log(deckList); - // These are the four basic fellas for this - // from public/board.js where current game testing/logic is applied - // So creating as a test for a 'room' to see if it's simple enough to - // send and play with - // BUILD THE DATA TO SEND TO CLIENTS! (Kinda) - let item = []; - let itemCount = 0; - let deckData = {}; // New but may be useful - let deckIn = {}; // Which deck the item is in? Kinda the player/boardelement thing? - let cardData = {}; - let boardElement = {}; - // TODO: Set the player. For now will do this in front-end as testing currently - - // Loop and create the deck first - //console.log(decks); - decks.forEach((deck) => { - item.push(itemCount); // Add new item to add stuff for - deckData[itemCount] = {'deckId':deck.deckId, 'playerId':deck.playerId, 'deckName':deck.deckName}; - boardElement[itemCount] = 'realDeck'; - itemCount++; }) - console.log(deckData); - console.log(deckList); - - // Loop each item in the deckList - // Loop inside it X times where X is cardCount - // Add the builtCard with same cardId as deckList item X times - deckList.forEach((deckListItem) => { - - let deckItem = null; - // Loop each deck, if the deck/playerIds match, add association - for(key in deckData){ - //Object.keys(deckData).forEach(function(key) { // Less efficient than for - // Needs to check deck AND player id, as that's the primary key (together) - if(deckData[key].deckId == deckListItem.deckId && deckData[key].playerId == deckListItem.playerId){ - deckItem = key; // Key is the `item` key - } - - }; - // For each new card, loop to the cardCount (how many cards in deck) - // and add to the deck - for(let i = 0; i < deckListItem.cardCount; i++){ - item.push(itemCount); // Add new item to add stuff for - // ^ not using item.length incase anything in future gets deleted - // from item for instance - cardData[itemCount] = builtCards[deckListItem.cardId]; // builtCards id set to cardId from DB, so adding the builtCard object is based on the deckList's cardId (from DB) - boardElement[itemCount] = 'deck'; // Add all cards to deck at match start - // Associate the card with the deck - // TODO: Change deckIn to something more sensical - deckIn[itemCount] = deckItem; - itemCount++; // Increment item to not overwrite - } - - }); - - // item, itemCount, deckData, cardData, boardElement - //console.log(cardData); - - // Returning everything to be looped in the front-end - // This won't be looped as it will at final, instead just for deck generation - // Returned as object over array, as easier to disect when gets to front-end - let dataReturn = { - item: item, - itemCount: itemCount, - deckData: deckData, - deckIn: deckIn, - cardData: cardData, - boardElement: boardElement, - }; - return resolve(dataReturn); - //return resolve(cardData); - - // Promise stuff testing - })() + .catch(err => { + response.message = err; + io.to(socket.id).emit('responseStartGame', err); }); - - // TODO: In future the item inc. all the decks, cards, locations, and other attributes - // will come from here - - // TODO: unrelated, but thought. If lots of players on, generating cards each time - // will be a big hit, as well as all the DB hits, so may need to load all cardObjects - // into memory/array when server starts. This then prevents any DB changes to alter - // things, but outside of testing that won't occur(?), may need to test this at some - // point to see. For now DB, and generating is ok, as still working on it } -// For testing -requestDeck(); - -function requestDeckStart(socket){ - // For future: - // Don't try to use promises between server/client - // It's painful and doesn't appear to work. Instead, let client have a loading - // screen, or wait for a while before doing something that requires server data - // like loading the decks... Then have the socket.on clientside trigger - // whatever functions, and stuff needs to happen - // Wasted so much time trying to do async server-client stuff. Don't bother - response = {success: false, message: 'Failed requestDeckStart() server.js'}; - requestDeck().then(data => { +function requestRoomGeneration(socket){ + response = {success: false, message: 'Failed requestRoomGeneration() server.js'}; + roomMod.roomGeneration().then(data => { response.success = true; response.message = data; - io.to(socket.id).emit('responseGetDeck', response); - + io.to(socket.id).emit('responseRoomGeneration', response); }) .catch(err => { response.message = err; - io.to(socket.id).emit('responseGetDeck', err); + io.to(socket.id).emit('responseRoomGeneration', err); }); -} -function buildCards(cards, cardClasses, cardColourRequirements){ - - const dPromise = new Promise((resolve, reject) => { - - builtCards = {}; - - //console.log(cards); - // Loop the cards and build the base - cards.forEach((card) => { - - let builtCard = { - id: card.id, - name: card.cardName, - colour: [], - cost: card.cardCost, - costReq: [], - type: card.cardType, - atk: card.cardAttack, - rarity: card.cardRarity, - effect: [], - cardClass: [], - }; - // Give the card an easily accessible Id for compares - // and to add to the cardItem being built - builtCards[card.id] = builtCard; - - }); - //console.log(builtCards); - - // Next loop each class, and add to the new builtCard (if cardIds match) - cardClasses.forEach((cardClass) => { - - // Check the card exists (it should always, but don't want jank) - if(cardClass.cardId in builtCards){ - // It's the Id for now - // TODO: Send the text too (from join) but don't use for compares - //console.log(cardClass.cardId); - //console.log(cardClass.classId); - //console.log('---'); - //console.log(builtCards[cardClass.cardId]); - - // Add the class to the class array (cards can have multiple) - builtCards[cardClass.cardId].cardClass.push(cardClass.classId); - - // TODO: As an array [classId, className] then - // card.classes[x][0] can be compared as numeric in code - } - - }); - - resolve(builtCards); - - }); - return dPromise; } -// TODO: Add the effects onto the cards passed through -// Takes cards array (of obj), and array of effects (from DB or built, probabaly pre-built?) -function addCardsEffects(){} - -// End For testing function onConnection(socket){ console.log('+ User connected'); console.log(''); - socket.on('requestGetCards', function(deckId, playerId) { - requestGetCards(socket, deckId, playerId); - }); // New testing fella (working afaik) // TODO: request specific deckId/playerId (and multiples, i.e. get 6 decks at same // time, based on deckId/playerId combo. Maybe pass as array [deckId, playerId],[deck - socket.on('requestDeck', function() { - requestDeckStart(socket); + socket.on('requestStartGame', function() { + requestStartGame(socket); }); - socket.on("exampleEvent", (data) => { - io.emit("exampleEvent", "hello from server"); + socket.on('requestRoomGeneration', function() { + requestRoomGeneration(socket); }); socket.on('requestRooms', function(filter) { @@ -619,23 +246,3 @@ function requestJoinRoom(socket, playerName, roomId){ } -// Change to Decks, or 'getPlayerDecks' for the game -// Could also be a specific deck if player clicks into one? Although maybe when entering -// decks they get all their decks (maybe just id actually) then load from there? -function requestGetCards(socket, deckId, playerId){ - console.log(deckId); - console.log(playerId); - let response = {'success':false, 'message':'Nothing happened'}; - response.success = true; - - // Await promise, once it's done get the data, if errors send err - database.getCardsFromDeck(deckId, playerId).then(data => { - console.log(data); - response.message = data; - io.to(socket.id).emit('responseGetCards', response); - }) - .catch(err => { - response.message = err; - io.to(socket.id).emit('responseGetCards', err); - }) -}