You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
cardGame/public/board.js

1206 lines
36 KiB
JavaScript

const ctx = canvas.getContext('2d');
const canvasLeft = canvas.offsetLeft + canvas.clientLeft;
const canvasTop = canvas.offsetTop + canvas.clientTop;
const cardWidth = 80;
const cardHeight = 120;
const cards = new Image();
const back = new Image();
// TODO: Remove once decks are migrated to ECS
let clickableItems = [];
// 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?
let elements = ['deck','board','hand','mana','shield', 'grave'];
let elementsSizes = {}; // TODO: May need to have the base XY WH of board, hand, etc. stored for loop draw
// Array of items, the 'Entity Manager' as such
let item = [];
let itemCount = 0;
// Component 1: Where on the board, hand, deck, mana, shield, etc.
// send back item, itemCount and individual comp. from server?
let boardElement = {};
let cardData = {};
let position = {};
let size = {};
//let shape = {};
let cardStatus = {}; // tapped, attacking, inspected, untargettable (TODO:maybe used this instead of inEvent later)
let player = {};
let listPosition = {};
let inEvent = null;
// TODO: Do something else ECSey, think most logic for this is about
let playerDeck = [];
let opponentDeck = [];
// TODO: Re-implement, then remove existing code
let playerMana = [];
let playerHand = [];
// To disable drawing each time something changes
let drawEachEvent = true; // For disabling draw each time and only occuring where I want to test
let yourPlayerId = 0; // To compare click events of your/opponents cards
let viewingPlayerId = 0; // To show the board from your/opponent/teammates perspective, etc. without play permission
let deckCount = 35;
let deckCountOpponent = 35;
const maxHandSize = 4;
const maxBoardSize = 3;
const maxShield = 4;
// Gonna need lots of refactoring, and sorting
class Board{
constructor(){
console.log('initBoard');
ctx.font = "12px Arial";
canvas.style.backgroundColor = 'rgb(143 153 150)';
cards.src = 'images/deck.svg';
back.src = 'images/uno.svg';
ctx.fillStyle = '#000';
}
drawBoard(force = false){
if(drawEachEvent == false && force == false){
return 0;
}
// Reset board
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Room Name
ctx.fillText(name, 0, 10);
this.drawDecks();
this.drawPlayerNames('Nathan', 'Evil Nathan');
this.drawCardsECS(); // Atop most everything atm for testing
if(this.checkGameWin == true){
this.drawWin();
}
}
checkGameWin(){
// TODO: Change logic to ECSey, and move into main loop (for other win-cons besides direct attack)
//if(opponentShield.length <= 0){return true;
return false;
}
drawWin(){
var winBoard = new Shape({
name: name,
x: 0,
y: canvas.height/2 - (canvas.height/4)/2,
width: canvas.width,
height: canvas.height/4,
fillStyle: '#CCC',
strokeStyle: '#00EEAA'
});
winBoard.draw();
ctx.fillStyle = '#000';
ctx.font = "bold 50pt Arial";
ctx.fillText('WINNER', 200, 300);
ctx.font = "10pt Arial";
}
drawPlayerNames(playerName, opponentName = false){
// Player Name
ctx.fillText(playerName, 50, canvas.height - 50);
// Opponent's Name
if(opponentName){
ctx.fillText(opponentName, canvas.width - (ctx.measureText(opponentName).width + 50), 50);
}
}
drawCardsECS(){
// ALL NON DECK CARDS DO BE DRAWN IN SAME LOOP (ideally)
// Loop all items
for(let itemKey = 0; itemKey < item.length; itemKey++){
// Loop each element, and player
for(let elementCount in elements){
// Don't draw deck TODO:/gy/void
// TODO: Unless inspecting
let element = elements[elementCount];
if(element == 'deck'){
continue;
}
// Draw Elements
// Loop each item left, and draw if element is currently looped. board,mana,etc.
if(itemKey in boardElement && boardElement[itemKey] == element){
if(boardElement[itemKey] == 'board' && player[itemKey] == 0){
console.log('PLAYER BOARD');
}
// Get the player the item belongs to
let itemPlayer = player[itemKey];
//console.log('Element: '+element+', Player: '+itemPlayer);
calculateItemSizePosition(itemKey);
this.printCardToCanvas(itemKey);
}
}
}
}
printCardToCanvas(itemKey){
// If the itemKey has cardData, position, size, and boardElement we can draw it
// TODO: Add a check for error handling
// Check status, and change border colour for display (for tapped atm)
let border = null;
if(cardStatus[itemKey] == 'tapped'){border = '#E0BC00';}
if(cardStatus[itemKey] == 'attacking'){border = '#C92D22';}
// Set the card 'cardboard' colour based on the card colour type
let colourId = cardData[itemKey].colour;
let fill = null;
if(colourId == 0){ fill = '#EEE'; }
else if(colourId == 1){ fill = '#0033EE'; }
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];
// Draw the card shape itself
let shape = new Shape({
name: name,
x: positionX,
y: positionY,
width: width,
height: height,
fillStyle: fill,
strokeStyle: border
});
shape.draw();
this.printCardImage(itemKey);
this.printCardDetails(itemKey);
}
printCardImage(itemKey){
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];
let fill = '#BBB';
let shape = 'semi'; // Will be decided based on cardData.something? (for unit, spell, etc)
// Add 'image' shape, will need to blitz sprite here in the future (based on cardData.id)
let cardImageContainer = new Shape({
shape: 'semi',
name: 'cardImageContainer_'+name,
x: positionX+height/3,
y: positionY+width/2,
width: width*.9,
height: height*.9,
fillStyle: fill
});
cardImageContainer.draw();
// Draw the actual image too
}
printCardDetails(itemKey){
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];
// Add card name
let fontSize = width/cardWidth*10; // 10 = baseFontSize of 10pt
ctx.font = "bold "+fontSize+"pt Arial";
ctx.fillStyle = '#000';
ctx.fillText(
cardData[itemKey]['name']
, positionX + (ctx.measureText(cardData[itemKey]['name']/2).width) - width/4
, positionY+height*.25
);
// Add card type
ctx.fillText(
cardData[itemKey]['type']
, positionX + (ctx.measureText(cardData[itemKey]['type']/2).width) - width/4
, positionY+height*.7
);
// Add text/effect area
if(cardData[itemKey]['effect'] !== null){
ctx.fillText(
cardData[itemKey]['effect']
, positionX + (ctx.measureText(cardData[itemKey]['effect']/2).width) - width/4
, positionY+height*.8
);
}
// Attack
ctx.fillText(
cardData[itemKey]['atk']
, positionX + (ctx.measureText(cardData[itemKey]['atk']).width)
, positionY+height*.95
);
// Add cost
ctx.fillText(
cardData[itemKey]['cost']
, positionX + (ctx.measureText(cardData[itemKey]['cost']).width)
, positionY+height*.1
);
// Unbold font for other draws
ctx.font = "10pt Arial";
}
// Draw Invidual Cards, called by other deck stuff
// Might be put into a card class, makes sense, eh.
drawCard(array, arrayKey, name, positionX, positionY, width, height, fill, border){
// Card Colour
//console.log('drawCard card: '+JSON.stringify(array[arrayKey]));
let colourId = array[arrayKey].colour;
if(colourId == 0){ fill = '#EEE' }
else if(colourId == 1){ fill = '#0033EE' }
if(array[arrayKey].tapped){
border = '#E0BC00';
console.log('drawCard tapped');
}
var cardClickable = new Shape({
name: name,
x: positionX,
y: positionY,
width: width,
height: height,
fillStyle: fill,
strokeStyle: border
});
array[arrayKey]['clickable'] = cardClickable;
array[arrayKey]['clickable'].draw();
// Add image
// TODO:half circle for unit Set start angle to 0 and end angle to Math.PI.
// TODO:Ellipse for token (near full size)
// TODO:Octagon for spell
let cardImageContainer = new Shape({
shape: 'semi',
name: 'cardImageContainer_'+name,
x: positionX+height/3,
y: positionY+width/2,
width: width*.9,
height: height*.9,
fillStyle: "#BBB"
});
cardImageContainer.draw();
// Add card name
let fontSize = width/cardWidth*10; // 10 = baseFontSize of 10pt
ctx.font = "bold "+fontSize+"pt Arial";
ctx.fillStyle = '#000';
ctx.fillText(
array[arrayKey]['name']
, positionX + (ctx.measureText(array[arrayKey]['name']/2).width) - width/4
, positionY+height*.25
);
// Add card type
ctx.fillText(
array[arrayKey]['type']
, positionX + (ctx.measureText(array[arrayKey]['type']/2).width) - width/4
, positionY+height*.7
);
// Add text/effect area
if(array[arrayKey]['effect'] !== null){
ctx.fillText(
array[arrayKey]['effect']
, positionX + (ctx.measureText(array[arrayKey]['effect']/2).width) - width/4
, positionY+height*.8
);
}
// Attack
ctx.fillText(
array[arrayKey]['atk']
, positionX + (ctx.measureText(array[arrayKey]['atk']).width)
, positionY+height*.95
);
// Add cost
ctx.fillText(
array[arrayKey]['cost']
, positionX + (ctx.measureText(array[arrayKey]['cost']).width)
, positionY+height*.1
);
// Unbold font for other draws
ctx.font = "10pt Arial";
}
drawDecks(){
// Draw all decks belonging to players (currently 2, could be 3-5 in future, for PvE raids)
// TODO: Probably add this as an item of boardElement 'realDeck', as 'deck' is cards in deck
// actually, maybe change current 'deck' to 'inDeck', reads better
let name = null;
let countername = null;
let x = null;
let counterx = null;
let y = null;
let countery = null;
let width = null;
let counterwidth = null;
let height = null;
let counterheight = null;
let fill = null;
let counterfill = '#FFF';
let textx = null;
let texty = null;
let deckLength = 0;
for(let playerId = 0; playerId < players; playerId++){
if(playerId == 0){
// Deck
name= 'deck';
x= canvas.width-cardWidth*1.5-40;
y= canvas.height-cardHeight*1.5-60;
width= cardWidth*1.5;
height= cardHeight*1.5;
fill= "#0000FF";
countername= 'deckCounter';
counterx= canvas.width-cardWidth*.6;
countery= canvas.height-cardHeight*.6;
counterwidth= cardWidth*.375;
counterheight= cardHeight*.375;
// TODO: Center in the circle
deckLength = getCurrentPositionAndLength('deck', 0)[1];
textx = canvas.width-cardWidth*.6 - (ctx.measureText(deckLength).width) + 7;
texty = canvas.height-cardHeight*.6 + 5;
}
if(playerId == 1){
// Opponent's Deck
name= 'deckOpponent';
x= 40;
y= 60;
width= cardWidth*1.5;
height= cardHeight*1.5;
fill= "#FF0000";
countername= 'deckCounterOpponent';
counterx= cardWidth*1.5+(cardWidth*.375);
countery= cardHeight*1.5+(cardHeight*.375);
counterwidth= cardWidth*.375;
counterheight= cardHeight*.375;
counterfill= "#FFF";
deckLength = getCurrentPositionAndLength('deck', 1)[1];
textx=cardWidth*1.5 + (ctx.measureText(deckLength).width) + 10;
texty=cardHeight*1.9;
}
// Actually draw the decks base on the size/position, etc.
clickableItems[name+'Sprite'] = new Shape({
name: name,
x: x,
y: y,
width: width,
height: height,
fillStyle: fill
});
//ctx.fillRect(canvas.width-cardWidth/2-60, canvas.height/2-cardHeight/4, cardWidth/2, cardHeight/2);
clickableItems[name+'Sprite'].draw();
// Draw circle for deck count to sit in
let deckCounterSprite = new Shape({
shape: 'circle',
name: countername,
x: counterx,
y: countery,
width: counterwidth,
height: counterheight,
fillStyle: counterfill
});
deckCounterSprite.draw();
// Draw deck count text
// TODO: Center in the circle
ctx.fillStyle = '#000';
ctx.fillText(deckLength, textx, texty);
}
}
addFromBoardElement(playerFrom, fromPosition, elementFrom, elementTo, toPosition=null, playerTo=null){
// Move itemKey fromPosition in elementFrom to toPosition in elementTo
// can also switch item between players (of from and to supplied)
if(playerTo == null){
playerTo = playerFrom;
}
// Loop all items, get the item with boardElement elementFrom and player playerFrom
for(let itemKey = 0; itemKey < item.length; itemKey++){
// Get the item from the element
if(itemKey in boardElement && boardElement[itemKey] == elementFrom){
// Check if item belongs to playerFrom
if(itemKey in player && player[itemKey] == playerFrom){
// Get item based on fromPosition listPosition
if(listPosition[itemKey] == fromPosition){
// Check if a toPostion supplied (likely won't be, dunno if I'll do this)
if(toPosition == null){
// Get the new position of item based on new boardElement
toPosition = getCurrentPositionAndLength(elementTo, playerTo)[0]+1
//console.log(toPosition);
}
//console.log('itemKey: '+itemKey+' fromPosition: '+fromPosition+' elementFrom: '+elementFrom+' elementTo: '+elementTo+' toPosition: '+toPosition+' playerFrom: '+playerFrom+' playerTo: '+playerTo);
listPosition[itemKey] = toPosition;
// Move from elementFrom to elementTo
//console.log('elementTo: '+elementTo);
//console.log(boardElement[itemKey]);
boardElement[itemKey] = elementTo;
//console.log(itemKey);
//console.log(boardElement[itemKey]);
// Move down(0) the positions of elementFrom, from fromPosition for player
//console.log(JSON.stringify(boardElement));
//console.log(JSON.stringify(listPosition));
this.moveElementPositions(0, elementFrom, fromPosition, playerFrom);
//console.log(JSON.stringify(boardElement));
//console.log(JSON.stringify(listPosition));
this.drawBoard();
return 1; // Got a loop that calls a loop, and checks the new values atm, so this keeps counting down
}
}
}
}
}
moveElementPositions(direction, elementFrom, fromPosition, playerFrom){
// Move the positions directionally 1 for elementFrom from fromPosition
//console.log(listPosition);
//console.log('Direction: '+direction+' elementFrom: '+elementFrom+' fromPosition: '+fromPosition+' player: '+playerId);
// Loop the elementFrom, and move positions for anything after
// item taken from position 34, 35..60 need to be moved down to 34..59
// For each item
for(let itemKey = 0; itemKey < item.length; itemKey++){
// If boardElement is elementFrom
if(itemKey in boardElement && boardElement[itemKey] == elementFrom){
// And from the playerFrom (id)
if(itemKey in player && player[itemKey] == playerFrom){
//console.log(elementFrom);
//console.log(boardElement[itemKey]);
//console.log(listPosition[itemKey]);
//console.log(itemKey);
if(listPosition[itemKey] > fromPosition){
//console.log('position: '+listPosition[itemKey]+' fromPosition: '+fromPosition);
// Move items up, i.e. added to top of deck
if(direction){
listPosition[itemKey]++;
}
// Move items down, i.e. taken from top of deck
listPosition[itemKey]--;
}
}
}
}
//console.log(listPosition);
}
// Draw a card, traditional TCG
drawACard(playerId, cardsToDraw = 1){
for(let draw = 0; draw < cardsToDraw; draw++){
// Move from player1, position 0 (top) of deck, to hand
// Check there's space in hand
let elementLength = getCurrentPositionAndLength('hand', playerId)[1];
if(elementLength >= maxHandSize){
alert('Hand full '+elementLength+'/'+maxHandSize);
return 0;
}
this.addFromBoardElement(playerId, 0, 'deck', 'hand', null, null);
}
}
// Currently only functionality in hand
playCardToBoard(positionFrom, fromElement, toElement, fromPlayer, toPlayer = null, cardsToPlay = 1){
// TODO: if from hand, use mana according to the card cost + mana types
if(toPlayer === null){
toPlayer = fromPlayer;
}
// Loop probably not needed, but may be for eg. 'play X cards from top of deck'
for(let play = 0; play < cardsToPlay; play++){
// Check there's space on board TODO: change to locationTo
// TODO: Normalise this for all element/player combos
let elementLength = getCurrentPositionAndLength(toElement, toPlayer)[1];
// TODO: Compare against the maxSize for the boardElement
// this is fine(ish) for now, as just using hand
if(elementLength >= maxHandSize){
alert('Board full '+elementLength+'/'+maxHandSize);
return 0;
}
// When done remove false &&
// TODO: Rewrite for ECS
// Mana cost required and mana tapping for playing a card from hand, etc
if(false && cardPlayed.cost > playerMana.length){
alert('Not enough mana');
return 0;
}
// Move from player0, position 0 (top) of deck, to hand, to pos(null/auto) for toPlayer
this.addFromBoardElement(fromPlayer, positionFrom, fromElement, toElement, null, toPlayer);
}
// Needs doing for playing cards from hand
if(false){
let manaUsed = [];
if(cardPlayed.cost > playerMana.length){
alert('Not enough mana');
return 0;
}else{
let canPlay = false;
let needsMana = 1;
let usedMana = 0;
playerMana.forEach(function(manaCard, key){
if(cardPlayed.colour == manaCard.colour && manaCard.tapped == false && needsMana > usedMana){
console.log(manaCard);
// Needs changing for multiple colour usage
// 2 red, 1 red + 1 blue, etc.
// Currently only gets one mana of the cards colour
manaUsed.push(key);
usedMana++;
canPlay = true;
}
});
if(!canPlay){
alert('Mana conditions not met, right click hand unit to play as mana');
return 0;
}
}
// Tap mana to be used
manaUsed.forEach(function(cardKey, key){
playerMana[cardKey].tapped = true;
});
}
this.drawBoard();
}
inspectCard(cardToInspect){
// Set inspectedCard (for now) to the card itemKey
inEvent = ['inspect', cardToInspect];
this.drawBoard();
}
cancelInspect(){
if(inEvent && inEvent[0] == 'inspect'){
inEvent = null;
this.drawBoard();
}
}
startAttack(itemAttacking){
// Selects the card that will be attacking
// Rename to attack intent?
// Do error/legal checks here
if(cardStatus[itemAttacking] == 'tapped'){
alert('Cannot attack, as tapped');
return false;
}
// Set event
inEvent = ['attack', itemAttacking];
// Set the status of card to attacking for drawing.
// TODO:In future this may be what's used for check (or other in case you can attack with multiple units at once)
cardStatus[itemAttacking] = 'attacking';
this.drawBoard();
}
// Do the attack
makeAttack(itemDefending, itemAttacking = null){
// TODO: Check if mana owner has any 'block', etc. first, then prevent if so
// If itemAttacking not defined, use the item from inEvent
if(itemAttacking == null){
itemAttacking = inEvent[1];
}
if(cardStatus[itemAttacking] == 'tapped'){
alert('Cannot attack, as tapped');
return false;
}
switch (boardElement[itemDefending]) {
// If card on 'board' attacked
// Compare attackingCard and defendingCard
case 'board':
let atkAttacker = cardData[itemAttacking];
let atkDefender = cardData[itemDefending];
// Does Attacker kill Defender
if(atkDefender <= atkAttacker){
this.sendToGrave(itemDefending);
}
// Does Defender kill Attacker
if(atkAttacker <= atkDefender){
this.sendToGrave(itemAttacking);
this.endAttackFor(itemAttacking);
}else{
// If not, end the attacker cards attack 'phase'
this.endAttackFor(itemAttacking);
}
break;
case 'shield':
// If the shield is tapped 'destroy' it
if(cardStatus[itemDefending] == 'tapped'){
// Remove from shield, add to hand
// Untap, add to hand (when moving item between status, ANY ITEM. remove status)
this.destroyShield(itemDefending);
}
else{
this.tapCard(itemDefending);
}
// End the attacker card attack 'phase'
this.endAttackFor(itemAttacking);
break;
}
this.drawBoard();
}
endAttackFor(itemAttacking){
// Tap the card (this would check if after attack cards taps or not.
this.tapCard(itemAttacking);
// If the item attacking was from the 'attack' event, remove the event
if(itemAttacking == inEvent[1]){
inEvent = null;
}
}
cancelAttackFor(itemAttacking){
if(itemAttacking != inEvent[1]){
return false;
}
// If the card to attack is from inEvent of 'attack', remove it and its cardStatus
cardStatus[itemAttacking] = null;
inEvent = null;
this.drawBoard();
}
// Like everything else, need to consolidate into one function that
// can work for both players, and even more for 2v2 3v1 combats, etc.
playShield(fromPosition, fromElement, playerId, cardsToPlay = 1){
for(let shieldNo = 0; shieldNo < cardsToPlay; shieldNo++){
// Check there's space for shield TODO: change to locationTo
// TODO: Normalise this for all element/player combos
let elementLength = getCurrentPositionAndLength('shield', playerId)[1];
if(elementLength >= maxShield){
alert('Shield full '+elementLength+'/'+maxShield);
// Kill loop if there's too many shiled already, no need re-notifying
return 0;
}
// Move from player, position 0 (top) of deck, to hand (next available position)
this.addFromBoardElement(playerId, fromPosition, fromElement, 'shield', null, null);
}
}
playMana(fromPosition, fromElement, cardsToPlay = 1){
// Move from player0, fromPosition of hand (for now), to mana
// TODO: FOR ALL addFromBoardElements, if 'fromPosition' not passed get the
// fromPosition and boardElementFrom from the itemId (will need to change to pass this)
this.addFromBoardElement(0, fromPosition, fromElement, 'mana', null, null);
}
// HELPER METHODS, to simplify code-base for me in the future
destroyShield(itemKey){
// Adds from 'shield' to 'hand' boardElement
// Sets the listPosition to the next in line for new boardElement
// Changes the listPosition of each item in previous boardElement
// All for same player, although below playerFrom/playerTo can be set to switch between players
// TODO: Removes any cardStatus
//addFromBoardElement(playerFrom, fromPosition, elementFrom, elementTo, toPosition=null, playerTo=null)
this.addFromBoardElement(player[itemKey], listPosition[itemKey], boardElement[itemKey], 'hand', null, null);
}
sendToGrave(itemKey){
console.log('SendToGrave: '+itemKey);
//addFromBoardElement(playerFrom, fromPosition, elementFrom, elementTo, toPosition=null, playerTo=null)
this.addFromBoardElement(player[itemKey], listPosition[itemKey], boardElement[itemKey], 'grave', null, null);
}
tapCard(itemKey){
// Set the cardStatus to tapped
cardStatus[itemKey] = 'tapped';
// Do any other 'on tap' effects, etc. in the future
}
}
// TODO: TEMP!! Replace soon
createDeckList(playerDeck, deckCount, 0);
createDeckList(opponentDeck, deckCountOpponent, 1);
// Run board commands here for testing
let board = new Board;
// Play 4 shield from top (0) of each players deck
for(let currentPlayer = 0; currentPlayer <= players-1; currentPlayer++){
board.playShield(0, 'deck', currentPlayer, 4);
}
board.drawBoard(true);
// Right Click, Rightclick, rightclick, right click
canvas.addEventListener('contextmenu', function(event) {
event.preventDefault();
var x = event.pageX - canvasLeft,
y = event.pageY - canvasTop;
for(let itemKey = 0; itemKey < item.length; itemKey++){
if(elements[itemKey] == 'deck'){
continue;
}
// Check the item has a size and position
if(itemKey in size && itemKey in position){
// Compare the event XY position to the item
if(clickableCheck(x,y,false,itemKey)){
// Only want to happen once (for now)
// Maybe in future add to hand would trigger another event if there's an effect?
// Check the location of element
switch(boardElement[itemKey]){
// Check item location, and trigger events based on it
case 'board':
// TODO: If the current event is attack, right clicking same item in event
// will end the attack event
// If there's an attack event, target other cards
// If there is an event, the event is attack event, and the item currently loop
// is the same one as currently in the event
if(inEvent && inEvent[0] == 'attack' && itemKey == inEvent[1]){
board.cancelAttackFor(itemKey);
}
board.drawBoard();
break;
case 'hand':
// Can be played as mana (right click for now)
// Play item from boardElement hand. To boardElement mana (explanitory)
board.playMana(listPosition[itemKey], 'hand');
board.drawBoard();
break;
}
}
}
}
// Return false to prevent context opening:
// https://stackoverflow.com/a/4236294
return false;
}, false);
// Left Click, Leftclick, leftclick, left click
canvas.addEventListener('click', function(event) {
var x = event.pageX - canvasLeft,
y = event.pageY - canvasTop;
// Check clicks against itemKey position+size
// Will be the new way
// TODO:Maybe write this into a function? If XY WH is hit return true, and the itemKey
// then it can be re-used in contextclick, hover, etc without rewrite
//console.log('X: '+x+' Y: '+y);
for(let itemKey = 0; itemKey < item.length; itemKey++){
if(elements[itemKey] == 'deck'){
continue;
}
// Check the item has a size and position
if(itemKey in size && itemKey in position){
// Compare the event XY position to the item
if(clickableCheck(x,y,false,itemKey)){
// Only want to happen once (for now)
// Maybe in future add to hand would trigger another event if there's an effect?
// Get the playerId of who the item belongs to
// TODO: Some other playerId may be needed to for 'mind control' cards
let playerId = player[itemKey];
// Check the location of element
switch(boardElement[itemKey]){
// Check item location, and trigger events based on it
// TODO: Change inEvent locations, and checks elsewhere?
// TODO: Make existing mechanics ECSey
case 'board':
// player/opponentBoard not seperated, as want to have
// each player able to have effects, etc. to make others attack
// etc.
// yourPlayerId in for now to prevent using opponents card
// TODO:the cards may be used to attack, there will be another
// check like 'canUseOpponentsBoard' or something
if(!inEvent && cardStatus[itemKey] != 'tapped' && playerId == yourPlayerId){
board.startAttack(itemKey);
break;
}
// opponentBoard
// If there's an attack event, target other cards
// Again yourPlayerId check for now, this time so your can't hit your own
if(inEvent && inEvent[0] == 'attack' && inEvent[1] != itemKey && playerId != yourPlayerId){
// Make attack on the card clicked, with card in inEvent[1]
board.makeAttack(itemKey);
break;
}
// If no event, and clicked an TODO:OPPONENT CARD (for now)
// Wants to be on option on r/l click maybe
// left click inspects then you choose play from there?
// inspect the card (zoom in on it)
if(!inEvent && playerId != yourPlayerId){
board.inspectCard(itemKey);
break;
}
else if(inEvent && inEvent[0] == 'inspect' && inEvent[1] == itemKey){
board.cancelInspect();
break;
}
break;
case 'hand':
// TODO: Ensure it can be played
board.playCardToBoard(listPosition[itemKey], 'hand', 'board', playerId, playerId, 1);
break;
case 'shield':
// If you have an attack, and click opponent shield,
// destroy (unless they can block, etc.)
if(inEvent && inEvent[0] == 'attack' && inEvent[1] != itemKey){
board.makeAttack(itemKey);
}
break;
}
board.drawBoard();
}
}
}
// Decks kept as-is for now
// TODO: boardElement realDeck, or smth and add these to the loop
// # PLAYER DECK
clickable = clickableItems['deckSprite'];
if(clickableCheck(x,y,clickable) && !inEvent){
board.drawACard(0);
}
// TODO:
// # OPPONENT DECK
clickable = clickableItems['deckOpponentSprite'];
if(clickableCheck(x,y,clickable) && !inEvent){
board.drawACard(1);
}
}, false);
function clickableCheck(x,y,clickable=false,itemKey=false){
// Collision detection between clicked offset and clickableItems
// https://stackoverflow.com/a/9880302
// Temp solution to add the check while the old way also exists
// simultaneously. It works, so that's nice
if(clickable === false && itemKey !== false){
clickable = {};
//console.log(clickable);
//console.log(itemKey);
clickable.x = position[itemKey][0];
clickable.y = position[itemKey][1];
clickable.width = size[itemKey][0];
clickable.height = size[itemKey][1];
//console.log(clickable);
}
// Debug Stuff
let debug = false
if(debug){
console.log(clickable.y+' <');
console.log(y+ '<')
console.log(clickable.y+clickable.height);
}
if(
y > clickable.y && y < clickable.y + clickable.height
&& x > clickable.x && x < clickable.x + clickable.width)
{
return true;
}
return false;
}
// TEMP: Create a deck of X different cards that can be drawn/played
// for UI development, will be done on server and not shown to clients
// TODO: Randomly create a deck from objects stored in file (for now) then DB
// 3 of each card max, and likely one colour too for time being
function createDeckList(deck, deckCount, playerId){
for(let i = 0; i < deckCount; i++){
// Randomise colour
let colour = Math.floor(Math.random() * 2);
let effect = Math.floor(Math.random() * 5);
if(effect == 0){ effect = 'effect here'; } else{ effect = null }
item.push(itemCount);
boardElement[itemCount] = 'deck';
cardData[itemCount] = {
'name':'CardName '+(i+1)
, 'cost':1
, 'atk':1
, 'def':1
, 'rarity': 'common'
, 'effect':effect
, 'type':'human'
, 'colour':colour
, 'tapped':false
};
player[itemCount] = playerId;
listPosition[itemCount] = i;
// Previous iteration, kept to prevent errors for now
deck.push(cardData[itemCount]);
itemCount++;
}
//TODO: shuffleDeck(0); Make shuffleDeck work, and call after created the deck (for now)
}
function shuffleDeck(playerId){
let tempDeck = [];
for(let itemKey = 0; itemKey < item.length; itemKey++){
// Check if item is in deck
// 'key' in 'object'
if(itemKey in boardElement && boardElement[itemKey] == 'deck'){
// Check if item belongs to opponent
if(itemKey in player && player[itemKey] == playerId){
// This will shuffle, but not from OG deck state.
// Will need to get the listPosition and order by that first
//console.log(itemKey);
tempDeck.push(itemKey);
}
}
}
// Temporary shuffle until algo selected/written
let id, shuffledPile, i;
console.log(tempDeck.length);
for (i = tempDeck.length - 1; i > 0; i--) {
id = Math.floor(Math.random() * (i + 1));
//shuffledPile = tempDeck[i];
//tempDeck[i] = tempDeck[id];
//tempDeck[id] = shuffledPile;
// Change the position within the deck
listPosition[tempDeck[id]] = i;
}
}
function untapZone(elementFrom, playerFrom){
// TODO: Currently copy of the loop. Make the loop multi-purpose without needing to dupe the layers
// of the loop (THIS APPLIES FOR ALL USES OF THE LOOP!!)
for(let itemKey = 0; itemKey < item.length; itemKey++){
// Get the item from the element
if(itemKey in boardElement && boardElement[itemKey] == elementFrom){
// Check if item belongs to playerFrom
if(itemKey in player && player[itemKey] == playerFrom){
// Just basic, if tapped, untap logic
// See why the loop shouldn't need re-adding each time?
if(cardStatus[itemKey] == 'tapped'){
cardStatus[itemKey] = null;
}
}
}
}
board.drawBoard();
}
function untapAllZones(currentPlayer = null){
if(currentPlayer === null){
currentPlayer = 0;
}
for(let currentPlayer = 0; currentPlayer <= players-1; currentPlayer++){
let elements = ['deck','board','hand','mana','shield', 'grave'];
// Loop all the elements, and utap each card in the zone
for(let element = 0; element < elements.length; element++){
untapZone(elements[element], currentPlayer);
}
}
}
function getCurrentPositionAndLength(elementName, playerId){
let highestListPosition = 0;
let length = 0;
// Loop all the items
// Think making the for loop, and the if into something to be called that fires functions would be smart?
for(let itemKey = 0; itemKey < item.length; itemKey++){
// Check the item is the correct boardElement
// 'key' in 'object'
if(itemKey in boardElement && boardElement[itemKey] == elementName){
// Check if item belongs to the player
if(itemKey in player && player[itemKey] == playerId){
if(listPosition[itemKey]){
if(listPosition[itemKey] >= highestListPosition){
highestListPosition = listPosition[itemKey];
length++;
}
}
}
}
}
return [highestListPosition, length];
}
function calculateItemSizePosition(itemKey){
// Calc position and size of the item
// Used for drawing and interacting
// TODO: Make better calculations and positions for here (also rescaling in future)
// TODO: Maybe instead of checking each player/element combo in if, do some fancy loop?
let itemPlayer = player[itemKey];
let itemElement = boardElement[itemKey];
let itemListPositionLength = getCurrentPositionAndLength(itemElement, itemPlayer);
let itemListPositionNext = itemListPositionLength[0];
let itemListLength = itemListPositionLength[1];
let cardMargin = 10;
let positionX = 0;
let positionY = 0;
let width = 0;
let height = 0;
let i = listPosition[itemKey];
//console.log('cardName: '+cardData[itemKey].name+' listPosition/i: '+i);
if(itemPlayer == 1 && itemElement == 'board'){
positionX = canvas.width/2 - (cardWidth * (itemListLength - i) - (cardMargin * i));
positionY = cardHeight + 30;
width = cardWidth;
height = cardHeight;
}
if(itemPlayer == 1 && itemElement == 'hand'){
positionX = canvas.width/2 - (cardWidth * (itemListLength - (i+1)) - (cardMargin * (i+1)));
positionY = 20;
width = cardWidth;
height = cardHeight;
}
if(itemPlayer == 1 && itemElement == 'mana'){
// TODO: Opponent Mana
}
if(itemPlayer == 1 && itemElement == 'shield'){
let fromX = canvas.width-60;
let fromY = 300;
let cWidth = cardWidth*.8;
let cHeight = cardHeight*.8;
let split = 0;
// i-1 here as it's based on 0 being start, like array.
// If there's 2 in a row, split onto next row
// TODO: Not sure if I want to start elements at 1 (for clienty) or 0 (for programmy)
if(i-1>=2){ split = 1; }
// i%2 0 = 0, 1 = 1, 2 = 0, 3 = 1 to prevent margin from X/Y axis, and just between cards
positionX = (fromX+((i%2)*cardMargin))+(i%2*cWidth)-(cWidth*2);
positionY = fromY+(split*cHeight+(cardMargin*split))-(cHeight*2 + cardMargin);
width = cWidth;
height = cHeight;
}
if(itemPlayer == 0 && itemElement == 'board'){
positionX = canvas.width/2 - (cardWidth * (itemListLength - (i+1)) - (cardMargin * (i+1)));
positionY = canvas.height - cardHeight-30-(cardHeight);
width = cardWidth;
height = cardHeight;
}
if(itemPlayer == 0 && itemElement == 'hand'){
positionX = canvas.width/2 - (cardWidth * (itemListLength - (i+1)) - (cardMargin * (i+1)));
positionY = canvas.height-cardWidth*1.5-20;
width = cardWidth;
height = cardHeight;
}
if(itemPlayer == 0 && itemElement == 'mana'){
let fromX = 60;
let fromY = 60;
let cWidth = cardWidth*.5;
let cHeight = cardHeight*.5;
positionX = (fromX+cardMargin)+(i*cWidth-cardMargin);
positionY = canvas.height-fromY;
width = cWidth;
height = cHeight;
}
if(itemPlayer == 0 && itemElement == 'shield'){
let fromX = 60;
let fromY = 300;
let cWidth = cardWidth*.8;
let cHeight = cardHeight*.8;
let split = 0;
// i-1 here as it's based on 0 being start, like array.
// TODO: Not sure if I want to start elements at 1 (for clienty) or 0 (for programmy)
if(i-1>=2){ split = 1; }
positionX = (fromX+((i%2)*cardMargin)) +(i%2*cWidth);
positionY = canvas.height-fromY+(split*cHeight+(cardMargin*split));
width = cWidth;
height = cHeight;
}
// Inspected Card
// TODO: May need to make a new itemKey for inspectedCard to loop through
if(inEvent && inEvent[0] == 'inspect' && inEvent[1] == itemKey){
positionX = canvas.width/2 - cardWidth;
positionY = canvas.height/2 - cardHeight;
width = cardWidth*2;
height = cardHeight*2;
}
//console.log('cardName: '+cardData[itemKey].name+', i/listPosition: '+i+', listPosition Length: '+itemListLength);
// Set the size/position of the item
size[itemKey] = [width, height];
position[itemKey] = [positionX,positionY];
}