Compare commits
159 Commits
master
...
feature/cl
@ -0,0 +1,3 @@
|
|||||||
|
package-lock.json
|
||||||
|
node_modules
|
||||||
|
node_modules/*
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
# Card Game
|
||||||
|
|
||||||
|
## Technology
|
||||||
|
|
||||||
|
- node.js
|
||||||
|
- express.js
|
||||||
|
- socketIO
|
||||||
|
- HTML5 (Canvas)
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
`sudo apt install npm`
|
||||||
|
`git pull <this-repo>`
|
||||||
|
`npm install express --save`
|
||||||
|
|
||||||
|
socketio, mysql
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
`node server.js`
|
||||||
|
|
||||||
|
Access through
|
||||||
|
|
||||||
|
`http://localhost:3000`
|
||||||
|
|
||||||
|
### Development
|
||||||
|
|
||||||
|
BACKEND/SERVER DEVELOPMENT ONLY
|
||||||
|
|
||||||
|
To prevent the exhausting task of restarting the server on change
|
||||||
|
install and run with nodemon, it'll handle server restart each time
|
||||||
|
a js file is saved (this does also restart for front-end)
|
||||||
|
|
||||||
|
`npm install -g nodemon`
|
||||||
|
|
||||||
|
Run with
|
||||||
|
|
||||||
|
`nodemon --inspect server.js`
|
||||||
|
|
||||||
@ -0,0 +1,762 @@
|
|||||||
|
// Need to require DB again. Apparently this is ok, as it'll
|
||||||
|
// still only have one require cross project (?)
|
||||||
|
const database = require('./database');
|
||||||
|
const util = require('util')
|
||||||
|
|
||||||
|
|
||||||
|
// Get the decks requested
|
||||||
|
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(deckIds).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(deckIds = false){
|
||||||
|
const dPromise = new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
database.dbGetDeckList(deckIds).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;
|
||||||
|
}
|
||||||
|
function getCards(cardIds = false){
|
||||||
|
const dPromise = new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
database.dbGetCards(cardIds).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;
|
||||||
|
}
|
||||||
|
function getCardClasses(cardIds = false){
|
||||||
|
const dPromise = new Promise((resolve, reject) => {
|
||||||
|
database.dbGetCardClasses(cardIds).then(data => {
|
||||||
|
|
||||||
|
let cardClasses = [];
|
||||||
|
|
||||||
|
data.forEach((cardClass) => {
|
||||||
|
|
||||||
|
cardClasses.push({
|
||||||
|
'cardId': cardClass.cardId,
|
||||||
|
'classId': cardClass.classId,
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
resolve(cardClasses);
|
||||||
|
})
|
||||||
|
.catch(err => { throw err; })
|
||||||
|
});
|
||||||
|
return dPromise;
|
||||||
|
}
|
||||||
|
function getCardColourRequirement(cardIds = false){
|
||||||
|
const dPromise = new Promise((resolve, reject) => {
|
||||||
|
database.dbGetCardColourRequirement(cardIds).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;
|
||||||
|
}
|
||||||
|
function getCardManaColour(cardIds = false){
|
||||||
|
const cPromise = new Promise((resolve, reject) => {
|
||||||
|
database.dbGetCardManaColour(cardIds).then(data => {
|
||||||
|
|
||||||
|
let manaColours = [];
|
||||||
|
|
||||||
|
data.forEach((cardManaColour) => {
|
||||||
|
|
||||||
|
manaColours.push({
|
||||||
|
'cardId': cardManaColour.cardId,
|
||||||
|
'colourId': cardManaColour.colourId,
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
resolve(manaColours);
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch(err => { throw err; })
|
||||||
|
});
|
||||||
|
return cPromise;
|
||||||
|
}
|
||||||
|
// Build card effects
|
||||||
|
// TODO: Accept card Ids (array)
|
||||||
|
function buildCardEffects(effects, effectSteps, effectTriggers){
|
||||||
|
const cPromise = new Promise((resolve, reject) => {
|
||||||
|
|
||||||
|
let cardEffects = {};
|
||||||
|
let effectData = {};
|
||||||
|
|
||||||
|
effects.forEach((effect) => {
|
||||||
|
// If card doesn't currently have an effect
|
||||||
|
if(cardEffects[effect.cardId] === undefined){
|
||||||
|
cardEffects[effect.cardId] = [];
|
||||||
|
}
|
||||||
|
// Add the effectId to the temp cardId
|
||||||
|
cardEffects[effect.cardId].push(effect.effectId);
|
||||||
|
|
||||||
|
// Now actually start the effect
|
||||||
|
effectData[effect.effectId]={};
|
||||||
|
|
||||||
|
// Add the description to new effect
|
||||||
|
effectData[effect.effectId]['description'] = effect.description;
|
||||||
|
effectData[effect.effectId]['step'] = {};
|
||||||
|
effectData[effect.effectId]['trigger'] = {};
|
||||||
|
});
|
||||||
|
//console.log(cardEffects);
|
||||||
|
//console.log(effectData);
|
||||||
|
|
||||||
|
effectSteps.forEach((step) => {
|
||||||
|
// If step doesn't exist, add step (id of DB item)
|
||||||
|
if(effectData[step.effectId]['step'][step.stepId] === undefined){
|
||||||
|
effectData[step.effectId]['step'][step.stepId] = {};
|
||||||
|
|
||||||
|
// Add the step effect details
|
||||||
|
effectData[step.effectId]['step'][step.stepId] = {
|
||||||
|
// Order steps occur in, two same = 'simultaneous'
|
||||||
|
'stepOrder': step.stepOrder,
|
||||||
|
// The hardcoded effect that will occur at step
|
||||||
|
'basicEffectId': step.basicEffectId,
|
||||||
|
// How much/many the effect will do/trigger
|
||||||
|
'amount': step.amount,
|
||||||
|
'target': [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// At this point there should be data in effectStep
|
||||||
|
// Add a new 'target' (each step can have many)
|
||||||
|
effectData[step.effectId]['step'][step.stepId]['target']
|
||||||
|
.push({
|
||||||
|
'colourId': step.colourId,
|
||||||
|
'typeId': step.typeId,
|
||||||
|
'classId': step.classId,
|
||||||
|
'passiveId': step.passiveId,
|
||||||
|
'itemFromStep': step.itemFromStep,
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
//console.log(util.inspect(effectData, true, 4, true))
|
||||||
|
|
||||||
|
effectTriggers.forEach((trigger) => {
|
||||||
|
// If trigger doesn't exist, add trigger (id of DB item)
|
||||||
|
if(effectData[trigger.effectId]['trigger'][trigger.triggerId] === undefined){
|
||||||
|
effectData[trigger.effectId]['trigger'][trigger.triggerId] = {};
|
||||||
|
|
||||||
|
// Add the trigger effect details
|
||||||
|
effectData[trigger.effectId]['trigger'][trigger.triggerId] = {
|
||||||
|
'triggerTypeId': trigger.triggerTypeId,
|
||||||
|
'amount': trigger.amount,
|
||||||
|
'target': [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a new 'target' (each trigger can have many)
|
||||||
|
effectData[trigger.effectId]['trigger'][trigger.triggerId]['target']
|
||||||
|
.push({
|
||||||
|
'colourId': trigger.colourId,
|
||||||
|
'typeId': trigger.typeId,
|
||||||
|
'classId': trigger.classId,
|
||||||
|
'passiveId': trigger.passiveId,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
//console.log(util.inspect(effectData, true, 5, true))
|
||||||
|
|
||||||
|
resolve([cardEffects, effectData]);
|
||||||
|
|
||||||
|
});
|
||||||
|
return cPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function getCardPassive(){
|
||||||
|
const cPromise = new Promise((resolve, reject) => {
|
||||||
|
database.dbGetPassive().then(data => {
|
||||||
|
|
||||||
|
let passives = [];
|
||||||
|
|
||||||
|
data.forEach((passive) => {
|
||||||
|
|
||||||
|
passives.push({
|
||||||
|
'cardId': passive.cardId,
|
||||||
|
'passiveId': passive.passiveId,
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
console.log(passives);
|
||||||
|
resolve(passives);
|
||||||
|
|
||||||
|
})
|
||||||
|
.catch(err => { throw err; })
|
||||||
|
});
|
||||||
|
return cPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// https://www.geeksforgeeks.org/how-to-wait-for-multiple-promises-in-javascript/
|
||||||
|
// https://medium.com/@nikolozz/using-socket-io-with-async-await-13fa8c2dc9d9
|
||||||
|
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
|
||||||
|
|
||||||
|
// Build array of decks to get
|
||||||
|
let deckIds = [];
|
||||||
|
for(let i = 0; i < itemData.players.length; i++){
|
||||||
|
let deckStuff = itemData.players[i][1].deck;
|
||||||
|
deckIds.push([deckStuff.playerId, deckStuff.deckId]);
|
||||||
|
// So should be array of [playerId, deckId] which is primary key in DB
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get said decks, and their deckLists
|
||||||
|
const [decks, deckList] =
|
||||||
|
await Promise.all([
|
||||||
|
getDecks(deckIds),
|
||||||
|
getDeckList(deckIds)
|
||||||
|
]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
console.log('--- decks ---');
|
||||||
|
console.log(decks);
|
||||||
|
console.log('= deckLists =')
|
||||||
|
console.log(deckList);
|
||||||
|
console.log('------');
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Now loop the deckList for the cardIds
|
||||||
|
let deckCardIds = [];
|
||||||
|
deckList.forEach((deckItem) => {
|
||||||
|
deckCardIds.push(deckItem.cardId);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Next, get the cards in the deck by their ID
|
||||||
|
// TODO: https://stackoverflow.com/a/65510676
|
||||||
|
|
||||||
|
// Get each cards data, colourReqs, and classes
|
||||||
|
const [cards, cardClasses, cardColourRequirements, cardManaColours, cardPassives] =
|
||||||
|
await Promise.all([
|
||||||
|
getCards(deckCardIds),
|
||||||
|
getCardClasses(deckCardIds),
|
||||||
|
getCardColourRequirement(deckCardIds),
|
||||||
|
getCardManaColour(deckCardIds),
|
||||||
|
getCardPassive(deckCardIds),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Return all effect data from DB
|
||||||
|
const [effects] =
|
||||||
|
await Promise.all([
|
||||||
|
database.dbGetEffect(deckCardIds), // Get effects
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Loop the effects for their effectIds to then get the steps/triggers from DB
|
||||||
|
let effectIds = [];
|
||||||
|
await effects.forEach((effect) => {
|
||||||
|
effectIds.push(effect.effectId);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Then pass the effectIds to get their steps/triggers
|
||||||
|
const [effectSteps, effectTriggers] =
|
||||||
|
await Promise.all([
|
||||||
|
database.dbGetEffectStep(effectIds),
|
||||||
|
database.dbGetEffectTrigger(effectIds),
|
||||||
|
]);
|
||||||
|
|
||||||
|
/*
|
||||||
|
console.log('--- Effects ---');
|
||||||
|
console.log(effects);
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Build Effects
|
||||||
|
const [cardEffects] =
|
||||||
|
await Promise.all([
|
||||||
|
buildCardEffects(effects, effectSteps, effectTriggers),
|
||||||
|
]);
|
||||||
|
// cardEffects[0][cardId] == [array of effect IDs]
|
||||||
|
// cardEffects[1][effectId] == {object of the effect data}
|
||||||
|
//console.log(cardEffects);
|
||||||
|
|
||||||
|
// Build the cardData (maybe do all the components here too)
|
||||||
|
const [builtCards] =
|
||||||
|
await Promise.all([
|
||||||
|
buildCards(cards, cardClasses, cardColourRequirements, cardManaColours, cardEffects, cardPassives),
|
||||||
|
]);
|
||||||
|
|
||||||
|
//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?
|
||||||
|
let cardManaColour = {}; // The card colour value when played in mana zone
|
||||||
|
let cardEffect = {};
|
||||||
|
// Passive
|
||||||
|
let flight = {};
|
||||||
|
let reach = {};
|
||||||
|
|
||||||
|
// 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 TODO: REDO FOR itemData.component instead of existing itemData
|
||||||
|
decks.forEach((deck) => {
|
||||||
|
|
||||||
|
item.push(itemCount); // Add new item to add stuff for
|
||||||
|
itemData.component.deck[itemCount] = {'deckId':deck.deckId, 'playerId':deck.playerId, 'deckName':deck.deckName, 'deckSize': 0}; // Probably remove maxlength or change how I've just done it?
|
||||||
|
player[itemCount] = forPlayer; // TODO: Actually set to the correct player somehow
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
deckData[itemCount] = {'deckId':deck.deckId, 'playerId':deck.playerId, 'deckName':deck.deckName, 'maxLength': 0}; // Max length for listPositioning later in cardBuild
|
||||||
|
|
||||||
|
itemData.component.cardCount.deck[forPlayer] = 0;
|
||||||
|
|
||||||
|
boardElement[itemCount] = 'realDeck';
|
||||||
|
cardFace[itemCount] = 0; // Face down for deck
|
||||||
|
|
||||||
|
|
||||||
|
itemCount++; // Needed/good
|
||||||
|
forPlayer++; // Jank/bad
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
// CARDS IN DECKS
|
||||||
|
// TODO: REDO FOR itemData.component instead of existing itemData
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
// TODO: change deckData[key] to itemData.component.deck[key] allover then remove deckData
|
||||||
|
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
|
||||||
|
// TODO: Associate player to deck differently (both could be using same deck
|
||||||
|
// via some kind of 'try a deck' or 'use friends deck' option)
|
||||||
|
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)
|
||||||
|
|
||||||
|
// The data to be passed (for basic stat/inspect basically)
|
||||||
|
// atk, mana, cost, etc. will be their own components that alter game by game
|
||||||
|
|
||||||
|
// TODO: Make this the cardData that's important/will be passed to the clients
|
||||||
|
itemData.component.cardData[itemCount] = {
|
||||||
|
'id': builtCards[deckListItem.cardId].id,
|
||||||
|
'name': builtCards[deckListItem.cardId].name,
|
||||||
|
'cost': builtCards[deckListItem.cardId].cost,
|
||||||
|
'attack': builtCards[deckListItem.cardId].atk,
|
||||||
|
'type': builtCards[deckListItem.cardId].type, // Unit, Spell, Token, etc.
|
||||||
|
'passive': builtCards[deckListItem.cardId].passive,
|
||||||
|
'class': builtCards[deckListItem.cardId].cardClass,
|
||||||
|
'manaColour': builtCards[deckListItem.cardId].cardManaColour,
|
||||||
|
'colour': builtCards[deckListItem.cardId].colourRequirements,
|
||||||
|
'rarity': builtCards[deckListItem.cardId].rarity,
|
||||||
|
'effect': []
|
||||||
|
};
|
||||||
|
// Effects should just be the description to draw onto the card(s)
|
||||||
|
for (const [key, effect] of Object.entries(builtCards[deckListItem.cardId].effect)) {
|
||||||
|
itemData.component.cardData[itemCount]['effect'].push(effect.description);
|
||||||
|
}
|
||||||
|
// TODO: Above cardData should be built correctly first off probably. Then clean-up all the
|
||||||
|
// dupe code created for the tidy up
|
||||||
|
// For above 'builtCards' should be changed I think
|
||||||
|
//
|
||||||
|
|
||||||
|
boardElement[itemCount] = 'deck'; // Add all cards to deck at match start
|
||||||
|
|
||||||
|
// Add the entity into 'inDeck' component
|
||||||
|
// also add a listPosition based on current deckLength
|
||||||
|
itemData.component.inDeck[itemCount] = true; // Not sure what to store here
|
||||||
|
itemData.component.listPosition[itemCount] = itemData.component.cardCount.deck[forPlayer] + 1;
|
||||||
|
|
||||||
|
// Types in new component
|
||||||
|
switch(builtCards[deckListItem.cardId].type){
|
||||||
|
case(1): // Unit
|
||||||
|
itemData.component.type.unit[itemCount] = itemCount;
|
||||||
|
break;
|
||||||
|
case(2): // Spell
|
||||||
|
itemData.component.type.spell[itemCount] = itemCount;
|
||||||
|
break;
|
||||||
|
case(3): // Token
|
||||||
|
itemData.component.type.token[itemCount] = itemCount;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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++;
|
||||||
|
|
||||||
|
// TODO: everything like below, using the new component.XYZ
|
||||||
|
//itemData.component.deck[deckItem].deckSize++;
|
||||||
|
itemData.component.cardCount.deck[forPlayer]++;
|
||||||
|
//
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
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
|
||||||
|
player[itemCount] = forPlayer; // Jank TODO: actually set to correct player
|
||||||
|
|
||||||
|
// Add the cardEffect(s) to the cardEffect component
|
||||||
|
// only if there is an effect, starting to think about
|
||||||
|
// not wasting RAM/storage, y'know
|
||||||
|
if(cardData[itemCount].effect.length > 0){
|
||||||
|
cardEffect[itemCount] = cardData[itemCount].effect;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add each passive
|
||||||
|
if(cardData[itemCount].passive.length > 0){
|
||||||
|
//console.log(cardData[itemCount].passive);
|
||||||
|
for(let i = 0; i < cardData[itemCount].passive.length; i++){
|
||||||
|
|
||||||
|
switch (cardData[itemCount].passive[i]){
|
||||||
|
|
||||||
|
// Flight
|
||||||
|
case 1:
|
||||||
|
flight[itemCount] = itemCount;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Reach
|
||||||
|
case 2:
|
||||||
|
reach[itemCount] = itemCount;
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the handSize to 0, will need moving somewhere else
|
||||||
|
//itemData.component.deck[deckItem].handSize = 0;
|
||||||
|
itemData.component.cardCount.hand[forPlayer] = 0;
|
||||||
|
itemData.component.cardCount.board[forPlayer] = 0;
|
||||||
|
itemData.component.cardCount.shield[forPlayer] = 0;
|
||||||
|
itemData.component.cardCount.mana[forPlayer] = 0;
|
||||||
|
itemData.component.cardCount.grave[forPlayer] = 0;
|
||||||
|
itemData.component.cardCount.void[forPlayer] = 0;
|
||||||
|
itemCount++; // Increment item to not overwrite
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop adding cards is done, so set the cardCount of deck (so plays know deck length without the cards)
|
||||||
|
// for each player
|
||||||
|
//itemData.component.cardCount.deck[forPlayer] = deckData[deckItem].maxLength;
|
||||||
|
//itemData.component.deck[itemCount].deckSize++;
|
||||||
|
// Handsize here too, why not. It's always 0 to start (on generation)
|
||||||
|
// When it comes to reconnecting the actual data will be needed
|
||||||
|
//itemData.component.cardCount.hand[forPlayer] = 0;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// ADD all new elements, and updated data into itemData
|
||||||
|
itemData.item = item;
|
||||||
|
itemData.component.item = item;
|
||||||
|
itemData.itemCount = itemCount;
|
||||||
|
itemData.component.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;
|
||||||
|
itemData.cardManaColour = cardManaColour;
|
||||||
|
itemData.cardEffect = cardEffect;
|
||||||
|
itemData.flight = flight;
|
||||||
|
itemData.reach = reach;
|
||||||
|
|
||||||
|
// 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, cardManaColours, cardEffects, cardPassives){
|
||||||
|
|
||||||
|
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: [],
|
||||||
|
cardManaColour: [], // Should probably be an object
|
||||||
|
type: card.cardType,
|
||||||
|
atk: card.cardAttack,
|
||||||
|
rarity: card.cardRarity,
|
||||||
|
effect: [],
|
||||||
|
cardClass: [],
|
||||||
|
passive: [],
|
||||||
|
};
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add the card effects that have been pre-built
|
||||||
|
// Loop the cards (each with an array of effect IDs)
|
||||||
|
for (const [key] of Object.entries(cardEffects[0])) {
|
||||||
|
|
||||||
|
// If the cardId is not in builtCards, skip
|
||||||
|
// should always be, but eh
|
||||||
|
let cardId = cardEffects[0][key];
|
||||||
|
if(builtCards[cardId] === undefined){ continue; }
|
||||||
|
|
||||||
|
// Loop the effects in each of the card's effect arrays
|
||||||
|
for (const [key, value] of Object.entries(cardEffects[1])) {
|
||||||
|
// Add each effect that belongs to the card
|
||||||
|
builtCards[cardId].effect.push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add card mana colours (colour value when played in mana zone)
|
||||||
|
cardPassives.forEach((passive) => {
|
||||||
|
|
||||||
|
// Check the card exists (it should always, but don't want jank)
|
||||||
|
if(passive.cardId in builtCards){
|
||||||
|
// Add the colours to the class array (cards can have multiple)
|
||||||
|
builtCards[passive.cardId]['passive'].push(passive.passiveId);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
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
|
||||||
|
};
|
||||||
@ -0,0 +1,114 @@
|
|||||||
|
// A seperate list for components, so they're easy to recall
|
||||||
|
|
||||||
|
// Done as object, so it can be added to different rooms with components = NEW component?
|
||||||
|
const component = {
|
||||||
|
|
||||||
|
// Entity Stuff
|
||||||
|
item : [],
|
||||||
|
itemCount : 0,
|
||||||
|
|
||||||
|
roomId : null,
|
||||||
|
turn : 0,
|
||||||
|
playerTurn : 0,
|
||||||
|
player : {},
|
||||||
|
|
||||||
|
cardCount : {
|
||||||
|
deck : {},
|
||||||
|
hand : {},
|
||||||
|
board : {},
|
||||||
|
shield : {},
|
||||||
|
mana : {}, // For some of these, the length of object/array would be better...
|
||||||
|
grave : {},
|
||||||
|
void : {},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Card Stuff
|
||||||
|
cardData : {},
|
||||||
|
cardFace : {},
|
||||||
|
cardStatus : {
|
||||||
|
tapped : {},
|
||||||
|
attacking : {},
|
||||||
|
inspected : {},
|
||||||
|
targetable : {},
|
||||||
|
},
|
||||||
|
cardAttack : {},
|
||||||
|
cardColours : {}, // Replace with colour
|
||||||
|
colour : {
|
||||||
|
white : {}, // entityId, amountOfColour
|
||||||
|
blue : {},
|
||||||
|
red : {},
|
||||||
|
},
|
||||||
|
cardManaColour : {},
|
||||||
|
cardEffect : {}, // TODO: Split this into effect, trigger, step, targets.
|
||||||
|
cardCost : {},
|
||||||
|
cardSprite : {}, // id, position in spritesheet [0,4] e.g.
|
||||||
|
//cardPlayer = {},
|
||||||
|
|
||||||
|
// position 1..x, card, effect, effect part
|
||||||
|
stack : {},
|
||||||
|
|
||||||
|
// Deck Stuff?
|
||||||
|
deckIn : {},
|
||||||
|
deckData : {},
|
||||||
|
|
||||||
|
// UI (so users know what's been targetted)
|
||||||
|
selected : {},
|
||||||
|
selectable : {},
|
||||||
|
|
||||||
|
// Effect (break it up?)
|
||||||
|
effect : {},
|
||||||
|
effectTrigger : {},
|
||||||
|
// etc, etc.
|
||||||
|
|
||||||
|
// Board Elements
|
||||||
|
// loop component.shield for shield items
|
||||||
|
boardElement : {},
|
||||||
|
// Replace with following
|
||||||
|
deck : {},
|
||||||
|
inDeck : {},
|
||||||
|
hand : {},
|
||||||
|
board : {},
|
||||||
|
shield : {},
|
||||||
|
mana : {},
|
||||||
|
grave : {},
|
||||||
|
void : {},
|
||||||
|
|
||||||
|
//
|
||||||
|
listPosition : {},
|
||||||
|
// position (clientside)
|
||||||
|
// size (clientside)
|
||||||
|
|
||||||
|
// Passives
|
||||||
|
// component.passive.flight ?
|
||||||
|
passive : {
|
||||||
|
flight : {},
|
||||||
|
reach : {},
|
||||||
|
taunt : {},
|
||||||
|
},
|
||||||
|
|
||||||
|
type : {
|
||||||
|
unit : {},
|
||||||
|
spell : {},
|
||||||
|
token : {},
|
||||||
|
},
|
||||||
|
|
||||||
|
classes : {
|
||||||
|
orc : {},
|
||||||
|
human : {},
|
||||||
|
spirit : {},
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// For front-end
|
||||||
|
// position, size
|
||||||
|
|
||||||
|
// These should be used as such (Not 100% as yet)
|
||||||
|
// For onBoard()... for player()... for passive.flight()...
|
||||||
|
// Check the board, items belonging to playerX, for flight passive?
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
component
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,263 @@
|
|||||||
|
const mysql = require('mysql');
|
||||||
|
const con = mysql.createConnection({
|
||||||
|
host: "localhost",
|
||||||
|
user: "realmsdivided",
|
||||||
|
password: "realmsdivided",
|
||||||
|
database: "realms_divided"
|
||||||
|
});
|
||||||
|
|
||||||
|
function connect(){
|
||||||
|
con.connect(function(err) {
|
||||||
|
if (err) { console.log(err); }
|
||||||
|
else { console.log('DB Connected'); }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function disconnect(){
|
||||||
|
con.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
// My DB stuffs
|
||||||
|
function dbGetDecks(deckIds = false){
|
||||||
|
const dPromise = new Promise((resolve, reject) => {
|
||||||
|
let cards = [];
|
||||||
|
let sql = `SELECT
|
||||||
|
deckId
|
||||||
|
,playerId
|
||||||
|
,deckName
|
||||||
|
FROM deck
|
||||||
|
`;
|
||||||
|
|
||||||
|
// TODO: Jank, need to unjank it
|
||||||
|
if(deckIds){
|
||||||
|
for(let i = 0; i < deckIds.length; i++){
|
||||||
|
if(i == 0){ sql += ' WHERE '; }
|
||||||
|
else{ sql += ' OR '; }
|
||||||
|
sql += '(deckId = '+deckIds[i][1]+' AND playerId = '+deckIds[i][0]+')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
con.query(sql, function (err, result, fields) {
|
||||||
|
if (err) { throw err; reject(new Error(err)); }
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return dPromise;
|
||||||
|
}
|
||||||
|
function dbGetDeckList(deckIds = false){
|
||||||
|
const dPromise = new Promise((resolve, reject) => {
|
||||||
|
let cards = [];
|
||||||
|
let sql = `SELECT
|
||||||
|
deckId
|
||||||
|
,playerId
|
||||||
|
,cardId
|
||||||
|
,cardCount
|
||||||
|
FROM deck_cards
|
||||||
|
`;
|
||||||
|
|
||||||
|
// TODO: Jank, need to unjank it
|
||||||
|
if(deckIds){
|
||||||
|
for(let i = 0; i < deckIds.length; i++){
|
||||||
|
if(i == 0){ sql += ' WHERE '; }
|
||||||
|
else{ sql += ' OR '; }
|
||||||
|
sql += '(deckId = '+deckIds[i][1]+' AND playerId = '+deckIds[i][0]+')';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
con.query(sql, function (err, result, fields) {
|
||||||
|
if (err) { throw err; reject(new Error(err)); }
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return dPromise;
|
||||||
|
}
|
||||||
|
function dbGetCards(cardIds = false){
|
||||||
|
// Start with basic stuff in card table
|
||||||
|
const dPromise = new Promise((resolve, reject) => {
|
||||||
|
let sql = `SELECT
|
||||||
|
id
|
||||||
|
,cardName
|
||||||
|
,cardCost
|
||||||
|
,cardType
|
||||||
|
,cardAttack
|
||||||
|
,cardRarity
|
||||||
|
FROM card
|
||||||
|
`;
|
||||||
|
if(cardIds){ sql += 'WHERE card.id IN ('+cardIds+')'; }
|
||||||
|
|
||||||
|
con.query(sql, function (err, result, fields) {
|
||||||
|
if (err) { throw err; reject(new Error(err)); }
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return dPromise;
|
||||||
|
}
|
||||||
|
function dbGetCardClasses(cardIds = false){
|
||||||
|
// Get the classes assoc. on each card
|
||||||
|
const dPromise = new Promise((resolve, reject) => {
|
||||||
|
let sql = `SELECT
|
||||||
|
cardId
|
||||||
|
,classId
|
||||||
|
FROM card_class
|
||||||
|
`;
|
||||||
|
if(cardIds){ sql += 'WHERE cardId IN ('+cardIds+')'; }
|
||||||
|
|
||||||
|
con.query(sql, function (err, result, fields) {
|
||||||
|
if (err) { throw err; reject(new Error(err)); }
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return dPromise;
|
||||||
|
}
|
||||||
|
function dbGetCardColourRequirement(cardIds = false){
|
||||||
|
// Get the classes assoc. on each card
|
||||||
|
const dPromise = new Promise((resolve, reject) => {
|
||||||
|
let sql = `SELECT
|
||||||
|
cardId
|
||||||
|
,colourId
|
||||||
|
,cost
|
||||||
|
FROM card_colour_requirement
|
||||||
|
`;
|
||||||
|
if(cardIds){ sql += 'WHERE cardId IN ('+cardIds+')'; }
|
||||||
|
|
||||||
|
con.query(sql, function (err, result, fields) {
|
||||||
|
if (err) { throw err; reject(new Error(err)); }
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return dPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dbGetCardManaColour(cardIds = false){
|
||||||
|
// Get the classes assoc. on each card
|
||||||
|
const cPromise = new Promise((resolve, reject) => {
|
||||||
|
let sql = `SELECT
|
||||||
|
cardId
|
||||||
|
,colourId
|
||||||
|
FROM card_mana_colour
|
||||||
|
`;
|
||||||
|
if(cardIds){ sql += 'WHERE cardId IN ('+cardIds+')'; }
|
||||||
|
|
||||||
|
con.query(sql, function (err, result, fields) {
|
||||||
|
if (err) { throw err; reject(new Error(err)); }
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return cPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Effect stuff
|
||||||
|
function dbGetEffect(cardIds = false){
|
||||||
|
const ePromise = new Promise((resolve, reject) => {
|
||||||
|
let sql = `SELECT
|
||||||
|
cardId
|
||||||
|
,effectId
|
||||||
|
,description
|
||||||
|
FROM card_effect
|
||||||
|
|
||||||
|
INNER JOIN effect
|
||||||
|
ON effect.id = card_effect.effectId
|
||||||
|
`;
|
||||||
|
if(cardIds){ sql += 'WHERE cardId IN ('+cardIds+')'; }
|
||||||
|
|
||||||
|
con.query(sql, function (err, result, fields) {
|
||||||
|
if (err) { throw err; reject(new Error(err)); }
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return ePromise;
|
||||||
|
}
|
||||||
|
function dbGetEffectStep(effectIds = false){
|
||||||
|
const ePromise = new Promise((resolve, reject) => {
|
||||||
|
let sql = `SELECT
|
||||||
|
effectId,
|
||||||
|
effect_step.id AS stepId,
|
||||||
|
stepOrder,
|
||||||
|
basicEffectId,
|
||||||
|
amount,
|
||||||
|
colourId,
|
||||||
|
typeId,
|
||||||
|
classId,
|
||||||
|
passiveId,
|
||||||
|
itemFromStep
|
||||||
|
FROM effect_step
|
||||||
|
|
||||||
|
LEFT JOIN -- May not be a target?
|
||||||
|
effect_step_target
|
||||||
|
ON effect_step_target.effectStep = effect_step.id
|
||||||
|
`;
|
||||||
|
if(effectIds){ sql += 'WHERE effectId IN ('+effectIds+')'; }
|
||||||
|
|
||||||
|
con.query(sql, function (err, result, fields) {
|
||||||
|
if (err) { throw err; reject(new Error(err)); }
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return ePromise;
|
||||||
|
}
|
||||||
|
// Effect Trigger stuff
|
||||||
|
function dbGetEffectTrigger(effectIds = false){
|
||||||
|
const ePromise = new Promise((resolve, reject) => {
|
||||||
|
let sql = `SELECT
|
||||||
|
effect_trigger.id AS triggerId,
|
||||||
|
cardId,
|
||||||
|
effectId,
|
||||||
|
triggerTypeId,
|
||||||
|
amount,
|
||||||
|
colourId,
|
||||||
|
typeId,
|
||||||
|
classId,
|
||||||
|
passiveId
|
||||||
|
|
||||||
|
FROM effect_trigger
|
||||||
|
|
||||||
|
INNER JOIN card_effect
|
||||||
|
ON card_effect.effectId = effect_trigger.cardEffectId
|
||||||
|
INNER JOIN trigger_type
|
||||||
|
ON trigger_type.id = effect_trigger.triggerTypeId
|
||||||
|
|
||||||
|
LEFT JOIN -- May not be a target
|
||||||
|
effect_trigger_target
|
||||||
|
ON effect_trigger_target.effectTriggerId = effect_trigger.triggerTypeId
|
||||||
|
`;
|
||||||
|
if(effectIds){ sql += 'WHERE effectId IN ('+effectIds+')'; }
|
||||||
|
|
||||||
|
con.query(sql, function (err, result, fields) {
|
||||||
|
if (err) { throw err; reject(new Error(err)); }
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return ePromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Passive stuff
|
||||||
|
function dbGetPassive(cardIds = false){
|
||||||
|
const pPromise = new Promise((resolve, reject) => {
|
||||||
|
let sql = `SELECT
|
||||||
|
cardId
|
||||||
|
,passiveId
|
||||||
|
FROM card_passive
|
||||||
|
`;
|
||||||
|
if(cardIds){ sql += 'WHERE card.id IN ('+cardIds+')'; }
|
||||||
|
|
||||||
|
con.query(sql, function (err, result, fields) {
|
||||||
|
if (err) { throw err; reject(new Error(err)); }
|
||||||
|
resolve(result);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return pPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
connect, disconnect
|
||||||
|
// Testing, and trailing
|
||||||
|
, dbGetDecks
|
||||||
|
, dbGetDeckList
|
||||||
|
, dbGetCards
|
||||||
|
, dbGetCardClasses
|
||||||
|
, dbGetCardColourRequirement
|
||||||
|
, dbGetCardManaColour
|
||||||
|
, dbGetEffect
|
||||||
|
, dbGetEffectStep
|
||||||
|
, dbGetEffectTrigger
|
||||||
|
, dbGetPassive
|
||||||
|
};
|
||||||
@ -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);
|
||||||
@ -0,0 +1,124 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS `basic_effect` (
|
||||||
|
`id` smallint(6) NOT NULL AUTO_INCREMENT,
|
||||||
|
`effectName` tinytext DEFAULT NULL,
|
||||||
|
`effectDescription` varchar(250) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
INSERT INTO `basic_effect` (`id`, `effectName`, `effectDescription`) VALUES
|
||||||
|
(1, 'Equip', 'Add this cards attack, and effect(s) to another'),
|
||||||
|
(2, 'Heal', 'Untap X shield(s)'),
|
||||||
|
(3, 'Hurt', 'Deal X damage to target unit, this combat'),
|
||||||
|
(4, 'Recruit', 'Play from Hand'),
|
||||||
|
(5, 'Give Flight', 'Give a unit [Flight]');
|
||||||
|
|
||||||
|
|
||||||
|
DROP TABLE `card_effect`;
|
||||||
|
CREATE TABLE IF NOT EXISTS `card_effect` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`cardId` int(11) DEFAULT NULL,
|
||||||
|
`effectId` int(11) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
INSERT INTO `card_effect` (`id`, `cardId`, `effectId`) VALUES
|
||||||
|
(1, 1, 1);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `card_passive` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`cardId` int(11) DEFAULT NULL,
|
||||||
|
`passiveId` tinyint(4) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
|
||||||
|
DROP TABLE `effect`;
|
||||||
|
CREATE TABLE IF NOT EXISTS `effect` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`description` varchar(255) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
INSERT INTO `effect` (`id`, `description`) VALUES
|
||||||
|
(1, '(Tap and Pay 1 Red): [Recruit] a [Red][Orc] unit; Give it [Flight] this turn.');
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `effect_step` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`effectId` int(11) DEFAULT NULL,
|
||||||
|
`stepOrder` tinyint(4) DEFAULT NULL,
|
||||||
|
`basicEffectId` tinyint(4) DEFAULT NULL,
|
||||||
|
`amount` smallint(6) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
INSERT INTO `effect_step` (`id`, `effectId`, `stepOrder`, `basicEffectId`, `amount`) VALUES
|
||||||
|
(1, 1, 1, 4, 1),
|
||||||
|
(2, 1, 2, 5, 1);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `effect_step_target` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`effectStep` int(11) DEFAULT NULL,
|
||||||
|
`colourId` tinyint(4) DEFAULT NULL,
|
||||||
|
`typeId` tinyint(4) DEFAULT NULL,
|
||||||
|
`classId` tinyint(4) DEFAULT NULL,
|
||||||
|
`passiveId` tinyint(4) DEFAULT NULL,
|
||||||
|
`itemFromStep` int(11) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
INSERT INTO `effect_step_target` (`id`, `effectStep`, `colourId`, `typeId`, `classId`, `passiveId`, `itemFromStep`) VALUES
|
||||||
|
(1, 1, 3, 1, 5, NULL, NULL),
|
||||||
|
(2, 2, NULL, NULL, NULL, NULL, 1);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `effect_trigger` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`triggerTypeId` int(11) DEFAULT NULL,
|
||||||
|
`cardEffectId` int(11) DEFAULT NULL,
|
||||||
|
`amount` smallint(6) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
INSERT INTO `effect_trigger` (`id`, `triggerTypeId`, `cardEffectId`, `amount`) VALUES
|
||||||
|
(1, 1, 1, NULL),
|
||||||
|
(2, 2, 1, 1);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `effect_trigger_target` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`effectTriggerId` int(11) DEFAULT NULL,
|
||||||
|
`colourId` tinyint(4) DEFAULT NULL,
|
||||||
|
`typeId` tinyint(4) DEFAULT NULL,
|
||||||
|
`classId` tinyint(4) DEFAULT NULL,
|
||||||
|
`passiveId` int(11) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
INSERT INTO `effect_trigger_target` (`id`, `effectTriggerId`, `colourId`, `typeId`, `classId`, `passiveId`) VALUES
|
||||||
|
(1, 2, 3, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `passive` (
|
||||||
|
`id` tinyint(4) NOT NULL AUTO_INCREMENT,
|
||||||
|
`passiveName` varchar(50) DEFAULT NULL,
|
||||||
|
`passiveDescription` varchar(255) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
INSERT INTO `passive` (`id`, `passiveName`, `passiveDescription`) VALUES
|
||||||
|
(1, 'Flight', 'Ignore taunt, unattackable by non-[Flight] units'),
|
||||||
|
(2, 'Reach', 'Can attack [Flight] units');
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `trigger_type` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` varchar(50) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
INSERT INTO `trigger_type` (`id`, `name`) VALUES
|
||||||
|
(1, 'Tap'),
|
||||||
|
(2, 'Pay');
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
INSERT INTO `card_passive` (`id`, `cardId`, `passiveId`) VALUES (1, 1, 1);
|
||||||
|
INSERT INTO `card_passive` (`id`, `cardId`, `passiveId`) VALUES (2, 5, 2);
|
||||||
|
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
INSERT INTO `passive` (`id`, `passiveName`, `passiveDescription`) VALUES (3, 'Taunt', 'Must be targetted by attacks');
|
||||||
|
|
||||||
@ -0,0 +1,206 @@
|
|||||||
|
-- --------------------------------------------------------
|
||||||
|
-- Host: 127.0.0.1
|
||||||
|
-- Server version: 10.7.8-MariaDB-1:10.7.8+maria~ubu2004 - mariadb.org binary distribution
|
||||||
|
-- Server OS: debian-linux-gnu
|
||||||
|
-- HeidiSQL Version: 12.6.0.6765
|
||||||
|
-- --------------------------------------------------------
|
||||||
|
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET NAMES utf8 */;
|
||||||
|
/*!50503 SET NAMES utf8mb4 */;
|
||||||
|
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||||
|
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||||
|
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||||
|
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||||
|
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||||
|
|
||||||
|
|
||||||
|
-- Dumping database structure for realms_divided
|
||||||
|
CREATE DATABASE IF NOT EXISTS `realms_divided` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */;
|
||||||
|
USE `realms_divided`;
|
||||||
|
|
||||||
|
-- Dumping structure for table realms_divided.card
|
||||||
|
CREATE TABLE IF NOT EXISTS `card` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`cardName` varchar(255) NOT NULL,
|
||||||
|
`cardCost` tinyint(4) NOT NULL DEFAULT 2,
|
||||||
|
`cardType` tinyint(4) DEFAULT NULL,
|
||||||
|
`cardAttack` int(11) DEFAULT NULL,
|
||||||
|
`cardRarity` tinyint(4) NOT NULL DEFAULT 1,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
-- Dumping data for table realms_divided.card: ~5 rows (approximately)
|
||||||
|
INSERT INTO `card` (`id`, `cardName`, `cardCost`, `cardType`, `cardAttack`, `cardRarity`) VALUES
|
||||||
|
(1, 'Red1', 1, 1, 500, 1),
|
||||||
|
(2, 'White1', 1, 1, 1000, 1),
|
||||||
|
(3, 'Blue1', 2, 1, 1000, 1),
|
||||||
|
(4, 'White 8', 3, 2, NULL, 2),
|
||||||
|
(5, 'Red7', 2, 2, NULL, 2);
|
||||||
|
|
||||||
|
-- Dumping structure for table realms_divided.card_class
|
||||||
|
CREATE TABLE IF NOT EXISTS `card_class` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`cardId` int(11) DEFAULT NULL,
|
||||||
|
`classId` smallint(6) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
-- Dumping data for table realms_divided.card_class: ~4 rows (approximately)
|
||||||
|
INSERT INTO `card_class` (`id`, `cardId`, `classId`) VALUES
|
||||||
|
(1, 1, 1),
|
||||||
|
(2, 2, 1),
|
||||||
|
(3, 3, 1),
|
||||||
|
(4, 1, 2);
|
||||||
|
|
||||||
|
-- Dumping structure for table realms_divided.card_colour_requirement
|
||||||
|
CREATE TABLE IF NOT EXISTS `card_colour_requirement` (
|
||||||
|
`id` tinyint(4) NOT NULL AUTO_INCREMENT,
|
||||||
|
`cardId` int(11) DEFAULT NULL,
|
||||||
|
`colourId` tinyint(4) DEFAULT NULL,
|
||||||
|
`cost` tinyint(4) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
-- Dumping data for table realms_divided.card_colour_requirement: ~5 rows (approximately)
|
||||||
|
INSERT INTO `card_colour_requirement` (`id`, `cardId`, `colourId`, `cost`) VALUES
|
||||||
|
(1, 1, 3, 1),
|
||||||
|
(2, 2, 1, 1),
|
||||||
|
(3, 3, 2, 1),
|
||||||
|
(4, 4, 1, 2),
|
||||||
|
(5, 5, 3, 2);
|
||||||
|
|
||||||
|
-- Dumping structure for table realms_divided.card_effect
|
||||||
|
CREATE TABLE IF NOT EXISTS `card_effect` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`cardId` int(11) DEFAULT NULL,
|
||||||
|
`effectId` smallint(6) DEFAULT NULL,
|
||||||
|
`value` smallint(6) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
-- Dumping data for table realms_divided.card_effect: ~5 rows (approximately)
|
||||||
|
INSERT INTO `card_effect` (`id`, `cardId`, `effectId`, `value`) VALUES
|
||||||
|
(1, 1, 2, NULL),
|
||||||
|
(2, 3, 1, NULL),
|
||||||
|
(3, 2, 3, NULL),
|
||||||
|
(4, 4, 4, 1000),
|
||||||
|
(5, 5, 5, 3000);
|
||||||
|
|
||||||
|
-- Dumping structure for table realms_divided.class
|
||||||
|
CREATE TABLE IF NOT EXISTS `class` (
|
||||||
|
`id` smallint(6) NOT NULL AUTO_INCREMENT,
|
||||||
|
`parentId` tinyint(4) DEFAULT NULL,
|
||||||
|
`className` tinytext NOT NULL,
|
||||||
|
`primaryColour` smallint(6) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
-- Dumping data for table realms_divided.class: ~10 rows (approximately)
|
||||||
|
INSERT INTO `class` (`id`, `parentId`, `className`, `primaryColour`) VALUES
|
||||||
|
(1, NULL, 'Goblin', 3),
|
||||||
|
(2, NULL, 'Human', 1),
|
||||||
|
(3, NULL, 'Spirit', 2),
|
||||||
|
(4, NULL, 'Warrior', 1),
|
||||||
|
(5, NULL, 'Orc', 3),
|
||||||
|
(6, NULL, 'Beast', 5),
|
||||||
|
(7, NULL, 'Plant', 5),
|
||||||
|
(8, NULL, 'Undead', 4),
|
||||||
|
(9, NULL, 'Mechanical', 6),
|
||||||
|
(10, NULL, 'Void', 7);
|
||||||
|
|
||||||
|
-- Dumping structure for table realms_divided.colour
|
||||||
|
CREATE TABLE IF NOT EXISTS `colour` (
|
||||||
|
`id` tinyint(4) NOT NULL AUTO_INCREMENT,
|
||||||
|
`colourName` tinytext DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
-- Dumping data for table realms_divided.colour: ~7 rows (approximately)
|
||||||
|
INSERT INTO `colour` (`id`, `colourName`) VALUES
|
||||||
|
(1, 'White'),
|
||||||
|
(2, 'Blue'),
|
||||||
|
(3, 'Red'),
|
||||||
|
(4, 'Black'),
|
||||||
|
(5, 'Green'),
|
||||||
|
(6, 'Brown'),
|
||||||
|
(7, 'Void');
|
||||||
|
|
||||||
|
-- Dumping structure for table realms_divided.deck
|
||||||
|
CREATE TABLE IF NOT EXISTS `deck` (
|
||||||
|
`deckId` int(11) NOT NULL,
|
||||||
|
`playerId` int(11) NOT NULL,
|
||||||
|
`deckName` tinytext NOT NULL,
|
||||||
|
PRIMARY KEY (`deckId`,`playerId`) USING BTREE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
-- Dumping data for table realms_divided.deck: ~2 rows (approximately)
|
||||||
|
INSERT INTO `deck` (`deckId`, `playerId`, `deckName`) VALUES
|
||||||
|
(1, 1, 'Deck 1 P1'),
|
||||||
|
(1, 2, 'Deck 1 P2');
|
||||||
|
|
||||||
|
-- Dumping structure for table realms_divided.deck_cards
|
||||||
|
CREATE TABLE IF NOT EXISTS `deck_cards` (
|
||||||
|
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||||
|
`deckId` int(11) DEFAULT NULL,
|
||||||
|
`playerId` int(11) DEFAULT NULL,
|
||||||
|
`cardId` int(11) DEFAULT NULL,
|
||||||
|
`cardCount` tinyint(4) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
-- Dumping data for table realms_divided.deck_cards: ~5 rows (approximately)
|
||||||
|
INSERT INTO `deck_cards` (`id`, `deckId`, `playerId`, `cardId`, `cardCount`) VALUES
|
||||||
|
(1, 1, 1, 1, 25),
|
||||||
|
(2, 1, 1, 5, 10),
|
||||||
|
(3, 1, 2, 2, 15),
|
||||||
|
(4, 1, 2, 3, 14),
|
||||||
|
(5, 1, 2, 4, 6);
|
||||||
|
|
||||||
|
-- Dumping structure for table realms_divided.effect
|
||||||
|
CREATE TABLE IF NOT EXISTS `effect` (
|
||||||
|
`id` smallint(6) NOT NULL AUTO_INCREMENT,
|
||||||
|
`effectName` tinytext DEFAULT NULL,
|
||||||
|
`effectDescription` varchar(250) DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
-- Dumping data for table realms_divided.effect: ~5 rows (approximately)
|
||||||
|
INSERT INTO `effect` (`id`, `effectName`, `effectDescription`) VALUES
|
||||||
|
(1, 'Flight', 'Can ignore Taunt, cannot be tagetted by attack'),
|
||||||
|
(2, 'Reach', 'Can attack Flight units'),
|
||||||
|
(3, 'Equip', 'Add this cards attack, and effect(s) to another'),
|
||||||
|
(4, 'Heal', 'Untap X shield(s)'),
|
||||||
|
(5, 'Hurt', 'Deal X damage to target unit, this combat');
|
||||||
|
|
||||||
|
-- Dumping structure for table realms_divided.rarity
|
||||||
|
CREATE TABLE IF NOT EXISTS `rarity` (
|
||||||
|
`id` tinyint(4) NOT NULL AUTO_INCREMENT,
|
||||||
|
`rarityName` tinytext DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
-- Dumping data for table realms_divided.rarity: ~2 rows (approximately)
|
||||||
|
INSERT INTO `rarity` (`id`, `rarityName`) VALUES
|
||||||
|
(1, 'Common'),
|
||||||
|
(2, 'Uncommon'),
|
||||||
|
(3, 'Rare');
|
||||||
|
|
||||||
|
-- Dumping structure for table realms_divided.type
|
||||||
|
CREATE TABLE IF NOT EXISTS `type` (
|
||||||
|
`id` tinyint(4) NOT NULL AUTO_INCREMENT,
|
||||||
|
`typeName` tinytext DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
|
|
||||||
|
-- Dumping data for table realms_divided.type: ~2 rows (approximately)
|
||||||
|
INSERT INTO `type` (`id`, `typeName`) VALUES
|
||||||
|
(1, 'Unit'),
|
||||||
|
(2, 'Spell'),
|
||||||
|
(3, 'Token');
|
||||||
|
|
||||||
|
/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */;
|
||||||
|
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
|
||||||
|
/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;
|
||||||
|
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */;
|
||||||
@ -0,0 +1,92 @@
|
|||||||
|
// Moves card positions down/up for the old/new element for player in room
|
||||||
|
function setCardPosition(roomId, player, card, newPosition, newElement, oldPosition, oldElement){
|
||||||
|
|
||||||
|
// Move anything in the old boardElement after the old listPosition down by one
|
||||||
|
moveElementPositions(roomId, player, 0, oldElement, oldPosition);
|
||||||
|
|
||||||
|
// Move anything in the new boardElement after (including) the new listPosition up by one
|
||||||
|
moveElementPositions(roomId, player, 1, newElement, newPosition);
|
||||||
|
|
||||||
|
// Then fit the card into the new gap that's opened up
|
||||||
|
listPosition[card] = newPosition;
|
||||||
|
|
||||||
|
// Remove from oldElement
|
||||||
|
delete oldElement[card]
|
||||||
|
// Add to newElement
|
||||||
|
newElement[card] = newPosition;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// direction 0 up, 1 down
|
||||||
|
function moveElementPositions(roomId, player, direction, element, position){
|
||||||
|
|
||||||
|
position = position;
|
||||||
|
listPosition = global.roomData[roomId].itemData.component.listPosition;
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(element)) {
|
||||||
|
|
||||||
|
if(global.roomData[roomId].itemData.component.player[key] != player){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Move down from (not including) the position passed
|
||||||
|
if(direction == 0 && listPosition[key] > position){
|
||||||
|
listPosition[key]--;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move everything from (including) the new position up
|
||||||
|
if(direction == 1 && listPosition[key] >= position){
|
||||||
|
console.log('hit');
|
||||||
|
console.log(listPosition[key]);
|
||||||
|
console.log(position);
|
||||||
|
listPosition[key]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function canAttack(roomId, playerId, cardId){
|
||||||
|
|
||||||
|
if(cardId in global.roomData[roomId].itemData.component.cardStatus.tapped){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
function getTargetableCards(roomId, playerId, cardId){
|
||||||
|
|
||||||
|
// i.e. If no flight/reach, you cant attack flight, if not all shield tapped, cant attack tapped shield
|
||||||
|
// TODO: The above, for now just letting all units/shields targetable
|
||||||
|
|
||||||
|
let targetable = {};
|
||||||
|
// Board
|
||||||
|
for (const [key, value] of Object.entries(global.roomData[roomId].itemData.component.board)) {
|
||||||
|
// If not the players card (for now, will need better checks in future)
|
||||||
|
if(global.roomData[roomId].itemData.player[key] !== playerId){
|
||||||
|
targetable[key] = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Shield
|
||||||
|
for (const [key, value] of Object.entries(global.roomData[roomId].itemData.component.shield)) {
|
||||||
|
// If not the players card (for now, will need better checks in future)
|
||||||
|
if(global.roomData[roomId].itemData.player[key] !== playerId){
|
||||||
|
//if(!(key in global.roomData[roomId].itemData.player[playerId])){
|
||||||
|
targetable[key] = key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(targetable);
|
||||||
|
return targetable;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
setCardPosition
|
||||||
|
,canAttack
|
||||||
|
,getTargetableCards
|
||||||
|
};
|
||||||
|
|
||||||
@ -0,0 +1,628 @@
|
|||||||
|
const gameHelper = require('./gameHelper');
|
||||||
|
|
||||||
|
// For anything related to the actual game itself (kinda)
|
||||||
|
// this will be split into different bits, but should be what manages a rooms
|
||||||
|
// game states, and alladat
|
||||||
|
// Basically here to prevent circular dependencies (where I can)
|
||||||
|
|
||||||
|
|
||||||
|
// PlayerId is using array 0,1,2 for now, not the actual id
|
||||||
|
// actual Id would be better, but the player should be passed correctly
|
||||||
|
// from the client. They can edit data, but server-side validation SHOULD prevent
|
||||||
|
// in the future
|
||||||
|
function gameStart(roomId){
|
||||||
|
|
||||||
|
// Each player shuffles
|
||||||
|
for(const player of roomData[roomId].playerData){
|
||||||
|
|
||||||
|
// TODO: Make sure this only does for players, not spectators (in fut.)
|
||||||
|
shuffleDeck(roomId, player.playerDataId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each player plays X shield
|
||||||
|
for(const player of roomData[roomId].playerData){
|
||||||
|
|
||||||
|
// TODO: Make sure this only does for players, not spectators (in fut.)
|
||||||
|
// If shieldCount is less than the 'most' shield at start of game
|
||||||
|
for(let shieldCount = 0; shieldCount < 4; shieldCount++){
|
||||||
|
playShield(roomId, player.playerDataId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each player draws X cards to hand
|
||||||
|
for(const player of roomData[roomId].playerData){
|
||||||
|
|
||||||
|
// TODO: Make sure this only does for players, not spectators (in fut.)
|
||||||
|
//
|
||||||
|
for(let handCount = 0; handCount < 1; handCount++){
|
||||||
|
drawACard(roomId, player.playerDataId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function passTurn(roomId, playerId){
|
||||||
|
|
||||||
|
// TODO:Check playerId and roomId before doing the stuff, to verify the user
|
||||||
|
// IS the user, and in the room
|
||||||
|
|
||||||
|
let playerTurn = global.roomData[roomId].itemData.component.playerTurn;
|
||||||
|
//global.socketAlert(roomData[roomId].playerData[playerId].socketId, playerTurn, 'log');
|
||||||
|
|
||||||
|
if(playerTurn != playerId){
|
||||||
|
global.socketAlert(global.getPlayerSocketFromRoom(playerId, roomId), 'Not your turn', 'alert');
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Turns are 0,1,0,1 at the mo, no coinflip or re-order, etc. so this JANK is ok for now
|
||||||
|
// %2 as 2 players and Ids are 0 and 1 so it works
|
||||||
|
let newPlayerTurn = (playerTurn + 1)%maxPlayersPerRoom;
|
||||||
|
global.roomData[roomId].itemData.component.playerTurn = newPlayerTurn;
|
||||||
|
|
||||||
|
// If it's back to the player that went first, the turn count increased too
|
||||||
|
if(playerTurn == 0){
|
||||||
|
global.roomData[roomId].itemData.component.turn++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send turn data to each player
|
||||||
|
global.socketResponsePassTurn(roomId);
|
||||||
|
|
||||||
|
// Let the player know it's their turn via alert too (in case tabbed out)
|
||||||
|
// TODO: This could probably be done front-end from the newPlayerTurn in socketResponsePassTurn
|
||||||
|
global.socketAlert(roomData[roomId].playerData[newPlayerTurn].socketId, 'Your turn', 'alert');
|
||||||
|
|
||||||
|
// Start of the new players turn, draw a card
|
||||||
|
drawACard(roomId, newPlayerTurn);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function playShield(roomId, playerId){
|
||||||
|
|
||||||
|
if(global.roomData[roomId].itemData.component.shield[playerId] >= 2){
|
||||||
|
global.socketAlert(roomData[roomId].playerData[playerId].socketId, 'Shield full; cannot play shield', 'alert');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change position to last position available in shield zone
|
||||||
|
let fromPosition = global.roomData[roomId].itemData.component.cardCount.deck[playerId]; // 'top' of deck
|
||||||
|
let toPosition = global.roomData[roomId].itemData.component.cardCount.shield[playerId]+1; // newest shield pos.
|
||||||
|
|
||||||
|
// TODO: This is essential the same as in drawACard() so should be normalised into a function
|
||||||
|
// Get each card from the deck
|
||||||
|
for (const [key, value] of Object.entries(global.roomData[roomId].itemData.component.inDeck)) {
|
||||||
|
// Key is the entity here
|
||||||
|
|
||||||
|
// If the card inDeck does not belongs to the player, skip over it
|
||||||
|
if(global.roomData[roomId].itemData.component.player[key] != playerId){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the card isn't the last (bottom) card of deck, skip over it
|
||||||
|
// TODO: -1 is jank, sort so listPositions all start from 1..x
|
||||||
|
if(global.roomData[roomId].itemData.component.listPosition[key] != fromPosition){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The main man
|
||||||
|
// Move positions in hand/deck, and put the item from the deck into the shield
|
||||||
|
gameHelper.setCardPosition(roomId, playerId, key, toPosition, global.roomData[roomId].itemData.component.shield, fromPosition, global.roomData[roomId].itemData.component.inDeck);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reduce deckSize by 1 for the player that drew
|
||||||
|
global.roomData[roomId].itemData.component.cardCount.deck[playerId]--;
|
||||||
|
// And increase the shield size by 1
|
||||||
|
global.roomData[roomId].itemData.component.cardCount.shield[playerId]++;
|
||||||
|
|
||||||
|
// Then emit the deckSize and hand size to all the player's sockets
|
||||||
|
global.socketResponsePlayedShield(roomId, playerId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawACard(roomId, playerId){
|
||||||
|
|
||||||
|
if(global.roomData[roomId].itemData.component.cardCount.hand[playerId] >= 2){
|
||||||
|
global.socketAlert(roomData[roomId].playerData[playerId].socketId, 'Hand full; cannot draw card', 'alert');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(global.roomData[roomId].itemData.component.cardCount.deck[playerId] <= 0){
|
||||||
|
global.socketAlert(roomData[roomId].playerData[playerId].socketId, 'Deck empty; cannot draw card', 'alert');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Check no card event/trigger occured that prevents/change draw card
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Change position to last position available in hand
|
||||||
|
let fromPosition = global.roomData[roomId].itemData.component.cardCount.deck[playerId]; // 'top' of deck
|
||||||
|
let toPosition = global.roomData[roomId].itemData.component.cardCount.hand[playerId] + 1; // Rightmost hand pos (starting at 1)
|
||||||
|
|
||||||
|
// ECSey att2, there's surely a better way of getting playerX top card within inDeck?
|
||||||
|
// Tried unions but it messes up the object data. Maybe need to have no data in each object
|
||||||
|
// and have it literally just be keys?
|
||||||
|
|
||||||
|
// Get each card from the deck
|
||||||
|
for (const [key, value] of Object.entries(global.roomData[roomId].itemData.component.inDeck)) {
|
||||||
|
// Key is the entity here
|
||||||
|
|
||||||
|
// If the card inDeck does not belongs to the player, skip over it
|
||||||
|
if(global.roomData[roomId].itemData.component.player[key] != playerId){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the card isn't the last (bottom) card of deck, skip over it
|
||||||
|
// TODO: -1 is jank, sort so listPositions all start from 1..x
|
||||||
|
if(global.roomData[roomId].itemData.component.listPosition[key] != fromPosition){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The main man
|
||||||
|
// Move positions in hand/deck, and put the item from the deck into the hand
|
||||||
|
gameHelper.setCardPosition(roomId, playerId, key, toPosition, global.roomData[roomId].itemData.component.hand, fromPosition, global.roomData[roomId].itemData.component.inDeck);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reduce deckSize by 1 for the player that drew
|
||||||
|
global.roomData[roomId].itemData.component.cardCount.deck[playerId]--;
|
||||||
|
// And increase the hand size by 1
|
||||||
|
global.roomData[roomId].itemData.component.cardCount.hand[playerId]++;
|
||||||
|
|
||||||
|
// Then emit the deckSize and hand size to all the player's sockets
|
||||||
|
global.socketResponseDrawCard(roomId, playerId);
|
||||||
|
|
||||||
|
// Emit the 'hand' and related cardData for cards in the players hand
|
||||||
|
// Could merge this with the top?
|
||||||
|
global.socketResponsePlayerDrewCard(roomId, playerId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function playManaFromHand(roomId, playerId, position){
|
||||||
|
playFromHand(roomId, playerId, position, true);
|
||||||
|
}
|
||||||
|
// TODO: Rename and rejig the 3 play from hand functions
|
||||||
|
function playFromHand(roomId, playerId, position, mana = false){
|
||||||
|
|
||||||
|
let cardId = null;
|
||||||
|
// Get the cardId of the card from position within players hand
|
||||||
|
for (const [key, value] of Object.entries(global.roomData[roomId].itemData.component.hand)) {
|
||||||
|
// Key is the entity here
|
||||||
|
|
||||||
|
// If the card in hand's position does not match the position passed, skip it
|
||||||
|
if(global.roomData[roomId].itemData.component.listPosition[key] != position){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
cardId = key;
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then play the card
|
||||||
|
if(cardId == null){
|
||||||
|
// TODO: Respond to player (who triggered play) that this
|
||||||
|
// is an 'illegal/errored' move via socket
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to play the card from hand
|
||||||
|
if(!playACardFromHand(roomId, playerId, cardId, mana)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function playACardFromHand(roomId, playerId, cardId, mana = false){
|
||||||
|
|
||||||
|
// Add the card to field (and its effect to 'stack') or spell to the 'stack'
|
||||||
|
if(playACard(roomId, playerId, cardId, 'hand', mana) !== true){
|
||||||
|
// TODO: Return socket to player about 'illegal move' and why
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mana){
|
||||||
|
global.socketResponsePlayManaFromHand(roomId, playerId, cardId);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Maybe update with 'location played to' so animations, draws, etc. are correct
|
||||||
|
global.socketResponsePlayFromHand(roomId, playerId, cardId);
|
||||||
|
|
||||||
|
// TODO: Above can probably be the same/similar to what is added to the roomData on
|
||||||
|
// client-end (when it's fully done)
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'Play' a card is activation with cost (triggering play events)
|
||||||
|
// 'Summon' puts it onto the board without play events
|
||||||
|
// yada yada to think about in future
|
||||||
|
function playACard(roomId, playerId, cardId, playedFrom, mana = false){
|
||||||
|
|
||||||
|
// Play a mana (a card that was played as mana, loses it's normal card data)
|
||||||
|
if(mana){
|
||||||
|
// Play to board. If there's a 'onPlay' effect, add that to the 'stack'
|
||||||
|
return playMana(roomId, playerId, cardId, playedFrom);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Play a unit
|
||||||
|
if(cardId in global.roomData[roomId].itemData.component.type.unit){
|
||||||
|
// Play to board. If there's a 'onPlay' effect, add that to the 'stack'
|
||||||
|
return playAUnit(roomId, playerId, cardId, playedFrom);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cast a spell
|
||||||
|
if(cardId in global.roomData[roomId].itemData.component.type.spell){
|
||||||
|
// Add the card/effect onto the 'stack'. When complete/cancelled send to grave
|
||||||
|
return playASpell(roomId, playerId, cardId, playedFrom);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a token
|
||||||
|
if(cardId in global.roomData[roomId].itemData.component.type.token){
|
||||||
|
// ???? Tokens maybe just onto other cards as 'equips' or as standalones? IDK
|
||||||
|
return playAToken(roomId, playerId, cardId, playedFrom);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasSpaceOnBoard(roomId, playerId){
|
||||||
|
// TODO:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
function removeFromHand(roomId, playerId, cardId){
|
||||||
|
console.log('remove from hand');
|
||||||
|
if(cardId in global.roomData[roomId].itemData.component.hand){
|
||||||
|
delete(global.roomData[roomId].itemData.component.hand[cardId]);
|
||||||
|
}
|
||||||
|
global.roomData[roomId].itemData.component.cardCount.hand[playerId]--;
|
||||||
|
}
|
||||||
|
|
||||||
|
function playAUnit(roomId, playerId, cardId, playedFrom){
|
||||||
|
|
||||||
|
console.log('playAUnit');
|
||||||
|
|
||||||
|
// TODO: Make work, AND allow to play to opponent board (in future)
|
||||||
|
if(hasSpaceOnBoard(roomId, playerId) !== true){
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Costs
|
||||||
|
|
||||||
|
if(playedFrom == 'hand'){
|
||||||
|
|
||||||
|
// Remove from hand
|
||||||
|
removeFromHand(roomId, playerId, cardId);
|
||||||
|
|
||||||
|
// Add unit to board
|
||||||
|
global.roomData[roomId].itemData.component.board[cardId] = cardId;
|
||||||
|
global.roomData[roomId].itemData.component.cardCount.board[playerId]++;
|
||||||
|
|
||||||
|
// Change list positions of hand and board
|
||||||
|
// Next position on board (using cardCount to determine here)
|
||||||
|
// From current position in hand (the listPosition of the entity at this current point)
|
||||||
|
gameHelper.setCardPosition(roomId, playerId, cardId, global.roomData[roomId].itemData.component.cardCount.board[playerId], global.roomData[roomId].itemData.component.board, global.roomData[roomId].itemData.component.listPosition[cardId], global.roomData[roomId].itemData.component.hand);
|
||||||
|
|
||||||
|
// TODO: unit onPlay effects to the stack
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to board
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
function playASpell(roomId, playerId, cardId, playedFrom){
|
||||||
|
|
||||||
|
console.log('playASpell');
|
||||||
|
|
||||||
|
// TODO: Pay costs (Need to play mana first...)
|
||||||
|
|
||||||
|
// TODO: If spell has different effects, select which one/ensure
|
||||||
|
// correct one is used based on criteria
|
||||||
|
|
||||||
|
if(playedFrom == 'hand'){
|
||||||
|
// Remove from hand
|
||||||
|
console.log(cardId);
|
||||||
|
console.log(global.roomData[roomId].itemData.component.hand);
|
||||||
|
|
||||||
|
// Remove from ahnd
|
||||||
|
removeFromHand(roomId, playerId, cardId);
|
||||||
|
// if Spell when it's final effect finished (or removed) from stack, it should get
|
||||||
|
// added to grave then
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// If card was played, add its effect(s) to the stack
|
||||||
|
// If it should be added to stack (spell, or onPlay effect) do so
|
||||||
|
// Add to stack (each part of the spells effect)
|
||||||
|
// TODO: Use actual effects, for now just adding a 'drawCard' for testing
|
||||||
|
addToStack(roomId, playerId, cardId, null);
|
||||||
|
|
||||||
|
// Spell can/'has' been played
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
function playAToken(roomId, playerId, cardId, playedFrom){
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
function playMana(roomId, playerId, cardId, playedFrom){
|
||||||
|
|
||||||
|
console.log('playMana');
|
||||||
|
|
||||||
|
// TODO: Check if mana can be played (default 10 max)
|
||||||
|
|
||||||
|
// TODO: Check if a mana has already been played this turn (default 1 per turn per player)
|
||||||
|
|
||||||
|
if(playedFrom == 'hand'){
|
||||||
|
// Remove from hand
|
||||||
|
removeFromHand(roomId, playerId, cardId);
|
||||||
|
|
||||||
|
// Add card to mana zone
|
||||||
|
global.roomData[roomId].itemData.component.mana[cardId] = cardId;
|
||||||
|
global.roomData[roomId].itemData.component.cardCount.mana[playerId]++;
|
||||||
|
|
||||||
|
console.log(global.roomData[roomId].itemData.component.mana);
|
||||||
|
console.log(global.roomData[roomId].itemData.component.cardCount.mana[playerId]);
|
||||||
|
|
||||||
|
// Change list positions of hand and mana
|
||||||
|
gameHelper.setCardPosition(roomId, playerId, cardId
|
||||||
|
, global.roomData[roomId].itemData.component.cardCount.mana[playerId]
|
||||||
|
, global.roomData[roomId].itemData.component.mana
|
||||||
|
, global.roomData[roomId].itemData.component.listPosition[cardId]
|
||||||
|
, global.roomData[roomId].itemData.component.hand
|
||||||
|
);
|
||||||
|
|
||||||
|
// Mana has been played
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not 100% sure how to implement the stack
|
||||||
|
// TODO: Make it better
|
||||||
|
// TODO: Make it do actual things, currently just adds 'drawCard' effect
|
||||||
|
function addToStack(roomId, playerId, cardId, effectId){
|
||||||
|
|
||||||
|
// Stack does its own effect, or 'counter' etc. the card prior to it on the stack
|
||||||
|
// etc. etc.
|
||||||
|
// TODO: Add card effect to stack in reverse order OR have the stack work from x..0
|
||||||
|
// prolly the latter, makes sense to me
|
||||||
|
|
||||||
|
let stack = global.roomData[roomId].itemData.component.stack;
|
||||||
|
let stackLength = Object.keys(stack).length;
|
||||||
|
|
||||||
|
// TODO: First ensure the cardEffects are added in their step order 1..x
|
||||||
|
// Add as next event in the stack 1..x
|
||||||
|
// TODO: Use actual effect, not just 'draw' as that's just for testing
|
||||||
|
global.roomData[roomId].itemData.component.stack[stackLength + 1] =
|
||||||
|
{
|
||||||
|
'cardId': cardId
|
||||||
|
,'effect': null
|
||||||
|
,'effectStep': null
|
||||||
|
,'targetCard': null
|
||||||
|
,'targetPlayer': playerId
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log(global.roomData[roomId].itemData.component.stack);
|
||||||
|
|
||||||
|
// Send addToStack response to trigger any animations, etc.
|
||||||
|
global.socketResponseAddToStack(roomId);
|
||||||
|
|
||||||
|
// TODO: TEMP, this will need to wait for a 'resolve' accept from both players before the stack
|
||||||
|
// would trigger.
|
||||||
|
//resolveStack(roomId);
|
||||||
|
|
||||||
|
// TODO: Improve this, potentially drop out of function, and have a while stack > 0
|
||||||
|
// do the stack stuff. If it's <= 0 then other functionality is as normal
|
||||||
|
// Need to write a game loop for this rather than a
|
||||||
|
// nested function calling the getStackResponse.
|
||||||
|
getStackResponse(roomId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStackResponse(roomId){
|
||||||
|
|
||||||
|
// If there's something in the stack both/all players must accept to resolve
|
||||||
|
// and/or have a chance to play a counter/chain card/effect atop of the current
|
||||||
|
// top of the stack
|
||||||
|
|
||||||
|
// TODO: Opponent gets chance to chain first, then player
|
||||||
|
// if opponent chained, player gets to chain that chain first before stopping
|
||||||
|
// opponent double chaining
|
||||||
|
|
||||||
|
global.socketGetStackResponse(roomId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function acceptResolveStack(roomId, playerId){
|
||||||
|
|
||||||
|
// TODO: Make so each player needs to accept
|
||||||
|
// with whoever is to counter getting to ability to chain first
|
||||||
|
|
||||||
|
// Once the player has resolved, the next player gets the option to chain/resolve
|
||||||
|
|
||||||
|
// If all players have resolved, then resolve the top of the stack
|
||||||
|
resolveStack(roomId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveStack(roomId){
|
||||||
|
|
||||||
|
// Resolve the stack if all players have requested
|
||||||
|
// to resolve the stack, and not chain anything atop of it
|
||||||
|
|
||||||
|
// Does the next effect in the stack, if something
|
||||||
|
// is to chain onto the stack that would instead trigger
|
||||||
|
// 'addToStack' after paying any costs
|
||||||
|
|
||||||
|
// If there is anything in the stack
|
||||||
|
let stackLength = Object.keys(global.roomData[roomId].itemData.component.stack).length;
|
||||||
|
if(stackLength > 0){
|
||||||
|
|
||||||
|
// Send the 'resolve' response to room to trigger any animations, etc.
|
||||||
|
global.socketResponseResolveStack(roomId);
|
||||||
|
|
||||||
|
// Trigger the last (most recently added to) the stack effect
|
||||||
|
// THIS WILL NOW ACTUALLY CAST THE EFFECT STEP WITHOUT INTERRUPT
|
||||||
|
// While the stack is being resolved their are no counters/chains until
|
||||||
|
// the next stack action which players will get option to chain or not again
|
||||||
|
|
||||||
|
|
||||||
|
let stackTrigger = global.roomData[roomId].itemData.component.stack[stackLength];
|
||||||
|
|
||||||
|
// TODO: actually trigger the correct effect, etc.
|
||||||
|
// check if targets, check validity, etc. then trigger
|
||||||
|
|
||||||
|
// TODO: Remove drawACard and use actual triggers/effects
|
||||||
|
drawACard(roomId, stackTrigger.targetPlayer);
|
||||||
|
|
||||||
|
// Once the effect atop the stack has triggered, remove it from the stack
|
||||||
|
delete(global.roomData[roomId].itemData.component.stack[stackLength]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shuffle the deck 'randomly' for a certain player
|
||||||
|
function shuffleDeck(roomId, playerId){
|
||||||
|
|
||||||
|
// Create a tempDeck array of same length of the player deck
|
||||||
|
let deckLength = global.roomData[roomId].itemData.component.cardCount.deck[playerId];
|
||||||
|
let tempDeck = Array.from(Array(deckLength).keys());
|
||||||
|
|
||||||
|
// Loop the tempDeck and shuffle
|
||||||
|
// https://stackoverflow.com/a/73603221
|
||||||
|
for(let i = 0; i < deckLength; i++){
|
||||||
|
|
||||||
|
// picks the random number between 0 and length of the deck (-1 so 0..34)
|
||||||
|
let shuffle = Math.floor(Math.random() * (tempDeck.length - 1));
|
||||||
|
|
||||||
|
// swap the current listPosition with a random one with the deck count
|
||||||
|
[ tempDeck[i], tempDeck[shuffle] ] = [ tempDeck[shuffle], tempDeck[i] ];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
let tempDeckItem = 0;
|
||||||
|
// Now change the related inDeck entities listPositions to match the random number from tempDeck
|
||||||
|
for (const [key, value] of Object.entries(global.roomData[roomId].itemData.component.inDeck)) {
|
||||||
|
|
||||||
|
if(global.roomData[roomId].itemData.component.player[key] != playerId){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the tempDeckItems number as the listPosition for the inDeck item
|
||||||
|
// + 1 as listPositions start at 1
|
||||||
|
global.roomData[roomId].itemData.component.listPosition[key] = tempDeck[tempDeckItem] + 1;
|
||||||
|
|
||||||
|
// Move to next tempDeckItem
|
||||||
|
tempDeckItem++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
global.socketResponseShuffleDeck(roomId, playerId, true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function tapCard(roomId, playerId, cardId){
|
||||||
|
global.roomData[roomId].itemData.component.cardStatus.tapped[cardId] = cardId;
|
||||||
|
global.socketResponseTapped(roomId, cardId);
|
||||||
|
}
|
||||||
|
function untapCard(roomId, playerId, cardId){
|
||||||
|
delete(global.roomData[roomId].itemData.component.cardStatus.tapped[cardId]);
|
||||||
|
global.socketResponseUntapped(roomId, cardId);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function startAttack(roomId, playerId, cardId){
|
||||||
|
|
||||||
|
console.log('start attack');
|
||||||
|
if(!gameHelper.canAttack){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the card to 'attacking'
|
||||||
|
global.roomData[roomId].itemData.component.cardStatus.attacking[cardId] = cardId;
|
||||||
|
|
||||||
|
// TODO: Maybe set targetable as component and return that
|
||||||
|
global.roomData[roomId].itemData.component.cardStatus.targetable = gameHelper.getTargetableCards(roomId, playerId, cardId);
|
||||||
|
|
||||||
|
// Return the available targets (give them a border in UI)
|
||||||
|
global.socketResponseUpdateTargetable(roomId, playerId,
|
||||||
|
global.roomData[roomId].itemData.component.cardStatus.targetable
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// DATA RETURNER DUDES
|
||||||
|
// TODO: Where to put this? Kind of a helper, kind of functionality. Hmmmmm
|
||||||
|
// maybe do a dataHelper? then anything to return data can be included there?
|
||||||
|
// TODO: May get all the data from hand, board, grave, etc. in functions
|
||||||
|
// like this, then union all the data and return that in one swoomp
|
||||||
|
// Probably better to just get all the keys from the boardlemenets and do
|
||||||
|
// the loop once though...
|
||||||
|
function getPlayerHandData(roomId, playerId){
|
||||||
|
|
||||||
|
let handEntities = {};
|
||||||
|
let handPositions = {};
|
||||||
|
let handCardData = {};
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(global.roomData[roomId].itemData.component.hand)) {
|
||||||
|
|
||||||
|
// Key is entity ID here
|
||||||
|
|
||||||
|
// If the entity in hand belongs to the player, then they are allowed its data
|
||||||
|
if(global.roomData[roomId].itemData.component.player[key] == playerId){
|
||||||
|
|
||||||
|
// Get entity of items in the hand
|
||||||
|
handEntities[key] = global.roomData[roomId].itemData.component.hand[key];
|
||||||
|
// Get listPosition of just items in the hand
|
||||||
|
handPositions[key] = global.roomData[roomId].itemData.component.listPosition[key];
|
||||||
|
// Same for cardData
|
||||||
|
handCardData[key] = global.roomData[roomId].itemData.component.cardData[key]; // TODO: Nothing on client side?
|
||||||
|
|
||||||
|
// Leaving other bits for now
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: This is here to prevent overwriting with less content when a draw happens.
|
||||||
|
// Will want reverting at some point (or other functions for returning only certain bits
|
||||||
|
// everywhere else should be written)
|
||||||
|
|
||||||
|
//handEntities = global.roomData[roomId].itemData.component.hand;
|
||||||
|
handPositions = global.roomData[roomId].itemData.component.listPosition;
|
||||||
|
handCardData = global.roomData[roomId].itemData.component.cardData;
|
||||||
|
|
||||||
|
return {
|
||||||
|
'handEntities': handEntities,
|
||||||
|
'handPositions': handPositions,
|
||||||
|
'handCardData': handCardData
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
passTurn
|
||||||
|
,getPlayerHandData
|
||||||
|
,drawACard
|
||||||
|
,shuffleDeck
|
||||||
|
,playFromHand
|
||||||
|
,playManaFromHand
|
||||||
|
,acceptResolveStack
|
||||||
|
,gameStart
|
||||||
|
,startAttack
|
||||||
|
// TEMP
|
||||||
|
,tapCard
|
||||||
|
,untapCard
|
||||||
|
};
|
||||||
|
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "cardgame",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "Card Game",
|
||||||
|
"main": "server.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node server.js"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/eperezcosano/Uno.git"
|
||||||
|
},
|
||||||
|
"author": "Nathan (Aney) Steel",
|
||||||
|
"license": "???",
|
||||||
|
"homepage": "anetwork.uk",
|
||||||
|
"dependencies": {
|
||||||
|
"express": "^4.21.0",
|
||||||
|
"mysql": "^2.18.1",
|
||||||
|
"socket.io": "^4.8.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 6.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
@ -0,0 +1,151 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html lang="en">
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<link rel="stylesheet" href="/style.css">
|
||||||
|
<title>cardGame</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<div class="wrap">
|
||||||
|
<p>Rooms will not be HTML buttons eventually, but for now...</p>
|
||||||
|
<button onclick="requestRooms()" class="dib">Get Rooms</button>
|
||||||
|
<ul id="joinRoomButtons" class="joinRoomButtons dib" style="display:inline;">
|
||||||
|
</ul>
|
||||||
|
<button onclick="requestCreateRoom()" style="background:#EEE">Create Room</button>
|
||||||
|
</div>
|
||||||
|
<canvas id="canvas" width="1000" height="600"></canvas>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<button onclick="passTurn()">Pass Turn</button>
|
||||||
|
<button onclick="requestDrawACard()">Draw Card</button>
|
||||||
|
<button onclick="requestShuffleDeck()">Shuffle Deck</button>
|
||||||
|
<button onclick="requestResolveStack()">Resolve Stack</button>
|
||||||
|
<button onclick="requestFinaliseAttack()">Finalise Attack</button>
|
||||||
|
<br><br>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<button onclick="untapAllZones()">Untap all</button>
|
||||||
|
<button onclick="echoCards()">Print cardlist to console</button>
|
||||||
|
<button onclick="echoCardsInDeck(0)">Print cardsInDeck for player0</button>
|
||||||
|
<button onclick="echoCardsInDeck(1)">Print cardsInDeck for player1</button>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div>
|
||||||
|
<button onclick="getItemsAndPrintFrontEnd()">Echo cards: </button>
|
||||||
|
|
||||||
|
<select name="boardElementId" id="boardElementId">
|
||||||
|
<option value="">All</option>
|
||||||
|
<option value="realDeck">realDeck</option>
|
||||||
|
<option value="deck">deck</option>
|
||||||
|
<option value="hand">hand</option>
|
||||||
|
<option value="mana">mana</option>
|
||||||
|
<option value="shield">shield</option>
|
||||||
|
<option value="board">board</option>
|
||||||
|
<option value="grave">grave</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select name="playerId" id="playerId">
|
||||||
|
<option value="">All</option>
|
||||||
|
<option value="0">Player0</option>
|
||||||
|
<option value="1">Player1/Opponent</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<input name="listPositionId" id="listPositionId" placeholder="In position 1,2,3..."></input>
|
||||||
|
|
||||||
|
<select name="cardStatusId" id="cardStatusId">
|
||||||
|
<option value="">All</option>
|
||||||
|
<option value="attacking">Attacking</option>
|
||||||
|
<option value="tapped">Tapped</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select name="itemOrCardData" id="itemOrCardData">
|
||||||
|
<option value="item">Item Data</option>
|
||||||
|
<option value="card">Card Data</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div>
|
||||||
|
<button onclick="debugEffect();">Debug Effect</button>
|
||||||
|
|
||||||
|
<input type="text" placeholder="damage amount (X000s)" id="effectDamageAmount">
|
||||||
|
<input type="text" placeholder="targetId" id="effectTargetId">
|
||||||
|
<input type="text" placeholder="targetId2" id="effectTargetId2">
|
||||||
|
<input type="text" placeholder="targetPlayer" id="effectTargetPlayer">
|
||||||
|
|
||||||
|
<select name="effectAddRemove" id="effectAddRemove">
|
||||||
|
<option value="add">Add</option>
|
||||||
|
<option value="remove">Remove</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select name="effect" id="effect">
|
||||||
|
<option value="hurt">Hurt</option>
|
||||||
|
<option value="heal">Heal</option>
|
||||||
|
<option value="draw">Draw</option>
|
||||||
|
|
||||||
|
<option value="">-- Add/Remove --</option>
|
||||||
|
<option value="flight">Flight</option>
|
||||||
|
<option value="reach">Reach</option>
|
||||||
|
<option value="taunt">Taunt</option>
|
||||||
|
|
||||||
|
<option value="equipCard">Equip</option>
|
||||||
|
<option value="unequipCard">unEquip</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div>
|
||||||
|
<button onclick="debugTrigger();">Debug Trigger</button>
|
||||||
|
|
||||||
|
<input type="text" placeholder="targetId" id="triggerTargetId">
|
||||||
|
<select id="trigger">
|
||||||
|
<option value="tap">Tap</option>
|
||||||
|
<option value="pay">Pay</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<input placeholder="Amount" id="triggerAmount">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
<div>
|
||||||
|
<button onclick="debugEffectCanTrigger();">Debug Effect Can Trigger</button>
|
||||||
|
|
||||||
|
<input type="text" placeholder="targetId" id="ecTriggerTargetId">
|
||||||
|
<input type="text" placeholder="effectIndex" id="ecTriggerIndex">
|
||||||
|
<select id="ecDoTrigger">
|
||||||
|
<option value="0">Just Check</option>
|
||||||
|
<option value="1">Trigger Effect</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/socket.io/socket.io.js"></script>
|
||||||
|
|
||||||
|
<!-- Will probably minifier all the JS together when done -->
|
||||||
|
<script src="/js/global.js"></script>
|
||||||
|
<script src="/js/game/components.js"></script>
|
||||||
|
<script src="/js/game/dataUpdate.js"></script>
|
||||||
|
<script src="/js/canvas/main.js"></script>
|
||||||
|
<script src="/js/canvas/helpers.js"></script>
|
||||||
|
<script src="/js/canvas/draw.js"></script>
|
||||||
|
<script src="/js/canvas/interactionMenu.js"></script>
|
||||||
|
<script src="/js/canvas/interaction.js"></script>
|
||||||
|
|
||||||
|
<script src="/js/shapes.js"></script>
|
||||||
|
|
||||||
|
<script src="/js/main.js"></script>
|
||||||
|
<!-- Sockets likely rely on other things existing -->
|
||||||
|
<script src="/js/game/socket.js"></script>
|
||||||
|
|
||||||
|
<!-- <script src="/js/board.js"></script> -->
|
||||||
|
<script src="/js/helper.js"></script>
|
||||||
|
<script src="/js/effect.js"></script>
|
||||||
|
<script src="/js/debug.js"></script>
|
||||||
|
</body>
|
||||||
|
|
||||||
|
</html>
|
||||||
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,694 @@
|
|||||||
|
function drawGameBoard(){
|
||||||
|
|
||||||
|
// Reset board
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
drawPlayerNames();
|
||||||
|
calculateDeckPositions();
|
||||||
|
calculateHandPositions();
|
||||||
|
calculateBoardPositions();
|
||||||
|
calculateShieldPositions();
|
||||||
|
calculateManaPositions();
|
||||||
|
|
||||||
|
drawEntities();
|
||||||
|
|
||||||
|
// TEMP. Likely want to pass entities, but if you don't have cardData for them
|
||||||
|
// then don't draw the card face up?
|
||||||
|
drawFakeHand();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawPlayerNames(){
|
||||||
|
|
||||||
|
let playerWeight = 'normal';
|
||||||
|
if(gameData.playerId == gameData.playerTurn){ playerWeight = 'bold'; }
|
||||||
|
let opponentWeight = 'normal';
|
||||||
|
if(gameData.opponentId == gameData.playerTurn){ opponentWeight = 'bold'; }
|
||||||
|
|
||||||
|
// Player Name
|
||||||
|
printText(gameData.playerId,
|
||||||
|
50,
|
||||||
|
canvas.height - 70,
|
||||||
|
'left', 'alphabetic', 'normal', playerWeight, '10', 'Arial', '#000'
|
||||||
|
);
|
||||||
|
printText(gameData.players[gameData.playerId][1].playerId,
|
||||||
|
50,
|
||||||
|
canvas.height - 50,
|
||||||
|
'left', 'alphabetic', 'normal', playerWeight, '10', 'Arial', '#000'
|
||||||
|
);
|
||||||
|
|
||||||
|
// Opponent Name
|
||||||
|
printText(gameData.opponentId,
|
||||||
|
canvas.width - (ctx.measureText(gameData.opponentId).width + 50),
|
||||||
|
50,
|
||||||
|
'left', 'alphabetic', 'normal', opponentWeight, '10', 'Arial', '#000'
|
||||||
|
);
|
||||||
|
printText(gameData.players[gameData.opponentId][1].playerId,
|
||||||
|
canvas.width - (ctx.measureText(gameData.players[gameData.opponentId][1].playerId).width + 50),
|
||||||
|
70,
|
||||||
|
'left', 'alphabetic', 'normal', opponentWeight, '10', 'Arial', '#000'
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Move from draw into somewhere else
|
||||||
|
function calculateDeckPositions(){
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(gameData.deck)) {
|
||||||
|
|
||||||
|
// If the deckItem (key is the entityId),
|
||||||
|
// belongs to the player (matches this sockets player)
|
||||||
|
switch(gameData.player[key]){
|
||||||
|
|
||||||
|
// Set position for player deck
|
||||||
|
case gameData.playerId:
|
||||||
|
gameData.position[key] = [canvas.width-cardWidth-40, canvas.height-cardHeight-60]; // X/Y
|
||||||
|
gameData.size[key] = [cardWidth, cardHeight];
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Same for opponent. This will need redoing when 2v2,4v1,etc. are added
|
||||||
|
case gameData.opponentId:
|
||||||
|
gameData.position[key] = [40, 60]; // X/Y
|
||||||
|
gameData.size[key] = [cardWidth, cardHeight];
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// TODO: Move from draw into somewhere else
|
||||||
|
function calculateHandPositions(){
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(gameData.hand)) {
|
||||||
|
|
||||||
|
// key is entity Id here
|
||||||
|
|
||||||
|
switch(gameData.player[key]){
|
||||||
|
|
||||||
|
// Set position for player hand (all the time at current)
|
||||||
|
case gameData.playerId:
|
||||||
|
|
||||||
|
let cardsInHand = gameData.cardCount.hand[gameData.playerId];
|
||||||
|
let positionInHand = gameData.listPosition[key];
|
||||||
|
|
||||||
|
gameData.position[key] = [
|
||||||
|
canvas.width/2 - ((cardWidth * handScale) * (cardsInHand - (positionInHand+1)) - (cardMargin * (positionInHand+1)))
|
||||||
|
,canvas.height-(cardHeight * handScale)-20
|
||||||
|
];
|
||||||
|
gameData.size[key] = [cardWidth * handScale, cardHeight * handScale];
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Opponent, currently done in fakeHand
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
// TODO: Move from draw into somewhere else
|
||||||
|
function calculateBoardPositions(){
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(gameData.board)) {
|
||||||
|
|
||||||
|
// key is entity Id here
|
||||||
|
let cardsOnBoard = 0;
|
||||||
|
let position = 0;
|
||||||
|
|
||||||
|
switch(gameData.player[key]){
|
||||||
|
|
||||||
|
// Set position for player hand (all the time at current)
|
||||||
|
case gameData.playerId:
|
||||||
|
|
||||||
|
cardsOnBoard = gameData.cardCount.board[gameData.playerId];
|
||||||
|
position = gameData.listPosition[key];
|
||||||
|
|
||||||
|
gameData.position[key] = [
|
||||||
|
canvas.width/2 - (cardWidth * (cardsOnBoard - (position+1)) - (cardMargin * (position+1)))
|
||||||
|
,canvas.height-((cardHeight*2) * handScale)-30
|
||||||
|
];
|
||||||
|
|
||||||
|
gameData.size[key] = [cardWidth * handScale, cardHeight * handScale];
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Opponent
|
||||||
|
case gameData.opponentId:
|
||||||
|
|
||||||
|
cardsOnBoard = gameData.cardCount.board[gameData.opponentId];
|
||||||
|
position = gameData.listPosition[key];
|
||||||
|
|
||||||
|
gameData.position[key] = [
|
||||||
|
canvas.width/2 - (cardWidth * (cardsOnBoard - (position+1)) - (cardMargin * (position+1)))
|
||||||
|
,(cardHeight * handScale)+30
|
||||||
|
];
|
||||||
|
gameData.size[key] = [cardWidth * handScale, cardHeight * handScale];
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
function calculateShieldPositions(){
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(gameData.shield)) {
|
||||||
|
|
||||||
|
// key is entity Id here
|
||||||
|
let cardsOnBoard = 0;
|
||||||
|
let position = 0;
|
||||||
|
|
||||||
|
let fromX = 0;
|
||||||
|
let fromY = 0;
|
||||||
|
let split = 0;
|
||||||
|
|
||||||
|
let shieldScale = .5; // TODO: Make global (like handScale)
|
||||||
|
|
||||||
|
switch(gameData.player[key]){
|
||||||
|
|
||||||
|
// Set position for player hand (all the time at current)
|
||||||
|
case gameData.playerId:
|
||||||
|
|
||||||
|
position = gameData.listPosition[key];
|
||||||
|
|
||||||
|
fromX = 60;
|
||||||
|
fromY = 300;
|
||||||
|
|
||||||
|
// 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(position-1>=2){ split = 1; }
|
||||||
|
|
||||||
|
gameData.position[key] = [
|
||||||
|
(fromX+((position%2)*cardMargin)) +(position%2*(cardWidth*shieldScale))
|
||||||
|
,canvas.height-fromY+(split*(cardHeight*shieldScale)+(cardMargin*split))
|
||||||
|
];
|
||||||
|
|
||||||
|
gameData.size[key] = [cardWidth * shieldScale, cardHeight * shieldScale];
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Opponent
|
||||||
|
case gameData.opponentId:
|
||||||
|
|
||||||
|
position = gameData.listPosition[key];
|
||||||
|
|
||||||
|
fromX = canvas.width-60;
|
||||||
|
fromY = 300;
|
||||||
|
|
||||||
|
if(position-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
|
||||||
|
gameData.position[key] = [
|
||||||
|
(fromX+((position%2)*cardMargin)) +(position%2*(cardWidth*shieldScale)-(cardWidth*2*shieldScale))
|
||||||
|
,fromY+(split*(cardHeight*shieldScale)+(cardMargin*split)-((cardHeight*2*shieldScale) + cardMargin))
|
||||||
|
];
|
||||||
|
|
||||||
|
gameData.size[key] = [cardWidth * shieldScale, cardHeight * shieldScale];
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
function calculateManaPositions(){
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(gameData.mana)) {
|
||||||
|
|
||||||
|
// key is entity Id here
|
||||||
|
let cardsOnBoard = 0;
|
||||||
|
let position = 0;
|
||||||
|
|
||||||
|
let fromX = 0;
|
||||||
|
let fromY = 0;
|
||||||
|
|
||||||
|
let manaScale = .3;
|
||||||
|
|
||||||
|
switch(gameData.player[key]){
|
||||||
|
|
||||||
|
// Set position for player hand (all the time at current)
|
||||||
|
case gameData.playerId:
|
||||||
|
|
||||||
|
position = gameData.listPosition[key] - 1; // Position starts at 1
|
||||||
|
|
||||||
|
fromX = 60;
|
||||||
|
fromY = 60 + cardHeight*manaScale;
|
||||||
|
|
||||||
|
gameData.position[key] = [
|
||||||
|
(fromX)+(position*(cardWidth*manaScale)+cardMargin*position)
|
||||||
|
,canvas.height-fromY
|
||||||
|
];
|
||||||
|
|
||||||
|
gameData.size[key] = [cardWidth * manaScale, cardHeight * manaScale];
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Opponent
|
||||||
|
case gameData.opponentId:
|
||||||
|
|
||||||
|
position = gameData.listPosition[key] - 1; // Position starts at 1
|
||||||
|
|
||||||
|
fromX = 60;
|
||||||
|
fromY = 60;// + cardHeight*manaScale;
|
||||||
|
|
||||||
|
gameData.position[key] = [
|
||||||
|
// TODO: correct (then again all position need correcting tbf)
|
||||||
|
canvas.width - fromX-cardWidth*manaScale-(position*(cardWidth*manaScale))-cardMargin*position
|
||||||
|
,fromY
|
||||||
|
];
|
||||||
|
|
||||||
|
gameData.size[key] = [cardWidth * manaScale, cardHeight * manaScale];
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Move this function elsewhere, not really a draw function
|
||||||
|
function calculateCardSpacing(positionInt, size, standardSize){
|
||||||
|
|
||||||
|
let scaleMultiplier = size/standardSize;
|
||||||
|
return positionInt * scaleMultiplier;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function drawEntities(){
|
||||||
|
|
||||||
|
// Loop position component for entities with a position
|
||||||
|
for (const [key, value] of Object.entries(gameData.position)) {
|
||||||
|
// Key is the entityId here
|
||||||
|
|
||||||
|
// If the entity has a position AND a size, we can print it
|
||||||
|
if(gameData.size[key] !== undefined){
|
||||||
|
|
||||||
|
// Should prolly switch from ifs somehow, but it works for now
|
||||||
|
// If the entity is a deck
|
||||||
|
if(key in gameData.deck){
|
||||||
|
drawDeck(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If card in (player) hand
|
||||||
|
if(key in gameData.hand){
|
||||||
|
drawCardInHand(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If card on board
|
||||||
|
if(key in gameData.board){
|
||||||
|
drawCardOnBoard(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If shield
|
||||||
|
if(key in gameData.shield){
|
||||||
|
drawShield(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mana
|
||||||
|
if(key in gameData.mana){
|
||||||
|
drawMana(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(key in gameData.inInteractionMenu){
|
||||||
|
// Add the menu with 'play', 'activate effect', 'inspect', etc.
|
||||||
|
drawInteractionMenu(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawDeck(entity){
|
||||||
|
|
||||||
|
// Draw the deck shape
|
||||||
|
drawCardBack(entity);
|
||||||
|
|
||||||
|
// Draw circle for deck count to sit in
|
||||||
|
let deckCounterSprite = new Shape({
|
||||||
|
shape: 'circle',
|
||||||
|
x: gameData.position[entity][0],
|
||||||
|
y: gameData.position[entity][1],
|
||||||
|
width: gameData.size[entity][0]*.25,
|
||||||
|
height: gameData.size[entity][1]*.25,
|
||||||
|
fillStyle: '#DCDCDC'
|
||||||
|
});
|
||||||
|
deckCounterSprite.draw();
|
||||||
|
|
||||||
|
// Draw deck count text
|
||||||
|
ctx.fillStyle = '#000';
|
||||||
|
|
||||||
|
// Deck count for the deck belonging to player
|
||||||
|
let deckCount=gameData.cardCount.deck[gameData.player[entity]];
|
||||||
|
let textX=gameData.position[entity][0];//-(ctx.measureText(deckCount).width);
|
||||||
|
let textY=gameData.position[entity][1]+(ctx.measureText(deckCount).width/2);
|
||||||
|
|
||||||
|
printText(deckCount, textX, textY, 'center', 'bottom', 'normal', 'bold', '10', 'Arial', '#000');
|
||||||
|
|
||||||
|
}
|
||||||
|
function drawCardInHand(entity){
|
||||||
|
|
||||||
|
// TODO: Change card colour based on its colours
|
||||||
|
|
||||||
|
// Draw the card shape
|
||||||
|
let strokeStyle = '#AAA';
|
||||||
|
if(entity in gameData.cardStatus.targetable){ strokeStyle = '#222'; }
|
||||||
|
let shape = new Shape({
|
||||||
|
x: gameData.position[entity][0],
|
||||||
|
y: gameData.position[entity][1],
|
||||||
|
width: gameData.size[entity][0],
|
||||||
|
height: gameData.size[entity][1],
|
||||||
|
fillStyle: '#EEE',
|
||||||
|
strokeStyle: strokeStyle,
|
||||||
|
});
|
||||||
|
shape.draw();
|
||||||
|
|
||||||
|
drawCardDetails(entity);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawCardOnBoard(entity){
|
||||||
|
|
||||||
|
// TODO: Tapped, Attacking, Targettable, Activatable borders
|
||||||
|
// TODO: Passives, flight, etc. effects
|
||||||
|
|
||||||
|
let strokeStyle = '#AAA';
|
||||||
|
if(entity in gameData.cardStatus.tapped){ strokeStyle = '#6D0202'; }
|
||||||
|
if(entity in gameData.cardStatus.targetable){ strokeStyle = '#FF9A00'; }
|
||||||
|
if(entity in gameData.cardStatus.targetted){ strokeStyle = '#EC5300'; }
|
||||||
|
// Draw the card shape
|
||||||
|
let shape = new Shape({
|
||||||
|
x: gameData.position[entity][0],
|
||||||
|
y: gameData.position[entity][1],
|
||||||
|
width: gameData.size[entity][0],
|
||||||
|
height: gameData.size[entity][1],
|
||||||
|
fillStyle: '#EEE',
|
||||||
|
strokeStyle: strokeStyle,
|
||||||
|
});
|
||||||
|
shape.draw();
|
||||||
|
|
||||||
|
drawCardDetails(entity);
|
||||||
|
|
||||||
|
}
|
||||||
|
function drawShield(entity){
|
||||||
|
|
||||||
|
// TODO: Tapped
|
||||||
|
drawCardBack(entity);
|
||||||
|
|
||||||
|
let strokeStyle = '#AAA';
|
||||||
|
if(entity in gameData.cardStatus.tapped){ strokeStyle = '#555'; }
|
||||||
|
if(entity in gameData.cardStatus.targetable){ strokeStyle = '#222'; }
|
||||||
|
// Draw the card shape
|
||||||
|
let shape = new Shape({
|
||||||
|
x: gameData.position[entity][0],
|
||||||
|
y: gameData.position[entity][1],
|
||||||
|
width: gameData.size[entity][0],
|
||||||
|
height: gameData.size[entity][1],
|
||||||
|
fillStyle: false,
|
||||||
|
strokeStyle: strokeStyle,
|
||||||
|
});
|
||||||
|
shape.draw();
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
function drawMana(entity){
|
||||||
|
|
||||||
|
// TODO: Show the colour/Icon of the mana for ease when tapping, etc.
|
||||||
|
console.log('drawMana');
|
||||||
|
console.log(entity);
|
||||||
|
|
||||||
|
// TODO: Tapped
|
||||||
|
drawCardBack(entity);
|
||||||
|
|
||||||
|
let strokeStyle = '#AAA';
|
||||||
|
if(entity in gameData.cardStatus.tapped){ strokeStyle = '#555'; }
|
||||||
|
if(entity in gameData.cardStatus.targetable){ strokeStyle = '#222'; }
|
||||||
|
// Draw the card shape
|
||||||
|
let shape = new Shape({
|
||||||
|
x: gameData.position[entity][0],
|
||||||
|
y: gameData.position[entity][1],
|
||||||
|
width: gameData.size[entity][0],
|
||||||
|
height: gameData.size[entity][1],
|
||||||
|
fillStyle: false,
|
||||||
|
strokeStyle: strokeStyle,
|
||||||
|
});
|
||||||
|
shape.draw();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// The draw all the card data, name, colour, etc.
|
||||||
|
function drawCardDetails(entity){
|
||||||
|
|
||||||
|
//console.log(gameData.cardData[entity]);
|
||||||
|
drawCardImage(entity); // TODO: Use a sprite per card, not just temp.
|
||||||
|
drawCardText(entity);
|
||||||
|
/*
|
||||||
|
this.printColourRequirements(entity);
|
||||||
|
this.printCardPassives(entity);
|
||||||
|
*/
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawCardImage(entity){
|
||||||
|
|
||||||
|
let positionX = gameData.position[entity][0];
|
||||||
|
let positionY = gameData.position[entity][1];
|
||||||
|
let width = gameData.size[entity][0];
|
||||||
|
let height = gameData.size[entity][1];
|
||||||
|
|
||||||
|
// Create the clipping shape
|
||||||
|
let cardImageContainer = new Shape({
|
||||||
|
shape: 'unit',
|
||||||
|
x: positionX+width*.5, // Center (probably change to an int in future)
|
||||||
|
y: positionY+calculateCardSpacing(65, height, cardHeight),
|
||||||
|
width: calculateCardSpacing(100, width, cardWidth),
|
||||||
|
height: calculateCardSpacing(150, height, cardHeight)
|
||||||
|
});
|
||||||
|
// Save canvas drawing, start the clip
|
||||||
|
cardImageContainer.startClip();
|
||||||
|
|
||||||
|
// Draw the image into the clipping mask
|
||||||
|
// image, dx,dy,dw,dh
|
||||||
|
// image, sx,sy, sw,sh,dx,dy,dw,dh
|
||||||
|
|
||||||
|
// TODO: give cards/cardData a sprite position and use it [0,0], [1,0], [1,4], etc...
|
||||||
|
let spriteSheetX = 80;//*cardSprite[entity][0];
|
||||||
|
let spriteSheetY = 120;//*cardSprite[entity][1];
|
||||||
|
ctx.drawImage(cardArt, spriteSheetX,spriteSheetY
|
||||||
|
,80
|
||||||
|
,120
|
||||||
|
,positionX
|
||||||
|
,positionY
|
||||||
|
,width
|
||||||
|
,height
|
||||||
|
);
|
||||||
|
|
||||||
|
// Restore the canvas draw post clip applied, to get everything else back too
|
||||||
|
cardImageContainer.endClip();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawCardText(entity){
|
||||||
|
|
||||||
|
let positionX = gameData.position[entity][0];
|
||||||
|
let positionY = gameData.position[entity][1];
|
||||||
|
let width = gameData.size[entity][0];
|
||||||
|
let height = gameData.size[entity][1];
|
||||||
|
|
||||||
|
// Calculate the fontsize for the card at different 'zooms'/sizes
|
||||||
|
let fontSize = width/cardWidth*8; // 8pt. 10 = baseFontSize of 10pt
|
||||||
|
//ctx.font = "bold "+fontSize+"pt Arial";
|
||||||
|
let fillStyle = '#000';
|
||||||
|
let strokeStyle = '#FFF';
|
||||||
|
let strokeSize = fontSize/12;
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// 5px on regular 80px = 80/16, so width/16 = the same spacing. Probbaly just another
|
||||||
|
// function to calc the spacings like cardSize/actualSizeOfSpace (80/5) = 16, then. sizeOfThisCard/16 = RETURN
|
||||||
|
// This is fow the positionX + Xs as the Xs will not be that size if the card size is smaller/bigger
|
||||||
|
// so TODO gotta make it scaled % wise with the 80/16 I wrote above
|
||||||
|
|
||||||
|
// NAME
|
||||||
|
if(gameData.cardData[entity] !== undefined && gameData.cardData[entity]['name'] !== undefined){
|
||||||
|
printText(gameData.cardData[entity]['name']
|
||||||
|
, positionX + 5 + 10 // + 5 (spacing for cost) + 10 for more spacing
|
||||||
|
, positionY + 10 + 5 // + 10pt + 5 as it's the spacing I actually want
|
||||||
|
, 'left', 'alphabetic', 'normal', 'normal', fontSize, 'Arial', fillStyle, strokeStyle, strokeSize, false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// COST
|
||||||
|
if(gameData.cardData[entity] !== undefined && gameData.cardData[entity]['cost'] !== undefined){
|
||||||
|
printText(gameData.cardData[entity]['cost']
|
||||||
|
, positionX + 5
|
||||||
|
, positionY + 10 + 5 // + 10pt + 5 as it's the spacing I actually want
|
||||||
|
, 'left', 'alphabetic', 'normal', 'normal', fontSize, 'Arial', fillStyle, strokeStyle, strokeSize, false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TYPE
|
||||||
|
if(gameData.cardData[entity] !== undefined && gameData.cardData[entity]['type'] !== undefined){
|
||||||
|
printText(
|
||||||
|
CLASS[gameData.cardData[entity]['type']]
|
||||||
|
, positionX + 5
|
||||||
|
, positionY + width // To be slightly over bottom of image (which uses width to position)
|
||||||
|
, 'left', 'alphabetic', 'normal', 'normal', fontSize, 'Arial', fillStyle, strokeStyle, strokeSize, false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// CLASS(ES)
|
||||||
|
if(gameData.cardData[entity] !== undefined && gameData.cardData[entity]['class'] !== undefined){
|
||||||
|
let classes = '';
|
||||||
|
// Loop the classes, and add each of them to the card
|
||||||
|
for(let i = 0; i < gameData.cardData[entity]['class'].length; i++){
|
||||||
|
// Add a comma to seperate if it's not the first class
|
||||||
|
if(i > 0){ classes += ', '; }
|
||||||
|
// Add the class name to the list to print
|
||||||
|
classes += CLASS[gameData.cardData[entity]['class'][i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
printText(
|
||||||
|
classes
|
||||||
|
, positionX + 5 + 5 + ctx.measureText(CLASS[gameData.cardData[entity]['type']]).width // + another 5 and width of type for spacing
|
||||||
|
, positionY + width // To be slightly over bottom of image (which uses width to position)
|
||||||
|
, 'left', 'alphabetic', 'normal', 'normal', fontSize, 'Arial', fillStyle, strokeStyle, strokeSize, false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// EFFECT(S)
|
||||||
|
if(gameData.cardData[entity] !== undefined && gameData.cardData[entity]['effect'] !== undefined){
|
||||||
|
// TODO: Split the text onto lines based on character count (but not splitting words) so they fit into the
|
||||||
|
// bounds of the card element
|
||||||
|
let effects = '';
|
||||||
|
// Loop the classes, and add each of them to the card
|
||||||
|
for(let i = 0; i < gameData.cardData[entity]['effect'].length; i++){
|
||||||
|
// Split effects with an additonal break (any more than 1st effect)
|
||||||
|
if(i > 0){ classes += '\n'; }
|
||||||
|
// Add the class name to the list to print
|
||||||
|
effects += gameData.cardData[entity]['effect'][i];
|
||||||
|
}
|
||||||
|
printText(
|
||||||
|
effects
|
||||||
|
, positionX + 5
|
||||||
|
, positionY + width + 10 // To be beneath image, and type + classes
|
||||||
|
, 'left', 'alphabetic', 'normal', 'normal', fontSize, 'Arial', fillStyle, strokeStyle, strokeSize, false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ATTACK
|
||||||
|
if(gameData.cardData[entity] !== undefined && gameData.cardData[entity]['attack'] !== undefined){
|
||||||
|
printText(gameData.cardData[entity]['attack']
|
||||||
|
, positionX + width/2 - ctx.measureText(gameData.cardData[entity]['attack']).width/2 // Should be centred
|
||||||
|
, positionY + 10 + (height - 10) // + 10pt + 5 as it's the spacing I actually want
|
||||||
|
, 'left', 'alphabetic', 'normal', 'normal', fontSize, 'Arial', fillStyle, strokeStyle, strokeSize, false
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset font for other draws
|
||||||
|
// TODO: Probably just use a save/restore for the font draws in here
|
||||||
|
ctx.font = "10pt Arial";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function drawCardBack(entity){
|
||||||
|
ctx.drawImage(
|
||||||
|
cardBackArt
|
||||||
|
,0 // X Position From in Spritesheet
|
||||||
|
,0 // Y Position From in Spritesheet
|
||||||
|
,80 // Width of image selection from Spritesheet
|
||||||
|
,120 // Height of image selection from Spritesheet
|
||||||
|
,gameData.position[entity][0] // X
|
||||||
|
,gameData.position[entity][1] // Y
|
||||||
|
,gameData.size[entity][0] // Width
|
||||||
|
,gameData.size[entity][1] // Height
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TEMP, or maybe not for opponent. Not 100% how to draw opponent hand without passing the data
|
||||||
|
// avoiding passing the data (for now) to prevent cheating by checking their cardIds etc. in the
|
||||||
|
// console. An issue that comes with making this in JS...
|
||||||
|
function drawFakeHand(){
|
||||||
|
|
||||||
|
// itemList length is the count (/highest listPosition) in the hand in this case
|
||||||
|
// i is the listPosition of the entity (which since this isn't using the entities at the mo...)
|
||||||
|
|
||||||
|
// All jank temp kinda vars
|
||||||
|
// Needed as i substitute to track the fake hand drawn count
|
||||||
|
let opponentHandItem = 0;
|
||||||
|
let itemListLength = 0; // Believe value should just be the count of current cardCount.hand
|
||||||
|
let i = 0; // Believe value should just be the count of current cardCount.hand
|
||||||
|
|
||||||
|
// Loop each 'hand' not actual hand, but count of cards in hand
|
||||||
|
for (const [key, value] of Object.entries(gameData.cardCount.hand)) {
|
||||||
|
|
||||||
|
// key is the playerId here
|
||||||
|
switch(gameData.player[key]){
|
||||||
|
|
||||||
|
// Set position for opponents deck
|
||||||
|
case gameData.opponentId:
|
||||||
|
|
||||||
|
// Then loop the size of the hand
|
||||||
|
itemListLength = value; // Believe value should just be the count of current cardCount.hand
|
||||||
|
i = opponentHandItem; // Believe value should just be the count of current cardCount.hand
|
||||||
|
|
||||||
|
for(i; i < itemListLength; i++){
|
||||||
|
ctx.drawImage(
|
||||||
|
cardBackArt
|
||||||
|
,0 // X Position From in Spritesheet
|
||||||
|
,0 // Y Position From in Spritesheet
|
||||||
|
,80 // Width of image selection from Spritesheet
|
||||||
|
,120 // Height of image selection from Spritesheet
|
||||||
|
,canvas.width/2 - ((cardWidth*handScale) * (itemListLength - (i+1)) - (cardMargin * (i+1)))
|
||||||
|
// X
|
||||||
|
,20 // Y
|
||||||
|
,cardWidth*handScale // Width
|
||||||
|
,cardHeight*handScale // Height
|
||||||
|
);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawInteractionMenu(entity){
|
||||||
|
// Draws the interactable options availabe for an entity
|
||||||
|
// TODO: Draw atop/below depening on position, etc.
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(gameData.interactionOption)) {
|
||||||
|
|
||||||
|
// Draw the interaction box (TODO: make much better);
|
||||||
|
let menuItem = new Shape({
|
||||||
|
x: value.x,
|
||||||
|
y: value.y,
|
||||||
|
width: value.width,
|
||||||
|
height: value.height,
|
||||||
|
fillStyle: '#DDD',
|
||||||
|
strokeStyle: '#666'
|
||||||
|
});
|
||||||
|
menuItem.draw();
|
||||||
|
|
||||||
|
// Add the text
|
||||||
|
printText(
|
||||||
|
key
|
||||||
|
,value.x + value.width/2
|
||||||
|
,value.y + value.height/2
|
||||||
|
, 'center', 'middle', 'normal', 'normal', 8, 'Arial', '#333', false, false, false
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Put the stack into the UI
|
||||||
|
function drawStackResolve(){
|
||||||
|
|
||||||
|
}
|
||||||
|
// TODO: Draw stack, all cards in the stack in order (use the cardId for display)
|
||||||
|
// make inspectable, and display which part of the effect will be triggered in it's
|
||||||
|
// stack position
|
||||||
|
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
function printText(text, positionX, positionY, alignment = 'left', baseline = 'alphabetic', style = 'normal', weight = 'normal', size = '10', font = 'Arial', colour = '#000', strokeStyle = false, lineWidth = false, strokeOnly = false){
|
||||||
|
|
||||||
|
// Save the styling, and content already on the canvas
|
||||||
|
ctx.save();
|
||||||
|
|
||||||
|
// Do the alterations and print the text
|
||||||
|
context.textAlign = alignment;
|
||||||
|
context.textBaseline = baseline;
|
||||||
|
|
||||||
|
// Set the font styling
|
||||||
|
ctx.font = style+' '+weight+' '+size+'pt'+' '+font;
|
||||||
|
//ctx.font-style = fontStyle; // normal, italic, oblique
|
||||||
|
ctx.fillStyle = colour;
|
||||||
|
|
||||||
|
if(strokeStyle && lineWidth){
|
||||||
|
// Set the stroke styling
|
||||||
|
ctx.strokeStyle = strokeStyle;
|
||||||
|
ctx.lineWidth = lineWidth;
|
||||||
|
|
||||||
|
// Add the stroke (first, before fill) as it looks better
|
||||||
|
ctx.strokeText(text, positionX, positionY);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!strokeOnly){
|
||||||
|
// Actually add the text
|
||||||
|
ctx.fillText(text, positionX, positionY);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore the prior existing canvas content
|
||||||
|
ctx.restore();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,125 @@
|
|||||||
|
// Clickable checks, event listeners, etc.
|
||||||
|
|
||||||
|
function clickableCheck(cursorX,cursorY,entity = null, positions = null){
|
||||||
|
|
||||||
|
if(entity == null && positions == null){ return false; }
|
||||||
|
if(entity != null){
|
||||||
|
positions = {
|
||||||
|
'x': gameData.position[entity][0],
|
||||||
|
'y': gameData.position[entity][1],
|
||||||
|
'width': gameData.size[entity][0],
|
||||||
|
'height': gameData.size[entity][0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collision detection between clicked offset and clickableItems
|
||||||
|
// https://stackoverflow.com/a/9880302
|
||||||
|
if(
|
||||||
|
cursorY > positions['y'] && cursorY < positions['y'] + positions['height']
|
||||||
|
&& cursorX > positions['x'] && cursorX < positions['x'] + positions['width']
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Left click
|
||||||
|
canvas.addEventListener('click', function(event) {
|
||||||
|
|
||||||
|
var x = event.pageX - canvasLeft,
|
||||||
|
y = event.pageY - canvasTop;
|
||||||
|
|
||||||
|
|
||||||
|
console.log('LEFT CLICK X: '+x+' Y: '+y);
|
||||||
|
|
||||||
|
// First check for interaction menu and options and interact
|
||||||
|
// with them if they exist
|
||||||
|
if(interactionMenuAvailable()){
|
||||||
|
|
||||||
|
// TODO: Maybe speed this up, pass the entity clicked and get just iMenu
|
||||||
|
// data of that entity
|
||||||
|
|
||||||
|
// Loop interaction menu positions and 'trigger' the event
|
||||||
|
for (const [key, value] of Object.entries(gameData.interactionOption)) {
|
||||||
|
|
||||||
|
// If one of the options is clicked, trigger it
|
||||||
|
// Passes x,y,w,h of the interactionOption
|
||||||
|
if(!clickableCheck(x,y,null,value)){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
doiMenuPressed(key);
|
||||||
|
|
||||||
|
// After an interaction, clear the menu to prevent redraw
|
||||||
|
clearInteractionMenu();
|
||||||
|
// Then return true to prevent another interaction
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// If ANYWHERE but an interactionOption selected, close the interactionMenu
|
||||||
|
clearInteractionMenu();
|
||||||
|
|
||||||
|
// Loop the entities that are interactable
|
||||||
|
// I.e. Have size, and positions
|
||||||
|
for (const [key, value] of Object.entries(gameData.size)) {
|
||||||
|
// Key is item here
|
||||||
|
|
||||||
|
// If there's no position data skip, can't interact
|
||||||
|
if(gameData.position === undefined){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the X/Y of cursor isn't within the shape's bounds can't interact
|
||||||
|
if(!clickableCheck(x,y,key)){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Other checks, in event/animation, can be interacted with
|
||||||
|
// etc, etc.
|
||||||
|
|
||||||
|
// If it's deck
|
||||||
|
if(gameData.deck[key] !== undefined){
|
||||||
|
// If deck belongs to player
|
||||||
|
if(gameData.player[key] == gameData.playerId){
|
||||||
|
requestDrawACard();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Card in hand
|
||||||
|
if(gameData.hand[key] !== undefined){
|
||||||
|
// If entity belongs to player (for now, will be able to use
|
||||||
|
// opponents hand at certain times in future)
|
||||||
|
if(gameData.player[key] == gameData.playerId){
|
||||||
|
console.log(key);
|
||||||
|
openInteractionMenu(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
openInteractionMenu(key);
|
||||||
|
|
||||||
|
// TODO:If clicked anywhere but an interaction option,
|
||||||
|
// close interaction option
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// Right click
|
||||||
|
canvas.addEventListener('contextmenu', function(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
var x = event.pageX - canvasLeft,
|
||||||
|
y = event.pageY - canvasTop;
|
||||||
|
|
||||||
|
console.log('RIGHT CLICK X: '+x+' Y: '+y);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
@ -0,0 +1,205 @@
|
|||||||
|
function clearInteractionMenu(){
|
||||||
|
// Clear the existing interaction menu
|
||||||
|
gameData.inInteractionMenu = {};
|
||||||
|
gameData.interactionOption = {};
|
||||||
|
drawGameBoard();
|
||||||
|
}
|
||||||
|
function openInteractionMenu(entity){
|
||||||
|
|
||||||
|
console.log('open interaction menu: '+entity);
|
||||||
|
|
||||||
|
// Only one interaction menu up at once (for now)
|
||||||
|
clearInteractionMenu();
|
||||||
|
|
||||||
|
// Add the 'new' entity interactionMenu
|
||||||
|
gameData.inInteractionMenu[entity] = entity;
|
||||||
|
|
||||||
|
// Add the available interaction(s) with size+positions
|
||||||
|
|
||||||
|
// TODO: Actually add the corresponding interactions depending on card, and boardElement
|
||||||
|
if(entity in gameData.hand){
|
||||||
|
gameData.interactionOption['Play to Board'] = {
|
||||||
|
x: gameData.position[entity][0] + gameData.size[entity][0]*.1/2,
|
||||||
|
y: gameData.position[entity][1] + gameData.size[entity][1] - (35 * (Object.entries(gameData.interactionOption).length + 1)),
|
||||||
|
width: gameData.size[entity][0]*.9,
|
||||||
|
height: 30
|
||||||
|
}
|
||||||
|
gameData.interactionOption['Play as Mana'] = {
|
||||||
|
x: gameData.position[entity][0] + gameData.size[entity][0]*.1/2,
|
||||||
|
y: gameData.position[entity][1] + gameData.size[entity][1] - (35 * (Object.entries(gameData.interactionOption).length + 1)),
|
||||||
|
width: gameData.size[entity][0]*.9,
|
||||||
|
height: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interact
|
||||||
|
|
||||||
|
// Attack
|
||||||
|
// Start Attack
|
||||||
|
if(entity in gameData.board && !(entity in gameData.cardStatus.tapped)){
|
||||||
|
|
||||||
|
// TODO: Make the object within each interationOption a function to return instead of duping
|
||||||
|
gameData.interactionOption['Attack'] = {
|
||||||
|
x: gameData.position[entity][0] + gameData.size[entity][0]*.1/2,
|
||||||
|
y: gameData.position[entity][1] + gameData.size[entity][1] - (35 * (Object.entries(gameData.interactionOption).length + 1)),
|
||||||
|
width: gameData.size[entity][0]*.9,
|
||||||
|
height: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Target Attack Target
|
||||||
|
|
||||||
|
// Tap
|
||||||
|
// TAP (TEMP TODO: remove or add in a statement to hide)
|
||||||
|
if(entity in gameData.cardStatus.tapped){
|
||||||
|
gameData.interactionOption['Untap'] = {
|
||||||
|
x: gameData.position[entity][0] + gameData.size[entity][0]*.1/2,
|
||||||
|
y: gameData.position[entity][1] + gameData.size[entity][1] - (35 * (Object.entries(gameData.interactionOption).length + 1)),
|
||||||
|
width: gameData.size[entity][0]*.9,
|
||||||
|
height: 30
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
gameData.interactionOption['Tap'] = {
|
||||||
|
x: gameData.position[entity][0] + gameData.size[entity][0]*.1/2,
|
||||||
|
y: gameData.position[entity][1] + gameData.size[entity][1] - (35 * (Object.entries(gameData.interactionOption).length + 1)),
|
||||||
|
width: gameData.size[entity][0]*.9,
|
||||||
|
height: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let addEffects = true;
|
||||||
|
if(entity in gameData.shield || entity in gameData.mana){
|
||||||
|
addEffects = false;
|
||||||
|
}
|
||||||
|
// Add interaction options for effects of the card
|
||||||
|
if(addEffects && gameData.cardData[entity] !== undefined && gameData.cardData[entity].effect.length > 0){
|
||||||
|
|
||||||
|
|
||||||
|
for(let i = 0; i < gameData.cardData[entity].effect.length; i++){
|
||||||
|
// TODO: Check if effect is triggerable from it's location
|
||||||
|
// If not don't add effect trigger. If it is, but criteria not met
|
||||||
|
// add but grey out (need to add this functionality to interaction menu)
|
||||||
|
gameData.interactionOption['Trigger Effect '+(i+1)] = {
|
||||||
|
x: gameData.position[entity][0] + gameData.size[entity][0]*.1/2,
|
||||||
|
y: gameData.position[entity][1] + gameData.size[entity][1] - (35 * (Object.entries(gameData.interactionOption).length + 1)),
|
||||||
|
width: gameData.size[entity][0]*.9,
|
||||||
|
height: 30
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// If selectable, give option to select. If selected, deselect
|
||||||
|
if(gameData.cardStatus.targetable[entity] !== undefined){
|
||||||
|
|
||||||
|
gameData.interactionOption['Target'] = {
|
||||||
|
x: gameData.position[entity][0] + gameData.size[entity][0]*.1/2,
|
||||||
|
y: gameData.position[entity][1] + gameData.size[entity][1] - (35 * (Object.entries(gameData.interactionOption).length + 1)),
|
||||||
|
width: gameData.size[entity][0]*.9,
|
||||||
|
height: 30
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if(gameData.cardStatus.targetable[entity] !== undefined && gameData.cardStatus.targetted[entity] !== undefined){
|
||||||
|
|
||||||
|
gameData.interactionOption['Untarget'] = {
|
||||||
|
x: gameData.position[entity][0] + gameData.size[entity][0]*.1/2,
|
||||||
|
y: gameData.position[entity][1] + gameData.size[entity][1] - (35 * (Object.entries(gameData.interactionOption).length + 1)),
|
||||||
|
width: gameData.size[entity][0]*.9,
|
||||||
|
height: 30
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
drawGameBoard();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function interactionMenuAvailable(){
|
||||||
|
|
||||||
|
if(Object.entries(gameData.inInteractionMenu).length == 0){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Object.entries(gameData.interactionOption).length == 0){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function doiMenuPressed(iMenuKey){
|
||||||
|
console.log(iMenuKey);
|
||||||
|
|
||||||
|
// TODO: Checks for the actual entities position too
|
||||||
|
if(iMenuKey == 'Play to Board'){
|
||||||
|
iMenuPlayToBoard();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(iMenuKey == 'Play as Mana'){
|
||||||
|
iMenuPlayAsMana();
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Trigger effect
|
||||||
|
|
||||||
|
if(iMenuKey == 'Tap'){
|
||||||
|
iMenuTap();
|
||||||
|
}
|
||||||
|
if(iMenuKey == 'Untap'){
|
||||||
|
iMenuUntap();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attack
|
||||||
|
// Start Attack (Card that's to attack)
|
||||||
|
if(iMenuKey == 'Attack'){
|
||||||
|
iMenuStartAttack();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Target
|
||||||
|
if(iMenuKey == 'Target'){
|
||||||
|
iMenuTarget();
|
||||||
|
}
|
||||||
|
if(iMenuKey == 'Untarget'){
|
||||||
|
iMenuUntarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
function iMenuPlayToBoard(){
|
||||||
|
requestPlayFromHand(gameData.listPosition[gameData.inInteractionMenu[Object.keys(gameData.inInteractionMenu)[0]]])
|
||||||
|
}
|
||||||
|
function iMenuPlayAsMana(){
|
||||||
|
requestPlayManaFromHand(gameData.listPosition[gameData.inInteractionMenu[Object.keys(gameData.inInteractionMenu)[0]]])
|
||||||
|
}
|
||||||
|
function iMenuTriggerEffect(){
|
||||||
|
console.log('iMenuTriggerEffect');
|
||||||
|
}
|
||||||
|
function iMenuTap(){
|
||||||
|
console.log('iMenuTap');
|
||||||
|
// This gets the entity ID (the key of entity that's iMenu has been clicked)
|
||||||
|
console.log(Object.keys(gameData.inInteractionMenu)[0]);
|
||||||
|
// TEMP for testing, should select all 'target' cards, and 'accept' then server will tap
|
||||||
|
// them all and return the new tapped list
|
||||||
|
requestTapCard(gameData.inInteractionMenu[Object.keys(gameData.inInteractionMenu)[0]]);
|
||||||
|
}
|
||||||
|
function iMenuUntap(){
|
||||||
|
// Like above, TEMP for testing TODO: get rid of these and let server do it when things are further on
|
||||||
|
console.log('iMenuTap');
|
||||||
|
// This gets the entity ID (the key of entity that's iMenu has been clicked)
|
||||||
|
console.log(Object.keys(gameData.inInteractionMenu)[0]);
|
||||||
|
requestUntapCard(gameData.inInteractionMenu[Object.keys(gameData.inInteractionMenu)[0]]);
|
||||||
|
}
|
||||||
|
function iMenuStartAttack(){
|
||||||
|
requestStartAttack(Object.keys(gameData.inInteractionMenu)[0]);
|
||||||
|
}
|
||||||
|
function iMenuTarget(){
|
||||||
|
gameData.cardStatus.targetted[Object.keys(gameData.inInteractionMenu)[0]] = Object.keys(gameData.inInteractionMenu)[0];
|
||||||
|
clearInteractionMenu(); // Clears and redraws the board
|
||||||
|
console.log('target');
|
||||||
|
}
|
||||||
|
function iMenuUntarget(){
|
||||||
|
delete(gameData.cardStatus.targetted[Object.keys(gameData.inInteractionMenu)[0]]);
|
||||||
|
console.log('UNtarget');
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
const ctx = canvas.getContext('2d');
|
||||||
|
const canvasLeft = canvas.offsetLeft + canvas.clientLeft;
|
||||||
|
const canvasTop = canvas.offsetTop + canvas.clientTop;
|
||||||
|
|
||||||
|
ctx.font = "12px Arial";
|
||||||
|
canvas.style.backgroundColor = 'rgb(143 153 150)';
|
||||||
|
cardArt.src = '/images/cardArt.jpg';
|
||||||
|
cardBackArt.src = '/images/cardBack.jpg';
|
||||||
|
ctx.fillStyle = '#000';
|
||||||
|
|
||||||
@ -0,0 +1,207 @@
|
|||||||
|
// For front-end debugging
|
||||||
|
// Anything to be tested that would otherwise need server logs, etc.
|
||||||
|
// should go here
|
||||||
|
|
||||||
|
function debugEffect(){
|
||||||
|
|
||||||
|
let damageAmount = document.getElementById("effectDamageAmount").value;
|
||||||
|
if(damageAmount == ""){ damageAmount = null; }
|
||||||
|
|
||||||
|
let targetId = document.getElementById("effectTargetId").value;
|
||||||
|
if(targetId == ""){ targetId = null; }
|
||||||
|
|
||||||
|
let targetId2 = document.getElementById("effectTargetId2").value;
|
||||||
|
if(targetId2 == ""){ targetId2 = null; }
|
||||||
|
|
||||||
|
let targetPlayer = document.getElementById("effectTargetPlayer").value;
|
||||||
|
if(targetPlayer == ""){ targetPlayer = null; }
|
||||||
|
|
||||||
|
let effectAddRemove = document.getElementById("effectAddRemove").value;
|
||||||
|
if(effectAddRemove == ""){ effectAddRemove = null; }
|
||||||
|
|
||||||
|
let effect = document.getElementById("effect").value;
|
||||||
|
if(effect == ""){ effect = null; }
|
||||||
|
|
||||||
|
debugEffectFunction(damageAmount, targetId, targetId2, targetPlayer, effectAddRemove, effect);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function debugEffectFunction(damageAmount = null, targetId = null, targetId2 = null, targetPlayer = null, effectAddRemove = null, effect = null){
|
||||||
|
|
||||||
|
if(effect == 'hurt'){
|
||||||
|
hurt(damageAmount, targetId, targetPlayer);
|
||||||
|
}
|
||||||
|
if(effect == 'heal'){
|
||||||
|
heal(damageAmount, targetPlayer);
|
||||||
|
}
|
||||||
|
if(effect == 'flight'){
|
||||||
|
console.log(targetId+' Flight: '+flight[targetId]);
|
||||||
|
|
||||||
|
console.log(effectAddRemove);
|
||||||
|
|
||||||
|
if(effectAddRemove == 'remove'){
|
||||||
|
removeFlight(targetId);
|
||||||
|
}else{
|
||||||
|
giveFlight(targetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(targetId+' Flight: '+flight[targetId]);
|
||||||
|
}
|
||||||
|
if(effect == 'reach'){
|
||||||
|
console.log(targetId+' Reach: '+reach[targetId]);
|
||||||
|
|
||||||
|
console.log(effectAddRemove);
|
||||||
|
|
||||||
|
if(effectAddRemove == 'remove'){
|
||||||
|
removeReach(targetId);
|
||||||
|
}else{
|
||||||
|
giveReach(targetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(targetId+' Reach: '+reach[targetId]);
|
||||||
|
}
|
||||||
|
if(effect == 'taunt'){
|
||||||
|
console.log(targetId+' Taunt: '+taunt[targetId]);
|
||||||
|
|
||||||
|
console.log(effectAddRemove);
|
||||||
|
|
||||||
|
if(effectAddRemove == 'remove'){
|
||||||
|
removeTaunt(targetId);
|
||||||
|
}else{
|
||||||
|
giveTaunt(targetId);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(targetId+' Taunt: '+reach[targetId]);
|
||||||
|
}
|
||||||
|
if(effect == 'draw'){
|
||||||
|
drawCard(damageAmount, targetPlayer);
|
||||||
|
}
|
||||||
|
if(effect == 'equip'){}
|
||||||
|
|
||||||
|
board.drawBoard();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function debugTrigger(){
|
||||||
|
let targetId = document.getElementById("triggerTargetId").value;
|
||||||
|
if(targetId == ""){ targetId = null; }
|
||||||
|
|
||||||
|
let trigger = document.getElementById("trigger").value;
|
||||||
|
if(trigger == ""){ trigger = null; }
|
||||||
|
|
||||||
|
let triggerAmount = document.getElementById("triggerAmount").value;
|
||||||
|
if(triggerAmount == ""){ triggerAmount = null; }
|
||||||
|
|
||||||
|
debugTriggerFunction(targetId, trigger, triggerAmount);
|
||||||
|
}
|
||||||
|
function debugTriggerFunction(targetId = null, trigger = null, triggerAmount = null){
|
||||||
|
|
||||||
|
if(targetId == null){ return false; }
|
||||||
|
|
||||||
|
if(trigger == 'tap'){
|
||||||
|
triggerTap(targetId);
|
||||||
|
}
|
||||||
|
if(trigger == 'pay'){
|
||||||
|
triggerPay(triggerAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
board.drawBoard();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Builds console log for card effect (inc.
|
||||||
|
// all triggers, etc)
|
||||||
|
function debugGetCardEffects(itemKey){
|
||||||
|
|
||||||
|
let effectData = cardEffect[itemKey];
|
||||||
|
console.log(effectData);
|
||||||
|
if(effectData === undefined){ return false; }
|
||||||
|
|
||||||
|
effectData.forEach((effect, effectIndex) => {
|
||||||
|
|
||||||
|
console.log('--- Effect '+ effectIndex +' ---');
|
||||||
|
console.log(effect['description']);
|
||||||
|
|
||||||
|
// Loop the triggers
|
||||||
|
for (const [key, value] of Object.entries(effect['trigger'])) {
|
||||||
|
|
||||||
|
console.log('--- Trigger ---');
|
||||||
|
let effectTrigger = effect['trigger'][key];
|
||||||
|
console.log(triggerTypes[effectTrigger['triggerTypeId']]);
|
||||||
|
|
||||||
|
console.log('--- Trigger Amount ---');
|
||||||
|
console.log(effectTrigger['amount']);
|
||||||
|
|
||||||
|
// Loop targets for said trigger
|
||||||
|
effectTrigger['target'].forEach((effectTriggerTarget) => {
|
||||||
|
console.log('--- Trigger Target ---');
|
||||||
|
console.log(effectTriggerTarget.classId);
|
||||||
|
console.log(effectTriggerTarget.colourId);
|
||||||
|
console.log(effectTriggerTarget.passiveId);
|
||||||
|
console.log(effectTriggerTarget.typeId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop the effects
|
||||||
|
for (const [key, value] of Object.entries(effect['step'])) {
|
||||||
|
// amoutn, basicEffectId, stepOrder, target (also itemfromstep)
|
||||||
|
console.log(effect['step'][key]);
|
||||||
|
|
||||||
|
console.log('--- Effect Step ---');
|
||||||
|
let effectStep = effect['step'][key];
|
||||||
|
console.log(basicEffects[effectStep['basicEffectId']]);
|
||||||
|
|
||||||
|
console.log('--- Effect Step Amount ---');
|
||||||
|
console.log(effectStep['amount']);
|
||||||
|
|
||||||
|
// Loop targets for effectStep
|
||||||
|
effectStep['target'].forEach((effectStepTarget) => {
|
||||||
|
console.log('--- Effect Step Target ---');
|
||||||
|
console.log(effectStepTarget.classId);
|
||||||
|
console.log(effectStepTarget.colourId);
|
||||||
|
console.log(effectStepTarget.passiveId);
|
||||||
|
console.log(effectStepTarget.typeId);
|
||||||
|
console.log(effectStepTarget.itemFromStep);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function debugEffectCanTrigger(){
|
||||||
|
|
||||||
|
let ecTriggerTargetId = document.getElementById("ecTriggerTargetId").value;
|
||||||
|
if(ecTriggerTargetId == ""){ ecTriggerTargetId = null; }
|
||||||
|
|
||||||
|
let ecTriggerIndex = document.getElementById("ecTriggerIndex").value;
|
||||||
|
if(ecTriggerIndex == ""){ ecTriggerIndex = 0; }
|
||||||
|
|
||||||
|
let ecDoTrigger = document.getElementById("ecDoTrigger").value;
|
||||||
|
if(ecDoTrigger == ""){ ecDoTrigger = 0; }
|
||||||
|
|
||||||
|
if(doEffectTriggers(ecTriggerTargetId, ecTriggerIndex, true) != true){
|
||||||
|
console.log('Effect cannot be triggered');
|
||||||
|
//queueEffect(ecTriggerTargetId, ecTriggerIndex);
|
||||||
|
//doNextInQueue();
|
||||||
|
//loopTriggerQueue();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Effect CAN be triggered');
|
||||||
|
|
||||||
|
if(ecDoTrigger){
|
||||||
|
// Do everything needed in triggers
|
||||||
|
doEffectTriggers(ecTriggerTargetId, ecTriggerIndex);
|
||||||
|
|
||||||
|
// Then queue the effects to happen (queued so they can be chained/countered)
|
||||||
|
queueEffect(ecTriggerTargetId, ecTriggerIndex);
|
||||||
|
|
||||||
|
// Do all effects in queue (for debug), in actual will do one, then allow chain triggers, etc.
|
||||||
|
loopTriggerQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
board.drawBoard();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,680 @@
|
|||||||
|
// Theorising, if this is what's wanted then will need doing for everything
|
||||||
|
// not just in effects. eg. 'attack', 'effect1p1', 'flip', yadda yadda
|
||||||
|
let triggerQueue = [];
|
||||||
|
let triggerId = 1; // So any other effects, etc. queued for a destroyed card can be removed at once
|
||||||
|
let triggerDone = []; // The triggers that have completed
|
||||||
|
let triggerQueueTargets = []; // Whatever was targetted by the effect, etc. in triggerQueue (for ref by effectStep, etc)
|
||||||
|
|
||||||
|
// Passive effects (just does stuff)
|
||||||
|
let flight = {};
|
||||||
|
let reach = {};
|
||||||
|
let taunt = {};
|
||||||
|
let equipped = {}; // Entity x has [a,b,c] equipped to it
|
||||||
|
|
||||||
|
|
||||||
|
// Events (when these occur check if this is part of an effect trigger)
|
||||||
|
// startOfGame, startOfTurn, endOfTurn
|
||||||
|
// cardSummoned, cardRecruited, effectTriggered
|
||||||
|
// etc.
|
||||||
|
|
||||||
|
|
||||||
|
// Trigger types
|
||||||
|
const triggerTypes = {
|
||||||
|
'tap': 1,
|
||||||
|
'pay': 2,
|
||||||
|
};
|
||||||
|
// Basic effects
|
||||||
|
// TODO: Pull display name/description from DB?
|
||||||
|
const basicEffects = {
|
||||||
|
1: 'Equip',
|
||||||
|
2: 'Heal',
|
||||||
|
3: 'Hurt',
|
||||||
|
4: 'Recruit',
|
||||||
|
5: 'Give Flight',
|
||||||
|
6: 'Taunt',
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Actives to add passives
|
||||||
|
function giveFlight(card){
|
||||||
|
flight[card] = true;
|
||||||
|
}
|
||||||
|
function removeFlight(card){
|
||||||
|
// If the card has flight delete entity component
|
||||||
|
if(card in flight){
|
||||||
|
delete flight[card];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function giveReach(card){
|
||||||
|
reach[card] = true;
|
||||||
|
}
|
||||||
|
function removeReach(card){
|
||||||
|
if(card in reach){
|
||||||
|
delete reach[card];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function giveTaunt(card){
|
||||||
|
taunt[card] = true;
|
||||||
|
}
|
||||||
|
function removeTaunt(card){
|
||||||
|
// If the card has flight delete entity component
|
||||||
|
if(card in taunt){
|
||||||
|
delete taunt[card];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Active
|
||||||
|
function equip(){
|
||||||
|
// Equip card to another unit
|
||||||
|
|
||||||
|
}
|
||||||
|
function unequip(){
|
||||||
|
// Remove card from its equipped to unit
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawCard(drawAmount, player){
|
||||||
|
board.drawACard(player, drawAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
function heal(healAmount, healPlayer){
|
||||||
|
// For each heal 1..4
|
||||||
|
for(let i = 0; i < healAmount; i++){
|
||||||
|
// Loop shield
|
||||||
|
let items = this.getItems('shield', healPlayer, null, null);
|
||||||
|
for(let item = 0; item < items.length; item++){
|
||||||
|
|
||||||
|
let itemKey = items[item];
|
||||||
|
// If a shield is tapped, untap it to 'heal'
|
||||||
|
if(board.isTapped(itemKey)){
|
||||||
|
board.untapCard(itemKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function hurt(hurtDamage, hurtTarget = null, hurtPlayer = null){
|
||||||
|
// Deal X000 damage to unit this turn, or deal Y to opponent's shield
|
||||||
|
|
||||||
|
// Set player/element
|
||||||
|
if(hurtPlayer == null && hurtTarget != null){
|
||||||
|
hurtPlayer = player[hurtTarget];
|
||||||
|
hurtElement = boardElement[hurtTarget];
|
||||||
|
}else{
|
||||||
|
hurtElement = 'shield'
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(hurtElement){
|
||||||
|
case 'board':
|
||||||
|
// TODO: Make temp damage this turn as well
|
||||||
|
if(hurtDamage >= cardAttack[hurtTarget][0]){
|
||||||
|
board.sendToGrave(hurtTarget);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'shield':
|
||||||
|
let destroyCount = cardAttackToShieldDamage(hurtDamage);
|
||||||
|
console.log(destroyCount);
|
||||||
|
// TODO: loop player's shield, tap if they can be tapped, destroy
|
||||||
|
// if all already tapped
|
||||||
|
|
||||||
|
// While there's still damage to deal
|
||||||
|
while(destroyCount > 0){
|
||||||
|
|
||||||
|
// Keep looping through the shield belonging to target user
|
||||||
|
let items = board.getItems('shield', hurtPlayer, null, null);
|
||||||
|
let tappedItems = board.getItems('shield', hurtPlayer, 'tapped', null);
|
||||||
|
|
||||||
|
for(let item = 0; item < items.length; item++){
|
||||||
|
|
||||||
|
// If nothing more to destroy, exit loop
|
||||||
|
if(destroyCount <= 0){ break; }
|
||||||
|
|
||||||
|
let itemKey = items[item];
|
||||||
|
console.log(itemKey);
|
||||||
|
console.log(cardStatus[itemKey]);
|
||||||
|
|
||||||
|
// If there's anything to tap, tap it
|
||||||
|
if(cardStatus[itemKey] == null){
|
||||||
|
console.log('been tapped');
|
||||||
|
board.tapCard(itemKey);
|
||||||
|
destroyCount--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there's nothing to tap, destroy it
|
||||||
|
if(items.length == tappedItems.length){
|
||||||
|
console.log('been destroyed');
|
||||||
|
board.destroyShield(itemKey);
|
||||||
|
destroyCount--;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
board.drawBoard();
|
||||||
|
break; // end case 'shield'
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Effect Trigger(s)
|
||||||
|
// Non-event triggers. i.e. these are activated triggers
|
||||||
|
// rather than things such as 'On attack', 'When opponent draws'
|
||||||
|
|
||||||
|
// When card has been actively tapped
|
||||||
|
function triggerTap(card){
|
||||||
|
if(canTriggerTap(card) == false){ return false; }
|
||||||
|
|
||||||
|
board.tapCard(card);
|
||||||
|
|
||||||
|
console.log('triggerTap');
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
// Check a card can actively be tapped, otherwise don't start trigger
|
||||||
|
function canTriggerTap(card){
|
||||||
|
if(board.isTapped(card)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if(boardElement[card] != 'board'){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('can triggerTap');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pay the mana cost(s) to trigger event
|
||||||
|
function triggerPay(triggerAmount){
|
||||||
|
if(canTriggerPay(triggerAmount) == false){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let effectCosts = [{1:0,2:0,3:0,4:0}, triggerAmount];
|
||||||
|
board.tapManaRequired(null, 0, false, effectCosts);
|
||||||
|
|
||||||
|
console.log('triggerPay');
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
// Check the mana cost(s) can be paid, otherwise don't start trigger
|
||||||
|
function canTriggerPay(triggerAmount){
|
||||||
|
// For player0 only at the mo. and with no specific colour costs
|
||||||
|
let effectCosts = [{1:0,2:0,3:0,4:0}, triggerAmount];
|
||||||
|
if(board.tapManaRequired(null, 0, true, effectCosts) == false){
|
||||||
|
console.log('cannot trigger pay');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Can trigger pay');
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pay/Activate (all) the triggers
|
||||||
|
function doEffectTriggers(itemKey, effectIndex, checkOnly = false){
|
||||||
|
|
||||||
|
// Check card has effectData
|
||||||
|
let effectData = cardEffect[itemKey];
|
||||||
|
if(effectData === undefined){ return false; } // No effect
|
||||||
|
|
||||||
|
// Check effectData contains target effect
|
||||||
|
let effect = effectData[effectIndex];
|
||||||
|
if(effect === undefined){ return false; } // No effect
|
||||||
|
|
||||||
|
// Loop each trigger, AND activate them
|
||||||
|
for (const [key, value] of Object.entries(effect['trigger'])) {
|
||||||
|
|
||||||
|
let effectTrigger = effect['trigger'][key];
|
||||||
|
console.log('--- Trigger '+key+' ---');
|
||||||
|
console.log(effectTrigger['triggerTypeId']);
|
||||||
|
|
||||||
|
// TAP TRIGGER
|
||||||
|
if(effectTrigger['triggerTypeId'] == triggerTypes.tap){
|
||||||
|
if(canTriggerTap(itemKey) == false){
|
||||||
|
console.log('Tap trigger, cannot be triggered');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the trigger, then continue to next trigger
|
||||||
|
if(!checkOnly){board.tapCard(itemKey);}
|
||||||
|
continue;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// PAY TRIGGER
|
||||||
|
if(effectTrigger['triggerTypeId'] == triggerTypes.pay){
|
||||||
|
|
||||||
|
// To build the colourReq TODO: Change as could be 1 of either red/blue
|
||||||
|
// for instance
|
||||||
|
let colourReq = {1:0,2:0,3:0,4:0};
|
||||||
|
|
||||||
|
// Loop pay required (colours) for said trigger
|
||||||
|
// BUILD the colourReq loop needed for tapMana check
|
||||||
|
effectTrigger['target'].forEach((effectTriggerTarget) => {
|
||||||
|
|
||||||
|
// Increment colourReq by one of that colour
|
||||||
|
// THIS WILL NEED REDOING, AS MAYBE IT'S 2 OF RED OR BLUE!!
|
||||||
|
colourReq[effectTriggerTarget.colourId]++;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if the cost (and colour req.) can be paid
|
||||||
|
canPay = board.tapManaRequired(null, null, true, [colourReq, effectTrigger['amount']]);
|
||||||
|
if(canPay !== true){
|
||||||
|
console.log('Pay trigger, cannot be triggered');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pay trigger costs, then continue to next trigger
|
||||||
|
if(!checkOnly){
|
||||||
|
board.tapManaRequired(null, null, false, [colourReq, effectTrigger['amount']]);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// OTHER TRIGGERS
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function activateEffect(){
|
||||||
|
// Do effect triggers, then doEffect once payed
|
||||||
|
}
|
||||||
|
|
||||||
|
function getEffect(itemKey, effectIndex){
|
||||||
|
// Check card has effectData
|
||||||
|
let effectData = cardEffect[itemKey];
|
||||||
|
if(effectData === undefined){ return false; } // No effect
|
||||||
|
|
||||||
|
// Check effectData contains target effect
|
||||||
|
let effect = effectData[effectIndex];
|
||||||
|
if(effect === undefined){ return false; } // No effect
|
||||||
|
|
||||||
|
return effect;
|
||||||
|
}
|
||||||
|
// Get all the targets for effect step, so they can be targgeted
|
||||||
|
// by the effect/selected by the player
|
||||||
|
function getEffectStepTargets(itemKey, effectIndex, effectStep, fromBoardElement, playerId, effectTriggerId = null){
|
||||||
|
|
||||||
|
// Get Effect
|
||||||
|
let effect = getEffect(itemKey, effectIndex);
|
||||||
|
if(effect == false){ return false; }
|
||||||
|
|
||||||
|
// Get the step
|
||||||
|
let step = effect['step'][effectStep];
|
||||||
|
if(step == undefined){ return false; }
|
||||||
|
|
||||||
|
// The items that can be targetted with the effectStep
|
||||||
|
let itemsToSelectFrom = [];
|
||||||
|
|
||||||
|
// Loop the target location (or all locations)
|
||||||
|
// Check which cards meet the criteria in the DB
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: Not 100% sure how to keep track. May need a var for each item in previous steps
|
||||||
|
// or previous chains so they can be accessed, and used
|
||||||
|
// If itemFromStep, return the itemKeys of the items selected from a previous step
|
||||||
|
|
||||||
|
console.log('HELLOO');
|
||||||
|
console.log(step['target']);
|
||||||
|
|
||||||
|
// TODO: If target is 'itemFromStep', then get whatever item(s) were selected in
|
||||||
|
// said step, and add them to the thing
|
||||||
|
// TODO TODO
|
||||||
|
|
||||||
|
// If the target requirement is something from a previous step
|
||||||
|
// Check if target criteria is something from another step
|
||||||
|
let isFromStep = false;
|
||||||
|
step['target'].forEach((target) => {
|
||||||
|
if(target['itemFromStep'] != null){
|
||||||
|
|
||||||
|
console.log(target['itemFromStep']);
|
||||||
|
console.log(effectTriggerId);
|
||||||
|
//console.log(triggerQueue);
|
||||||
|
//console.log(triggerDone);
|
||||||
|
|
||||||
|
// Loop all triggers that have been done (completed)
|
||||||
|
triggerDone.forEach((oldTrigger) => {
|
||||||
|
|
||||||
|
// If not same triggerId, or the effectStep is not that of itemFromStep
|
||||||
|
if(oldTrigger['triggerId'] != effectTriggerId || target['itemFromStep'] != oldTrigger['effectStep']){
|
||||||
|
return isFromStep = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(oldTrigger);
|
||||||
|
return isFromStep = oldTrigger['targets'];
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
// If above returns a step, need to get the target(s) from that prior step to use a targets now
|
||||||
|
if(isFromStep){
|
||||||
|
console.log(isFromStep);
|
||||||
|
console.log('AAAAAAAAAAAAAAAAAAAAAA');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
let items = [];
|
||||||
|
// If from a previous step, loop those
|
||||||
|
if(isFromStep !== null && isFromStep !== undefined && isFromStep !== false){ items = isFromStep; }
|
||||||
|
// If not, get all the related doodads
|
||||||
|
else{
|
||||||
|
items = board.getItems(fromBoardElement, playerId, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop the boardlements and compare the colours, classes, etc. to match effect target criteria
|
||||||
|
// boardElement, playerId, cardStatus, listPosition
|
||||||
|
for(let item = 0; item < items.length; item++){
|
||||||
|
// TODO: MAYBE ADD THE COLOUR/PASSIVE CHECKS to the getItems itself
|
||||||
|
|
||||||
|
let itemKey = items[item];
|
||||||
|
//console.log(cardColours[itemKey]);
|
||||||
|
|
||||||
|
// If the item from getItems meets the criterias of target DB
|
||||||
|
step['target'].forEach((target) => {
|
||||||
|
|
||||||
|
//console.log(target);
|
||||||
|
//console.log(triggerDone);
|
||||||
|
|
||||||
|
|
||||||
|
// Check the class the same for target, and item
|
||||||
|
// If targetDB has null this means 'any' so is always correct
|
||||||
|
// Check Class (TODO: mixed classes, colours, etc ie select 1 red+blue card)
|
||||||
|
/*
|
||||||
|
if(class[itemKey] == target['classId'] || target['classId'] == null){
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Check colour
|
||||||
|
// If not null check the colours, otherwise null means any colour is ok
|
||||||
|
if(target['colourId'] !== null){
|
||||||
|
// Check the colours to see if one is of requirement
|
||||||
|
cardColours[itemKey].forEach((colour) => {
|
||||||
|
// If the colour doesn't match, continue to next target (forEach)
|
||||||
|
if(colour[0] != target['colourId']){
|
||||||
|
// The return below is just a 'continue' in forEach terms
|
||||||
|
return; // forEach jank. forEach executes the 'function' 'target' each loop
|
||||||
|
}
|
||||||
|
// If the colour is correct, keep checking requirements
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check passive (If hasPassive(id))
|
||||||
|
/*
|
||||||
|
if(cardColours[itemKey] == target['passiveId'] || target['passiveId'] == null){
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
if(cardType[itemKey] == target['typeId'] || target['typeId'] == null){
|
||||||
|
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Once all the target Req. have been checked against the item
|
||||||
|
// and they match, add to selectable list
|
||||||
|
itemsToSelectFrom.push(itemKey);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemsToSelectFrom;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function addTargettedCardsToQueueEvent(queueTriggerId, triggerStep, targets){
|
||||||
|
|
||||||
|
console.log('ADD TARGETTED TO QUEUED TRIGGER');
|
||||||
|
console.log(queueTriggerId);
|
||||||
|
console.log(triggerStep);
|
||||||
|
console.log(targets);
|
||||||
|
|
||||||
|
//console.log(triggerQueue[queueTriggerId]);
|
||||||
|
//console.log(targets);
|
||||||
|
|
||||||
|
console.log(triggerQueue);
|
||||||
|
|
||||||
|
triggerQueue.forEach((queued) => {
|
||||||
|
console.log(queued);
|
||||||
|
if(
|
||||||
|
queued.triggerId == queueTriggerId
|
||||||
|
&&
|
||||||
|
queued.effectStep == triggerStep
|
||||||
|
){
|
||||||
|
console.log('THIS SHOULD HAVE TARGETS ADDED');
|
||||||
|
queued['targets'] = targets;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(triggerQueue);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function queueEffect(itemKey, effectIndex){
|
||||||
|
|
||||||
|
let effect = getEffect(itemKey, effectIndex);
|
||||||
|
if(effect == false){ return false; }
|
||||||
|
|
||||||
|
// TODO: Sort steps by stepOrder incase wrong in DB, etc.
|
||||||
|
for (const [stepKey, step] of Object.entries(effect['step'])) {
|
||||||
|
triggerQueue.push(
|
||||||
|
// event, item, effectId, target, triggerId (commented atop)
|
||||||
|
{
|
||||||
|
'event': 'effect' // Which event. attack, destroy, effect, etc.
|
||||||
|
,'item': itemKey // Which card is doing event
|
||||||
|
,'effectIndex': effectIndex // Which effect (if effect)
|
||||||
|
,'effectStep': stepKey // Which effect (if effect)
|
||||||
|
,'target': null // ?
|
||||||
|
,'triggerId': triggerId
|
||||||
|
,'targets': null // ?
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Increment triggerId
|
||||||
|
triggerId++;
|
||||||
|
console.log(triggerQueue);
|
||||||
|
}
|
||||||
|
|
||||||
|
function doNextInQueue(){
|
||||||
|
|
||||||
|
if(triggerQueue.length <= 0){
|
||||||
|
console.log('Nothing in queue, doing next phase/event/whatever');
|
||||||
|
}
|
||||||
|
|
||||||
|
// If effect, trigger it (should already have paid the trigger costs)
|
||||||
|
switch(triggerQueue[0].event){
|
||||||
|
case 'effect':
|
||||||
|
// Do the effect
|
||||||
|
doEffect(triggerQueue[0].item, triggerQueue[0].effectIndex, triggerQueue[0].effectStep, triggerQueue[0].triggerId);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
alert('Error in doNextInQueue');
|
||||||
|
return 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add to completed triggers
|
||||||
|
triggerDone.push(triggerQueue[0]);
|
||||||
|
|
||||||
|
// And remove from the triggerQueue (first index)
|
||||||
|
triggerQueue.shift();
|
||||||
|
|
||||||
|
}
|
||||||
|
function loopTriggerQueue(){
|
||||||
|
|
||||||
|
while(triggerQueue.length > 0){
|
||||||
|
doNextInQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recusively call doEffect until each is done?
|
||||||
|
// Once recruit (play from hand) is triggered, need to allow user to select
|
||||||
|
// then when done move to next step
|
||||||
|
function doEffect(itemKey, effectIndex, effectStep, effectTriggerId){
|
||||||
|
|
||||||
|
console.log('doEffect');
|
||||||
|
|
||||||
|
let effect = getEffect(itemKey, effectIndex);
|
||||||
|
if(effect == false){ return false; }
|
||||||
|
|
||||||
|
// Get the step
|
||||||
|
let step = effect['step'][effectStep];
|
||||||
|
if(step == undefined){ return false; }
|
||||||
|
|
||||||
|
|
||||||
|
// For each step, activate the correct effect type on
|
||||||
|
// the correct targets.
|
||||||
|
|
||||||
|
|
||||||
|
// Get targets TODO: Use this instead of each case having it
|
||||||
|
//let targets = getEffectStepTargets(itemKey, effectIndex, effectStep, fromBoardElement, playerId, effectTriggerId);
|
||||||
|
|
||||||
|
switch (step['basicEffectId']){
|
||||||
|
|
||||||
|
// Recruit
|
||||||
|
case 4:
|
||||||
|
recruitCard(itemKey, effectIndex, effectStep, step['amount'], effectTriggerId);
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Give Flight
|
||||||
|
case 5:
|
||||||
|
givePassive(itemKey, effectIndex, effectStep, step['amount'], effectTriggerId, 'flight');
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Give Taunt
|
||||||
|
case 6:
|
||||||
|
givePassive(itemKey, effectIndex, effectStep, step['amount'], effectTriggerId, 'taunt');
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the selected targets to the queuedItem so it can be referred to in future
|
||||||
|
|
||||||
|
// Now do the next step, if there's another in the effect
|
||||||
|
// Commented out while testing triggerQueue
|
||||||
|
/*
|
||||||
|
if(effect['step'][effectStep++] !== undefined){
|
||||||
|
doEffect(itemKey, effectIndex, effectStep++);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
function givePassive(itemKey, effectIndex, effectStep, targetAmount, effectTriggerId, passive){
|
||||||
|
|
||||||
|
console.log('GIVE PASSIVE: '+passive);
|
||||||
|
|
||||||
|
let effect = getEffect(itemKey, effectIndex);
|
||||||
|
if(effect == false){ return false; }
|
||||||
|
|
||||||
|
// Get the step
|
||||||
|
let step = effect['step'][effectStep];
|
||||||
|
if(step == undefined){ return false; }
|
||||||
|
|
||||||
|
console.log(step);
|
||||||
|
//return false;
|
||||||
|
|
||||||
|
// TODO: null, 0 are boardElement/playerId (for now) need to redo, rethink
|
||||||
|
console.log('HELP ME');
|
||||||
|
let targets = getEffectStepTargets(itemKey, effectIndex, effectStep, null, 0, effectTriggerId);
|
||||||
|
console.log(targets);
|
||||||
|
|
||||||
|
if(targetAmount > 0 && targets.length > 0){
|
||||||
|
|
||||||
|
// TODO: better, and if just one thing omit, and do automatically
|
||||||
|
let selectedTarget = prompt("Select a card to gain flight: \n"+targets, targets[0]);
|
||||||
|
// User didn't select anything
|
||||||
|
if (selectedTarget == null || selectedTarget == "") {
|
||||||
|
alert('No card recruited, c ya');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// User inputted card not in ID (obv temp, as will be done in game UI)
|
||||||
|
// Huh I forgot why this is written like this, maybe it doens't work?
|
||||||
|
if (!selectedTarget.includes(selectedTarget)){
|
||||||
|
alert('Not in selection');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Remove the card from the selection (in case there's another in this loop)
|
||||||
|
targets.splice(targets.indexOf(selectedTarget), 1);
|
||||||
|
|
||||||
|
// Give the passive
|
||||||
|
if(passive == 'flight'){
|
||||||
|
giveFlight(selectedTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function removePassive(){}
|
||||||
|
function hasPassive(){}
|
||||||
|
|
||||||
|
function recruitCard(itemKey, effectIndex, effectStep, targetAmount, effectTriggerId = null){
|
||||||
|
console.log('RECRUIT');
|
||||||
|
|
||||||
|
let fromBoardElement = 'hand'; // FOR NOW, JUST TO TEST, THIS WILL BE PER BASIC EFFECT
|
||||||
|
let playerId = 0;
|
||||||
|
|
||||||
|
let targets = getEffectStepTargets(itemKey, effectIndex, effectStep, fromBoardElement, playerId, effectTriggerId);
|
||||||
|
let targettedCards = [];
|
||||||
|
|
||||||
|
//console.log(targets);
|
||||||
|
|
||||||
|
if(targetAmount > 0 && targets.length > 0){
|
||||||
|
|
||||||
|
let selectedTarget = prompt("Select a card to recruit: \n"+targets, targets[0]);
|
||||||
|
// User didn't select anything
|
||||||
|
if (selectedTarget == null || selectedTarget == "") {
|
||||||
|
alert('No card recruited, c ya');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// User inputted card not in ID (obv temp, as will be done in game UI)
|
||||||
|
if (!selectedTarget.includes(selectedTarget)){
|
||||||
|
alert('Not in selection');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove the card from the selection (in case there's another)
|
||||||
|
targets.splice(targets.indexOf(selectedTarget), 1);
|
||||||
|
|
||||||
|
// Add to targetted (for future ref)
|
||||||
|
targettedCards.push(selectedTarget);
|
||||||
|
|
||||||
|
// Play recruited card
|
||||||
|
// TODO: Need to pass player/listPosition better?
|
||||||
|
board.playRecruitedCard(player[selectedTarget], listPosition[selectedTarget]);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
addTargettedCardsToQueueEvent(effectTriggerId, effectStep, targettedCards);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
// Should mostly match server's components.js
|
||||||
|
// Misc. game data required
|
||||||
|
let gameData = {
|
||||||
|
|
||||||
|
item : null,
|
||||||
|
itemCount : null,
|
||||||
|
|
||||||
|
playerId : null,
|
||||||
|
opponentId : null,
|
||||||
|
roomId : null,
|
||||||
|
turn : 0,
|
||||||
|
playerTurn : 0,
|
||||||
|
players : null,
|
||||||
|
player : {},
|
||||||
|
|
||||||
|
cardCount : {
|
||||||
|
deck : {},
|
||||||
|
hand : {},
|
||||||
|
board : {},
|
||||||
|
shield : {},
|
||||||
|
mana : {},
|
||||||
|
grave : {},
|
||||||
|
void : {},
|
||||||
|
},
|
||||||
|
|
||||||
|
inInteractionMenu : {},
|
||||||
|
interactionOption : {},
|
||||||
|
|
||||||
|
|
||||||
|
// Real components from here?
|
||||||
|
listPosition : {},
|
||||||
|
cardData : {},
|
||||||
|
cardStatus : {
|
||||||
|
tapped : {},
|
||||||
|
attacking : {},
|
||||||
|
inspected : {},
|
||||||
|
targetable : {},
|
||||||
|
targetted : {},
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// Board elements
|
||||||
|
deck : {},
|
||||||
|
hand : {},
|
||||||
|
board : {},
|
||||||
|
shield : {},
|
||||||
|
mana : {},
|
||||||
|
grave : {},
|
||||||
|
void : {},
|
||||||
|
|
||||||
|
// Local components, not done on serverside
|
||||||
|
// calculated, etc. by client
|
||||||
|
position : {},
|
||||||
|
size : {},
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,123 @@
|
|||||||
|
// To set the server data to related client data
|
||||||
|
|
||||||
|
// Update all the relevent game data from the server's data response
|
||||||
|
function updateGameData(data){
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
updatePlayers(data.players);
|
||||||
|
updatePlayerId(data.playerId);
|
||||||
|
updateOpponentId(data.opponentId);
|
||||||
|
updateRoomId(data.roomId);
|
||||||
|
updateTurn(data.component.turn, data.component.playerTurn);
|
||||||
|
|
||||||
|
updateItems(data.component.item, data.component.itemCount);
|
||||||
|
updatePlayerComponent(data.component.player);
|
||||||
|
updateDecks(data.component.deck);
|
||||||
|
updateCardCount(data.component.cardCount);
|
||||||
|
|
||||||
|
console.log(gameData);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Individual or minor updates
|
||||||
|
// This will ideally be how each game function updates the data
|
||||||
|
// e.g. drawACard() would update the hand, deck, cardData, effects?
|
||||||
|
function updatePlayers(players = null){
|
||||||
|
gameData.players = players;
|
||||||
|
}
|
||||||
|
function updatePlayerId(playerId = null){
|
||||||
|
gameData.playerId = playerId;
|
||||||
|
}
|
||||||
|
function updateOpponentId(opponentId = null){
|
||||||
|
gameData.opponentId = opponentId;
|
||||||
|
}
|
||||||
|
function updateRoomId(roomId = null){
|
||||||
|
gameData.roomId = roomId;
|
||||||
|
}
|
||||||
|
function updateTurn(turn = null, playersTurn = null){
|
||||||
|
gameData.turn = turn;
|
||||||
|
gameData.playerTurn = playersTurn;
|
||||||
|
}
|
||||||
|
function updateItems(item = null, itemCount = null){
|
||||||
|
gameData.item = item;
|
||||||
|
gameData.itemCount = itemCount;
|
||||||
|
}
|
||||||
|
function updateDecks(deck = null){
|
||||||
|
gameData.deck = deck;
|
||||||
|
}
|
||||||
|
function updatePlayerComponent(player = null){
|
||||||
|
gameData.player = player;
|
||||||
|
}
|
||||||
|
function updateCardCount(cardCount = null){
|
||||||
|
console.log(cardCount);
|
||||||
|
gameData.cardCount = cardCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cards in hand
|
||||||
|
function updatePlayerHand(data){
|
||||||
|
console.log('Update player hand');
|
||||||
|
// TODO: These will likely not be = as will overwrite.
|
||||||
|
// Likely to union all the data server-side and pass as one
|
||||||
|
// function such as updateEntities() or something
|
||||||
|
gameData.hand = data.handEntities;
|
||||||
|
gameData.listPosition = data.handPositions; // Change to all listPositions
|
||||||
|
gameData.cardData = data.handCardData; // Changed to all cardData
|
||||||
|
// Changes made here, as when drawing a card updateBoard data was wiped and caused
|
||||||
|
// 'undefined' listPositions and other data...
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateBoard(data){
|
||||||
|
// TODO: ONLY RETURN NEEDED DATA
|
||||||
|
// AND ONLY UPDATE NEEDED DATA
|
||||||
|
// TODO: Currently everything is being passed
|
||||||
|
// which is 1 slow, and 2 bad because it gives players
|
||||||
|
// too much information, they only need what's to be
|
||||||
|
// drawn and interacted with
|
||||||
|
console.log('TODO: updateBoard correctly');
|
||||||
|
console.log(data);
|
||||||
|
gameData.board = data.board;
|
||||||
|
gameData.passive = data.passive;
|
||||||
|
gameData.listPosition = data.listPosition
|
||||||
|
gameData.cardData = data.cardData;
|
||||||
|
gameData.cardCost = data.cardCost;
|
||||||
|
gameData.cardColours = data.cardColours;
|
||||||
|
//console.log(data);
|
||||||
|
}
|
||||||
|
function updateShield(data){
|
||||||
|
console.log('TODO: updateShield correctly');
|
||||||
|
console.log(data);
|
||||||
|
gameData.shield = data.shield;
|
||||||
|
gameData.listPosition = data.listPosition;
|
||||||
|
// TODO: Tapped
|
||||||
|
}
|
||||||
|
function updateMana(data){
|
||||||
|
console.log('TODO: updateMana correctly');
|
||||||
|
console.log(data);
|
||||||
|
gameData.mana = data.mana;
|
||||||
|
gameData.listPosition = data.listPosition;
|
||||||
|
gameData.cardData = data.cardData;
|
||||||
|
gameData.cardColours = data.cardColours;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tapped
|
||||||
|
function updateTapped(data){
|
||||||
|
console.log(data);
|
||||||
|
gameData.cardStatus.tapped = data;
|
||||||
|
console.log(gameData.cardStatus.tapped);
|
||||||
|
}
|
||||||
|
// Targetable
|
||||||
|
function updateTargetable(data){
|
||||||
|
console.log(data);
|
||||||
|
// Clear targetable if null returned
|
||||||
|
if(data == null){ gameData.cardStatus.targetable = {}; }
|
||||||
|
gameData.cardStatus.targetable = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// To prevent typical functionality (draw, etc.)
|
||||||
|
// if there's a stack in play.
|
||||||
|
// TODO: Can only chain the stack or resolve it
|
||||||
|
function updateStack(data){
|
||||||
|
alert('You must resolve the stack, or chain an effect');
|
||||||
|
console.log(data);
|
||||||
|
console.log(gameData);
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,170 @@
|
|||||||
|
// Any socket request/responses for the actual game (when in a match)
|
||||||
|
|
||||||
|
// PASS TURN
|
||||||
|
function requestPassTurn(){
|
||||||
|
console.log('>> passTurn');
|
||||||
|
socket.emit('passTurn', gameData.roomId, gameData.playerId);
|
||||||
|
}
|
||||||
|
socket.on('responsePassTurn', function (data) {
|
||||||
|
console.log('<< passTurn');
|
||||||
|
// Set turn data for clients (bolds their name)
|
||||||
|
updateTurn(data.turn, data.playerTurn);
|
||||||
|
drawGameBoard();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// DRAW A CARD
|
||||||
|
function requestDrawACard(){
|
||||||
|
console.log('>> drawACard');
|
||||||
|
socket.emit('drawACard', gameData.roomId, gameData.playerId);
|
||||||
|
}
|
||||||
|
// Both players get new hand + deck counts updated
|
||||||
|
socket.on('responseDrawCard', function (data) {
|
||||||
|
console.log('<< drawCard');
|
||||||
|
updateCardCount(data);
|
||||||
|
drawGameBoard();
|
||||||
|
});
|
||||||
|
socket.on('responseCardCounts', function (data) {
|
||||||
|
console.log('<< responseCardCounts');
|
||||||
|
updateCardCount(data);
|
||||||
|
drawGameBoard();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Player drew card
|
||||||
|
// Player that drew the card (atm) gets the cardData, listPosition
|
||||||
|
// TODO: related attack, cost, effects, etc.
|
||||||
|
socket.on('responsePlayerDrewCard', function (data) {
|
||||||
|
console.log('<< playerDrewCard');
|
||||||
|
updatePlayerHand(data);
|
||||||
|
drawGameBoard();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
socket.on('responseUpdateBoard', function (data) {
|
||||||
|
console.log('<< updateBoard');
|
||||||
|
updateBoard(data);
|
||||||
|
drawGameBoard();
|
||||||
|
});
|
||||||
|
|
||||||
|
// SHUFFLE DECK
|
||||||
|
function requestShuffleDeck(){
|
||||||
|
console.log('>> shuffleDeck');
|
||||||
|
socket.emit('shuffleDeck', gameData.roomId, gameData.playerId);
|
||||||
|
}
|
||||||
|
// Both players get an emit that the deck has been shuffled
|
||||||
|
// but do not recieve any other data (not needed)
|
||||||
|
// Only need to know a deck has been shuffled to perform animation in future
|
||||||
|
socket.on('responseShuffleDeck', function (data) {
|
||||||
|
console.log('<< shuffleDeck');
|
||||||
|
alert(data[0] + ' shuffled');
|
||||||
|
// TODO
|
||||||
|
// animateDeckShuffle(playerX from data);
|
||||||
|
// drawGameBoard(); // From this point drawGameBoard should be a 60FPS loop
|
||||||
|
// not a one off draw after emits
|
||||||
|
});
|
||||||
|
|
||||||
|
function requestPlayFromHand(listPosition){
|
||||||
|
console.log('>> playFromHand');
|
||||||
|
socket.emit('playFromHand', gameData.roomId, gameData.playerId, listPosition);
|
||||||
|
}
|
||||||
|
socket.on('responsePlayFromHand', function (data) {
|
||||||
|
// Return boardData, update hand
|
||||||
|
console.log('<< playFromHand');
|
||||||
|
console.log(data);
|
||||||
|
});
|
||||||
|
function requestPlayManaFromHand(listPosition){
|
||||||
|
console.log('>> playManaFromHand');
|
||||||
|
socket.emit('playManaFromHand', gameData.roomId, gameData.playerId, listPosition);
|
||||||
|
}
|
||||||
|
socket.on('responseUpdateMana', function (data) {
|
||||||
|
// Return boardData, update hand
|
||||||
|
console.log('<< updateMana');
|
||||||
|
console.log(data);
|
||||||
|
updateMana(data);
|
||||||
|
drawGameBoard();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('responsePlayedShield', function (data) {
|
||||||
|
// The playerId that played it for animations
|
||||||
|
console.log('<< playedShield');
|
||||||
|
drawGameBoard();
|
||||||
|
});
|
||||||
|
socket.on('responseUpdateShield', function (data) {
|
||||||
|
console.log('<< updateShield');
|
||||||
|
console.log(data);
|
||||||
|
updateShield(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Tappage
|
||||||
|
function requestTapCard(cardId){
|
||||||
|
console.log('>> tapCard');
|
||||||
|
socket.emit('requestTapCard', gameData.roomId, gameData.playerId, cardId);
|
||||||
|
}
|
||||||
|
function requestUntapCard(cardId){
|
||||||
|
console.log('>> untapCard');
|
||||||
|
socket.emit('requestUntapCard', gameData.roomId, gameData.playerId, cardId);
|
||||||
|
}
|
||||||
|
socket.on('responseTapped', function (data) {
|
||||||
|
// The playerId that played it for animations
|
||||||
|
console.log('<< tapped');
|
||||||
|
console.log(data);
|
||||||
|
updateTapped(data[1]); // 0 is card tapped, 1 is all tapped cards
|
||||||
|
drawGameBoard();
|
||||||
|
});
|
||||||
|
socket.on('responseUntapped', function (data) {
|
||||||
|
// The playerId that played it for animations
|
||||||
|
console.log('<< untapped');
|
||||||
|
console.log(data);
|
||||||
|
updateTapped(data[1]);
|
||||||
|
drawGameBoard();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
function requestStartAttack(card){
|
||||||
|
console.log('>> startAttack');
|
||||||
|
socket.emit('requestStartAttack', gameData.roomId, gameData.playerId, card);
|
||||||
|
}
|
||||||
|
socket.on('responseTargetable', function (data) {
|
||||||
|
// The playerId that played it for animations
|
||||||
|
console.log('<< responseTargetable');
|
||||||
|
console.log(data);
|
||||||
|
updateTargetable(data);
|
||||||
|
drawGameBoard();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Stack
|
||||||
|
socket.on('responseAddToStack', function (data) {
|
||||||
|
console.log('<< addToStack');
|
||||||
|
console.log(data);
|
||||||
|
});
|
||||||
|
socket.on('responseResolveStack', function (data) {
|
||||||
|
console.log('<< resolveStack _x_');
|
||||||
|
console.log(data);
|
||||||
|
drawGameBoard();
|
||||||
|
});
|
||||||
|
socket.on('responseRemoveFromStack', function (data) {
|
||||||
|
console.log('<< removeFromStack ?');
|
||||||
|
console.log(data);
|
||||||
|
});
|
||||||
|
// All players need to accept the stack 'resolve' before it'll occur
|
||||||
|
socket.on('responseGetStackResponse', function (data) {
|
||||||
|
// TODO: Return all valid effect triggers/responses to the effect trigger
|
||||||
|
// on the top of the stack, and a 'resolve' option to not trigger anything
|
||||||
|
console.log('<< getStackResponse');
|
||||||
|
updateStack(data);
|
||||||
|
});
|
||||||
|
function requestResolveStack(){
|
||||||
|
// This is just for 'resolve' press. Not playing atop the stack.
|
||||||
|
console.log('>> requestResolveStack');
|
||||||
|
socket.emit('requestResolveStack', gameData.roomId, gameData.playerId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions like this would be elsewhere, do client-side
|
||||||
|
// validation THEN request stuff from the server?
|
||||||
|
// This is here for now, as it's used by the button
|
||||||
|
function passTurn(){
|
||||||
|
requestPassTurn();
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
const socket = io({autoConnect: false});
|
||||||
|
const canvas = document.getElementById('canvas');;
|
||||||
|
|
||||||
|
const cardWidth = 120; // 240px ~2.5 inch WAS 80 x3 (halfed for display)
|
||||||
|
const cardHeight = 168; // 336px ~3.5 inch WAS 120 x2.8 (halfed for display)
|
||||||
|
|
||||||
|
const cardArt = new Image();
|
||||||
|
const cardBackArt = new Image();
|
||||||
|
cardArt.src = 'images/cardArt.jpg';
|
||||||
|
cardBackArt.src = 'images/cardBack.jpg';
|
||||||
|
|
||||||
|
const COLOUR = {
|
||||||
|
'white':{'id': 1, 'name':'White','colour':'#EEE'},
|
||||||
|
'blue':{'id':2, 'name':'Blue','colour':'#0033EE'},
|
||||||
|
'red':{'id':3, 'name':'Red','colour':'#ED344A'},
|
||||||
|
};
|
||||||
|
const CLASS = {
|
||||||
|
1: 'Goblin'
|
||||||
|
,2: 'Human'
|
||||||
|
};
|
||||||
|
const TYPE = {
|
||||||
|
1: 'Unit'
|
||||||
|
,2: 'Spell'
|
||||||
|
,3: 'Token'
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
const maxHandSize = 4;
|
||||||
|
const maxBoardSize = 3;
|
||||||
|
const maxShield = 2;
|
||||||
|
|
||||||
|
const cardMargin = 10;
|
||||||
|
|
||||||
|
const handScale = .75;
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
function cardAttackToShieldDamage(attack){
|
||||||
|
if (attack >= 10000){ return 3; }
|
||||||
|
if (attack >= 5001){ return 2; }
|
||||||
|
if (attack >= 1){ return 1; }
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,192 @@
|
|||||||
|
// Canvas Initialisation
|
||||||
|
init();
|
||||||
|
function init() {
|
||||||
|
console.log('init');
|
||||||
|
|
||||||
|
// Init board here? Or keep in board JS?
|
||||||
|
|
||||||
|
document.addEventListener('touchstart', onclick, false);
|
||||||
|
document.addEventListener('click', onclick, false);
|
||||||
|
playerName = getCookie('playerName');
|
||||||
|
|
||||||
|
if (playerName == null) {
|
||||||
|
playerName = prompt('Enter your name: ', 'Guest');
|
||||||
|
|
||||||
|
if (playerName == null || playerName == "") {
|
||||||
|
playerName = 'Guest';
|
||||||
|
}
|
||||||
|
|
||||||
|
setCookie('playerName', playerName, 24 * 3600);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('>> socket.connect()');
|
||||||
|
socket.connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Cookies are temp, will need logins, etc.
|
||||||
|
function setCookie(name, value, seconds) {
|
||||||
|
let date = new Date();
|
||||||
|
date.setTime(date.getTime() + (seconds * 1000));
|
||||||
|
let expires = "expires=" + date.toUTCString();
|
||||||
|
document.cookie = name + "=" + value + ";" + expires + ";path=/";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCookie(name) {
|
||||||
|
name += "=";
|
||||||
|
let cookies = document.cookie.split(';');
|
||||||
|
|
||||||
|
for(let i = 0; i < cookies.length; i++) {
|
||||||
|
let cookie = cookies[i];
|
||||||
|
while (cookie.charAt(0) == ' ') {
|
||||||
|
cookie = cookie.substring(1);
|
||||||
|
}
|
||||||
|
if (cookie.indexOf(name) == 0) {
|
||||||
|
return cookie.substring(name.length, cookie.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Connect
|
||||||
|
socket.on('connect', connect);
|
||||||
|
function connect(){
|
||||||
|
console.log('+ Frontend Connect');
|
||||||
|
}
|
||||||
|
|
||||||
|
// getRooms
|
||||||
|
function requestRooms(filter = 'all'){
|
||||||
|
console.log('>> requestRooms:'+filter);
|
||||||
|
socket.emit('requestRooms', filter);
|
||||||
|
}
|
||||||
|
function clearRoomList(){
|
||||||
|
// Create a list of Rooms
|
||||||
|
let list = document.getElementById('joinRoomButtons');
|
||||||
|
// Empty the ul, as to not dupe room buttons, or have rooms that's don't exist
|
||||||
|
list.innerHTML = "";
|
||||||
|
}
|
||||||
|
function returnRooms(data){
|
||||||
|
clearRoomList();
|
||||||
|
|
||||||
|
// Create a list of Rooms
|
||||||
|
let list = document.getElementById('joinRoomButtons');
|
||||||
|
|
||||||
|
let roomCount = Object.keys(data['roomData']).length;
|
||||||
|
if(roomCount < 1){
|
||||||
|
alert('No Rooms');
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rooms = data['roomData'];
|
||||||
|
// Loops the returned rooms, and add to the list (to be joinable)
|
||||||
|
for (let room in rooms) {
|
||||||
|
//console.log(rooms[room].name);
|
||||||
|
|
||||||
|
// Making something akin to this:
|
||||||
|
//<li><button onclick="requestRoom(1)">Room 1</button></li>
|
||||||
|
let button = document.createElement('button');
|
||||||
|
button.appendChild(document.createTextNode(rooms[room].name));
|
||||||
|
button.setAttribute("onclick","requestJoinRoom("+rooms[room].id+");");
|
||||||
|
let li = document.createElement('li');
|
||||||
|
li.appendChild(button);
|
||||||
|
|
||||||
|
list.appendChild(li);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
socket.on('returnRooms', function (data) {
|
||||||
|
console.log('<< returnRooms');
|
||||||
|
returnRooms(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// createRoom
|
||||||
|
function requestCreateRoom(){
|
||||||
|
console.log('+ requestCreateRoom');
|
||||||
|
socket.emit('requestCreateRoom', playerName);
|
||||||
|
}
|
||||||
|
function returnCreateRoom(data){
|
||||||
|
console.log(data);
|
||||||
|
if(!data.success){
|
||||||
|
alert(data.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
socket.on('returnCreateRoom', function (data) {
|
||||||
|
console.log('<< returnCreateRoom');
|
||||||
|
returnCreateRoom(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// joinRoom
|
||||||
|
function requestJoinRoom(roomId) {
|
||||||
|
console.log('+ requestJoinRoom '.roomId);
|
||||||
|
socket.emit('requestJoinRoom', playerName, roomId);
|
||||||
|
room = 0;
|
||||||
|
hand = [];
|
||||||
|
turn = false;
|
||||||
|
console.log('>> Room Request');
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.on('responseJoinRoom', function (name) {
|
||||||
|
if (name != 'error') {
|
||||||
|
room = name;
|
||||||
|
console.log('<< Room Response: ' + name);
|
||||||
|
//board.drawBoard();
|
||||||
|
} else {
|
||||||
|
socket.disconnect();
|
||||||
|
alert('Rooms are full! Try again later');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// getCards (TODO: remove if test below is a-okay)
|
||||||
|
function requestGetCards(deckId, playerId){
|
||||||
|
console.log('+ requestGetCards');
|
||||||
|
socket.emit('requestGetCards', deckId, playerId);
|
||||||
|
}
|
||||||
|
function responseGetCards(data){
|
||||||
|
console.log(data);
|
||||||
|
if(!data.success){
|
||||||
|
alert(data.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
socket.on('responseGetCards', function (data) {
|
||||||
|
console.log('<< responseGetCards');
|
||||||
|
responseGetCards(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
// When game starts, loadBoard to add data for clients
|
||||||
|
// and display the starting board
|
||||||
|
function responseStartGame(data){
|
||||||
|
if(!data.success){
|
||||||
|
alert(data.message);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
socket.on('responseStartGame', function (data) {
|
||||||
|
console.log('<< responseStartGame');
|
||||||
|
responseStartGame(data);
|
||||||
|
if(data.success !== true){
|
||||||
|
alert('Err with responseStartGame. '+data.message);
|
||||||
|
}
|
||||||
|
// Pass only the data to loadBoard
|
||||||
|
//loadBoard(data.message); // THE OLD/WORKING BOARD. Uncomment (and comment below) if needed for compare
|
||||||
|
|
||||||
|
// Draw the new/simplified board over the top of the working board
|
||||||
|
// TEMP until the new board is working as the old board did.
|
||||||
|
console.log('EXISTING BOARD STILL EXISTS! JUST NEED TO COMMENT drawGameBoard() in main.js');
|
||||||
|
updateGameData(data.message);
|
||||||
|
drawGameBoard();
|
||||||
|
});
|
||||||
|
|
||||||
|
// ALERTS
|
||||||
|
socket.on('alert', function (data) {
|
||||||
|
console.log('<< alert');
|
||||||
|
alert(data.message);
|
||||||
|
});
|
||||||
|
socket.on('log', function (data) {
|
||||||
|
console.log('<< log');
|
||||||
|
console.log(data.message);
|
||||||
|
});
|
||||||
|
|
||||||
@ -0,0 +1,146 @@
|
|||||||
|
// https://stackoverflow.com/a/29676404
|
||||||
|
|
||||||
|
context = document.getElementById("canvas").getContext("2d");
|
||||||
|
let defaultFillStyle = '#000';
|
||||||
|
let shapeDebug = true;
|
||||||
|
let shapeDebugColour = '#FF00FF';
|
||||||
|
|
||||||
|
class Shape extends Path2D{
|
||||||
|
constructor(params){
|
||||||
|
super();
|
||||||
|
this.shape = params.shape || 'rectangle';
|
||||||
|
this.name = params.name;
|
||||||
|
this.x = params.x || 0;
|
||||||
|
this.y = params.y || 0;
|
||||||
|
this.width = params.width || 0;
|
||||||
|
this.height = params.height || 0;
|
||||||
|
this.fillStyle = params.fillStyle || false;
|
||||||
|
this.strokeStyle = params.strokeStyle || false;
|
||||||
|
this.lineWidth = params.lineWidth || 0;
|
||||||
|
this.shadow = params.shadow || false;
|
||||||
|
}
|
||||||
|
setShadow(shadow){
|
||||||
|
this.shadow = shadow;
|
||||||
|
}
|
||||||
|
draw(){
|
||||||
|
//console.log('Draw Shape: '+this.name);
|
||||||
|
//console.log('X: '+this.x+' Y: '+this.y);
|
||||||
|
//console.log('W: '+this.width+' H: '+this.height);
|
||||||
|
//console.log('');
|
||||||
|
|
||||||
|
if (this.fillStyle) {
|
||||||
|
context.fillStyle = this.fillStyle;
|
||||||
|
|
||||||
|
if(this.shadow){
|
||||||
|
context.shadowColor = "#333";
|
||||||
|
context.shadowOffsetX = 1;
|
||||||
|
context.shadowOffsetY = 3;
|
||||||
|
context.shadowBlur = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.shape == 'circle'){
|
||||||
|
// X,Y,Radius, start, end
|
||||||
|
context.beginPath();
|
||||||
|
context.arc(this.x, this.y, this.width/2, 0, 2 * Math.PI);
|
||||||
|
context.fill();
|
||||||
|
context.closePath();
|
||||||
|
}else if(this.shape == 'semi'){
|
||||||
|
context.beginPath();
|
||||||
|
context.arc(this.x, this.y, this.width/2, Math.PI, 0);
|
||||||
|
context.fill();
|
||||||
|
context.closePath();
|
||||||
|
}else if (this.shape == 'rectangle'){
|
||||||
|
context.fillRect(this.x, this.y, this.width, this.height);
|
||||||
|
}
|
||||||
|
context.fillStyle = defaultFillStyle; // Reset back to default
|
||||||
|
context.shadowColor = 'transparent';
|
||||||
|
context.shadowBlur = null;
|
||||||
|
context.shadowOffsetX = null;
|
||||||
|
context.shadowOffsetY = null;
|
||||||
|
}
|
||||||
|
if (this.strokeStyle && this.lineWidth || shapeDebug) {
|
||||||
|
context.strokeStyle = this.strokeStyle;
|
||||||
|
context.lineWidth = this.lineWidth;
|
||||||
|
context.lineWidth = 2;
|
||||||
|
|
||||||
|
if(!this.strokeStyle && shapeDebug){
|
||||||
|
context.strokeStyle = shapeDebugColour;
|
||||||
|
}
|
||||||
|
if(this.shape == 'circle'){
|
||||||
|
// X,Y,Radius, start, end
|
||||||
|
context.beginPath();
|
||||||
|
context.arc(this.x, this.y, this.width/2, 0, 2 * Math.PI);
|
||||||
|
context.stroke();
|
||||||
|
context.closePath();
|
||||||
|
}else if(this.shape == 'semi'){
|
||||||
|
context.beginPath();
|
||||||
|
context.arc(this.x, this.y, this.width/2, Math.PI, 0);
|
||||||
|
context.stroke();
|
||||||
|
context.closePath();
|
||||||
|
}else if (this.shape == 'rectangle'){
|
||||||
|
context.strokeRect(this.x, this.y, this.width, this.height);
|
||||||
|
}
|
||||||
|
else if (this.shape == 'unit'){
|
||||||
|
context.beginPath();
|
||||||
|
context.arc(this.x, this.y, this.width/2, Math.PI, 0);
|
||||||
|
|
||||||
|
// Start at bottom left of the semi
|
||||||
|
context.moveTo(this.x-this.width/2, this.y);
|
||||||
|
// Draw accross to bottom right of semi
|
||||||
|
context.lineTo(this.x+this.width/2, this.y);
|
||||||
|
// Draw down to the desired height
|
||||||
|
context.lineTo(this.x+this.width/2, this.y+this.height/3);
|
||||||
|
// Draw accross to that height, but aligned to left of semi
|
||||||
|
context.lineTo(this.x-this.width/2, this.y+this.height/3);
|
||||||
|
|
||||||
|
context.stroke();
|
||||||
|
context.closePath();
|
||||||
|
}
|
||||||
|
context.strokeStyle = defaultFillStyle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
startClip(shape = null){
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/clip#creating_a_complex_clipping_region
|
||||||
|
if(this.shape == 'circle'){
|
||||||
|
// X,Y,Radius, start, end
|
||||||
|
context.beginPath();
|
||||||
|
context.arc(this.x, this.y, this.width/2, 0, 2 * Math.PI);
|
||||||
|
context.fill();
|
||||||
|
context.closePath();
|
||||||
|
}else if(this.shape == 'semi'){
|
||||||
|
context.beginPath();
|
||||||
|
context.arc(this.x, this.y, this.width/2, Math.PI, 0);
|
||||||
|
context.fill();
|
||||||
|
context.closePath();
|
||||||
|
}else if (this.shape == 'rectangle'){
|
||||||
|
context.fillRect(this.x, this.y, this.width, this.height);
|
||||||
|
}
|
||||||
|
// This is one of the shapes for the card images to sit it
|
||||||
|
// TODO: Will be: unit,token,spell, etc. ALSO will have each shape for UI elements too
|
||||||
|
else if (this.shape == 'unit'){
|
||||||
|
|
||||||
|
context.beginPath();
|
||||||
|
context.arc(this.x, this.y, this.width/2, Math.PI, 0);
|
||||||
|
|
||||||
|
// Start at bottom left of the semi
|
||||||
|
context.moveTo(this.x-this.width/2, this.y);
|
||||||
|
// Draw accross to bottom right of semi
|
||||||
|
context.lineTo(this.x+this.width/2, this.y);
|
||||||
|
// Draw down to the desired height
|
||||||
|
context.lineTo(this.x+this.width/2, this.y+this.height/3);
|
||||||
|
// Draw accross to that height, but aligned to left of semi
|
||||||
|
context.lineTo(this.x-this.width/2, this.y+this.height/3);
|
||||||
|
|
||||||
|
context.fill();
|
||||||
|
context.closePath();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.save(); // Save the canvas draw before the clip is applied
|
||||||
|
context.clip();
|
||||||
|
}
|
||||||
|
endClip(){
|
||||||
|
ctx.restore(); // Restore the canvas draw post clip applied, to get everything else back too
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,24 @@
|
|||||||
|
html,body{
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #a1abaa;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas{
|
||||||
|
margin: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrap{
|
||||||
|
margin: auto;
|
||||||
|
display: block;
|
||||||
|
width: 1000px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dib{display: inline-block; margin: 0; margin-bottom: 8px;}
|
||||||
|
|
||||||
|
.joinRoomButtons li{
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,112 @@
|
|||||||
|
// Build a room, fill will players, etc.
|
||||||
|
const cardGen = require('./cardGen');
|
||||||
|
const components = require('./components');
|
||||||
|
|
||||||
|
// 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(playerData, itemData){
|
||||||
|
|
||||||
|
itemData.player = {}; //let player = {}; // Player item belongs to
|
||||||
|
itemData.players = []; // List of the players (an associated playerItem?)
|
||||||
|
|
||||||
|
let playerNo = 0 + itemData['itemCount']; // Start loop from current itemCount
|
||||||
|
playerCount = playerData.length + itemData['itemCount']; // End at playerCount diff from itemCount
|
||||||
|
let i = 0;
|
||||||
|
for(playerNo; playerNo < playerCount; playerNo++){
|
||||||
|
|
||||||
|
itemData['players'].push([playerNo, playerData[i]]); // Add player no to array so can be looped
|
||||||
|
i++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For future, when 2v2s, and 5v1 Raids, etc.
|
||||||
|
function setTeams(){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function roomGeneration(roomId){
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
(async () => {
|
||||||
|
|
||||||
|
// Player's sockets
|
||||||
|
console.log('--- Room for generation ---');
|
||||||
|
console.log(io.sockets.adapter.rooms.get(roomId));
|
||||||
|
|
||||||
|
let itemData = startItemCount();
|
||||||
|
|
||||||
|
// Player data with Sockets
|
||||||
|
let playerData = [];
|
||||||
|
let playerOrder = {}; // Need a better name
|
||||||
|
let i = 1;
|
||||||
|
let clients = global.io.sockets.adapter.rooms.get(roomId);
|
||||||
|
for (const clientId of clients) {
|
||||||
|
const clientSocket = global.io.sockets.sockets.get(clientId);
|
||||||
|
|
||||||
|
// Which order the player is? Just using the array
|
||||||
|
playerOrder[clientSocket.playerId] = playerData.length;
|
||||||
|
|
||||||
|
playerData.push({
|
||||||
|
'playerDataId': playerData.length
|
||||||
|
,'playerId': clientSocket.playerId
|
||||||
|
,'deck':{'playerId':i,'deckId':1}
|
||||||
|
,'socketId': clientSocket.id // TODO: ONLY FOR SERVERSIDE!!!
|
||||||
|
});
|
||||||
|
|
||||||
|
i++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Add players for the room (seperate to playerData, currently what's used)
|
||||||
|
// ??
|
||||||
|
itemData = setPlayers(playerData, itemData);
|
||||||
|
|
||||||
|
// TODO: Get their selected decks
|
||||||
|
|
||||||
|
// Add all the empty components to the room itemData
|
||||||
|
itemData.component = components.component;
|
||||||
|
itemData.component.player = itemData.player; // TODO: do this in setPlayers
|
||||||
|
|
||||||
|
// Generate the decks, and card within the deck cardLists
|
||||||
|
[itemData] = await Promise.all([ cardGen.requestDeck(itemData) ]);
|
||||||
|
|
||||||
|
// Some room stuff, likely change this
|
||||||
|
itemData.roomId = roomId;
|
||||||
|
itemData.turn = 0; // The turn count of the match
|
||||||
|
itemData.playersTurn = 0; // This means it's playerData[0] turn
|
||||||
|
|
||||||
|
// Room has just been created, so add the itemData and playerData to the room
|
||||||
|
// on server-side so it's easily accessible
|
||||||
|
roomData[roomId].itemData = itemData;
|
||||||
|
roomData[roomId].playerData = playerData;
|
||||||
|
roomData[roomId].playerOrder = playerOrder;
|
||||||
|
|
||||||
|
|
||||||
|
// Return the all the itemData to the client(s)
|
||||||
|
// TODO: This will need to give different data for each, or at least
|
||||||
|
// to differ the boardside
|
||||||
|
return resolve(itemData);
|
||||||
|
})()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: disconnect, reconnect, resume
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
startItemCount
|
||||||
|
,setPlayers
|
||||||
|
,roomGeneration
|
||||||
|
};
|
||||||
|
|
||||||
@ -0,0 +1,246 @@
|
|||||||
|
const roomMod = require('./roomMod');
|
||||||
|
const gameMod = require('./gameMod');
|
||||||
|
|
||||||
|
// Variables for server overall
|
||||||
|
let numRooms = 0;
|
||||||
|
const maxRooms = 3;
|
||||||
|
const maxPlayersPerRoom = 2;
|
||||||
|
global.maxPlayersPerRoom = maxPlayersPerRoom;
|
||||||
|
const maxSpectatorsPerRoom = 0;
|
||||||
|
|
||||||
|
function requestRooms(socket, filter){
|
||||||
|
console.log('+ requestRooms recieved');
|
||||||
|
console.log('- filter: '+filter);
|
||||||
|
|
||||||
|
let response = getRooms(filter, dump = true);
|
||||||
|
global.io.to(socket.id).emit('returnRooms', response);
|
||||||
|
|
||||||
|
console.log('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRooms(filter = 'all', dump = false){
|
||||||
|
console.log('+ getRooms');
|
||||||
|
let response = {
|
||||||
|
random: 'randomStuff',
|
||||||
|
roomData: global.roomData,
|
||||||
|
};
|
||||||
|
|
||||||
|
if(dump){
|
||||||
|
console.log(response);
|
||||||
|
console.log('');
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestCreateRoom(socket, playerName){
|
||||||
|
console.log('+ createRoom recieved');
|
||||||
|
console.log('- requested by: '+playerName);
|
||||||
|
|
||||||
|
response = createRoom(roomId = false, dump = true);
|
||||||
|
global.io.to(socket.id).emit('returnCreateRoom', response);
|
||||||
|
|
||||||
|
if(response.success){
|
||||||
|
let response = getRooms(filter = 'all', dump = true);
|
||||||
|
global.io.to(socket.id).emit('returnRooms', response);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('');
|
||||||
|
}
|
||||||
|
|
||||||
|
function createRoom(roomId = false, dump = true){
|
||||||
|
let roomName = false;
|
||||||
|
if(roomId == false){
|
||||||
|
roomId = numRooms + 1;
|
||||||
|
}
|
||||||
|
console.log(roomId);
|
||||||
|
|
||||||
|
let response = {
|
||||||
|
success: false,
|
||||||
|
message: 'No idea bossman'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Max room limit reached
|
||||||
|
console.log(numRooms);
|
||||||
|
console.log(maxRooms);
|
||||||
|
if(numRooms >= maxRooms){
|
||||||
|
console.log('- Room limit reached');
|
||||||
|
|
||||||
|
response = {
|
||||||
|
success: false,
|
||||||
|
message: 'No space '+numRooms+' out of '+maxRooms+' created.'
|
||||||
|
};
|
||||||
|
|
||||||
|
// Create room
|
||||||
|
}else{
|
||||||
|
console.log('- Creating room')
|
||||||
|
let room = {};
|
||||||
|
room['id'] = roomId;
|
||||||
|
room['name'] = 'Room:'+room['id'];
|
||||||
|
roomName = room['name'];
|
||||||
|
room['password'] = '';
|
||||||
|
room['timeout'] = {};
|
||||||
|
room['timeout']['s'] = 10;
|
||||||
|
room['people'] = 0;
|
||||||
|
room['playerIds'] = {};
|
||||||
|
|
||||||
|
global.roomData[roomId] = room;
|
||||||
|
numRooms = numRooms + 1;
|
||||||
|
|
||||||
|
response = {
|
||||||
|
success: true,
|
||||||
|
message: 'Room Created: '+roomName,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if(dump){
|
||||||
|
console.log(response);
|
||||||
|
console.log('');
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestJoinRoom(socket, playerName, roomId){
|
||||||
|
|
||||||
|
console.log('+ requestJoinRoom recieved');
|
||||||
|
|
||||||
|
let room = global.roomData[roomId];
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/18096649
|
||||||
|
socket.playerId = playerName;
|
||||||
|
|
||||||
|
if(room === undefined){
|
||||||
|
console.log('>> Room does not exist');
|
||||||
|
return 'error';
|
||||||
|
}
|
||||||
|
|
||||||
|
let roomName = 'Room_' + roomId;
|
||||||
|
let people = room['people'];
|
||||||
|
|
||||||
|
if(isUserInRoom(playerName, roomId)){
|
||||||
|
console.log('>> Already in room');
|
||||||
|
return 'already in room';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (people < maxPlayersPerRoom) {
|
||||||
|
|
||||||
|
// Update people in room count
|
||||||
|
people = room['people'] += 1;
|
||||||
|
|
||||||
|
// Add playerId to room (playerName for now while Ids don't exist TODO)
|
||||||
|
room['playerIds'][playerName] = playerName;
|
||||||
|
|
||||||
|
// https://socket.io/docs/v4/rooms/
|
||||||
|
// https://stackoverflow.com/a/25028953
|
||||||
|
socket.join(roomId);
|
||||||
|
|
||||||
|
console.log('>> User ' + playerName +
|
||||||
|
' connected on ' + roomName + ' (' + (people) + '/' + maxPlayersPerRoom + ')');
|
||||||
|
|
||||||
|
// Joined room (emit to the player that just joined)
|
||||||
|
global.io.to(socket.id).emit('responseRoom', response);
|
||||||
|
|
||||||
|
if (people >= maxPlayersPerRoom) {
|
||||||
|
console.log('- starting game');
|
||||||
|
// startGame for room
|
||||||
|
startGame(roomId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Will need to be different to playerName in future (in case dupes)
|
||||||
|
// would use playerId TODO
|
||||||
|
function isUserInRoom(playerName, roomId){
|
||||||
|
if(playerName in global.roomData[roomId]['playerIds']){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startGame(roomId){
|
||||||
|
|
||||||
|
console.log('>> Room: ' + roomId + ': Requesting game...');
|
||||||
|
let people = global.roomData[roomId].players;
|
||||||
|
/*
|
||||||
|
try {
|
||||||
|
//people = io.sockets.adapter.rooms.get(roomId).size;
|
||||||
|
} catch (e) {
|
||||||
|
console.log('>> Room: ' + roomId + ': No people here...');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// For now, if there's 2 people only. Will need changing for
|
||||||
|
// 3v1, 5v1, 2v2, etc...
|
||||||
|
|
||||||
|
let response = {success: false, message: 'Failed requestStartGame() server.js'};
|
||||||
|
if(people < maxPlayersPerRoom){
|
||||||
|
console.log('Too few people');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('>> Room: ' + roomId + ': Starting');
|
||||||
|
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/25028953
|
||||||
|
//console.log(util.inspect(io.sockets.adapter.rooms.get(roomId), true, 4, true))
|
||||||
|
let clients = global.io.sockets.adapter.rooms.get(roomId);
|
||||||
|
|
||||||
|
// This should return the deck data, etc. for each client
|
||||||
|
// ideally only returning the items that the user can/should
|
||||||
|
// see i.e. shouldn't give them the inDeck card list just a counter
|
||||||
|
// shouldn't have opponent card data/their hand shouldn't be flipped
|
||||||
|
|
||||||
|
// Not sure how to catch errors for these await alls
|
||||||
|
// TODO: Look into error handling for await alls
|
||||||
|
const [itemData] =
|
||||||
|
await Promise.all([
|
||||||
|
roomMod.roomGeneration(roomId),
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
|
// data is the 'itemData' not all the roomData
|
||||||
|
response.success = true;
|
||||||
|
response.message = itemData;
|
||||||
|
|
||||||
|
// Each player then gets sent the roomGeneration stuff
|
||||||
|
// TODO:They should recieve different data based on what they can see/interact
|
||||||
|
|
||||||
|
for (const clientId of clients) {
|
||||||
|
|
||||||
|
const clientSocket = global.io.sockets.sockets.get(clientId);
|
||||||
|
console.log('>> responseStartGame: '+clientSocket.playerId);
|
||||||
|
|
||||||
|
// TODO: TESTING STUFF, REMOVE WHEN SORTED
|
||||||
|
let playerIdTemp = roomData[roomId].playerOrder[clientSocket.playerId];
|
||||||
|
//let message = 'You are player: '+playerIdTemp;
|
||||||
|
//global.socketAlert(clientSocket.id, message, 'alert');
|
||||||
|
|
||||||
|
// This is the data being sent, add each individual players Id/turnorder/whatever I call it
|
||||||
|
response.message['playerId'] = playerIdTemp;
|
||||||
|
|
||||||
|
// JANK TODO: better this.
|
||||||
|
// Ids are 0,1. Max players is 2, so can use modulo for now (while it's only ever 2 players)
|
||||||
|
// to set the opponent stuff. With the data passed this can probably be done better, but not sure
|
||||||
|
// at the mo. so to get progress, we're janking it in
|
||||||
|
// player 1+1 = 2 %= 0, player 0+1 = 1 %= 1
|
||||||
|
response.message['opponentId'] = (playerIdTemp+1)%maxPlayersPerRoom;
|
||||||
|
|
||||||
|
// Emit the itemData to client socket (TODO: only emit what each player should see/recieve)
|
||||||
|
global.io.to(clientSocket.id).emit('responseStartGame', response);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start game gameMod?
|
||||||
|
gameMod.gameStart(roomId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Need a 'leave room'/disconnect
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
requestRooms
|
||||||
|
,requestJoinRoom
|
||||||
|
,requestCreateRoom
|
||||||
|
};
|
||||||
@ -0,0 +1,283 @@
|
|||||||
|
const express = require('express');
|
||||||
|
|
||||||
|
const database = require('./database');
|
||||||
|
const rooms = require('./rooms');
|
||||||
|
const gameMod = require('./gameMod');
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
const http = require('http').Server(app);
|
||||||
|
const port = process.env.PORT || 3000;
|
||||||
|
const io = require('socket.io')(http);
|
||||||
|
global.io = io;
|
||||||
|
|
||||||
|
// To log the player sockets, so they can be easily referred to
|
||||||
|
// maybe jank, but can't see alternative currently
|
||||||
|
global.playerSocket = {};
|
||||||
|
global.roomData = {}; // Made global for now, as to not replicate. Maybe sub-optimal?
|
||||||
|
|
||||||
|
|
||||||
|
// util is what nodejs uses for console.log, but has a depth of 2 set
|
||||||
|
// so console.logs show [Array]/[Object] instead of useful info.
|
||||||
|
// This can be overridden console.log(util.inspect(LOGDATA, true, 4, true))
|
||||||
|
// 4 being new depth, true (last one) is to show colours
|
||||||
|
const util = require('util')
|
||||||
|
|
||||||
|
app.use(express.static(__dirname + '/public'));
|
||||||
|
|
||||||
|
http.listen(port, () => console.log('listening on port ' + port));
|
||||||
|
database.connect();
|
||||||
|
|
||||||
|
io.on('connection', onConnection);
|
||||||
|
|
||||||
|
function onConnection(socket){
|
||||||
|
|
||||||
|
console.log('+ User connected');
|
||||||
|
console.log('');
|
||||||
|
|
||||||
|
// Rooms (joining, creating, etc)
|
||||||
|
socket.on('requestRooms', function(filter) {
|
||||||
|
rooms.requestRooms(socket, filter);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('requestJoinRoom', function(playerName, roomId) {
|
||||||
|
rooms.requestJoinRoom(socket, playerName, roomId);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('requestCreateRoom', function(playerName) {
|
||||||
|
rooms.requestCreateRoom(socket, playerName);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Game (actual things relating to the game)
|
||||||
|
// The socket should only be in one game, so socket.on should
|
||||||
|
// do this, but passing room/player anyways as it's how I've written some
|
||||||
|
// roomData bits. TODO: Look if I can do this better...
|
||||||
|
socket.on('passTurn', function(roomId, playerId) {
|
||||||
|
gameMod.passTurn(roomId, playerId);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('drawACard', function(roomId, playerId) {
|
||||||
|
gameMod.drawACard(roomId, playerId);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('shuffleDeck', function(roomId, playerId) {
|
||||||
|
gameMod.shuffleDeck(roomId, playerId);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('playFromHand', function(roomId, playerId, listPosition) {
|
||||||
|
gameMod.playFromHand(roomId, playerId, listPosition);
|
||||||
|
});
|
||||||
|
socket.on('playManaFromHand', function(roomId, playerId, listPosition) {
|
||||||
|
gameMod.playManaFromHand(roomId, playerId, listPosition);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Stack
|
||||||
|
socket.on('requestResolveStack', function(roomId, playerId) {
|
||||||
|
gameMod.acceptResolveStack(roomId, playerId);
|
||||||
|
console.log('resolve stack yep yep yep');
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
socket.on('requestTapCard', function(roomId, playerId, card) {
|
||||||
|
gameMod.tapCard(roomId, playerId, card);
|
||||||
|
});
|
||||||
|
socket.on('requestUntapCard', function(roomId, playerId, card) {
|
||||||
|
gameMod.untapCard(roomId, playerId, card);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('requestStartAttack', function(roomId, playerId, card) {
|
||||||
|
gameMod.startAttack(roomId, playerId, card);
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
global.getPlayerSocketFromRoom = function(playerId, roomId){
|
||||||
|
|
||||||
|
return roomData[roomId].playerData[playerId].socketId;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
global.sendToEachSocket = function(roomId, responseName, data){
|
||||||
|
|
||||||
|
console.log(responseName);
|
||||||
|
|
||||||
|
let clients = global.io.sockets.adapter.rooms.get(roomId);
|
||||||
|
for (const clientId of clients) {
|
||||||
|
|
||||||
|
const clientSocket = global.io.sockets.sockets.get(clientId);
|
||||||
|
// Send to a client socket
|
||||||
|
global.io.to(clientSocket.id).emit(responseName, data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Globals for easier clientside alerts/logs, etc.
|
||||||
|
global.socketAlert = function(socket, message, type = 'alert'){
|
||||||
|
global.io.to(socket).emit(
|
||||||
|
type, {'message': message}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// passTurn response for all clients in room
|
||||||
|
// TODO: include spectators when added
|
||||||
|
global.socketResponsePassTurn = function(roomId){
|
||||||
|
|
||||||
|
let data = {
|
||||||
|
'playerTurn': global.roomData[roomId].itemData.component.playerTurn,
|
||||||
|
'turn': global.roomData[roomId].itemData.component.turn,
|
||||||
|
};
|
||||||
|
|
||||||
|
global.sendToEachSocket(roomId, 'responsePassTurn', data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then emit the deckSize and hand size to all the player's sockets
|
||||||
|
// TODO: spectators in future, won't comment about this from now on
|
||||||
|
global.socketResponseDrawCard = function(roomId, playerId){
|
||||||
|
|
||||||
|
console.log('>> '+roomData[roomId].playerData[playerId].playerId+' drew card');
|
||||||
|
|
||||||
|
global.socketReturnCardCounts(roomId, 'responseDrawCard');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
global.socketReturnCardCounts = function(roomId, responseName = 'responseCardCounts'){
|
||||||
|
|
||||||
|
let data = global.roomData[roomId].itemData.component.cardCount;
|
||||||
|
|
||||||
|
global.sendToEachSocket(roomId, responseName, data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
global.socketResponsePlayerDrewCard = function(roomId, playerId){
|
||||||
|
|
||||||
|
global.socketResponsePlayerHandData(roomId, playerId);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
global.socketResponsePlayerHandData = function(roomId, playerId){
|
||||||
|
|
||||||
|
// This function may be redundant fast, as hand, board, grave, void
|
||||||
|
// anywhere that's visible to one (or both) players will need all the data
|
||||||
|
// sending, so will likely be one function that returns it all?
|
||||||
|
// Could do each thing as their own, and union the data too?
|
||||||
|
global.io.to(global.getPlayerSocketFromRoom(playerId, roomId)).emit(
|
||||||
|
'responsePlayerDrewCard'
|
||||||
|
,gameMod.getPlayerHandData(roomId, playerId)
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
global.socketResponseShuffleDeck = function(roomId, playerId, hasShuffled){
|
||||||
|
|
||||||
|
global.io.to(global.getPlayerSocketFromRoom(playerId, roomId)).emit(
|
||||||
|
'responseShuffleDeck'
|
||||||
|
,[playerId, hasShuffled]
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
global.socketResponseUpdateBoard = function(roomId){
|
||||||
|
|
||||||
|
// TODO: Only return the data that's needed, no everything
|
||||||
|
let data = global.roomData[roomId].itemData.component;
|
||||||
|
// Return the data on the board (to both players)
|
||||||
|
// TODO: If card flips/face-downs are to be a thing, only return the cardData, etc.
|
||||||
|
// to the player who's side it's on
|
||||||
|
global.sendToEachSocket(roomId, 'responseUpdateBoard', data);
|
||||||
|
|
||||||
|
}
|
||||||
|
global.socketResponseUpdateMana = function(roomId){
|
||||||
|
|
||||||
|
// TODO: Only return the data that's needed, no everything
|
||||||
|
let data = global.roomData[roomId].itemData.component;
|
||||||
|
global.sendToEachSocket(roomId, 'responseUpdateMana', data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
global.socketResponsePlayFromHand = function(roomId, playerId, cardId){
|
||||||
|
|
||||||
|
console.log('>> '+roomData[roomId].playerData[playerId].playerId+' played from hand');
|
||||||
|
|
||||||
|
// Return the new cardCounts for hand/deck/grave, etc.
|
||||||
|
global.socketReturnCardCounts(roomId);
|
||||||
|
|
||||||
|
// Update hand of player that card's hand was played from (using existing logic)
|
||||||
|
global.socketResponsePlayerHandData(roomId, playerId);
|
||||||
|
|
||||||
|
// Update the board (for both players)
|
||||||
|
global.socketResponseUpdateBoard(roomId);
|
||||||
|
|
||||||
|
// So the animations can be played with the new cardData (in future)
|
||||||
|
global.sendToEachSocket(roomId, 'responsePlayFromHand', cardId);
|
||||||
|
}
|
||||||
|
global.socketResponsePlayManaFromHand = function(roomId, playerId, cardId){
|
||||||
|
|
||||||
|
console.log('>> '+roomData[roomId].playerData[playerId].playerId+' played mana from hand');
|
||||||
|
|
||||||
|
// Return the new cardCounts for hand/deck/grave, etc.
|
||||||
|
global.socketReturnCardCounts(roomId);
|
||||||
|
|
||||||
|
// Update hand of player that card's hand was played from (using existing logic)
|
||||||
|
global.socketResponsePlayerHandData(roomId, playerId);
|
||||||
|
|
||||||
|
// Update the mana (for both players)
|
||||||
|
global.socketResponseUpdateMana(roomId);
|
||||||
|
|
||||||
|
// So the animations can be played with the new cardData (in future)
|
||||||
|
//global.sendToEachSocket(roomId, 'responsePlayFromHand', cardId);
|
||||||
|
}
|
||||||
|
|
||||||
|
global.socketResponsePlayedShield = function(roomId, playerId){
|
||||||
|
|
||||||
|
//global.socketReturnCardCounts(roomId, 'responseDrawCard');
|
||||||
|
|
||||||
|
// Return playerId of who played a shield so it can be added then animated
|
||||||
|
global.sendToEachSocket(roomId, 'responsePlayedShield', playerId);
|
||||||
|
|
||||||
|
// Date for shield
|
||||||
|
let data = global.roomData[roomId].itemData.component;
|
||||||
|
global.sendToEachSocket(roomId, 'responseUpdateShield', data);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
global.socketResponseTapped = function(roomId, card){
|
||||||
|
let data = [card, global.roomData[roomId].itemData.component.cardStatus.tapped];
|
||||||
|
global.sendToEachSocket(roomId, 'responseTapped', data);
|
||||||
|
}
|
||||||
|
global.socketResponseUntapped = function(roomId, card){
|
||||||
|
let data = [card, global.roomData[roomId].itemData.component.cardStatus.tapped];
|
||||||
|
global.sendToEachSocket(roomId, 'responseUntapped', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
global.socketResponseStartAttack = function(roomId, playerId, targetable){
|
||||||
|
global.socketResponseUpdateTargetable(roomId, playerId, targetable);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
global.socketResponseUpdateTargetable = function(roomId, playerId, targetable){
|
||||||
|
|
||||||
|
global.io.to(global.getPlayerSocketFromRoom(playerId, roomId)).emit(
|
||||||
|
'responseTargetable'
|
||||||
|
,targetable
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
// Attack (return the attacking and 'defending' card and allow for interactions/stack
|
||||||
|
|
||||||
|
// Stack
|
||||||
|
global.socketResponseAddToStack = function(roomId){
|
||||||
|
global.sendToEachSocket(roomId, 'responseAddToStack', 'testData');
|
||||||
|
}
|
||||||
|
global.socketResponseResolveStack = function(roomId){
|
||||||
|
global.sendToEachSocket(roomId, 'responseResolveStack', 'testData');
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Each player one at a time to prevent just self-chaining 24/7
|
||||||
|
global.socketGetStackResponse = function(roomId){
|
||||||
|
// TODO: Return all valid effect triggers/responses to the effect trigger
|
||||||
|
// on the top of the stack, and a 'resolve' option to not trigger anything
|
||||||
|
global.sendToEachSocket(roomId, 'responseGetStackResponse', 'testData');
|
||||||
|
}
|
||||||
|
|
||||||
Loading…
Reference in New Issue