Merge branch 'feature/serverSideItems' into develop

develop
Nathan Steel 1 year ago
commit 90de381d69

@ -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
};

@ -16,162 +16,7 @@ function disconnect(){
con.end(); con.end();
} }
// Is calling a promise in a promise the best way to do this? // My DB stuffs
// 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
function dbGetDecks(){ function dbGetDecks(){
const dPromise = new Promise((resolve, reject) => { const dPromise = new Promise((resolve, reject) => {
let cards = []; let cards = [];
@ -265,9 +110,6 @@ function dbGetCardColourRequirement(){
module.exports = { module.exports = {
connect, disconnect connect, disconnect
, getCards
, getCardById
, getCardsFromDeck
// Testing, and trailing // Testing, and trailing
, dbGetDecks , dbGetDecks
, dbGetDeckList , dbGetDeckList

@ -8,6 +8,13 @@ const cardHeight = 120;
const cardArt = new Image(); const cardArt = new Image();
const cardBackArt = 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 // 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 // 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? 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 listPosition = {};
let cardFace = {}; let cardFace = {};
let cardSprite = {}; 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; let inEvent = null;
// To disable drawing each time something changes // To disable drawing each time something changes
@ -141,9 +152,9 @@ class Board{
let fill = null; let fill = null;
if(boardElement[itemKey] != 'realDeck'){ // TODO: Change these to isset checks instead... if(boardElement[itemKey] != 'realDeck'){ // TODO: Change these to isset checks instead...
let colourId = cardData[itemKey].colour; let colourId = cardData[itemKey].colour;
if(colourId == 0){ fill = '#EEE'; } if(colourId == COLOUR.white){ fill = '#EEE'; }
else if(colourId == 1){ fill = '#0033EE'; } else if(colourId == COLOUR.blue){ fill = '#0033EE'; }
else if(colourId == 2){ fill = '#ED344A'; } else if(colourId == COLOUR.red){ fill = '#ED344A'; }
}else{ }else{
fill = '#B0B0B0'; fill = '#B0B0B0';
} }
@ -807,14 +818,13 @@ let board = new Board;
// At least I think, for now. May need more stuff in future // At least I think, for now. May need more stuff in future
// Request the deck(s) from server/db // 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(); // This will trigger an emit to and on from the server will trigger loadBoard();
// public/main.js // public/main.js // socket.on('responseStartGame', function (data) {
// socket.on('responseGetDeck', function (data) {
function loadBoard(data) { function loadBoard(data) {
console.log('DATA');
console.log(data); console.log(data);
// Built the original boardstate using the data passed from server // Built the original boardstate using the data passed from server
@ -826,91 +836,57 @@ function loadBoard(data) {
cardData = data.cardData; cardData = data.cardData;
// position; // Not to do, as calculated per client (and will be per screen-size) // position; // Not to do, as calculated per client (and will be per screen-size)
// size; // Like position, calculated per client. // size; // Like position, calculated per client.
//cardStatus; // TODO: ? Statuses on load?, TODO: OR if player DCs and needs to reload! cardStatus = data.cardStatus;
player = {}; // TODO: Set each item to correct player player = data.player; // Set each item to correct player
listPosition = {}; // TODO: listPosition = data.listPosition;
cardFace = {}; // TODO: Like status, for DCs and reloads cardFace = data.cardFace; // Like status, for DCs and reloads
cardSprite = {}; // TODO: ? Maybe, or this could be done clientside based cardSprite = {}; // TODO: ? Maybe, or this could be done clientside based
// ^ on card id? // ^ on card id?
cardFace = {}; // TODO: For DB/Reconnect more-so deckIn = data.deckIn; // NEW, may be used, for now player substitute
let deckIn = data.deckIn; // NEW, may be used, for now player substitute deckData = data.deckData;
let 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;
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!! // TODO: JANK IN, CHANGE CODE TO USE NEW ARRAY!!
// Temp jank, set colour to first colour req. // Temp jank, set colour to first colour req.
if(cardData[i] !== undefined){ // i may not have carddata, as realDeck for(let i = 0; i < itemCount; i++){
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) // Set the artwork (this would be done front-end I think)
cardSprite[i] = [0,0]; cardSprite[i] = [0,0];
// Temp sprite set based on colour TODO: Change to set correct sprite from DB // Temp sprite set based on colour TODO: Change to set correct sprite from DB
switch (cardData[i].colour){ switch (cardData[i].colour){
case 0: // White case COLOUR.white: // White
cardSprite[i] = [0,0]; cardSprite[i] = [0,0];
break; break;
case 1: // Blue case COLOUR.blue: // Blue
cardSprite[i] = [0,1]; cardSprite[i] = [0,1];
break; break;
case 2: // Red case COLOUR.red: // Red
cardSprite[i] = [1,0]; cardSprite[i] = [1,0];
break; break;
case 3: // Green case COLOUR.green: // Green
cardSprite[i] = [1,1]; cardSprite[i] = [1,1];
break; break;
default: default:
break; 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(0); // Shuffle player 0 deck
shuffleDeck(1); // Shuffle player 1 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) // Play shield from top of each players deck to the maximum allowed (4 typically)
for(let currentPlayer = 0; currentPlayer <= players-1; currentPlayer++){ for(let currentPlayer = 0; currentPlayer <= players-1; currentPlayer++){
board.playShield(1, 'deck', currentPlayer, maxShield); board.playShield(1, 'deck', currentPlayer, maxShield);
@ -1003,6 +979,8 @@ canvas.addEventListener('click', function(event) {
if(boardElement[itemKey] == 'deck'){ if(boardElement[itemKey] == 'deck'){
continue; continue;
}else{ }else{
// Will print everything that's clickable (or should be clickable)
// any undefined attributes and such should display with this too
//printECSData([itemKey]); //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){ function shuffleDeck(playerId){
// Create a tempDeck array of same length of the player deck // Create a tempDeck array of same length of the player deck
@ -1277,15 +1182,12 @@ function calculateItemSizePosition(itemKey){
positionY = 60; positionY = 60;
width = cardWidth*1.5; width = cardWidth*1.5;
height = cardHeight*1.5; height = cardHeight*1.5;
console.log('HELLO 1');
} }
if(itemPlayer == 0 && itemElement == 'realDeck'){ if(itemPlayer == 0 && itemElement == 'realDeck'){
positionX = canvas.width-cardWidth*1.5-40; positionX = canvas.width-cardWidth*1.5-40;
positionY = canvas.height-cardHeight*1.5-60; positionY = canvas.height-cardHeight*1.5-60;
width = cardWidth*1.5; width = cardWidth*1.5;
height = cardHeight*1.5; height = cardHeight*1.5;
console.log('HELLO 0');
} }
if(itemPlayer == 1 && itemElement == 'board'){ if(itemPlayer == 1 && itemElement == 'board'){

@ -16,6 +16,8 @@
<button onclick="requestCreateRoom()" style="background:#EEE">Create Room</button> <button onclick="requestCreateRoom()" style="background:#EEE">Create Room</button>
</div> </div>
<canvas id="canvas" width="1000" height="600"></canvas> <canvas id="canvas" width="1000" height="600"></canvas>
<hr>
<button onclick="untapAllZones()">Untap all</button> <button onclick="untapAllZones()">Untap all</button>
<button onclick="echoCards()">Print cardlist to console</button> <button onclick="echoCards()">Print cardlist to console</button>
<button onclick="echoCardsInDeck(0)">Print cardsInDeck for player0</button> <button onclick="echoCardsInDeck(0)">Print cardsInDeck for player0</button>
@ -58,7 +60,8 @@
<hr> <hr>
<div> <div>
<button onclick="requestGetCards()">Get Cards (DB)</button> <button onclick="requestStartGame()">(Re)start game</button>
<button onclick="requestRoomGeneration()">RequestRoomGeneration</button>
</div> </div>
<script src="/socket.io/socket.io.js"></script> <script src="/socket.io/socket.io.js"></script>

@ -165,24 +165,44 @@ socket.on('responseGetCards', function (data) {
}); });
// Testing getting cards from server/DB // Testing getting cards from server/DB
function requestDeck(){ function requestStartGame(){
console.log('+ requestDeck'); console.log('+ requestStartGame');
socket.emit('requestDeck'); socket.emit('requestStartGame');
} }
function responseGetDeck(data){ function responseStartGame(data){
console.log(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){ if(!data.success){
alert(data.message); alert(data.message);
return false; return false;
} }
return data; return data;
} }
socket.on('responseGetDeck', function (data) { socket.on('responseRoomGeneration', function (data) {
console.log('<< responseGetDeck'); console.log('<< responseRoomGeneration');
responseGetDeck(data); responseStartGame(data);
console.log('Load board?');
if(data.success !== true){ if(data.success !== true){
alert('Err with responseGetDeck. '+data.message); alert('Err with responseRoomGeneration. '+data.message);
} }
// Pass only the data to loadBoard // Pass only the data to loadBoard
loadBoard(data.message); loadBoard(data.message);

@ -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
};

@ -1,6 +1,8 @@
const express = require('express'); const express = require('express');
const database = require('./database'); const database = require('./database');
const cardGen = require('./cardGen');
const roomMod = require('./roomMod');
const app = express(); const app = express();
const http = require('http').Server(app); const http = require('http').Server(app);
const port = process.env.PORT || 3000; const port = process.env.PORT || 3000;
@ -39,425 +41,50 @@ for (let roomId = 1; roomId <= numRoomsToPreGen; roomId++) {
createRoom(roomId); createRoom(roomId);
} }
// For testing to see console logs
roomMod.roomGeneration(2);
//cardGen.requestDeck();
// For testing DB/makeshift ORM/building my objects function requestStartGame(socket){
// Maybe if this works, put it into a deck and card module response = {success: false, message: 'Failed requestStartGame() server.js'};
// Doing similar to I have to other stuff, that will need to be cardGen.requestDeck().then(data => {
// migrated serverside anyways. response.success = true;
response.message = data;
/* io.to(socket.id).emit('responseStartGame', response);
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 => {
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); .catch(err => {
console.log(deckList); response.message = err;
io.to(socket.id).emit('responseStartGame', err);
// 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
})()
}); });
// 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 function requestRoomGeneration(socket){
requestDeck(); response = {success: false, message: 'Failed requestRoomGeneration() server.js'};
roomMod.roomGeneration().then(data => {
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 => {
response.success = true; response.success = true;
response.message = data; response.message = data;
io.to(socket.id).emit('responseGetDeck', response); io.to(socket.id).emit('responseRoomGeneration', response);
}) })
.catch(err => { .catch(err => {
response.message = 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){ function onConnection(socket){
console.log('+ User connected'); console.log('+ User connected');
console.log(''); console.log('');
socket.on('requestGetCards', function(deckId, playerId) {
requestGetCards(socket, deckId, playerId);
});
// New testing fella (working afaik) // New testing fella (working afaik)
// TODO: request specific deckId/playerId (and multiples, i.e. get 6 decks at same // 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 // time, based on deckId/playerId combo. Maybe pass as array [deckId, playerId],[deck
socket.on('requestDeck', function() { socket.on('requestStartGame', function() {
requestDeckStart(socket); requestStartGame(socket);
}); });
socket.on("exampleEvent", (data) => { socket.on('requestRoomGeneration', function() {
io.emit("exampleEvent", "hello from server"); requestRoomGeneration(socket);
}); });
socket.on('requestRooms', function(filter) { 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);
})
}

Loading…
Cancel
Save