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

681 lines
17 KiB
JavaScript

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