diff --git a/package-lock.json b/package-lock.json index 62879f0..b5765b2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -89,9 +89,9 @@ } }, "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "mime": { "version": "1.6.0", diff --git a/src/TaskQueue.js b/src/TaskQueue.js index f3a9c6a..f9d229a 100644 --- a/src/TaskQueue.js +++ b/src/TaskQueue.js @@ -1,10 +1,10 @@ -const TaskQueue = function() { - function TaskQueue() { +export default class TaskQueue { + constructor() { this.tasks = []; this.running = false; } - TaskQueue.prototype.push = function(run, dispose, duration) { + push(run, dispose, duration) { if (duration === undefined || duration === null) { this.tasks.push({runAndContinue: run, dispose}); } else { @@ -21,35 +21,31 @@ const TaskQueue = function() { runNextTask(this); }; - TaskQueue.prototype.continueWith = function(action) { + continueWith(action) { this.push(action, null, 0); }; +}; - function runNextTask(taskQueue) { - if (taskQueue.running || taskQueue.tasks.length === 0) { - return; - } - taskQueue.running = true; - const task = taskQueue.tasks.shift(); +function runNextTask(taskQueue) { + if (taskQueue.running || taskQueue.tasks.length === 0) { + return; + } + taskQueue.running = true; + const task = taskQueue.tasks.shift(); - if (task.runAndContinue) { - setTimeout(() => { - task.runAndContinue(() => { - task.dispose && task.dispose(); - taskQueue.running = false; + if (task.runAndContinue) { + setTimeout(() => { + task.runAndContinue(() => { + task.dispose && task.dispose(); + taskQueue.running = false; - setTimeout(() => { - runNextTask(taskQueue); - }); + setTimeout(() => { + runNextTask(taskQueue); }); - }, 0); - } - else { - runNextTask(taskQueue); - } + }); + }, 0); } - - return TaskQueue; -}(); - -export default TaskQueue; + else { + runNextTask(taskQueue); + } +}; diff --git a/src/index.js b/src/index.js index a01f912..c9c902c 100644 --- a/src/index.js +++ b/src/index.js @@ -27,30 +27,211 @@ function getCreatureDescription(card) { return 'Существо'; } - +class Creature extends Card { + constructor(name, maxPower, image) { + super(name, maxPower, image); + //this._currentPower = maxPower; + }; + get currentPower() { + return this._currentPower; + }; + set currentPower(value) { + this._currentPower = Math.min(Math.max(value, 0), this.maxPower); + }; + getDescriptions() { + return [ + getCreatureDescription(this), + ...super.getDescriptions(), + ]; + }; +}; // Основа для утки. -function Duck() { - this.quacks = function () { console.log('quack') }; - this.swims = function () { console.log('float: both;') }; -} +class Duck extends Creature { + constructor(name = "Мирная утка", maxPower = 2, image) { + super(name, maxPower, image) + }; + quacks() { + console.log('quack') + }; + swims() { + console.log('float: both;') + }; +}; // Основа для собаки. -function Dog() { +class Dog extends Creature { + constructor(name = "Пес-бандит", maxPower = 3, image) { + super(name, maxPower, image); + }; +}; + +class Trasher extends Dog { + constructor() { + super("Громила", 5); + } + modifyTakenDamage(value, fromCard, gameContext, continuation) { + this.view.signalAbility(() => continuation(value - 1)); + } + getDescriptions() { + return [ + "Громила получает на 1 меньше урона", + ...super.getDescriptions(), + ]; + } +} + +class Gatling extends Creature { + constructor() { + super("Гатлинг", 6); + } + attack(gameContext, continuation) { + const taskQueue = new TaskQueue(); + + const {currentPlayer, oppositePlayer, position, updateView} = gameContext; + + for (let card of oppositePlayer.table) { + if (!card) continue; + taskQueue.push(onDone => this.view.showAttack(onDone)); + taskQueue.push(onDone => this.dealDamageToCreature(2, card, gameContext, onDone)); + }; + + taskQueue.continueWith(continuation); + } + getDescriptions() { + return [ + "При атаке наносит 2 урона всем картам противника", + ...super.getDescriptions(), + ] + } +} + +class Lad extends Dog { + constructor() { + super("Браток", 2); + } + static getInGameCount() { + return this.inGameCount || 0; + } + static setInGameCount(value) { + this.inGameCount = value; + } + doAfterComingIntoPlay(gameContext, continuation) { + Lad.setInGameCount(Lad.getInGameCount() + 1); + continuation(); + } + doBeforeRemoving(continuation) { + Lad.setInGameCount(Lad.getInGameCount() - 1); + continuation(); + } + static getBonus() { + return this.inGameCount * (this.inGameCount + 1) / 2; + } + modifyDealedDamageToCreature(value, toCard, gameContext, continuation) { + this.view.signalAbility(() => + super.modifyDealedDamageToCreature(value + Lad.getBonus(), toCard, gameContext, continuation) + ) + } + modifyTakenDamage(value, fromCard, gameContext, continuation) { + this.view.signalAbility(() => + super.modifyTakenDamage(value - Lad.getBonus(), fromCard, gameContext, continuation) + ) + } + getDescriptions() { + const isLad = Lad.prototype.hasOwnProperty("modifyDealedDamageToCreature") && + Lad.prototype.hasOwnProperty("modifyDealedDamageToCreature"); + return isLad ? + ["Чем их больше, тем они сильнее", ...super.getDescriptions()] : + super.getDescriptions(); + } } +class Rogue extends Creature { + constructor() { + super("Изгой", 2); + } + doBeforeAttack(gameContext, continuation) { + const {currentPlayer, oppositePlayer, position, updateView} = gameContext; + const taskQueue = new TaskQueue(); + + const abilities = ["modifyDealedDamageToCreature", "modifyDealedDamageToPlayer", "modifyTakenDamage"]; + const toCard = oppositePlayer.table[position]; + const enemyCardProto = Object.getPrototypeOf(toCard); + taskQueue.push(onDone => { + abilities.forEach(ability => { + if(enemyCardProto.hasOwnProperty(ability)) + this[ability] = enemyCardProto[ability]; + delete enemyCardProto[ability]; + }); + this.view.signalAbility(onDone); + gameContext.updateView(); + }); + taskQueue.continueWith(continuation); + } +} + +class Brewer extends Duck { + constructor() { + super("Пивовар", 2); + } + doBeforeAttack(gameContext, continuation) { + const {currentPlayer, oppositePlayer, position, updateView} = gameContext; + const allCards = currentPlayer.table.concat(oppositePlayer.table); + const allDucks = allCards.filter(card => isDuck(card)); + const taskQueue = new TaskQueue(); + allDucks.forEach(duck => { + taskQueue.push(onDone => { + duck.maxPower += 1; + duck.currentPower += 2; + duck.view.signalHeal(onDone); + duck.updateView(); + }); + }); + taskQueue.continueWith(continuation); + } +} + +class PseudoDuck extends Dog { + constructor() { + super("Псевдоутка", 3); + } + swims() {} + quacks() {} +} + +class Nemo extends Creature { + constructor() { + super("Немо", 4); + } + doBeforeAttack(gameContext, continuation) { + const {currentPlayer, oppositePlayer, position, updateView} = gameContext; + const taskQueue = new TaskQueue(); + taskQueue.push(onDone => { + const toCard = oppositePlayer.table[position]; + Object.setPrototypeOf(this, Object.getPrototypeOf(toCard)); + this.doBeforeAttack(gameContext); + this.view.signalAbility(onDone); + gameContext.updateView(); + }) + taskQueue.continueWith(continuation); + } + getDescriptions() { + return [ + "Перед атакой на карту Немо крадёт все её способности", + ...super.getDescriptions(), + ] + } +} // Колода Шерифа, нижнего игрока. const seriffStartDeck = [ - new Card('Мирный житель', 2), - new Card('Мирный житель', 2), - new Card('Мирный житель', 2), -]; - + new Nemo(), +] // Колода Бандита, верхнего игрока. const banditStartDeck = [ - new Card('Бандит', 3), + new Brewer(), + new Brewer(), ];