Compare commits
141 Commits
@ -0,0 +1,3 @@
|
||||
package-lock.json
|
||||
node_modules
|
||||
node_modules/*
|
||||
@ -0,0 +1,39 @@
|
||||
# Card Game
|
||||
|
||||
## Technology
|
||||
|
||||
- node.js
|
||||
- express.js
|
||||
- socketIO
|
||||
- HTML5 (Canvas)
|
||||
|
||||
## Install
|
||||
|
||||
`sudo apt install npm`
|
||||
`git pull <this-repo>`
|
||||
`npm install express --save`
|
||||
|
||||
socketio, mysql
|
||||
|
||||
## Run
|
||||
|
||||
`node server.js`
|
||||
|
||||
Access through
|
||||
|
||||
`http://localhost:3000`
|
||||
|
||||
### Development
|
||||
|
||||
BACKEND/SERVER DEVELOPMENT ONLY
|
||||
|
||||
To prevent the exhausting task of restarting the server on change
|
||||
install and run with nodemon, it'll handle server restart each time
|
||||
a js file is saved (this does also restart for front-end)
|
||||
|
||||
`npm install -g nodemon`
|
||||
|
||||
Run with
|
||||
|
||||
`nodemon --inspect server.js`
|
||||
|
||||
@ -0,0 +1,677 @@
|
||||
// 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.forEach((deck) => {
|
||||
item.push(itemCount); // Add new item to add stuff for
|
||||
deckData[itemCount] = {'deckId':deck.deckId, 'playerId':deck.playerId, 'deckName':deck.deckName, 'maxLength': 0}; // Max length for listPositioning later in cardBuild
|
||||
boardElement[itemCount] = 'realDeck';
|
||||
cardFace[itemCount] = 0; // Face down for deck
|
||||
player[itemCount] = forPlayer;
|
||||
itemCount++; // Needed/good
|
||||
forPlayer++; // Jank/bad
|
||||
})
|
||||
//console.log(deckData);
|
||||
//console.log(deckList);
|
||||
|
||||
// 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)
|
||||
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)
|
||||
boardElement[itemCount] = 'deck'; // Add all cards to deck at match start
|
||||
// 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++;
|
||||
|
||||
// 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;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
itemCount++; // Increment item to not overwrite
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// ADD all new elements, and updated data into itemData
|
||||
itemData.item = item;
|
||||
itemData.itemCount = itemCount;
|
||||
itemData.player = player;
|
||||
itemData.players = players;
|
||||
itemData.deckData = deckData;
|
||||
itemData.deckIn = deckIn;
|
||||
itemData.cardData = cardData;
|
||||
itemData.boardElement = boardElement;
|
||||
itemData.cardStatus = cardStatus;
|
||||
itemData.listPosition = listPosition;
|
||||
itemData.cardFace = cardFace;
|
||||
itemData.cardColours = cardColours;
|
||||
itemData.cardAttack = cardAttack;
|
||||
itemData.cardSprite = cardSprite;
|
||||
itemData.cardManaColour = cardManaColour;
|
||||
itemData.cardEffect = cardEffect;
|
||||
itemData.flight = flight;
|
||||
itemData.reach = reach;
|
||||
|
||||
// item, itemCount, deckData, cardData, boardElement
|
||||
//console.log(cardData);
|
||||
|
||||
// Returning everything to be looped in the front-end
|
||||
// This won't be looped as it will at final, instead just for deck generation
|
||||
// Returned as object over array, as easier to disect when gets to front-end
|
||||
let dataReturn = itemData;
|
||||
return resolve(dataReturn);
|
||||
//return resolve(cardData);
|
||||
|
||||
// Promise stuff testing
|
||||
})()
|
||||
});
|
||||
|
||||
// TODO: In future the item inc. all the decks, cards, locations, and other attributes
|
||||
// will come from here
|
||||
|
||||
// TODO: unrelated, but thought. If lots of players on, generating cards each time
|
||||
// will be a big hit, as well as all the DB hits, so may need to load all cardObjects
|
||||
// into memory/array when server starts. This then prevents any DB changes to alter
|
||||
// things, but outside of testing that won't occur(?), may need to test this at some
|
||||
// point to see. For now DB, and generating is ok, as still working on it
|
||||
}
|
||||
|
||||
function buildCards(cards, cardClasses, cardColourRequirements, cardManaColours, cardEffects, cardPassives){
|
||||
|
||||
console.log(cardColourRequirements);
|
||||
const dPromise = new Promise((resolve, reject) => {
|
||||
|
||||
builtCards = {};
|
||||
|
||||
//console.log(cards);
|
||||
// Loop the cards and build the base
|
||||
cards.forEach((card) => {
|
||||
|
||||
let builtCard = {
|
||||
id: card.id,
|
||||
name: card.cardName,
|
||||
colour: [],
|
||||
cost: card.cardCost,
|
||||
colourRequirements: [],
|
||||
cardManaColour: [], // Should probably be an object
|
||||
type: card.cardType,
|
||||
atk: card.cardAttack,
|
||||
rarity: card.cardRarity,
|
||||
effect: [],
|
||||
cardClass: [],
|
||||
passive: [],
|
||||
};
|
||||
// Give the card an easily accessible Id for compares
|
||||
// and to add to the cardItem being built
|
||||
builtCards[card.id] = builtCard;
|
||||
|
||||
});
|
||||
//console.log(builtCards);
|
||||
|
||||
// Next loop each class, and add to the new builtCard (if cardIds match)
|
||||
cardClasses.forEach((cardClass) => {
|
||||
|
||||
// Check the card exists (it should always, but don't want jank)
|
||||
if(cardClass.cardId in builtCards){
|
||||
// It's the Id for now
|
||||
// TODO: Send the text too (from join) but don't use for compares
|
||||
//console.log(cardClass.cardId);
|
||||
//console.log(cardClass.classId);
|
||||
//console.log('---');
|
||||
//console.log(builtCards[cardClass.cardId]);
|
||||
|
||||
// Add the class to the class array (cards can have multiple)
|
||||
builtCards[cardClass.cardId].cardClass.push(cardClass.classId);
|
||||
|
||||
// TODO: As an array [classId, className] then
|
||||
// card.classes[x][0] can be compared as numeric in code
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Do the same for cardColourRequirements (if cardIds match)
|
||||
cardColourRequirements.forEach((colourReq) => {
|
||||
|
||||
// Check the card exists (it should always, but don't want jank)
|
||||
if(colourReq.cardId in builtCards){
|
||||
// Add the colours to the class array (cards can have multiple)
|
||||
builtCards[colourReq.cardId].colourRequirements.push([colourReq.colourId, colourReq.cost]);
|
||||
|
||||
// TODO: As an array [classId, className] then
|
||||
// card.classes[x][0] can be compared as numeric in code
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Add card mana colours (colour value when played in mana zone)
|
||||
cardManaColours.forEach((manaCol) => {
|
||||
|
||||
// Check the card exists (it should always, but don't want jank)
|
||||
if(manaCol.cardId in builtCards){
|
||||
// Add the colours to the class array (cards can have multiple)
|
||||
builtCards[manaCol.cardId].cardManaColour.push(manaCol.colourId);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Add the card effects that have been pre-built
|
||||
// Loop the cards (each with an array of effect IDs)
|
||||
for (const [key] of Object.entries(cardEffects[0])) {
|
||||
|
||||
// If the cardId is not in builtCards, skip
|
||||
// should always be, but eh
|
||||
let cardId = cardEffects[0][key];
|
||||
if(builtCards[cardId] === undefined){ continue; }
|
||||
|
||||
// Loop the effects in each of the card's effect arrays
|
||||
for (const [key, value] of Object.entries(cardEffects[1])) {
|
||||
// Add each effect that belongs to the card
|
||||
builtCards[cardId].effect.push(value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Add card mana colours (colour value when played in mana zone)
|
||||
cardPassives.forEach((passive) => {
|
||||
|
||||
// Check the card exists (it should always, but don't want jank)
|
||||
if(passive.cardId in builtCards){
|
||||
// Add the colours to the class array (cards can have multiple)
|
||||
builtCards[passive.cardId]['passive'].push(passive.passiveId);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
resolve(builtCards);
|
||||
|
||||
});
|
||||
return dPromise;
|
||||
}
|
||||
// TODO: Add the effects onto the cards passed through
|
||||
// Takes cards array (of obj), and array of effects (from DB or built, probabaly pre-built?)
|
||||
function addCardsEffects(){}
|
||||
|
||||
module.exports = {
|
||||
requestDeck
|
||||
};
|
||||
@ -0,0 +1,99 @@
|
||||
// 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,
|
||||
|
||||
// Card Stuff
|
||||
cardData : {},
|
||||
cardFace : {},
|
||||
cardStatus : {
|
||||
tapped : {},
|
||||
attacking : {},
|
||||
inspected : {},
|
||||
},
|
||||
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 = {},
|
||||
|
||||
// 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
|
||||
realDeck : {},
|
||||
inDeck : {},
|
||||
hand : {},
|
||||
board : {},
|
||||
shield : {},
|
||||
mana : {},
|
||||
grave : {},
|
||||
void : {},
|
||||
|
||||
//
|
||||
listPosition : {},
|
||||
// position (clientside)
|
||||
// size (clientside)
|
||||
|
||||
// Passives
|
||||
// component.passive.flight ?
|
||||
passive : {
|
||||
flight : {},
|
||||
reach : {},
|
||||
taunt : {},
|
||||
},
|
||||
|
||||
type : {
|
||||
unit : {},
|
||||
spell : {},
|
||||
token : {},
|
||||
},
|
||||
|
||||
classes : {
|
||||
orc : {},
|
||||
human : {},
|
||||
spirit : {},
|
||||
},
|
||||
|
||||
|
||||
};
|
||||
|
||||
// For front-end
|
||||
// position, size
|
||||
|
||||
// These should be used as such (Not 100% as yet)
|
||||
// For onBoard()... for player()... for passive.flight()...
|
||||
// Check the board, items belonging to playerX, for flight passive?
|
||||
|
||||
module.exports = {
|
||||
component
|
||||
}
|
||||
|
||||
@ -0,0 +1,263 @@
|
||||
const mysql = require('mysql');
|
||||
const con = mysql.createConnection({
|
||||
host: "localhost",
|
||||
user: "realmsdivided",
|
||||
password: "realmsdivided",
|
||||
database: "realms_divided"
|
||||
});
|
||||
|
||||
function connect(){
|
||||
con.connect(function(err) {
|
||||
if (err) { console.log(err); }
|
||||
else { console.log('DB Connected'); }
|
||||
});
|
||||
}
|
||||
function disconnect(){
|
||||
con.end();
|
||||
}
|
||||
|
||||
// My DB stuffs
|
||||
function dbGetDecks(deckIds = false){
|
||||
const dPromise = new Promise((resolve, reject) => {
|
||||
let cards = [];
|
||||
let sql = `SELECT
|
||||
deckId
|
||||
,playerId
|
||||
,deckName
|
||||
FROM deck
|
||||
`;
|
||||
|
||||
// TODO: Jank, need to unjank it
|
||||
if(deckIds){
|
||||
for(let i = 0; i < deckIds.length; i++){
|
||||
if(i == 0){ sql += ' WHERE '; }
|
||||
else{ sql += ' OR '; }
|
||||
sql += '(deckId = '+deckIds[i][1]+' AND playerId = '+deckIds[i][0]+')';
|
||||
}
|
||||
}
|
||||
|
||||
con.query(sql, function (err, result, fields) {
|
||||
if (err) { throw err; reject(new Error(err)); }
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
return dPromise;
|
||||
}
|
||||
function dbGetDeckList(deckIds = false){
|
||||
const dPromise = new Promise((resolve, reject) => {
|
||||
let cards = [];
|
||||
let sql = `SELECT
|
||||
deckId
|
||||
,playerId
|
||||
,cardId
|
||||
,cardCount
|
||||
FROM deck_cards
|
||||
`;
|
||||
|
||||
// TODO: Jank, need to unjank it
|
||||
if(deckIds){
|
||||
for(let i = 0; i < deckIds.length; i++){
|
||||
if(i == 0){ sql += ' WHERE '; }
|
||||
else{ sql += ' OR '; }
|
||||
sql += '(deckId = '+deckIds[i][1]+' AND playerId = '+deckIds[i][0]+')';
|
||||
}
|
||||
}
|
||||
|
||||
con.query(sql, function (err, result, fields) {
|
||||
if (err) { throw err; reject(new Error(err)); }
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
return dPromise;
|
||||
}
|
||||
function dbGetCards(cardIds = false){
|
||||
// Start with basic stuff in card table
|
||||
const dPromise = new Promise((resolve, reject) => {
|
||||
let sql = `SELECT
|
||||
id
|
||||
,cardName
|
||||
,cardCost
|
||||
,cardType
|
||||
,cardAttack
|
||||
,cardRarity
|
||||
FROM card
|
||||
`;
|
||||
if(cardIds){ sql += 'WHERE card.id IN ('+cardIds+')'; }
|
||||
|
||||
con.query(sql, function (err, result, fields) {
|
||||
if (err) { throw err; reject(new Error(err)); }
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
return dPromise;
|
||||
}
|
||||
function dbGetCardClasses(cardIds = false){
|
||||
// Get the classes assoc. on each card
|
||||
const dPromise = new Promise((resolve, reject) => {
|
||||
let sql = `SELECT
|
||||
cardId
|
||||
,classId
|
||||
FROM card_class
|
||||
`;
|
||||
if(cardIds){ sql += 'WHERE cardId IN ('+cardIds+')'; }
|
||||
|
||||
con.query(sql, function (err, result, fields) {
|
||||
if (err) { throw err; reject(new Error(err)); }
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
return dPromise;
|
||||
}
|
||||
function dbGetCardColourRequirement(cardIds = false){
|
||||
// Get the classes assoc. on each card
|
||||
const dPromise = new Promise((resolve, reject) => {
|
||||
let sql = `SELECT
|
||||
cardId
|
||||
,colourId
|
||||
,cost
|
||||
FROM card_colour_requirement
|
||||
`;
|
||||
if(cardIds){ sql += 'WHERE cardId IN ('+cardIds+')'; }
|
||||
|
||||
con.query(sql, function (err, result, fields) {
|
||||
if (err) { throw err; reject(new Error(err)); }
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
return dPromise;
|
||||
}
|
||||
|
||||
function dbGetCardManaColour(cardIds = false){
|
||||
// Get the classes assoc. on each card
|
||||
const cPromise = new Promise((resolve, reject) => {
|
||||
let sql = `SELECT
|
||||
cardId
|
||||
,colourId
|
||||
FROM card_mana_colour
|
||||
`;
|
||||
if(cardIds){ sql += 'WHERE cardId IN ('+cardIds+')'; }
|
||||
|
||||
con.query(sql, function (err, result, fields) {
|
||||
if (err) { throw err; reject(new Error(err)); }
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
return cPromise;
|
||||
}
|
||||
|
||||
// Effect stuff
|
||||
function dbGetEffect(cardIds = false){
|
||||
const ePromise = new Promise((resolve, reject) => {
|
||||
let sql = `SELECT
|
||||
cardId
|
||||
,effectId
|
||||
,description
|
||||
FROM card_effect
|
||||
|
||||
INNER JOIN effect
|
||||
ON effect.id = card_effect.effectId
|
||||
`;
|
||||
if(cardIds){ sql += 'WHERE cardId IN ('+cardIds+')'; }
|
||||
|
||||
con.query(sql, function (err, result, fields) {
|
||||
if (err) { throw err; reject(new Error(err)); }
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
return ePromise;
|
||||
}
|
||||
function dbGetEffectStep(effectIds = false){
|
||||
const ePromise = new Promise((resolve, reject) => {
|
||||
let sql = `SELECT
|
||||
effectId,
|
||||
effect_step.id AS stepId,
|
||||
stepOrder,
|
||||
basicEffectId,
|
||||
amount,
|
||||
colourId,
|
||||
typeId,
|
||||
classId,
|
||||
passiveId,
|
||||
itemFromStep
|
||||
FROM effect_step
|
||||
|
||||
LEFT JOIN -- May not be a target?
|
||||
effect_step_target
|
||||
ON effect_step_target.effectStep = effect_step.id
|
||||
`;
|
||||
if(effectIds){ sql += 'WHERE effectId IN ('+effectIds+')'; }
|
||||
|
||||
con.query(sql, function (err, result, fields) {
|
||||
if (err) { throw err; reject(new Error(err)); }
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
return ePromise;
|
||||
}
|
||||
// Effect Trigger stuff
|
||||
function dbGetEffectTrigger(effectIds = false){
|
||||
const ePromise = new Promise((resolve, reject) => {
|
||||
let sql = `SELECT
|
||||
effect_trigger.id AS triggerId,
|
||||
cardId,
|
||||
effectId,
|
||||
triggerTypeId,
|
||||
amount,
|
||||
colourId,
|
||||
typeId,
|
||||
classId,
|
||||
passiveId
|
||||
|
||||
FROM effect_trigger
|
||||
|
||||
INNER JOIN card_effect
|
||||
ON card_effect.effectId = effect_trigger.cardEffectId
|
||||
INNER JOIN trigger_type
|
||||
ON trigger_type.id = effect_trigger.triggerTypeId
|
||||
|
||||
LEFT JOIN -- May not be a target
|
||||
effect_trigger_target
|
||||
ON effect_trigger_target.effectTriggerId = effect_trigger.triggerTypeId
|
||||
`;
|
||||
if(effectIds){ sql += 'WHERE effectId IN ('+effectIds+')'; }
|
||||
|
||||
con.query(sql, function (err, result, fields) {
|
||||
if (err) { throw err; reject(new Error(err)); }
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
return ePromise;
|
||||
}
|
||||
|
||||
// Passive stuff
|
||||
function dbGetPassive(cardIds = false){
|
||||
const pPromise = new Promise((resolve, reject) => {
|
||||
let sql = `SELECT
|
||||
cardId
|
||||
,passiveId
|
||||
FROM card_passive
|
||||
`;
|
||||
if(cardIds){ sql += 'WHERE card.id IN ('+cardIds+')'; }
|
||||
|
||||
con.query(sql, function (err, result, fields) {
|
||||
if (err) { throw err; reject(new Error(err)); }
|
||||
resolve(result);
|
||||
});
|
||||
});
|
||||
return pPromise;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
connect, disconnect
|
||||
// Testing, and trailing
|
||||
, dbGetDecks
|
||||
, dbGetDeckList
|
||||
, dbGetCards
|
||||
, dbGetCardClasses
|
||||
, dbGetCardColourRequirement
|
||||
, dbGetCardManaColour
|
||||
, dbGetEffect
|
||||
, dbGetEffectStep
|
||||
, dbGetEffectTrigger
|
||||
, dbGetPassive
|
||||
};
|
||||
@ -0,0 +1,14 @@
|
||||
USE `realms_divided`;
|
||||
CREATE TABLE IF NOT EXISTS `card_mana_colour` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`cardId` int(11) DEFAULT NULL,
|
||||
`colourId` tinyint(4) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
INSERT INTO `card_mana_colour` (`id`, `cardId`, `colourId`) VALUES
|
||||
(1, 1, 3),
|
||||
(2, 2, 1),
|
||||
(3, 3, 2),
|
||||
(4, 4, 1),
|
||||
(5, 5, 3);
|
||||
@ -0,0 +1,124 @@
|
||||
CREATE TABLE IF NOT EXISTS `basic_effect` (
|
||||
`id` smallint(6) NOT NULL AUTO_INCREMENT,
|
||||
`effectName` tinytext DEFAULT NULL,
|
||||
`effectDescription` varchar(250) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
INSERT INTO `basic_effect` (`id`, `effectName`, `effectDescription`) VALUES
|
||||
(1, 'Equip', 'Add this cards attack, and effect(s) to another'),
|
||||
(2, 'Heal', 'Untap X shield(s)'),
|
||||
(3, 'Hurt', 'Deal X damage to target unit, this combat'),
|
||||
(4, 'Recruit', 'Play from Hand'),
|
||||
(5, 'Give Flight', 'Give a unit [Flight]');
|
||||
|
||||
|
||||
DROP TABLE `card_effect`;
|
||||
CREATE TABLE IF NOT EXISTS `card_effect` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`cardId` int(11) DEFAULT NULL,
|
||||
`effectId` int(11) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
INSERT INTO `card_effect` (`id`, `cardId`, `effectId`) VALUES
|
||||
(1, 1, 1);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `card_passive` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`cardId` int(11) DEFAULT NULL,
|
||||
`passiveId` tinyint(4) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
|
||||
DROP TABLE `effect`;
|
||||
CREATE TABLE IF NOT EXISTS `effect` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`description` varchar(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
INSERT INTO `effect` (`id`, `description`) VALUES
|
||||
(1, '(Tap and Pay 1 Red): [Recruit] a [Red][Orc] unit; Give it [Flight] this turn.');
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `effect_step` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`effectId` int(11) DEFAULT NULL,
|
||||
`stepOrder` tinyint(4) DEFAULT NULL,
|
||||
`basicEffectId` tinyint(4) DEFAULT NULL,
|
||||
`amount` smallint(6) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
INSERT INTO `effect_step` (`id`, `effectId`, `stepOrder`, `basicEffectId`, `amount`) VALUES
|
||||
(1, 1, 1, 4, 1),
|
||||
(2, 1, 2, 5, 1);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `effect_step_target` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`effectStep` int(11) DEFAULT NULL,
|
||||
`colourId` tinyint(4) DEFAULT NULL,
|
||||
`typeId` tinyint(4) DEFAULT NULL,
|
||||
`classId` tinyint(4) DEFAULT NULL,
|
||||
`passiveId` tinyint(4) DEFAULT NULL,
|
||||
`itemFromStep` int(11) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
INSERT INTO `effect_step_target` (`id`, `effectStep`, `colourId`, `typeId`, `classId`, `passiveId`, `itemFromStep`) VALUES
|
||||
(1, 1, 3, 1, 5, NULL, NULL),
|
||||
(2, 2, NULL, NULL, NULL, NULL, 1);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `effect_trigger` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`triggerTypeId` int(11) DEFAULT NULL,
|
||||
`cardEffectId` int(11) DEFAULT NULL,
|
||||
`amount` smallint(6) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
INSERT INTO `effect_trigger` (`id`, `triggerTypeId`, `cardEffectId`, `amount`) VALUES
|
||||
(1, 1, 1, NULL),
|
||||
(2, 2, 1, 1);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `effect_trigger_target` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`effectTriggerId` int(11) DEFAULT NULL,
|
||||
`colourId` tinyint(4) DEFAULT NULL,
|
||||
`typeId` tinyint(4) DEFAULT NULL,
|
||||
`classId` tinyint(4) DEFAULT NULL,
|
||||
`passiveId` int(11) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
INSERT INTO `effect_trigger_target` (`id`, `effectTriggerId`, `colourId`, `typeId`, `classId`, `passiveId`) VALUES
|
||||
(1, 2, 3, NULL, NULL, NULL);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `passive` (
|
||||
`id` tinyint(4) NOT NULL AUTO_INCREMENT,
|
||||
`passiveName` varchar(50) DEFAULT NULL,
|
||||
`passiveDescription` varchar(255) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
INSERT INTO `passive` (`id`, `passiveName`, `passiveDescription`) VALUES
|
||||
(1, 'Flight', 'Ignore taunt, unattackable by non-[Flight] units'),
|
||||
(2, 'Reach', 'Can attack [Flight] units');
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `trigger_type` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(50) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
INSERT INTO `trigger_type` (`id`, `name`) VALUES
|
||||
(1, 'Tap'),
|
||||
(2, 'Pay');
|
||||
@ -0,0 +1,3 @@
|
||||
INSERT INTO `card_passive` (`id`, `cardId`, `passiveId`) VALUES (1, 1, 1);
|
||||
INSERT INTO `card_passive` (`id`, `cardId`, `passiveId`) VALUES (2, 5, 2);
|
||||
|
||||
@ -0,0 +1,2 @@
|
||||
INSERT INTO `passive` (`id`, `passiveName`, `passiveDescription`) VALUES (3, 'Taunt', 'Must be targetted by attacks');
|
||||
|
||||
@ -0,0 +1,206 @@
|
||||
-- --------------------------------------------------------
|
||||
-- Host: 127.0.0.1
|
||||
-- Server version: 10.7.8-MariaDB-1:10.7.8+maria~ubu2004 - mariadb.org binary distribution
|
||||
-- Server OS: debian-linux-gnu
|
||||
-- HeidiSQL Version: 12.6.0.6765
|
||||
-- --------------------------------------------------------
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET NAMES utf8 */;
|
||||
/*!50503 SET NAMES utf8mb4 */;
|
||||
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
|
||||
|
||||
-- Dumping database structure for realms_divided
|
||||
CREATE DATABASE IF NOT EXISTS `realms_divided` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci */;
|
||||
USE `realms_divided`;
|
||||
|
||||
-- Dumping structure for table realms_divided.card
|
||||
CREATE TABLE IF NOT EXISTS `card` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`cardName` varchar(255) NOT NULL,
|
||||
`cardCost` tinyint(4) NOT NULL DEFAULT 2,
|
||||
`cardType` tinyint(4) DEFAULT NULL,
|
||||
`cardAttack` int(11) DEFAULT NULL,
|
||||
`cardRarity` tinyint(4) NOT NULL DEFAULT 1,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- Dumping data for table realms_divided.card: ~5 rows (approximately)
|
||||
INSERT INTO `card` (`id`, `cardName`, `cardCost`, `cardType`, `cardAttack`, `cardRarity`) VALUES
|
||||
(1, 'Red1', 1, 1, 500, 1),
|
||||
(2, 'White1', 1, 1, 1000, 1),
|
||||
(3, 'Blue1', 2, 1, 1000, 1),
|
||||
(4, 'White 8', 3, 2, NULL, 2),
|
||||
(5, 'Red7', 2, 2, NULL, 2);
|
||||
|
||||
-- Dumping structure for table realms_divided.card_class
|
||||
CREATE TABLE IF NOT EXISTS `card_class` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`cardId` int(11) DEFAULT NULL,
|
||||
`classId` smallint(6) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- Dumping data for table realms_divided.card_class: ~4 rows (approximately)
|
||||
INSERT INTO `card_class` (`id`, `cardId`, `classId`) VALUES
|
||||
(1, 1, 1),
|
||||
(2, 2, 1),
|
||||
(3, 3, 1),
|
||||
(4, 1, 2);
|
||||
|
||||
-- Dumping structure for table realms_divided.card_colour_requirement
|
||||
CREATE TABLE IF NOT EXISTS `card_colour_requirement` (
|
||||
`id` tinyint(4) NOT NULL AUTO_INCREMENT,
|
||||
`cardId` int(11) DEFAULT NULL,
|
||||
`colourId` tinyint(4) DEFAULT NULL,
|
||||
`cost` tinyint(4) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- Dumping data for table realms_divided.card_colour_requirement: ~5 rows (approximately)
|
||||
INSERT INTO `card_colour_requirement` (`id`, `cardId`, `colourId`, `cost`) VALUES
|
||||
(1, 1, 3, 1),
|
||||
(2, 2, 1, 1),
|
||||
(3, 3, 2, 1),
|
||||
(4, 4, 1, 2),
|
||||
(5, 5, 3, 2);
|
||||
|
||||
-- Dumping structure for table realms_divided.card_effect
|
||||
CREATE TABLE IF NOT EXISTS `card_effect` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`cardId` int(11) DEFAULT NULL,
|
||||
`effectId` smallint(6) DEFAULT NULL,
|
||||
`value` smallint(6) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- Dumping data for table realms_divided.card_effect: ~5 rows (approximately)
|
||||
INSERT INTO `card_effect` (`id`, `cardId`, `effectId`, `value`) VALUES
|
||||
(1, 1, 2, NULL),
|
||||
(2, 3, 1, NULL),
|
||||
(3, 2, 3, NULL),
|
||||
(4, 4, 4, 1000),
|
||||
(5, 5, 5, 3000);
|
||||
|
||||
-- Dumping structure for table realms_divided.class
|
||||
CREATE TABLE IF NOT EXISTS `class` (
|
||||
`id` smallint(6) NOT NULL AUTO_INCREMENT,
|
||||
`parentId` tinyint(4) DEFAULT NULL,
|
||||
`className` tinytext NOT NULL,
|
||||
`primaryColour` smallint(6) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- Dumping data for table realms_divided.class: ~10 rows (approximately)
|
||||
INSERT INTO `class` (`id`, `parentId`, `className`, `primaryColour`) VALUES
|
||||
(1, NULL, 'Goblin', 3),
|
||||
(2, NULL, 'Human', 1),
|
||||
(3, NULL, 'Spirit', 2),
|
||||
(4, NULL, 'Warrior', 1),
|
||||
(5, NULL, 'Orc', 3),
|
||||
(6, NULL, 'Beast', 5),
|
||||
(7, NULL, 'Plant', 5),
|
||||
(8, NULL, 'Undead', 4),
|
||||
(9, NULL, 'Mechanical', 6),
|
||||
(10, NULL, 'Void', 7);
|
||||
|
||||
-- Dumping structure for table realms_divided.colour
|
||||
CREATE TABLE IF NOT EXISTS `colour` (
|
||||
`id` tinyint(4) NOT NULL AUTO_INCREMENT,
|
||||
`colourName` tinytext DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- Dumping data for table realms_divided.colour: ~7 rows (approximately)
|
||||
INSERT INTO `colour` (`id`, `colourName`) VALUES
|
||||
(1, 'White'),
|
||||
(2, 'Blue'),
|
||||
(3, 'Red'),
|
||||
(4, 'Black'),
|
||||
(5, 'Green'),
|
||||
(6, 'Brown'),
|
||||
(7, 'Void');
|
||||
|
||||
-- Dumping structure for table realms_divided.deck
|
||||
CREATE TABLE IF NOT EXISTS `deck` (
|
||||
`deckId` int(11) NOT NULL,
|
||||
`playerId` int(11) NOT NULL,
|
||||
`deckName` tinytext NOT NULL,
|
||||
PRIMARY KEY (`deckId`,`playerId`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- Dumping data for table realms_divided.deck: ~2 rows (approximately)
|
||||
INSERT INTO `deck` (`deckId`, `playerId`, `deckName`) VALUES
|
||||
(1, 1, 'Deck 1 P1'),
|
||||
(1, 2, 'Deck 1 P2');
|
||||
|
||||
-- Dumping structure for table realms_divided.deck_cards
|
||||
CREATE TABLE IF NOT EXISTS `deck_cards` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`deckId` int(11) DEFAULT NULL,
|
||||
`playerId` int(11) DEFAULT NULL,
|
||||
`cardId` int(11) DEFAULT NULL,
|
||||
`cardCount` tinyint(4) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- Dumping data for table realms_divided.deck_cards: ~5 rows (approximately)
|
||||
INSERT INTO `deck_cards` (`id`, `deckId`, `playerId`, `cardId`, `cardCount`) VALUES
|
||||
(1, 1, 1, 1, 25),
|
||||
(2, 1, 1, 5, 10),
|
||||
(3, 1, 2, 2, 15),
|
||||
(4, 1, 2, 3, 14),
|
||||
(5, 1, 2, 4, 6);
|
||||
|
||||
-- Dumping structure for table realms_divided.effect
|
||||
CREATE TABLE IF NOT EXISTS `effect` (
|
||||
`id` smallint(6) NOT NULL AUTO_INCREMENT,
|
||||
`effectName` tinytext DEFAULT NULL,
|
||||
`effectDescription` varchar(250) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- Dumping data for table realms_divided.effect: ~5 rows (approximately)
|
||||
INSERT INTO `effect` (`id`, `effectName`, `effectDescription`) VALUES
|
||||
(1, 'Flight', 'Can ignore Taunt, cannot be tagetted by attack'),
|
||||
(2, 'Reach', 'Can attack Flight units'),
|
||||
(3, 'Equip', 'Add this cards attack, and effect(s) to another'),
|
||||
(4, 'Heal', 'Untap X shield(s)'),
|
||||
(5, 'Hurt', 'Deal X damage to target unit, this combat');
|
||||
|
||||
-- Dumping structure for table realms_divided.rarity
|
||||
CREATE TABLE IF NOT EXISTS `rarity` (
|
||||
`id` tinyint(4) NOT NULL AUTO_INCREMENT,
|
||||
`rarityName` tinytext DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- Dumping data for table realms_divided.rarity: ~2 rows (approximately)
|
||||
INSERT INTO `rarity` (`id`, `rarityName`) VALUES
|
||||
(1, 'Common'),
|
||||
(2, 'Uncommon'),
|
||||
(3, 'Rare');
|
||||
|
||||
-- Dumping structure for table realms_divided.type
|
||||
CREATE TABLE IF NOT EXISTS `type` (
|
||||
`id` tinyint(4) NOT NULL AUTO_INCREMENT,
|
||||
`typeName` tinytext DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||
|
||||
-- Dumping data for table realms_divided.type: ~2 rows (approximately)
|
||||
INSERT INTO `type` (`id`, `typeName`) VALUES
|
||||
(1, 'Unit'),
|
||||
(2, 'Spell'),
|
||||
(3, 'Token');
|
||||
|
||||
/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */;
|
||||
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */;
|
||||
@ -0,0 +1,25 @@
|
||||
// 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 passTurn(roomId, playerId){
|
||||
|
||||
// Check playerId (not correct atm) before doing the stuff, to verify the user
|
||||
// IS the user, and in the room
|
||||
|
||||
// Test alert so that different data/emits can be sent per-player
|
||||
global.socketAlert(roomData[roomId].playerData[0].socketId, 'Pass', 'alert');
|
||||
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
passTurn
|
||||
};
|
||||
|
||||
@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "cardgame",
|
||||
"version": "0.0.1",
|
||||
"description": "Card Game",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "node server.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/eperezcosano/Uno.git"
|
||||
},
|
||||
"author": "Nathan (Aney) Steel",
|
||||
"license": "???",
|
||||
"homepage": "anetwork.uk",
|
||||
"dependencies": {
|
||||
"express": "^4.21.0",
|
||||
"mysql": "^2.18.1",
|
||||
"socket.io": "^4.8.0"
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,21 @@
|
||||
// 0,1,2 = white,blue,red (for now)
|
||||
// TODO: Use a DB in future, this is just for testing
|
||||
let cardArray =
|
||||
[
|
||||
{ id: 1, name: 'Red1', colour: 2, cost: 1, type: 'Goblin', atk: 500, rarity: 'common', effect:'[[Sneak]] 500'},
|
||||
{ id: 2, name: 'Red2', colour: 2, cost: 1, type: 'Goblin', atk: 1500, rarity: 'common', effect:'Cannot attack direct'},
|
||||
{ id: 3, name: 'Red3', colour: 2, cost: 2, type: 'Goblin', atk: 1000, rarity: 'common', effect:'[[DEATH]] deal 1500 extra to killer'},
|
||||
{ id: 4, name: 'Red4', colour: 2, cost: 2, type: 'Goblin', atk: 2000, rarity: 'common', effect:null},
|
||||
{ id: 5, name: 'White1', colour: 0, cost: 1, type: 'Weapon', atk: 500, rarity: 'common', effect:'[[Equip]]'},
|
||||
{ id: 6, name: 'White2', colour: 0, cost: 2, type: 'Human', atk: 1500, rarity: 'common', effect:null},
|
||||
{ id: 7, name: 'White3', colour: 0, cost: 2, type: 'Human', atk: 2000, rarity: 'common', effect:'[[TAUNT]]'},
|
||||
{ id: 8, name: 'White5', colour: 0, cost: 2, type: 'Human', atk: 1500, rarity: 'common', effect:'[[REACH]]'},
|
||||
{ id: 9, name: 'Blue1', colour: 1, cost: 2, type: 'Spirit', atk: 1000, rarity: 'common', effect:null},
|
||||
{ id: 10, name: 'Blue3', colour: 1, cost: 2, type: 'Spirit', atk: 1000, rarity: 'common', effect:'[[DRAW]] 1'},
|
||||
{ id: 11, name: 'Blue4', colour: 1, cost: 2, type: 'Spirit', atk: 1500, rarity: 'common', effect:null},
|
||||
{ id: 12, name: 'Blue5', colour: 1, cost: 3, type: 'Spirit', atk: 2500, rarity: 'common', effect:'[[FLIGHT]]'}
|
||||
]
|
||||
|
||||
let deckListPlayer = [5,5,5,5,6,6,6,6,7,7,7,7,8,8,8,8,9,9,9,9,10,10,10,10];
|
||||
let deckListOpponent = [1,1,1,1,2,2,3,3,4,4,7,7,7,7,5,5,5,3,3,8,8];
|
||||
|
||||
@ -0,0 +1,207 @@
|
||||
// For front-end debugging
|
||||
// Anything to be tested that would otherwise need server logs, etc.
|
||||
// should go here
|
||||
|
||||
function debugEffect(){
|
||||
|
||||
let damageAmount = document.getElementById("effectDamageAmount").value;
|
||||
if(damageAmount == ""){ damageAmount = null; }
|
||||
|
||||
let targetId = document.getElementById("effectTargetId").value;
|
||||
if(targetId == ""){ targetId = null; }
|
||||
|
||||
let targetId2 = document.getElementById("effectTargetId2").value;
|
||||
if(targetId2 == ""){ targetId2 = null; }
|
||||
|
||||
let targetPlayer = document.getElementById("effectTargetPlayer").value;
|
||||
if(targetPlayer == ""){ targetPlayer = null; }
|
||||
|
||||
let effectAddRemove = document.getElementById("effectAddRemove").value;
|
||||
if(effectAddRemove == ""){ effectAddRemove = null; }
|
||||
|
||||
let effect = document.getElementById("effect").value;
|
||||
if(effect == ""){ effect = null; }
|
||||
|
||||
debugEffectFunction(damageAmount, targetId, targetId2, targetPlayer, effectAddRemove, effect);
|
||||
|
||||
}
|
||||
|
||||
function debugEffectFunction(damageAmount = null, targetId = null, targetId2 = null, targetPlayer = null, effectAddRemove = null, effect = null){
|
||||
|
||||
if(effect == 'hurt'){
|
||||
hurt(damageAmount, targetId, targetPlayer);
|
||||
}
|
||||
if(effect == 'heal'){
|
||||
heal(damageAmount, targetPlayer);
|
||||
}
|
||||
if(effect == 'flight'){
|
||||
console.log(targetId+' Flight: '+flight[targetId]);
|
||||
|
||||
console.log(effectAddRemove);
|
||||
|
||||
if(effectAddRemove == 'remove'){
|
||||
removeFlight(targetId);
|
||||
}else{
|
||||
giveFlight(targetId);
|
||||
}
|
||||
|
||||
console.log(targetId+' Flight: '+flight[targetId]);
|
||||
}
|
||||
if(effect == 'reach'){
|
||||
console.log(targetId+' Reach: '+reach[targetId]);
|
||||
|
||||
console.log(effectAddRemove);
|
||||
|
||||
if(effectAddRemove == 'remove'){
|
||||
removeReach(targetId);
|
||||
}else{
|
||||
giveReach(targetId);
|
||||
}
|
||||
|
||||
console.log(targetId+' Reach: '+reach[targetId]);
|
||||
}
|
||||
if(effect == 'taunt'){
|
||||
console.log(targetId+' Taunt: '+taunt[targetId]);
|
||||
|
||||
console.log(effectAddRemove);
|
||||
|
||||
if(effectAddRemove == 'remove'){
|
||||
removeTaunt(targetId);
|
||||
}else{
|
||||
giveTaunt(targetId);
|
||||
}
|
||||
|
||||
console.log(targetId+' Taunt: '+reach[targetId]);
|
||||
}
|
||||
if(effect == 'draw'){
|
||||
drawCard(damageAmount, targetPlayer);
|
||||
}
|
||||
if(effect == 'equip'){}
|
||||
|
||||
board.drawBoard();
|
||||
}
|
||||
|
||||
|
||||
function debugTrigger(){
|
||||
let targetId = document.getElementById("triggerTargetId").value;
|
||||
if(targetId == ""){ targetId = null; }
|
||||
|
||||
let trigger = document.getElementById("trigger").value;
|
||||
if(trigger == ""){ trigger = null; }
|
||||
|
||||
let triggerAmount = document.getElementById("triggerAmount").value;
|
||||
if(triggerAmount == ""){ triggerAmount = null; }
|
||||
|
||||
debugTriggerFunction(targetId, trigger, triggerAmount);
|
||||
}
|
||||
function debugTriggerFunction(targetId = null, trigger = null, triggerAmount = null){
|
||||
|
||||
if(targetId == null){ return false; }
|
||||
|
||||
if(trigger == 'tap'){
|
||||
triggerTap(targetId);
|
||||
}
|
||||
if(trigger == 'pay'){
|
||||
triggerPay(triggerAmount);
|
||||
}
|
||||
|
||||
board.drawBoard();
|
||||
}
|
||||
|
||||
// Builds console log for card effect (inc.
|
||||
// all triggers, etc)
|
||||
function debugGetCardEffects(itemKey){
|
||||
|
||||
let effectData = cardEffect[itemKey];
|
||||
console.log(effectData);
|
||||
if(effectData === undefined){ return false; }
|
||||
|
||||
effectData.forEach((effect, effectIndex) => {
|
||||
|
||||
console.log('--- Effect '+ effectIndex +' ---');
|
||||
console.log(effect['description']);
|
||||
|
||||
// Loop the triggers
|
||||
for (const [key, value] of Object.entries(effect['trigger'])) {
|
||||
|
||||
console.log('--- Trigger ---');
|
||||
let effectTrigger = effect['trigger'][key];
|
||||
console.log(triggerTypes[effectTrigger['triggerTypeId']]);
|
||||
|
||||
console.log('--- Trigger Amount ---');
|
||||
console.log(effectTrigger['amount']);
|
||||
|
||||
// Loop targets for said trigger
|
||||
effectTrigger['target'].forEach((effectTriggerTarget) => {
|
||||
console.log('--- Trigger Target ---');
|
||||
console.log(effectTriggerTarget.classId);
|
||||
console.log(effectTriggerTarget.colourId);
|
||||
console.log(effectTriggerTarget.passiveId);
|
||||
console.log(effectTriggerTarget.typeId);
|
||||
});
|
||||
}
|
||||
|
||||
// Loop the effects
|
||||
for (const [key, value] of Object.entries(effect['step'])) {
|
||||
// amoutn, basicEffectId, stepOrder, target (also itemfromstep)
|
||||
console.log(effect['step'][key]);
|
||||
|
||||
console.log('--- Effect Step ---');
|
||||
let effectStep = effect['step'][key];
|
||||
console.log(basicEffects[effectStep['basicEffectId']]);
|
||||
|
||||
console.log('--- Effect Step Amount ---');
|
||||
console.log(effectStep['amount']);
|
||||
|
||||
// Loop targets for effectStep
|
||||
effectStep['target'].forEach((effectStepTarget) => {
|
||||
console.log('--- Effect Step Target ---');
|
||||
console.log(effectStepTarget.classId);
|
||||
console.log(effectStepTarget.colourId);
|
||||
console.log(effectStepTarget.passiveId);
|
||||
console.log(effectStepTarget.typeId);
|
||||
console.log(effectStepTarget.itemFromStep);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function debugEffectCanTrigger(){
|
||||
|
||||
let ecTriggerTargetId = document.getElementById("ecTriggerTargetId").value;
|
||||
if(ecTriggerTargetId == ""){ ecTriggerTargetId = null; }
|
||||
|
||||
let ecTriggerIndex = document.getElementById("ecTriggerIndex").value;
|
||||
if(ecTriggerIndex == ""){ ecTriggerIndex = 0; }
|
||||
|
||||
let ecDoTrigger = document.getElementById("ecDoTrigger").value;
|
||||
if(ecDoTrigger == ""){ ecDoTrigger = 0; }
|
||||
|
||||
if(doEffectTriggers(ecTriggerTargetId, ecTriggerIndex, true) != true){
|
||||
console.log('Effect cannot be triggered');
|
||||
//queueEffect(ecTriggerTargetId, ecTriggerIndex);
|
||||
//doNextInQueue();
|
||||
//loopTriggerQueue();
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('Effect CAN be triggered');
|
||||
|
||||
if(ecDoTrigger){
|
||||
// Do everything needed in triggers
|
||||
doEffectTriggers(ecTriggerTargetId, ecTriggerIndex);
|
||||
|
||||
// Then queue the effects to happen (queued so they can be chained/countered)
|
||||
queueEffect(ecTriggerTargetId, ecTriggerIndex);
|
||||
|
||||
// Do all effects in queue (for debug), in actual will do one, then allow chain triggers, etc.
|
||||
loopTriggerQueue();
|
||||
}
|
||||
|
||||
board.drawBoard();
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,680 @@
|
||||
// Theorising, if this is what's wanted then will need doing for everything
|
||||
// not just in effects. eg. 'attack', 'effect1p1', 'flip', yadda yadda
|
||||
let triggerQueue = [];
|
||||
let triggerId = 1; // So any other effects, etc. queued for a destroyed card can be removed at once
|
||||
let triggerDone = []; // The triggers that have completed
|
||||
let triggerQueueTargets = []; // Whatever was targetted by the effect, etc. in triggerQueue (for ref by effectStep, etc)
|
||||
|
||||
// Passive effects (just does stuff)
|
||||
let flight = {};
|
||||
let reach = {};
|
||||
let taunt = {};
|
||||
let equipped = {}; // Entity x has [a,b,c] equipped to it
|
||||
|
||||
|
||||
// Events (when these occur check if this is part of an effect trigger)
|
||||
// startOfGame, startOfTurn, endOfTurn
|
||||
// cardSummoned, cardRecruited, effectTriggered
|
||||
// etc.
|
||||
|
||||
|
||||
// Trigger types
|
||||
const triggerTypes = {
|
||||
'tap': 1,
|
||||
'pay': 2,
|
||||
};
|
||||
// Basic effects
|
||||
// TODO: Pull display name/description from DB?
|
||||
const basicEffects = {
|
||||
1: 'Equip',
|
||||
2: 'Heal',
|
||||
3: 'Hurt',
|
||||
4: 'Recruit',
|
||||
5: 'Give Flight',
|
||||
6: 'Taunt',
|
||||
};
|
||||
|
||||
|
||||
// Actives to add passives
|
||||
function giveFlight(card){
|
||||
flight[card] = true;
|
||||
}
|
||||
function removeFlight(card){
|
||||
// If the card has flight delete entity component
|
||||
if(card in flight){
|
||||
delete flight[card];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function giveReach(card){
|
||||
reach[card] = true;
|
||||
}
|
||||
function removeReach(card){
|
||||
if(card in reach){
|
||||
delete reach[card];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
function giveTaunt(card){
|
||||
taunt[card] = true;
|
||||
}
|
||||
function removeTaunt(card){
|
||||
// If the card has flight delete entity component
|
||||
if(card in taunt){
|
||||
delete taunt[card];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Active
|
||||
function equip(){
|
||||
// Equip card to another unit
|
||||
|
||||
}
|
||||
function unequip(){
|
||||
// Remove card from its equipped to unit
|
||||
|
||||
}
|
||||
|
||||
function drawCard(drawAmount, player){
|
||||
board.drawACard(player, drawAmount);
|
||||
}
|
||||
|
||||
function heal(healAmount, healPlayer){
|
||||
// For each heal 1..4
|
||||
for(let i = 0; i < healAmount; i++){
|
||||
// Loop shield
|
||||
let items = this.getItems('shield', healPlayer, null, null);
|
||||
for(let item = 0; item < items.length; item++){
|
||||
|
||||
let itemKey = items[item];
|
||||
// If a shield is tapped, untap it to 'heal'
|
||||
if(board.isTapped(itemKey)){
|
||||
board.untapCard(itemKey);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hurt(hurtDamage, hurtTarget = null, hurtPlayer = null){
|
||||
// Deal X000 damage to unit this turn, or deal Y to opponent's shield
|
||||
|
||||
// Set player/element
|
||||
if(hurtPlayer == null && hurtTarget != null){
|
||||
hurtPlayer = player[hurtTarget];
|
||||
hurtElement = boardElement[hurtTarget];
|
||||
}else{
|
||||
hurtElement = 'shield'
|
||||
}
|
||||
|
||||
switch(hurtElement){
|
||||
case 'board':
|
||||
// TODO: Make temp damage this turn as well
|
||||
if(hurtDamage >= cardAttack[hurtTarget][0]){
|
||||
board.sendToGrave(hurtTarget);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'shield':
|
||||
let destroyCount = cardAttackToShieldDamage(hurtDamage);
|
||||
console.log(destroyCount);
|
||||
// TODO: loop player's shield, tap if they can be tapped, destroy
|
||||
// if all already tapped
|
||||
|
||||
// While there's still damage to deal
|
||||
while(destroyCount > 0){
|
||||
|
||||
// Keep looping through the shield belonging to target user
|
||||
let items = board.getItems('shield', hurtPlayer, null, null);
|
||||
let tappedItems = board.getItems('shield', hurtPlayer, 'tapped', null);
|
||||
|
||||
for(let item = 0; item < items.length; item++){
|
||||
|
||||
// If nothing more to destroy, exit loop
|
||||
if(destroyCount <= 0){ break; }
|
||||
|
||||
let itemKey = items[item];
|
||||
console.log(itemKey);
|
||||
console.log(cardStatus[itemKey]);
|
||||
|
||||
// If there's anything to tap, tap it
|
||||
if(cardStatus[itemKey] == null){
|
||||
console.log('been tapped');
|
||||
board.tapCard(itemKey);
|
||||
destroyCount--;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If there's nothing to tap, destroy it
|
||||
if(items.length == tappedItems.length){
|
||||
console.log('been destroyed');
|
||||
board.destroyShield(itemKey);
|
||||
destroyCount--;
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
board.drawBoard();
|
||||
break; // end case 'shield'
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Effect Trigger(s)
|
||||
// Non-event triggers. i.e. these are activated triggers
|
||||
// rather than things such as 'On attack', 'When opponent draws'
|
||||
|
||||
// When card has been actively tapped
|
||||
function triggerTap(card){
|
||||
if(canTriggerTap(card) == false){ return false; }
|
||||
|
||||
board.tapCard(card);
|
||||
|
||||
console.log('triggerTap');
|
||||
return true;
|
||||
|
||||
}
|
||||
// Check a card can actively be tapped, otherwise don't start trigger
|
||||
function canTriggerTap(card){
|
||||
if(board.isTapped(card)){
|
||||
return false;
|
||||
}
|
||||
if(boardElement[card] != 'board'){
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('can triggerTap');
|
||||
return true;
|
||||
}
|
||||
|
||||
// Pay the mana cost(s) to trigger event
|
||||
function triggerPay(triggerAmount){
|
||||
if(canTriggerPay(triggerAmount) == false){
|
||||
return false;
|
||||
}
|
||||
|
||||
let effectCosts = [{1:0,2:0,3:0,4:0}, triggerAmount];
|
||||
board.tapManaRequired(null, 0, false, effectCosts);
|
||||
|
||||
console.log('triggerPay');
|
||||
return true;
|
||||
|
||||
}
|
||||
// Check the mana cost(s) can be paid, otherwise don't start trigger
|
||||
function canTriggerPay(triggerAmount){
|
||||
// For player0 only at the mo. and with no specific colour costs
|
||||
let effectCosts = [{1:0,2:0,3:0,4:0}, triggerAmount];
|
||||
if(board.tapManaRequired(null, 0, true, effectCosts) == false){
|
||||
console.log('cannot trigger pay');
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('Can trigger pay');
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
// Pay/Activate (all) the triggers
|
||||
function doEffectTriggers(itemKey, effectIndex, checkOnly = false){
|
||||
|
||||
// Check card has effectData
|
||||
let effectData = cardEffect[itemKey];
|
||||
if(effectData === undefined){ return false; } // No effect
|
||||
|
||||
// Check effectData contains target effect
|
||||
let effect = effectData[effectIndex];
|
||||
if(effect === undefined){ return false; } // No effect
|
||||
|
||||
// Loop each trigger, AND activate them
|
||||
for (const [key, value] of Object.entries(effect['trigger'])) {
|
||||
|
||||
let effectTrigger = effect['trigger'][key];
|
||||
console.log('--- Trigger '+key+' ---');
|
||||
console.log(effectTrigger['triggerTypeId']);
|
||||
|
||||
// TAP TRIGGER
|
||||
if(effectTrigger['triggerTypeId'] == triggerTypes.tap){
|
||||
if(canTriggerTap(itemKey) == false){
|
||||
console.log('Tap trigger, cannot be triggered');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Do the trigger, then continue to next trigger
|
||||
if(!checkOnly){board.tapCard(itemKey);}
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
|
||||
// PAY TRIGGER
|
||||
if(effectTrigger['triggerTypeId'] == triggerTypes.pay){
|
||||
|
||||
// To build the colourReq TODO: Change as could be 1 of either red/blue
|
||||
// for instance
|
||||
let colourReq = {1:0,2:0,3:0,4:0};
|
||||
|
||||
// Loop pay required (colours) for said trigger
|
||||
// BUILD the colourReq loop needed for tapMana check
|
||||
effectTrigger['target'].forEach((effectTriggerTarget) => {
|
||||
|
||||
// Increment colourReq by one of that colour
|
||||
// THIS WILL NEED REDOING, AS MAYBE IT'S 2 OF RED OR BLUE!!
|
||||
colourReq[effectTriggerTarget.colourId]++;
|
||||
|
||||
});
|
||||
|
||||
// Check if the cost (and colour req.) can be paid
|
||||
canPay = board.tapManaRequired(null, null, true, [colourReq, effectTrigger['amount']]);
|
||||
if(canPay !== true){
|
||||
console.log('Pay trigger, cannot be triggered');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pay trigger costs, then continue to next trigger
|
||||
if(!checkOnly){
|
||||
board.tapManaRequired(null, null, false, [colourReq, effectTrigger['amount']]);
|
||||
}
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
// OTHER TRIGGERS
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
function activateEffect(){
|
||||
// Do effect triggers, then doEffect once payed
|
||||
}
|
||||
|
||||
function getEffect(itemKey, effectIndex){
|
||||
// Check card has effectData
|
||||
let effectData = cardEffect[itemKey];
|
||||
if(effectData === undefined){ return false; } // No effect
|
||||
|
||||
// Check effectData contains target effect
|
||||
let effect = effectData[effectIndex];
|
||||
if(effect === undefined){ return false; } // No effect
|
||||
|
||||
return effect;
|
||||
}
|
||||
// Get all the targets for effect step, so they can be targgeted
|
||||
// by the effect/selected by the player
|
||||
function getEffectStepTargets(itemKey, effectIndex, effectStep, fromBoardElement, playerId, effectTriggerId = null){
|
||||
|
||||
// Get Effect
|
||||
let effect = getEffect(itemKey, effectIndex);
|
||||
if(effect == false){ return false; }
|
||||
|
||||
// Get the step
|
||||
let step = effect['step'][effectStep];
|
||||
if(step == undefined){ return false; }
|
||||
|
||||
// The items that can be targetted with the effectStep
|
||||
let itemsToSelectFrom = [];
|
||||
|
||||
// Loop the target location (or all locations)
|
||||
// Check which cards meet the criteria in the DB
|
||||
|
||||
|
||||
// TODO: Not 100% sure how to keep track. May need a var for each item in previous steps
|
||||
// or previous chains so they can be accessed, and used
|
||||
// If itemFromStep, return the itemKeys of the items selected from a previous step
|
||||
|
||||
console.log('HELLOO');
|
||||
console.log(step['target']);
|
||||
|
||||
// TODO: If target is 'itemFromStep', then get whatever item(s) were selected in
|
||||
// said step, and add them to the thing
|
||||
// TODO TODO
|
||||
|
||||
// If the target requirement is something from a previous step
|
||||
// Check if target criteria is something from another step
|
||||
let isFromStep = false;
|
||||
step['target'].forEach((target) => {
|
||||
if(target['itemFromStep'] != null){
|
||||
|
||||
console.log(target['itemFromStep']);
|
||||
console.log(effectTriggerId);
|
||||
//console.log(triggerQueue);
|
||||
//console.log(triggerDone);
|
||||
|
||||
// Loop all triggers that have been done (completed)
|
||||
triggerDone.forEach((oldTrigger) => {
|
||||
|
||||
// If not same triggerId, or the effectStep is not that of itemFromStep
|
||||
if(oldTrigger['triggerId'] != effectTriggerId || target['itemFromStep'] != oldTrigger['effectStep']){
|
||||
return isFromStep = false;
|
||||
}
|
||||
|
||||
console.log(oldTrigger);
|
||||
return isFromStep = oldTrigger['targets'];
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
// If above returns a step, need to get the target(s) from that prior step to use a targets now
|
||||
if(isFromStep){
|
||||
console.log(isFromStep);
|
||||
console.log('AAAAAAAAAAAAAAAAAAAAAA');
|
||||
}
|
||||
|
||||
|
||||
let items = [];
|
||||
// If from a previous step, loop those
|
||||
if(isFromStep !== null && isFromStep !== undefined && isFromStep !== false){ items = isFromStep; }
|
||||
// If not, get all the related doodads
|
||||
else{
|
||||
items = board.getItems(fromBoardElement, playerId, null, null);
|
||||
}
|
||||
|
||||
// Loop the boardlements and compare the colours, classes, etc. to match effect target criteria
|
||||
// boardElement, playerId, cardStatus, listPosition
|
||||
for(let item = 0; item < items.length; item++){
|
||||
// TODO: MAYBE ADD THE COLOUR/PASSIVE CHECKS to the getItems itself
|
||||
|
||||
let itemKey = items[item];
|
||||
//console.log(cardColours[itemKey]);
|
||||
|
||||
// If the item from getItems meets the criterias of target DB
|
||||
step['target'].forEach((target) => {
|
||||
|
||||
//console.log(target);
|
||||
//console.log(triggerDone);
|
||||
|
||||
|
||||
// Check the class the same for target, and item
|
||||
// If targetDB has null this means 'any' so is always correct
|
||||
// Check Class (TODO: mixed classes, colours, etc ie select 1 red+blue card)
|
||||
/*
|
||||
if(class[itemKey] == target['classId'] || target['classId'] == null){
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
// Check colour
|
||||
// If not null check the colours, otherwise null means any colour is ok
|
||||
if(target['colourId'] !== null){
|
||||
// Check the colours to see if one is of requirement
|
||||
cardColours[itemKey].forEach((colour) => {
|
||||
// If the colour doesn't match, continue to next target (forEach)
|
||||
if(colour[0] != target['colourId']){
|
||||
// The return below is just a 'continue' in forEach terms
|
||||
return; // forEach jank. forEach executes the 'function' 'target' each loop
|
||||
}
|
||||
// If the colour is correct, keep checking requirements
|
||||
});
|
||||
}
|
||||
|
||||
// Check passive (If hasPassive(id))
|
||||
/*
|
||||
if(cardColours[itemKey] == target['passiveId'] || target['passiveId'] == null){
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
if(cardType[itemKey] == target['typeId'] || target['typeId'] == null){
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
// Once all the target Req. have been checked against the item
|
||||
// and they match, add to selectable list
|
||||
itemsToSelectFrom.push(itemKey);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return itemsToSelectFrom;
|
||||
|
||||
|
||||
}
|
||||
|
||||
function addTargettedCardsToQueueEvent(queueTriggerId, triggerStep, targets){
|
||||
|
||||
console.log('ADD TARGETTED TO QUEUED TRIGGER');
|
||||
console.log(queueTriggerId);
|
||||
console.log(triggerStep);
|
||||
console.log(targets);
|
||||
|
||||
//console.log(triggerQueue[queueTriggerId]);
|
||||
//console.log(targets);
|
||||
|
||||
console.log(triggerQueue);
|
||||
|
||||
triggerQueue.forEach((queued) => {
|
||||
console.log(queued);
|
||||
if(
|
||||
queued.triggerId == queueTriggerId
|
||||
&&
|
||||
queued.effectStep == triggerStep
|
||||
){
|
||||
console.log('THIS SHOULD HAVE TARGETS ADDED');
|
||||
queued['targets'] = targets;
|
||||
}
|
||||
});
|
||||
|
||||
console.log(triggerQueue);
|
||||
|
||||
}
|
||||
|
||||
|
||||
function queueEffect(itemKey, effectIndex){
|
||||
|
||||
let effect = getEffect(itemKey, effectIndex);
|
||||
if(effect == false){ return false; }
|
||||
|
||||
// TODO: Sort steps by stepOrder incase wrong in DB, etc.
|
||||
for (const [stepKey, step] of Object.entries(effect['step'])) {
|
||||
triggerQueue.push(
|
||||
// event, item, effectId, target, triggerId (commented atop)
|
||||
{
|
||||
'event': 'effect' // Which event. attack, destroy, effect, etc.
|
||||
,'item': itemKey // Which card is doing event
|
||||
,'effectIndex': effectIndex // Which effect (if effect)
|
||||
,'effectStep': stepKey // Which effect (if effect)
|
||||
,'target': null // ?
|
||||
,'triggerId': triggerId
|
||||
,'targets': null // ?
|
||||
}
|
||||
);
|
||||
}
|
||||
// Increment triggerId
|
||||
triggerId++;
|
||||
console.log(triggerQueue);
|
||||
}
|
||||
|
||||
function doNextInQueue(){
|
||||
|
||||
if(triggerQueue.length <= 0){
|
||||
console.log('Nothing in queue, doing next phase/event/whatever');
|
||||
}
|
||||
|
||||
// If effect, trigger it (should already have paid the trigger costs)
|
||||
switch(triggerQueue[0].event){
|
||||
case 'effect':
|
||||
// Do the effect
|
||||
doEffect(triggerQueue[0].item, triggerQueue[0].effectIndex, triggerQueue[0].effectStep, triggerQueue[0].triggerId);
|
||||
break;
|
||||
|
||||
default:
|
||||
alert('Error in doNextInQueue');
|
||||
return 0;
|
||||
break;
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Add to completed triggers
|
||||
triggerDone.push(triggerQueue[0]);
|
||||
|
||||
// And remove from the triggerQueue (first index)
|
||||
triggerQueue.shift();
|
||||
|
||||
}
|
||||
function loopTriggerQueue(){
|
||||
|
||||
while(triggerQueue.length > 0){
|
||||
doNextInQueue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Recusively call doEffect until each is done?
|
||||
// Once recruit (play from hand) is triggered, need to allow user to select
|
||||
// then when done move to next step
|
||||
function doEffect(itemKey, effectIndex, effectStep, effectTriggerId){
|
||||
|
||||
console.log('doEffect');
|
||||
|
||||
let effect = getEffect(itemKey, effectIndex);
|
||||
if(effect == false){ return false; }
|
||||
|
||||
// Get the step
|
||||
let step = effect['step'][effectStep];
|
||||
if(step == undefined){ return false; }
|
||||
|
||||
|
||||
// For each step, activate the correct effect type on
|
||||
// the correct targets.
|
||||
|
||||
|
||||
// Get targets TODO: Use this instead of each case having it
|
||||
//let targets = getEffectStepTargets(itemKey, effectIndex, effectStep, fromBoardElement, playerId, effectTriggerId);
|
||||
|
||||
switch (step['basicEffectId']){
|
||||
|
||||
// Recruit
|
||||
case 4:
|
||||
recruitCard(itemKey, effectIndex, effectStep, step['amount'], effectTriggerId);
|
||||
break;
|
||||
|
||||
// Give Flight
|
||||
case 5:
|
||||
givePassive(itemKey, effectIndex, effectStep, step['amount'], effectTriggerId, 'flight');
|
||||
break;
|
||||
|
||||
// Give Taunt
|
||||
case 6:
|
||||
givePassive(itemKey, effectIndex, effectStep, step['amount'], effectTriggerId, 'taunt');
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// Add the selected targets to the queuedItem so it can be referred to in future
|
||||
|
||||
// Now do the next step, if there's another in the effect
|
||||
// Commented out while testing triggerQueue
|
||||
/*
|
||||
if(effect['step'][effectStep++] !== undefined){
|
||||
doEffect(itemKey, effectIndex, effectStep++);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
function givePassive(itemKey, effectIndex, effectStep, targetAmount, effectTriggerId, passive){
|
||||
|
||||
console.log('GIVE PASSIVE: '+passive);
|
||||
|
||||
let effect = getEffect(itemKey, effectIndex);
|
||||
if(effect == false){ return false; }
|
||||
|
||||
// Get the step
|
||||
let step = effect['step'][effectStep];
|
||||
if(step == undefined){ return false; }
|
||||
|
||||
console.log(step);
|
||||
//return false;
|
||||
|
||||
// TODO: null, 0 are boardElement/playerId (for now) need to redo, rethink
|
||||
console.log('HELP ME');
|
||||
let targets = getEffectStepTargets(itemKey, effectIndex, effectStep, null, 0, effectTriggerId);
|
||||
console.log(targets);
|
||||
|
||||
if(targetAmount > 0 && targets.length > 0){
|
||||
|
||||
// TODO: better, and if just one thing omit, and do automatically
|
||||
let selectedTarget = prompt("Select a card to gain flight: \n"+targets, targets[0]);
|
||||
// User didn't select anything
|
||||
if (selectedTarget == null || selectedTarget == "") {
|
||||
alert('No card recruited, c ya');
|
||||
return false;
|
||||
}
|
||||
// User inputted card not in ID (obv temp, as will be done in game UI)
|
||||
// Huh I forgot why this is written like this, maybe it doens't work?
|
||||
if (!selectedTarget.includes(selectedTarget)){
|
||||
alert('Not in selection');
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Remove the card from the selection (in case there's another in this loop)
|
||||
targets.splice(targets.indexOf(selectedTarget), 1);
|
||||
|
||||
// Give the passive
|
||||
if(passive == 'flight'){
|
||||
giveFlight(selectedTarget);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
function removePassive(){}
|
||||
function hasPassive(){}
|
||||
|
||||
function recruitCard(itemKey, effectIndex, effectStep, targetAmount, effectTriggerId = null){
|
||||
console.log('RECRUIT');
|
||||
|
||||
let fromBoardElement = 'hand'; // FOR NOW, JUST TO TEST, THIS WILL BE PER BASIC EFFECT
|
||||
let playerId = 0;
|
||||
|
||||
let targets = getEffectStepTargets(itemKey, effectIndex, effectStep, fromBoardElement, playerId, effectTriggerId);
|
||||
let targettedCards = [];
|
||||
|
||||
//console.log(targets);
|
||||
|
||||
if(targetAmount > 0 && targets.length > 0){
|
||||
|
||||
let selectedTarget = prompt("Select a card to recruit: \n"+targets, targets[0]);
|
||||
// User didn't select anything
|
||||
if (selectedTarget == null || selectedTarget == "") {
|
||||
alert('No card recruited, c ya');
|
||||
return false;
|
||||
}
|
||||
// User inputted card not in ID (obv temp, as will be done in game UI)
|
||||
if (!selectedTarget.includes(selectedTarget)){
|
||||
alert('Not in selection');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove the card from the selection (in case there's another)
|
||||
targets.splice(targets.indexOf(selectedTarget), 1);
|
||||
|
||||
// Add to targetted (for future ref)
|
||||
targettedCards.push(selectedTarget);
|
||||
|
||||
// Play recruited card
|
||||
// TODO: Need to pass player/listPosition better?
|
||||
board.playRecruitedCard(player[selectedTarget], listPosition[selectedTarget]);
|
||||
|
||||
}
|
||||
|
||||
addTargettedCardsToQueueEvent(effectTriggerId, effectStep, targettedCards);
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
function cardAttackToShieldDamage(attack){
|
||||
if (attack >= 10000){ return 3; }
|
||||
if (attack >= 5001){ return 2; }
|
||||
if (attack >= 1){ return 1; }
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 6.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
@ -0,0 +1,133 @@
|
||||
<!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>
|
||||
<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>
|
||||
<script src="/cards.js"></script><!-- Temp until DB -->
|
||||
<script src="/shapes.js"></script>
|
||||
<script src="/main.js"></script>
|
||||
<script src="/board.js"></script>
|
||||
<script src="/helper.js"></script>
|
||||
<script src="/effect.js"></script>
|
||||
<script src="/debug.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
@ -0,0 +1,200 @@
|
||||
const socket = io({autoConnect: false});
|
||||
const canvas = document.getElementById('canvas');;
|
||||
|
||||
let room;
|
||||
let hand = [];
|
||||
let turn;
|
||||
let playerName;
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
||||
// ALERTS
|
||||
socket.on('alert', function (data) {
|
||||
console.log('<< alert');
|
||||
alert(data.message);
|
||||
});
|
||||
|
||||
// GAME SERVER STUFF
|
||||
// PASS TURN
|
||||
function passTurn(){
|
||||
// TODO: Use itemData.player/itemData.room or whatnot here
|
||||
console.log(roomId);
|
||||
let playerId = prompt('PlayerId 0/1', 0); // TODO: very temp, will be playerId/arrayId
|
||||
console.log('+ passTurn');
|
||||
socket.emit('passTurn', roomId, playerId);
|
||||
}
|
||||
|
||||
@ -0,0 +1,146 @@
|
||||
// https://stackoverflow.com/a/29676404
|
||||
|
||||
context = document.getElementById("canvas").getContext("2d");
|
||||
let defaultFillStyle = '#000';
|
||||
let shapeDebug = true;
|
||||
let shapeDebugColour = '#FF00FF';
|
||||
|
||||
class Shape extends Path2D{
|
||||
constructor(params){
|
||||
super();
|
||||
this.shape = params.shape || 'rectangle';
|
||||
this.name = params.name;
|
||||
this.x = params.x || 0;
|
||||
this.y = params.y || 0;
|
||||
this.width = params.width || 0;
|
||||
this.height = params.height || 0;
|
||||
this.fillStyle = params.fillStyle || "#FFFFFF";
|
||||
this.strokeStyle = params.strokeStyle || false;
|
||||
this.lineWidth = params.lineWidth || 0;
|
||||
this.shadow = params.shadow || false;
|
||||
}
|
||||
setShadow(shadow){
|
||||
this.shadow = shadow;
|
||||
}
|
||||
draw(){
|
||||
//console.log('Draw Shape: '+this.name);
|
||||
//console.log('X: '+this.x+' Y: '+this.y);
|
||||
//console.log('W: '+this.width+' H: '+this.height);
|
||||
//console.log('');
|
||||
|
||||
if (this.fillStyle) {
|
||||
context.fillStyle = this.fillStyle;
|
||||
|
||||
if(this.shadow){
|
||||
context.shadowColor = "#333";
|
||||
context.shadowOffsetX = 1;
|
||||
context.shadowOffsetY = 3;
|
||||
context.shadowBlur = 10;
|
||||
}
|
||||
|
||||
if(this.shape == 'circle'){
|
||||
// X,Y,Radius, start, end
|
||||
context.beginPath();
|
||||
context.arc(this.x, this.y, this.width/2, 0, 2 * Math.PI);
|
||||
context.fill();
|
||||
context.closePath();
|
||||
}else if(this.shape == 'semi'){
|
||||
context.beginPath();
|
||||
context.arc(this.x, this.y, this.width/2, Math.PI, 0);
|
||||
context.fill();
|
||||
context.closePath();
|
||||
}else if (this.shape == 'rectangle'){
|
||||
context.fillRect(this.x, this.y, this.width, this.height);
|
||||
}
|
||||
context.fillStyle = defaultFillStyle; // Reset back to default
|
||||
context.shadowColor = 'transparent';
|
||||
context.shadowBlur = null;
|
||||
context.shadowOffsetX = null;
|
||||
context.shadowOffsetY = null;
|
||||
}
|
||||
if (this.strokeStyle && this.lineWidth || shapeDebug) {
|
||||
context.strokeStyle = this.strokeStyle;
|
||||
context.lineWidth = this.lineWidth;
|
||||
context.lineWidth = 2;
|
||||
|
||||
if(!this.strokeStyle && shapeDebug){
|
||||
context.strokeStyle = shapeDebugColour;
|
||||
}
|
||||
if(this.shape == 'circle'){
|
||||
// X,Y,Radius, start, end
|
||||
context.beginPath();
|
||||
context.arc(this.x, this.y, this.width/2, 0, 2 * Math.PI);
|
||||
context.stroke();
|
||||
context.closePath();
|
||||
}else if(this.shape == 'semi'){
|
||||
context.beginPath();
|
||||
context.arc(this.x, this.y, this.width/2, Math.PI, 0);
|
||||
context.stroke();
|
||||
context.closePath();
|
||||
}else if (this.shape == 'rectangle'){
|
||||
context.strokeRect(this.x, this.y, this.width, this.height);
|
||||
}
|
||||
else if (this.shape == 'unit'){
|
||||
context.beginPath();
|
||||
context.arc(this.x, this.y, this.width/2, Math.PI, 0);
|
||||
|
||||
// Start at bottom left of the semi
|
||||
context.moveTo(this.x-this.width/2, this.y);
|
||||
// Draw accross to bottom right of semi
|
||||
context.lineTo(this.x+this.width/2, this.y);
|
||||
// Draw down to the desired height
|
||||
context.lineTo(this.x+this.width/2, this.y+this.height/3);
|
||||
// Draw accross to that height, but aligned to left of semi
|
||||
context.lineTo(this.x-this.width/2, this.y+this.height/3);
|
||||
|
||||
context.stroke();
|
||||
context.closePath();
|
||||
}
|
||||
context.strokeStyle = defaultFillStyle;
|
||||
}
|
||||
}
|
||||
startClip(shape = null){
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/clip#creating_a_complex_clipping_region
|
||||
if(this.shape == 'circle'){
|
||||
// X,Y,Radius, start, end
|
||||
context.beginPath();
|
||||
context.arc(this.x, this.y, this.width/2, 0, 2 * Math.PI);
|
||||
context.fill();
|
||||
context.closePath();
|
||||
}else if(this.shape == 'semi'){
|
||||
context.beginPath();
|
||||
context.arc(this.x, this.y, this.width/2, Math.PI, 0);
|
||||
context.fill();
|
||||
context.closePath();
|
||||
}else if (this.shape == 'rectangle'){
|
||||
context.fillRect(this.x, this.y, this.width, this.height);
|
||||
}
|
||||
// This is one of the shapes for the card images to sit it
|
||||
// TODO: Will be: unit,token,spell, etc. ALSO will have each shape for UI elements too
|
||||
else if (this.shape == 'unit'){
|
||||
|
||||
context.beginPath();
|
||||
context.arc(this.x, this.y, this.width/2, Math.PI, 0);
|
||||
|
||||
// Start at bottom left of the semi
|
||||
context.moveTo(this.x-this.width/2, this.y);
|
||||
// Draw accross to bottom right of semi
|
||||
context.lineTo(this.x+this.width/2, this.y);
|
||||
// Draw down to the desired height
|
||||
context.lineTo(this.x+this.width/2, this.y+this.height/3);
|
||||
// Draw accross to that height, but aligned to left of semi
|
||||
context.lineTo(this.x-this.width/2, this.y+this.height/3);
|
||||
|
||||
context.fill();
|
||||
context.closePath();
|
||||
|
||||
}
|
||||
|
||||
ctx.save(); // Save the canvas draw before the clip is applied
|
||||
context.clip();
|
||||
}
|
||||
endClip(){
|
||||
ctx.restore(); // Restore the canvas draw post clip applied, to get everything else back too
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
html,body{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #a1abaa;
|
||||
}
|
||||
|
||||
canvas{
|
||||
margin: auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.wrap{
|
||||
margin: auto;
|
||||
display: block;
|
||||
width: 1000px;
|
||||
}
|
||||
|
||||
.dib{display: inline-block; margin: 0; margin-bottom: 8px;}
|
||||
|
||||
.joinRoomButtons li{
|
||||
display: inline-block;
|
||||
margin-left: 6px;
|
||||
}
|
||||
|
||||
@ -0,0 +1,110 @@
|
||||
// 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;
|
||||
|
||||
// Generate the decks, and card within the deck cardLists
|
||||
[itemData] = await Promise.all([ cardGen.requestDeck(itemData) ]);
|
||||
|
||||
// Some room stuff, likely change this
|
||||
itemData.roomId = roomId;
|
||||
itemData.turn = 0; // The turn count of the match
|
||||
itemData.playersTurn = 0; // This means it's playerData[0] turn
|
||||
|
||||
// Room has just been created, so add the itemData and playerData to the room
|
||||
// on server-side so it's easily accessible
|
||||
roomData[roomId].itemData = itemData;
|
||||
roomData[roomId].playerData = playerData;
|
||||
roomData[roomId].playerOrder = playerOrder;
|
||||
|
||||
// Return the all the itemData to the client(s)
|
||||
// TODO: This will need to give different data for each, or at least
|
||||
// to differ the boardside
|
||||
return resolve(itemData);
|
||||
})()
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: disconnect, reconnect, resume
|
||||
|
||||
module.exports = {
|
||||
startItemCount
|
||||
,setPlayers
|
||||
,roomGeneration
|
||||
};
|
||||
|
||||
@ -0,0 +1,229 @@
|
||||
const roomMod = require('./roomMod');
|
||||
|
||||
// Variables for server overall
|
||||
let numRooms = 0;
|
||||
const maxRooms = 3;
|
||||
const maxPlayersPerRoom = 2;
|
||||
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
|
||||
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 message = 'You are player: '+roomData[roomId].playerOrder[clientSocket.playerId];
|
||||
global.socketAlert(clientSocket.id, message, 'alert');
|
||||
|
||||
// Emit the itemData to client socket (TODO: only emit what each player should see/recieve)
|
||||
global.io.to(clientSocket.id).emit('responseStartGame', response);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// TODO: Need a 'leave room'/disconnect
|
||||
|
||||
module.exports = {
|
||||
requestRooms
|
||||
,requestJoinRoom
|
||||
,requestCreateRoom
|
||||
};
|
||||
@ -0,0 +1,66 @@
|
||||
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);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// Globals for easier clientside alerts/logs, etc.
|
||||
global.socketAlert = function(socket, message, type = 'alert'){
|
||||
global.io.to(socket).emit(
|
||||
type, {'message': message}
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue