import { SoSUtility } from "./sos-utility.js"; /* -------------------------------------------- */ const NB_POKER_CARD = 54; const IDX2CARDFAMILY = ['c', 'd', 'h', 's']; /* -------------------------------------------- */ export class SoSCardDeck { /* -------------------------------------------- */ async initCardDeck(actor, savedDeck = undefined) { this.data = {}; this.data.deck = []; this.data.discard = []; this.data.cardState = []; this.data.cardEdge = []; if (savedDeck.deck && savedDeck.deck.length > 0) { this.data.deck = foundry.utils.duplicate(savedDeck.deck); } if (savedDeck.discard && savedDeck.discard.length > 0) { this.data.discard = foundry.utils.duplicate(savedDeck.discard); } if (savedDeck.cardEdge && savedDeck.cardEdge.length > 0) { this.data.cardEdge = foundry.utils.duplicate(savedDeck.cardEdge); } this.data.actor = actor; if (this.data.deck.length == 0 && this.data.discard.length == 0) { await this.shuffleDeck(); } } /* -------------------------------------------- */ async shuffleDeck() { this.cleanCardList(); // Randomize deck while (this.data.deck.length != NB_POKER_CARD) { let idx = Math.floor(Math.random() * 55); if (!this.data.cardState[idx - 1]) { if (idx == 53) { // Red Joker this.data.deck.push({ cardName: 'jr' }); } else if (idx == 54) { // Black Joker this.data.deck.push({ cardName: 'jb' }); } else { let familyIdx = idx % 4; let cardIdx = String((idx % 13) + 1); cardIdx = (cardIdx.length < 2) ? "0" + cardIdx : cardIdx; let cardName = IDX2CARDFAMILY[familyIdx] + cardIdx; this.data.deck.push({ cardName: cardName }); } this.data.cardState[idx - 1] = true; } } } /* -------------------------------------------- */ async resetDeck() { let newdeck = foundry.utils.duplicate(this.data.deck).concat(foundry.utils.duplicate(this.data.discard)) this.data.discard = [] // Reinit discard pile this.data.deck = [] let decklen = newdeck.length let cardState = [] for (let i = 0; i < decklen; i++) { cardState[i] = false } // Randomize deck while (this.data.deck.length != decklen) { let idx = Math.floor(Math.random() * (decklen + 1)); //console.log("Deck stuff", this.data.deck.length, decklen, idx) if (!cardState[idx - 1]) { this.data.deck.push(newdeck[idx - 1]) } cardState[idx - 1] = true } } /* -------------------------------------------- */ discardEdge(cardName) { let newEdge = this.data.cardEdge.filter(card => card.cardName != cardName); this.data.cardEdge = newEdge; // New edge list this.data.discard.push({ cardName: cardName }); // And push in the discard pile } /* -------------------------------------------- */ drawEdge(edgeNumber = 1) { for (let i = 0; i < edgeNumber; i++) { this.data.cardEdge.push(this.data.deck.pop()); console.log("DRAW EDGE", this.data.cardEdge); } } /* -------------------------------------------- */ cleanCardList() { this.data.discard = []; // Reinit discard pile this.data.deck = []; this.data.cardEdge = []; for (let i = 0; i < NB_POKER_CARD; i++) { this.data.cardState[i] = false; } } /* -------------------------------------------- */ getDeckSize() { return this.data.deck.length; } /* -------------------------------------------- */ getCardSuit(cardName) { if (cardName[0] == 'c') return 'club'; if (cardName[0] == 'd') return 'diamond'; if (cardName[0] == 'h') return 'hearts'; if (cardName[0] == 's') return 'spade'; if (cardName[0] == 'j') return 'joker'; } /* -------------------------------------------- */ drawFromDeck() { let card = this.data.deck.pop(); this.data.discard.push(card); return card; } /* -------------------------------------------- */ getFromEdge(cardName) { let card = this.data.cardEdge.find(card => card.cardName == cardName); // Get the card let newEdge = this.data.cardEdge.filter(card => card.cardName != cardName); // Remove used card this.data.cardEdge = newEdge; return card; } /* -------------------------------------------- */ getCardValue(cardName) { console.log(cardName); if (cardName[0] == 'j') return 0; // Joker case let parsed = cardName.match(/\w(\d\d)/i); let value = Number(parsed[1]); if (value > 10) value -= 10; return value; } /* -------------------------------------------- */ isCardFace(cardName) { if (cardName[0] == 'j') return false; // Joker case let parsed = cardName.match(/\w(\d\d)/i); let value = Number(parsed[1]); return (value > 10); } /* -------------------------------------------- */ setJoker(flipData) { console.log("THIS IS A JOKER !!!!"); flipData.cardSlot[0].total = 0; flipData.cardSlot[0].card1Path = `systems/foundryvtt-shadows-over-sol/img/cards/${flipData.cardSlot[0].card1.cardName}.webp`; flipData.isJoker = true; } /* -------------------------------------------- */ isJoker(cardName) { return cardName[0] == 'j'; } /* -------------------------------------------- */ async doFlipFromDeckOrEdge(flipData) { flipData.cardSlot = [{ total: 0 }]; flipData.isTrump = false; flipData.isJoker = false; flipData.fullTrump = false; if (flipData.edgeLuck) { flipData.cardOrigin = "Deck"; // Force Deck } // Select card origin if (flipData.cardOrigin == "Deck") { flipData.cardSlot[0].card1 = this.drawFromDeck(); } else { flipData.cardSlot[0].card1 = this.getFromEdge(flipData.edgeName); } let cardsuit = this.getCardSuit(flipData.cardSlot[0].card1.cardName); if (cardsuit == 'joker') { this.setJoker(flipData); } else { //console.log("First card : ", flipData.cardSlot[0].card1); // Face check for first card flipData.cardSlot[0].value1 = this.getCardValue(flipData.cardSlot[0].card1.cardName); flipData.cardSlot[0].isFace1 = this.isCardFace(flipData.cardSlot[0].card1.cardName); flipData.cardSlot[0].card1Path = `systems/foundryvtt-shadows-over-sol/img/cards/${flipData.cardSlot[0].card1.cardName}.webp`; flipData.cardSlot[0].card2 = false; if (flipData.cardSlot[0].isFace1) { flipData.cardSlot[0].card2 = this.drawFromDeck(); flipData.isJoker = this.isJoker(flipData.cardSlot[0].card2.cardName); flipData.cardSlot[0].value2 = this.getCardValue(flipData.cardSlot[0].card2.cardName); flipData.cardSlot[0].isFace2 = this.isCardFace(flipData.cardSlot[0].card2.cardName); flipData.cardSlot[0].card2Path = `systems/foundryvtt-shadows-over-sol/img/cards/${flipData.cardSlot[0].card2.cardName}.webp`; } else { flipData.cardSlot[0].value2 = 0; // Safe init } flipData.cardSlot[0].total = flipData.cardSlot[0].value1 + flipData.cardSlot[0].value2; // Trump check flipData.cardSlot[0].cardsuit = cardsuit; if (!flipData.isJoker && (cardsuit == flipData.stat.cardsuit || flipData.edgeLuck)) { // This is a trump ! flipData.cardSlot[1] = { total: 0 }; flipData.isTrump = true; flipData.cardSlot[1].card1 = this.drawFromDeck(); flipData.isJoker = this.isJoker(flipData.cardSlot[1].card1.cardName); flipData.cardSlot[1].card1Path = `systems/foundryvtt-shadows-over-sol/img/cards/${flipData.cardSlot[1].card1.cardName}.webp`; flipData.cardSlot[1].cardsuit = this.getCardSuit(flipData.cardSlot[1].card1.cardName); flipData.cardSlot[1].value1 = this.getCardValue(flipData.cardSlot[1].card1.cardName); flipData.cardSlot[1].isFace1 = this.isCardFace(flipData.cardSlot[1].card1.cardName); if (!flipData.isJoker && flipData.cardSlot[1].isFace1) { flipData.cardSlot[1].card2 = this.drawFromDeck(); flipData.isJoker = this.isJoker(flipData.cardSlot[1].card2.cardName); flipData.cardSlot[1].value2 = this.getCardValue(flipData.cardSlot[1].card2.cardName); flipData.cardSlot[1].isFace2 = this.isCardFace(flipData.cardSlot[1].card2.cardName); flipData.cardSlot[1].card2Path = `systems/foundryvtt-shadows-over-sol/img/cards/${flipData.cardSlot[1].card2.cardName}.webp`; } else { flipData.cardSlot[1].value2 = 0; // Safe init } if (flipData.cardSlot[1].cardsuit == cardsuit) { flipData.fullTrump = true; } flipData.cardSlot[1].total = flipData.cardSlot[1].value1 + flipData.cardSlot[1].value2; } } // Card Total flipData.cardTotal = flipData.cardSlot[0].total; flipData.cardSlotIndex = 0; if (flipData.fullTrump) { flipData.cardTotal = flipData.cardSlot[0].total + flipData.cardSlot[1].total; } else if (flipData.isTrump) { if (flipData.cardSlot[0].total > flipData.cardSlot[1].total) { flipData.cardSlotIndex = 0; flipData.cardTotal = flipData.cardSlot[0].total; } else { flipData.cardSlotIndex = 0; flipData.cardTotal = flipData.cardSlot[1].total; } } // Compute final result and compare if (flipData.mode == 'stat' || flipData.mode == 'weapon') { flipData.baseScore = flipData.stat.value + flipData.malusConsequence + flipData.bonusConsequence + flipData.woundMalus; } else if (flipData.mode == 'skill') { flipData.baseScore = Math.floor(flipData.stat.value / 2) + flipData.skill.system.value + flipData.malusConsequence + flipData.bonusConsequence + flipData.woundMalus; } flipData.finalScore = flipData.baseScore + flipData.cardTotal + Number(flipData.modifier); flipData.magnitude = flipData.finalScore - flipData.tn; flipData.result = (flipData.magnitude >= 0) ? "Success" : "Failure"; this.data.actor.saveDeck(); flipData.alias = this.data.actor.name; let html = await foundry.applications.handlebars.renderTemplate('systems/foundryvtt-shadows-over-sol/templates/chat-flip.html', flipData); ChatMessage.create({ content: html }); if (flipData.mode == 'weapon' && flipData.magnitude >= 0 && !flipData.isJoker) { // Success this.processWeapon(flipData); } if (flipData.isJoker) { // Critical mismatch ! // TODO } } /* -------------------------------------------- */ async processWeapon(flipData) { flipData.damageCardsuit = flipData.cardSlot[flipData.cardSlotIndex].cardsuit; let damageKey = 'damage_' + flipData.damageCardsuit; flipData.damageString = flipData.weapon.system[damageKey]; if (flipData.damageString.includes('Str')) { let damageRegexp = flipData.damageString.match(/Str([\d])?\+?([\d])?([LMSC])/i); flipData.damageValue = (flipData.actor.system.stats.strength.value * Number(damageRegexp[1] ? damageRegexp[1] : 1)) + Number(damageRegexp[2] ? damageRegexp[2] : 0); flipData.damageSeverity = damageRegexp[3]; } else { let damageRegexp = flipData.damageString.match(/(\d*)([LMSC])/i); flipData.damageValue = damageRegexp[1]; flipData.damageSeverity = damageRegexp[2]; } // Now process damage if (flipData.target) { if (game.user.isGM) { // Direct access SoSUtility.applyDamage(flipData); } else { game.socket.emit("system.foundryvtt-shadows-over-sol", { msg: "msg_request_defense", data: flipData }); } } else { let html = await renderTemplate('systems/foundryvtt-shadows-over-sol/templates/chat-damage-only.html', flipData); ChatMessage.create({ content: html }); } } /* -------------------------------------------- */ getDeckHTML() { return ""; } /* -------------------------------------------- */ getEdgeHTML() { let html = ""; for (let edge of this.data.cardEdge) { html += `` } return html; } /* -------------------------------------------- */ getEdgeHTMLForFlip() { let html = ""; for (let edge of this.data.cardEdge) { html += `` } return html; } /* -------------------------------------------- */ getDiscardTopHTML() { let html = ""; console.log("DISCARD: ", this.data.discard); if (this.data.discard.length > 0) { let card = this.data.discard[this.data.discard.length - 1]; html = ``; } return html; } }