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