Compare commits

..

No commits in common. 'feature/clientSideSimplify' and 'master' have entirely different histories.

3
.gitignore vendored

@ -1,3 +0,0 @@
package-lock.json
node_modules
node_modules/*

@ -1,39 +0,0 @@
# 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`

@ -1,762 +0,0 @@
// 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
};

@ -1,114 +0,0 @@
// 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
}

@ -1,263 +0,0 @@
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
};

@ -1,14 +0,0 @@
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);

@ -1,124 +0,0 @@
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');

@ -1,3 +0,0 @@
INSERT INTO `card_passive` (`id`, `cardId`, `passiveId`) VALUES (1, 1, 1);
INSERT INTO `card_passive` (`id`, `cardId`, `passiveId`) VALUES (2, 5, 2);

@ -1,2 +0,0 @@
INSERT INTO `passive` (`id`, `passiveName`, `passiveDescription`) VALUES (3, 'Taunt', 'Must be targetted by attacks');

@ -1,206 +0,0 @@
-- --------------------------------------------------------
-- 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) */;

@ -1,92 +0,0 @@
// 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
};

@ -1,628 +0,0 @@
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
};

@ -1,21 +0,0 @@
{
"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.

Before

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

@ -1,151 +0,0 @@
<!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

@ -1,694 +0,0 @@
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

@ -1,33 +0,0 @@
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();
}

@ -1,125 +0,0 @@
// 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);
});

@ -1,205 +0,0 @@
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');
}

@ -1,10 +0,0 @@
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';

@ -1,207 +0,0 @@
// 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();
}

@ -1,680 +0,0 @@
// 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);
}

@ -1,57 +0,0 @@
// 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 : {},
}

@ -1,123 +0,0 @@
// 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);
}

@ -1,170 +0,0 @@
// 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();
}

@ -1,39 +0,0 @@
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;

@ -1,6 +0,0 @@
function cardAttackToShieldDamage(attack){
if (attack >= 10000){ return 3; }
if (attack >= 5001){ return 2; }
if (attack >= 1){ return 1; }
}

@ -1,192 +0,0 @@
// 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);
});

@ -1,146 +0,0 @@
// 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
}
}

@ -1,24 +0,0 @@
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;
}

@ -1,112 +0,0 @@
// 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
};

@ -1,246 +0,0 @@
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
};

@ -1,283 +0,0 @@
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…
Cancel
Save