class TurnManager { constructor() { this.turnNumber = -1; }; nextTurn() { this.turnNumber++; }; }; class Effect { constructor(value, turns, name, netValueWhenDone) { Object.assign(this, { value: value, turns: turns, name: name, netValueWhenDone: netValueWhenDone }); } apply(card) { card.activeEffects[this.name] = {} Object.assign(card.activeEffects[this.name], { value: this.value, turns: this.turns, netValueWhenDone: this.netValueWhenDone, remainingTurns: this.turns, name: this.name }) } } class Deck { constructor(isHand, manaManager, weights) { var cards = {} Object.assign(this, { hasHadCardAdded: false, cards: Array(10).fill().map( () => new BlankCard ), currentId: 1, isHand: isHand, manaManager: manaManager, selectedCardID: -1, weights: weights ? expandWeights(weights) : undefined }); if (manaManager) { this.hand = this.isHand ? this : this.manaManager.deck; this.deck = this.isHand ? this.manaManager.deck : this; }; }; sealCards() { Object.seal(this.cards); }; updateEffects() { this.cards.forEach( (card) => { Object.values(card.activeEffects).forEach( (effect) => { let isNew = effect.turns === effect.remainingTurns if( isNew ) { card[effect.name] += effect.value; } else if ( effect.remainingTurns === 0 ) { card[effect.name] -= effect.value - effect.netValueWhenDone; delete card.activeEffects[effect.name] }; effect.remainingTurns--; }); }); }; addCardFromWeights() { var newCard = getRandomItem(this.weights); this.addCards(new newCard(this.hand, this.deck)); this.weights.splice(this.weights.indexOf(newCard), 1); }; get selectedCard() { return this.cards[this.selectedCardID]; }; enableEnemyDeck() { this.enemyDeck.ArrayOfCardIDs.forEach((cardID) => { this.enemyDeck.cards[cardID].locked = false; }); }; disableEnemyDeck() { this.enemyDeck.ArrayOfCardIDs.forEach((cardID) => { this.enemyDeck.cards[cardID].locked = true; }); }; get ArrayOfCards() { return Object.values(this.cards); }; get ArrayOfCardIDs() { return Object.keys(this.cards); }; attack() { // the opponent is always the person who attacks. var opponentCardID = this.enemyDeck.selectedCardID, opponentCard = this.enemyDeck.selectedCard, yourCardID = this.selectedCardID, yourCard = this.selectedCard; if (yourCardID + 1 && opponentCardID + 1 && !opponentCard.used && !yourCard.isLand && !yourCard.isPrimal) { opponentCard.health -= (yourCard.attack === "N/A" ? 0 : yourCard.attack); yourCard.health -= (opponentCard.attack === "N/A" ? 0 : opponentCard.attack); Object.assign(opponentCard, { used: true, selected: false }); if( opponentCard.effects ) { Object.values(opponentCard.effects).forEach( (item) => { item.apply(yourCard) }) } this.enemyDeck.selectedCardID = -1; this.selectedCardID = -1; this.enemyDeck.manaManager.mana -= opponentCard.manaCost; this.ArrayOfCards.forEach((card) => { if (card.health <= 0 && card.health !== null) { this.removeCards(card); }; }); if( this.hasHadCardAdded && this.ArrayOfCards.every( card => (card instanceof BlankCard || card instanceof Land)) ) { this.enemyDeck.win() } }; }; win() { if(this === enemyDeck ) { alert("Enemy wins!"); } else if ( this === playerDeck ) { alert("Player wins!"); } else { alert("Cat?") }; gameOver = true; }; addCards(...cards) { if( cards.some( (card) => { return !(card instanceof Land) && !(card instanceof BlankCard) })) { this.hasHadCardAdded = true; }; var emptyCardIDs = this.cards.filter( item => item.name === null ).map( item => this.cards.indexOf(item)); cards.forEach( (card) => { this.isHand ? card.inHand = true : card.inHand = false; card.ID = emptyCardIDs[0]; this.cards[emptyCardIDs[0]].propogate(card); emptyCardIDs.shift(); }); }; removeCards(...cards) { cards.forEach((card) => { this.cards[card.ID].propogate(new BlankCard); }); }; Lockdown(...cards) { this.ArrayOfCardIDs.forEach((cardID) => { this.cards[cardID].locked = true; }); cards.forEach((item) => { this.cards[item.ID].locked = false; }); }; OpenUp() { this.ArrayOfCardIDs.forEach((cardID) => { this.cards[cardID].locked = false; }); }; }; class Card { constructor(maxHealth, attack, nameColor, manaCost, name, inHand, hand, deck, manaPerTurn) { Object.assign(this, { maxHealth: maxHealth, health: maxHealth, attack: attack, name: name, nameColor: nameColor, inHand: inHand, deck: deck, manaCost: manaCost, manaManager: deck.manaManager, manaPerTurn: manaPerTurn, hand: hand, decks: [playerDeck, enemyDeck], selected: false, locked: false, used: false, activeEffects: {} }); }; discard() { if( this.isDecksTurn ) { if( confirm("Are you sure you wish to discard this card?") ) { if( this.inHand ) { this.hand.removeCards(this); } else { this.deck.removeCards(this); }; }; }; }; copy() { return Object.setPrototypeOf(Object.assign({}, this), this.__proto__); }; get indexInDecks() { return this.decks.indexOf(this.deck); }; get isDecksTurn() { // true means it is... and false means it is not. return (turnManager.turnNumber % 2 === this.decks.indexOf(this.deck)); } get isLand() { return this instanceof Land; } get isPrimal() { return this instanceof Primal; } onclick() { if (!this.used) { if (this.isDecksTurn) { if (this.manaManager.mana >= (this.manaCost === "N/A" ? 0 : this.manaCost) && !this.isLand) { if (this.isPrimal) { enemyWins(); } else if (!this.selected) { this.toggleSelected() this.deck.Lockdown(this); this.deck.enableEnemyDeck(); this.deck.selectedCardID = this.ID; } else if (this.selected) { this.toggleSelected(); this.deck.OpenUp(); this.deck.disableEnemyDeck(); this.deck.selectedCardID = -1; }; }; } else if (!this.isDecksTurn && this.deck.enemyDeck.selectedCardID + 1) { this.deck.selectedCardID = this.ID; this.deck.enemyDeck.OpenUp(); this.deck.attack(); }; }; }; propogate(card) { Object.assign(this, card); Object.setPrototypeOf(this, card.__proto__) } summon() { if (this.summonCost <= this.manaManager.mana && turnManager.turnNumber % 2 === this.decks.indexOf(this.deck)) { if (confirm('Are you sure you want to summon this card?')) { this.hand.manaManager.mana -= this.summonCost === "N/A" ? 0 : this.summonCost; Object.assign(this, { inHand: false, used: true // summoning sickness }); this.deck.addCards(this.copy()); this.hand.removeCards(this); }; }; }; toggleSelected() { Object.assign(this, { selected: !this.selected }); }; get style() { return `width: ${Math.floor(this.health / this.maxHealth * 100)}; background-color: ${this.barColor};`; }; get barColor() { var r = 255 - (this.health / this.maxHealth) * 255; var g = (this.health / this.maxHealth) * 255; return `rgb(${Math.floor(r)}, ${Math.floor(g)}, 0)`; }; get id() { return this.ID; }; }; class BlankCard extends Card { constructor() { super(null,null,null,null,null,null,null,{manaManager:null},null,null) } } class ManaManager { constructor(deck) { this.mana = 0; this.deck = deck; }; get maxMana() { return 20 + this.deck.ArrayOfCards.map((i) => (i instanceof Land ? i.manaPerTurn : 0)).reduce((totalManaPerTurn, cardManaPerTurn) => { return totalManaPerTurn + cardManaPerTurn; }); }; get manaBarWidth() { return Math.floor(this.mana / this.maxMana * 100); }; get manaPerTurn() { var result = this.deck.ArrayOfCards.reduce((accumulator, card) => { return accumulator + (card.manaPerTurn === "N/A" ? 0 : card.manaPerTurn); }, 0); return result; }; set manaGain(mana) { this.mana += mana; if (this.mana > this.maxMana) { this.mana = this.maxMana; }; }; }; class Primal extends Card { constructor(hand, deck, name) { super(null, "N/A", "#DD00DD", 30, name, true, hand, deck, "N/A"); this.summonCost = 0; }; }; class Land extends Card { constructor(manaPerTurn, name, nameColor, inHand, hand, deck) { super(null, "N/A", nameColor, "N/A", name, deck.isHand, hand, deck, manaPerTurn); this.summonCost = 0; }; };
Vue.component('card', { 'template': `<div class="card" v-if="item.name !== null" :style="{ 'border-radius': '4px', 'background-color': item.selected ? 'LightGrey' : 'transparent' }"> <div class="name"> <div class="tag" :style="{ 'cursor': !item.locked && !gameOver && !item.used && ( (item.inHand ? item.summonCost : item.manaCost) <= item.manaManager.mana) && (item.isDecksTurn) && (item.inHand ? true : !item.isLand) ? 'pointer' : 'default', 'text-shadow': item.selected ? ('0 0 4px' + item.nameColor) : 'none', 'width': '100px', 'color': (item.locked || item.used || gameOver || ( (item.inHand ? item.summonCost : item.manaCost) > item.manaManager.mana) || (item.isDecksTurn) && (item.inHand ? false : item.inHand)) ? (colors.DarkerColors[item.nameColor]) : ((item.selected) ? (colors.LighterColors[item.nameColor]) : (item.nameColor))}" align="center" :onclick=" gameOver ? void(0) : ((item.indexInDecks ? 'enemy' : 'player') + (item.inHand ? 'Hand' : 'Deck')+ '.cards['+item.ID+']' + (item.inHand ? '.summon()' : '.onclick()'))"> {{ item.name }} </div> <div class="discard" :style="{'cursor': item.isDecksTurn ? 'pointer' : 'default' }" :onclick="(item.indexInDecks ? 'enemy' : 'player') + (item.inHand ? 'Hand' : 'Deck')+ '.cards['+item.ID+']' + '.discard()'"> D </div> </div> <br v-if="item.health && !item.inHand"> <div v-if="item.health && !item.inHand" style="background-color:black; width:100px;" class="health"> <div :style="item.style" :title="'Health: ' + item.health +'/' + item.maxHealth">{{item.health}}</div> </div> <br> <div class="tagList"> <div class="manaCost" :title="(item.inHand ? 'Summon cost: ' + item.summonCost : 'Usage cost' + item.manaCost)">{{ item.inHand ? item.summonCost : item.manaCost }}</div> <div class="attack" :title="'Attack: '+item.attack">{{ item.attack }}</div> <div class="manaPerRound" :title="'Mana per turn: '+item.manaPerTurn">{{ item.manaPerTurn }}</div> </div> </div> <br v-if="item.name !== null">`, 'props': ['item'{ 'item': Card, 'turnManager': TurnManager, 'colors'] 'colors': Object, 'gameOver': Boolean } });
<html> <head> <script src="./lib/vue.js"></script> <script src="./src/components.js"></script> <title>Angels & Demons</title> <link rel="stylesheet" href="./data/styles.css"> <script src="./src/BaseClasses.js"></script> <script src="./src/components.js"></script> <script src="./src/Dark.js"></script> <script src="./src/Light.js"></script> <script src="./src/Land.js" ></script> <script src="./src/Crippler.js"></script> <script src="./data/DarkHand.js"></script> <script src="./data/LightHand.js"></script> <script src="./src/expandWeights.js"></script> </head> <div id="sandbox"></div> <body> <div id="game" style="display:block"> <div style="background-color:#FFFFFF; width:100px;"> <div class="manaBar" v-bind:style="{height:'20px',width: playerMana.manaBarWidth}"> {{ playerMana.mana }} </div> </div> <div style="float:right; background-color:#FFFFFF; width:100px;"> <div class="manaBar" v-bind:style="{'float':'right', 'width': enemyMana.manaBarWidth, 'text-align':'left'}"> {{ enemyMana.mana }} </div> </div> <br><br><br> <div id="playerHand" >id="playerHand"> <card v-for="item in playerHand" :key="item.ID" :item="item" :turn-manager="turnManager" :colors="colors"><colors="colors" :game-over="gameOver"></card> </div> <div id="playerUnits"> <card v-for="item in playerCards" :key="item.ID" :item="item" :turn-manager="turnManager" :colors="colors"><colors="colors" :game-over="gameOver"></card> </div> <div id="turnStats"> <h3 id="turn"> Turn: {{ turnManager.turnNumber % 2 ? "Demons" : "Angels" }} </h3> <button onclick="nextTurn:onclick=" gameOver ? 'void(0)">End' : 'nextTurn()'">End turn.</button> </div> <div id="enemyUnits">id="enemyUnits" style="float:right;"> <card v-for="item in enemyCards" :key="item.ID" :item="item" :turn-manager="turnManager" :colors="colors"><colors="colors" :game-over="gameOver"></card> </div> <div id="enemyHand">id="enemyHand" style="float:right;"> <card v-for="item in enemyHand" :key="item.ID" :item="item" :turn-manager="turnManager" :colors="colors"><colors="colors" :game-over="gameOver"></card> </div> </table> </div> <script> const turnManager = new TurnManagerTurnManager; var gameOver = false; function getRandomItem(array){ return array[Math.floor(Math.random()*array.length)] }; function nextTurn(){ turnManager.nextTurn(); if( turnManager.turnNumber % 2 === 0 ) { playerManaManager.manaGain = playerManaManager.manaPerTurn; try{playerHand.addCardFromWeights()}catch(e){}; playerDeck.updateEffects() } else if ( turnManager.turnNumber % 2 === 1 ) { enemyManaManager.manaGain = enemyManaManager.manaPerTurn; try{enemyHand.addCardFromWeights()}catch(e){}; }; playerDeck.ArrayOfCardIDs.forEach( (cardID) => { Object.assign(playerDeck.cards[cardID], { used: false, selected: false, locked: false }); }); playerDeck.selectedCardID = -1; enemyDeck.selectedCardID = -1; enemyDeck.ArrayOfCardIDs.forEach( (cardID) => { Object.assign(enemyDeck.cards[cardID], { used: false, selected: false, locked: false }); }); }; const DarkerColors = { "#DFB720": "#BF5700", "#B0C4DE": "#90A4CE", "#C0C0C0": "#A0A0A0", "#DD00DD": "#BB00BB", "#DF5F30": "#BF3F10" }, LighterColors = { "#DFB720": "#FFD940", "#B0C4DE": "#D0E4FE", "#C0C0C0": "#E0E0E0", "#DD00DD": "#FD00FD ", "#DF5F30": "#FF7F50" } var playerDeck = new Deck(false, null, playerWeights), enemyDeck = new Deck(false, null, enemyWeights), enemyHand = new Deck(true, new ManaManager(enemyDeck), enemyWeights), playerHand = new Deck(true, new ManaManager(playerDeck), playerWeights), playerManaManager = playerHand.manaManager, enemyManaManager = enemyHand.manaManager; Object.assign(enemyDeck, { manaManager: enemyManaManager, enemyDeck: playerDeck }); playerDeck.manaManager = playerManaManager; playerDeck.enemyDeck = enemyDeck; enemyDeck.Lockdown() new Vue({ el: "#game", data: { playerCards: playerDeck.cards, enemyCards: enemyDeck.cards, playerHand: playerHand.cards, enemyHand: enemyHand.cards, playerMana: playerManaManager, enemyMana: enemyManaManager, colors: { DarkerColors: DarkerColors, LighterColors: LighterColors }, turnManager: turnManager, gameOver: gameOver } }); playerDeck.sealCards(); playerHand.sealCards(); enemyDeck.sealCards(); enemyHand.sealCards(); nextTurn(); </script> </body> </html>