Merge branch 'feature/colourRequirements' into develop

develop
Nathan Steel 1 year ago
commit 34ffc136c9

@ -140,6 +140,27 @@ function getCardColourRequirement(){
});
return dPromise;
}
function getCardManaColour(){
const cPromise = new Promise((resolve, reject) => {
database.dbGetCardManaColour().then(data => {
let manaColours = [];
data.forEach((cardManaColour) => {
manaColours.push({
'cardId': cardManaColour.cardId,
'colourId': cardManaColour.colourId,
});
});
resolve(manaColours);
})
.catch(err => { throw err; })
});
return cPromise;
}
//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)
@ -187,11 +208,12 @@ function requestDeck(itemData = null){
// Change SQL to accept for just the cards passed
// Get each cards data, colourReqs, and classes
const [cards, cardClasses, cardColourRequirements] =
const [cards, cardClasses, cardColourRequirements, cardManaColours] =
await Promise.all([
getCards(),
getCardClasses(),
getCardColourRequirement(),
getCardManaColour(),
]);
// ^^^^ 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
@ -204,7 +226,7 @@ function requestDeck(itemData = null){
const [builtCards] =
await Promise.all([
buildCards(cards, cardClasses, cardColourRequirements),
buildCards(cards, cardClasses, cardColourRequirements, cardManaColours),
// TODO: builtEffects
]);
@ -262,6 +284,7 @@ function requestDeck(itemData = null){
let cardColours = {}; // Array of req.
let cardAttack = {}; // Base, Current
let cardSprite = {}; // Maybe from DB?
let cardManaColour = {}; // The card colour value when played in mana zone
// TODO: Set the player. For now will do this in front-end as testing currently
@ -320,6 +343,7 @@ function requestDeck(itemData = null){
cardStatus[itemCount] = null;
cardFace[itemCount] = 0; // Face down by default (in deck)
cardColours[itemCount] = cardData[itemCount].colourRequirements; // TODO;
cardManaColour[itemCount] = cardData[itemCount].cardManaColour;
// From cardData set the base attack, and current (same when deckbuild)
let atk = cardData[itemCount].atk;
cardAttack[itemCount] = [atk, atk]; // Base, Current
@ -346,6 +370,7 @@ function requestDeck(itemData = null){
itemData.cardColours = cardColours;
itemData.cardAttack = cardAttack;
itemData.cardSprite = cardSprite;
itemData.cardManaColour = cardManaColour;
// item, itemCount, deckData, cardData, boardElement
//console.log(cardData);
@ -371,7 +396,7 @@ function requestDeck(itemData = null){
// point to see. For now DB, and generating is ok, as still working on it
}
function buildCards(cards, cardClasses, cardColourRequirements){
function buildCards(cards, cardClasses, cardColourRequirements, cardManaColours){
console.log(cardColourRequirements);
const dPromise = new Promise((resolve, reject) => {
@ -388,6 +413,7 @@ function buildCards(cards, cardClasses, cardColourRequirements){
colour: [],
cost: card.cardCost,
colourRequirements: [],
cardManaColour: [], // Should probably be an object
type: card.cardType,
atk: card.cardAttack,
rarity: card.cardRarity,
@ -436,6 +462,17 @@ function buildCards(cards, cardClasses, cardColourRequirements){
});
// Add card mana colours (colour value when played in mana zone)
cardManaColours.forEach((manaCol) => {
// Check the card exists (it should always, but don't want jank)
if(manaCol.cardId in builtCards){
// Add the colours to the class array (cards can have multiple)
builtCards[manaCol.cardId].cardManaColour.push(manaCol.colourId);
}
});
resolve(builtCards);
});

@ -107,6 +107,22 @@ function dbGetCardColourRequirement(){
return dPromise;
}
function dbGetCardManaColour(){
// Get the classes assoc. on each card
const cPromise = new Promise((resolve, reject) => {
let sql = `SELECT
cardId
,colourId
FROM card_mana_colour
`;
con.query(sql, function (err, result, fields) {
if (err) { throw err; reject(new Error(err)); }
resolve(result);
});
});
return cPromise;
}
module.exports = {
connect, disconnect
@ -116,4 +132,5 @@ module.exports = {
, dbGetCards
, dbGetCardClasses
, dbGetCardColourRequirement
, dbGetCardManaColour
};

@ -0,0 +1,14 @@
USE `realms_divided`;
CREATE TABLE IF NOT EXISTS `card_mana_colour` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`cardId` int(11) DEFAULT NULL,
`colourId` tinyint(4) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
INSERT INTO `card_mana_colour` (`id`, `cardId`, `colourId`) VALUES
(1, 1, 3),
(2, 2, 1),
(3, 3, 2),
(4, 4, 1),
(5, 5, 3);

@ -10,9 +10,9 @@ const cardBackArt = new Image();
// Colours
const COLOUR = {
'white': 1,
'blue': 2,
'red': 3,
'white':{'id': 1, 'name':'White','colour':'#EEE'},
'blue':{'id':2, 'name':'Blue','colour':'#0033EE'},
'red':{'id':3, 'name':'Red','colour':'#ED344A'},
};
// Counters to keep track of players, and boardElements, may be changed in future
@ -39,6 +39,7 @@ let deckIn = {}; // NEW, may be used, for now player substitute
let deckData = {};
let cardAttack = {}; // TODO: add to the logic
let cardColours = {};
let cardManaColour = {};
let inEvent = null;
// To disable drawing each time something changes
@ -139,6 +140,50 @@ class Board{
}
}
setCardFill(itemKey){
let fill = '#B0B0B0';
let coloursOfCard = cardColours[itemKey];
if(coloursOfCard.length > 1){
// Create a gradient for the card colours
// x-start,y-start,x-end,y-end
const grad=ctx.createLinearGradient(
position[itemKey][0],
0, //position[itemKey][1],
position[itemKey][0] + size[itemKey][0],
0, //position[itemKey][1] + size[itemKey][1]
);
for(let i = 0; i < coloursOfCard.length; i++){
let gradientPos = 0;
if(coloursOfCard.length == i){
gradientPos = 1;
}else{
// Colour stops from 0..1 (0 to 100% along)
// If 4, first is 0, second is 100/3 * 1 = 33.33%
// 0,33,66,100. Need last to always be 100 (1)
gradientPos = (coloursOfCard.length - 1)*i;
}
// TODO: Make new array ensuring colours are ordered by highest
// cost. This will do as it adds the colours, but in future
grad.addColorStop(gradientPos, this.setFillByManaColour(cardColours[itemKey][i][0]));
}
fill = grad;
}else{
// Set to a normal fill of the first (only) colour
fill = this.setFillByManaColour(cardColours[itemKey][0][0]);
}
return fill;
}
printCardToCanvas(itemKey){
// If the itemKey has cardData, position, size, and boardElement we can draw it
// TODO: Add a check for error handling
@ -148,23 +193,18 @@ class Board{
if(this.isTapped(itemKey)){border = '#E0BC00';}
if(this.isAttacking(itemKey)){border = '#C92D22';}
// Set the card 'cardboard' colour based on the card colour type
let fill = null;
if(boardElement[itemKey] != 'realDeck'){ // TODO: Change these to isset checks instead...
let colourId = cardData[itemKey].colour;
if(colourId == COLOUR.white){ fill = '#EEE'; }
else if(colourId == COLOUR.blue){ fill = '#0033EE'; }
else if(colourId == COLOUR.red){ fill = '#ED344A'; }
}else{
fill = '#B0B0B0';
}
let name = itemKey; // Not needed really anymore, but keeping for now
let positionX = position[itemKey][0];
let positionY = position[itemKey][1];
let width = size[itemKey][0];
let height = size[itemKey][1];
// Set the card 'cardboard' colour based on the card colour type
let fill = '#B0B0B0';
if(boardElement[itemKey] != 'realDeck'){ // TODO: Change these to isset checks instead...
fill = this.setCardFill(itemKey);
}
// Draw the card shape itself
let shape = new Shape({
name: name,
@ -181,6 +221,7 @@ class Board{
if(this.isFaceUp(itemKey)){
this.addCardImage(itemKey);
this.printCardDetails(itemKey);
this.printColourRequirements(itemKey);
}
if(!this.isFaceUp(itemKey)){
this.addCardBack(itemKey);
@ -213,6 +254,22 @@ class Board{
ctx.fillStyle = '#000';
ctx.fillText(deckLength, textx, texty);
}
// If the item is a mana, draw the mana colour within it
// Temp solution, but works for UI and testing
if(boardElement[itemKey] == 'mana'){
let manaFill = this.setFillByManaColour(cardManaColour[itemKey][0]);
// TODO: Seperate this into a 'drawMana' or something?
let manaColour = new Shape({
shape: 'circle',
//name: 'deckCountererer',
x: positionX + width/2,
y: positionY + height/2,
width: width*.75,
height: height*.75,
fillStyle: manaFill // Fill should be the card's main colour
});
manaColour.draw();
}
}
isFaceUp(itemKey){
if(cardFace[itemKey] == 1){
@ -220,11 +277,17 @@ class Board{
}
return false;
}
flipCard(itemKey){
if(cardFace[itemKey] == 1){
flipCard(itemKey, direction = null){
// TODO: Add a third param for 'triggerEffects', for instance
// flipping up from deck to hand, or shield to hand shouldn't trigger
// MOST TIMES
if(cardFace[itemKey] == 1 && (direction == null || direction == 0)){
cardFace[itemKey] = 0;
}else{
return true;
}
if(cardFace[itemKey] == 0 && (direction == null || direction == 1)){
cardFace[itemKey] = 1;
return true;
}
// TODO:Activate any flip effects, etc.
}
@ -283,6 +346,78 @@ class Board{
// TODO: CardBack/Sleeves as spritesheet like cardArt
ctx.drawImage(cardBackArt, 0,0, 80,120,positionX,positionY,width,height);
}
printColourRequirements(itemKey){
// Set the size(s)
let width = size[itemKey][0]*.1;
let height = size[itemKey][1]*.1;
let positionX = position[itemKey][0] + (size[itemKey][0]*.1);
let positionY = position[itemKey][1] + (size[itemKey][0]*.2);
// Get all the colour reqs needed.
let colourReqs = cardColours[itemKey];
// Loop them all
let totalColours = 1; // How many colours the card has, for positioning
colourReqs.forEach((colour) => {
// Add each to the card (colour BG, and required count within it)
// TODO: Maybe building a class for each draw is bad, eh.
// TODO: Should probably change the shapes.js class into functions
// Will look into once animations, etc are in, incase classes are the way
// TODO: change colours to have id(done), name, and hexcode as const/enum
let colourId = colour[0];
let fill = this.setFillByManaColour(colourId);
// Draw the mana shape/icon
let manaColour = new Shape({
shape: 'circle',
//name: 'deckCountererer',
x: positionX,
y: positionY + height*totalColours,
width: width,
height: height,
fillStyle: fill // Fill should be the card's main colour
});
manaColour.draw();
// Draw the requirement within the shape/icon
// Make sure the text scales with card size
let fontSize = (width*10/cardWidth)*10; // 10 = baseFontSize of 10pt
// ^ calced with the width of the card overall
context.font = fontSize+"pt Arial";
// Get a constrating B/W to the background and set text to that
context.fillStyle = invertColour(fill, true);
// Center the text (cost) within the shape
this.printCenterText(colour[1], positionX, positionY + height*totalColours);
// Reset context stylings
context.font = "10pt Arial"; // Reset to default
context.fillStyle = "#000";
// Inc totalColours in case there's more
totalColours++;
});
}
setFillByManaColour(colourId){
for (const colour in COLOUR) {
if(colourId == COLOUR[colour]['id']){
return COLOUR[colour]['colour']; // Lmao
}
}
return '#B0B0B0';
}
printCenterText(text, positionX, positionY){
context.textAlign = 'center';
context.textBaseline = 'middle';
context.fillText(text, positionX, positionY);
// Now reset the context aligns to the defaults
context.textAlign = 'left';
context.textBaseline = 'alphabetic';
}
printCardDetails(itemKey){
let name = itemKey; // Not needed really anymore, but keeping for now
let positionX = position[itemKey][0];
@ -523,20 +658,12 @@ class Board{
alert(canPayMana);
return false;
}else{
// Tap mana and play TODO: TEMP SOLUTION
// TODO: May want to make this recursive, it's better and less bulky
// but still lots of dupe code
let items = this.getItems('mana', fromPlayer, null, null);
for(let item = 0; item < items.length; item++){
let mana = items[item];
// For now just tapping the first untapped mana
// TODO: Tap mana of correct colour (also allow player to select in fut)
if(!this.isTapped(mana) && cardData[mana].colour == cardData[itemKey].colour){
this.tapCard(mana);
break; // Temp TODO: Tap each mana needed until manacost/colours are met, also TODO: allow user to target own mana OR use non-multitypes first (not implemented yet)
}else{
continue;
}
// TODO: May be best to tap in canPayMana while looping reqs. and if the reqs aren't hit untap the mana that were tapped in that call?
// This atm will loop manaReq and tap.
let tapped = this.tapManaRequired(itemKey, fromPlayer);
if(tapped != true){
alert('tapManaRequired: '+tapped);
return false;
}
}
}
@ -548,6 +675,117 @@ class Board{
this.drawBoard();
}
tapManaRequired(itemToPayCost, playerId){
// TODO: Look at combining or normalising this and canPayMana()
let manaRequired = this.getManaTotalOf(itemToPayCost);
let noManaReq = {1:0, 2:0, 3:0, 4:0};
let manaToTap = [];
// Loop all the mana
let items = this.getItems('mana', playerId, null, null);
for(let item = 0; item < items.length; item++){
let mana = items[item];
// Deselect the mana at the start to ensure it's not doopid
this.deselectCard(mana);
if(this.isTapped(mana)){
continue;
}
let colourId = cardManaColour[mana][0];
console.log(JSON.stringify(manaRequired[0]));
// Loop the requirements of the cost to pay
for (const manaColour in manaRequired[0]) {
//console.log(manaColour+' '+manaRequired[manaColour]+', '+colourId);
// If the colour of the mana is in the requirements
// of the cost to pay, reduce the cost for that colour by
// 1 and tap the mana
if(manaColour == colourId && manaRequired[0][manaColour] > 0){
// Reduce the required mana
manaRequired[0][colourId]--;
manaToTap.push(mana);
this.selectCard(mana); // Make selected so that we know to tap
}
}
// End loop once mana Req are fulfilled
if(JSON.stringify(manaRequired[0]) == JSON.stringify(noManaReq)){
break;
}
}
// Now all the req. mana colours are tapped, check if the cost requires more
// For 3 cost cards that only require 1 red, two more mana (any colour)
// need to be tapped
// TODO: Better more efficiently and let user select...
let itemCostRemaining = cardData[itemToPayCost].cost - manaRequired[1];
if(itemCostRemaining > 0){
/*
let tapRequirement = (cardData[itemToPayCost].cost - manaRequired[1]);
alert('Tap: '+(tapRequirement)+' more mana to play');
// start mana tap event
*/
// TODO: decide 100% how cards are to be played/payed for
// don't want to slam time into a mana system if it'll be nuked.
// For now, reloop and tap first available mana so that card
// payment is satiated
// Using same items from above
for(let item = 0; item < items.length; item++){
let mana = items[item];
if(itemCostRemaining <= 0){
break;
}
if(this.isTapped(mana) || this.isSelected(mana)){
continue;
}
manaToTap.push(mana);
}
}
if(cardData[itemToPayCost].cost - manaToTap.length > 0){
return 'itemCostRemaining: '+itemCostRemaining; // Didn't find another mana to tap so cost not satiated
}
// If the mana to tap has been satitated then tap the mana selected
manaToTap.forEach(manaId => {
this.tapCard(manaId);
});
// And continue with whatever we were doing
return true;
}
isSelected(itemKey){
if(cardStatus[itemKey] == 'selected'){ return true; }
return false;
}
selectCard(itemKey){
// Sets card to 'selected/targetted' to show which have been selected
if(!this.isSelected[itemKey]){
cardStatus[itemKey] = 'selected';
}
return 'Cannot select a selected card';
}
deselectCard(itemKey){
if(this.isSelected[itemKey]){
cardStatus[itemKey] = null;
return true;
}
return 'Cannot unselect an unselected card';
}
getItemKey(boardElementId, listPositionId, playerFrom){
let itemKey = this.getItems(boardElementId, playerFrom, null, listPositionId);
if(itemKey.length < 1){
@ -579,23 +817,95 @@ class Board{
return 'Not enough mana';
}
// TODO: Check mana colours are available to play, then tap them
// TODO: Multicolours, and X of same colour checks
let manaUsed = [];
// For now, until adding colour, and other attributes to ECS modal
let manaColourRequired = cardData[itemToPlay].colour;
console.log(manaColourRequired);
let items = this.getItems('mana', player[itemToPlay], null, null);
// Check if the player has X amount of each mana colour required
// may not be needed to tap them all (if cost reduced), but they need
// to have those colours available.
// Setting them to 0 to start so they can be added to. Could be nicer
let manaAvailable = {1:0, 2:0, 3:0, 4:0};
// TODO: Thought, maybe instead of tapping colour used, it's just if you
// have the colour? May be a silly thought, but
// Loop the card to be played, and total its mana requirements
let manaRequired = this.getManaTotalOf(itemToPlay)[0];
// player check for this may need changing for "play from opp hand" etc?
let items = this.getItems('mana', playerId, null, null);
for(let item = 0; item < items.length; item++){
// Looping the mana to check player has equal mana/colours to req.
let itemKey = items[item];
if(cardData[itemKey].colour == manaColourRequired && !this.isTapped(itemKey)){
manaUsed.push(itemKey); // This would be how I'd loop for X colour, or multi
console.log(manaUsed);
return true;
// Check if the mana is tapped. Ignore if it is (for now)
if(this.isTapped(itemKey)){
continue;
}
let colourId = cardManaColour[itemKey][0];
manaAvailable[colourId] += 1;
}
// If the manaReq is satiated by the manaAvailable then return true
for (const manaColour in manaRequired) {
//console.log('req');
//console.log(manaRequired[manaColour]);
//console.log('av');
//console.log(manaAvailable[manaColour]);
if(manaRequired[manaColour] > manaAvailable[manaColour]){
return 'Do not have enough: '+manaColour+' mana';
}
}
return 'Do not have correct mana requirements';
//console.log('manaAvailable: ');
//console.log(manaAvailable);
return true;
}
tempGetPrimaryManaOfCard(targetCard){
// Loop the mana card's colours
// can be multi-coloured, and can be used as diff costs
// TODO: This currently takes a White/Red mana as 2 available manas
// when it should be 1 available of either type...
// TODO: FOR NOW, the colour of the mana is the primary colour
// will set 'manaColour' in DB at some point. So multi-colour
// cards aren't all those when in mana (ie. 6 colour decks can't play
// everything all the time)
let manaType = null; // TODO: Will use the highest mana req as it's colour for now
let manaColours = cardColours[targetCard];
for(let i = 0; i < manaColours.length; i++){
// Set mana colour for card if there isn't one
if(manaType == null){ manaType = manaColours[i]; }
// Check each other colour, use the highest cost colour
// as the colour type for this specific mana
// TODO: Do better, and nicer, manaType in DB with the colour?
if(manaColours[i][1] > manaType[1]){
manaType = manaColours[i];
}
}
let colourId = manaType[0];
return colourId;
}
getManaTotalOf(itemKey){
let manaTotal = {1:0, 2:0, 3:0, 4:0};
let manaColours = cardColours[itemKey];
let totalManaColourReq = 0;
console.log(manaColours.length);
for(let i = 0; i < manaColours.length; i++){
// Add one to the available mana
let colourId = manaColours[i][0];
manaTotal[colourId] += manaColours[i][1]; // Add the cost
totalManaColourReq += manaColours[i][1];
}
//console.log('manaTotal: ');
//console.log(manaTotal);
return [manaTotal, totalManaColourReq];
}
inspectCard(cardToInspect){
@ -742,7 +1052,14 @@ class Board{
}
playMana(fromPosition, fromElement, fromPlayer){
// TODO: getItemKey may be the best way to normalise other function calls instead of searching for item key within certain other funcs. (such as addFromBoardElement)
let itemKey = this.getItemKey(fromElement, fromPosition, fromPlayer);
console.log(itemKey);
// Now add to mana zone
this.addFromBoardElement(fromPlayer, fromPosition, fromElement, 'mana', null, null);
// Flip the card face down (for now) TODO: just add colour atop
this.flipCard(itemKey, 0); // Face down (TODO: Change to have spec. style)
}
// HELPER METHODS, to simplify code-base for me in the future
@ -847,30 +1164,32 @@ function loadBoard(data) {
deckData = data.deckData;
cardAttack = data.cardAttack; // TODO: add to the logic
cardColours = data.cardColours;
cardManaColour = data.cardManaColour;
// TODO: JANK IN, CHANGE CODE TO USE NEW ARRAY!!
// Temp jank, set colour to first colour req.
for(let i = 0; i < itemCount; i++){
console.log(itemCount);
console.log(cardColours[itemCount]);
//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
//console.log(i);
cardData[i].colour = board.tempGetPrimaryManaOfCard(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 COLOUR.white: // White
case COLOUR.white.id: // White
cardSprite[i] = [0,0];
break;
case COLOUR.blue: // Blue
case COLOUR.blue.id: // Blue
cardSprite[i] = [0,1];
break;
case COLOUR.red: // Red
case COLOUR.red.id: // Red
cardSprite[i] = [1,0];
break;
case COLOUR.green: // Green
case COLOUR.green.id: // Green
cardSprite[i] = [1,1];
break;
default:
@ -1344,3 +1663,35 @@ function echoCardsInDeck(playerId){
console.log(deckList);
}
// https://stackoverflow.com/a/35970186
function invertColour(hex, bw = true) {
// Split the hex code into individual alphanumerics
if (hex.indexOf('#') === 0) {
hex = hex.slice(1);
}
// convert 3-digit hex to 6-digits.
if (hex.length === 3) {
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
}
if (hex.length !== 6) { throw new Error('Invalid HEX colour.'); }
let r = parseInt(hex.slice(0, 2), 16),
g = parseInt(hex.slice(2, 4), 16),
b = parseInt(hex.slice(4, 6), 16);
// Make it black/white for contrast (typical behaviour here)
if (bw) {
// https://stackoverflow.com/a/3943023/112731
return (r * 0.299 + g * 0.587 + b * 0.114) > 186
? '#000000' : '#FFFFFF';
}
// Invert colour components
r = (255 - r).toString(16);
g = (255 - g).toString(16);
b = (255 - b).toString(16);
// Pad each with zeros and return
return "#" + padZero(r) + padZero(g) + padZero(b);
}

Loading…
Cancel
Save