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..37ea76a 100644 --- a/src/TaskQueue.js +++ b/src/TaskQueue.js @@ -1,10 +1,35 @@ -const TaskQueue = function() { - function TaskQueue() { +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; + + setTimeout(() => { + runNextTask(taskQueue); + }); + }); + }, 0); + } + else { + runNextTask(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 { @@ -19,37 +44,9 @@ 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(); - - if (task.runAndContinue) { - setTimeout(() => { - task.runAndContinue(() => { - task.dispose && task.dispose(); - taskQueue.running = false; - - setTimeout(() => { - runNextTask(taskQueue); - }); - }); - }, 0); - } - else { - runNextTask(taskQueue); - } } - - return TaskQueue; -}(); - -export default TaskQueue; +} diff --git a/src/index.js b/src/index.js index a01f912..bd1f33b 100644 --- a/src/index.js +++ b/src/index.js @@ -3,17 +3,14 @@ import Game from './Game.js'; import TaskQueue from './TaskQueue.js'; import SpeedRate from './SpeedRate.js'; -// Отвечает является ли карта уткой. function isDuck(card) { return card && card.quacks && card.swims; } -// Отвечает является ли карта собакой. function isDog(card) { return card instanceof Dog; } -// Дает описание существа по схожести с утками и собаками function getCreatureDescription(card) { if (isDuck(card) && isDog(card)) { return 'Утка-Собака'; @@ -27,40 +24,222 @@ function getCreatureDescription(card) { return 'Существо'; } +class Creature extends Card { + constructor(name, maxPower) { + super(name, maxPower); + this._currentPower = maxPower; + } + get currentPower() { + return this._currentPower; + } -// Основа для утки. -function Duck() { - this.quacks = function () { console.log('quack') }; - this.swims = function () { console.log('float: both;') }; + set currentPower(value) { + this._currentPower = value > this.maxPower ? this.maxPower : value; + } + + getDescriptions() { + return [getCreatureDescription(this), super.getDescriptions()]; + } } +class Duck extends Creature { + constructor(name = 'Мирная утка', maxPower = 2) { + super(name, maxPower); + } + + quacks() { + console.log('quack'); + }; + + swims() { + console.log('float: both;'); + }; +} -// Основа для собаки. -function Dog() { +class Dog extends Creature { + constructor(name = 'Пес-бандит', maxPower = 3) { + super(name, maxPower); + } +} + +class Trasher extends Dog { + constructor(name = 'Громила', maxPower = 5) { + super(name, maxPower); + } + + modifyTakenDamage(value, fromCard, gameContext, continuation) { + this.view.signalAbility(() => + super.modifyTakenDamage(value - 1, fromCard, gameContext, continuation)); + } + + getDescriptions() { + let descriptions = super.getDescriptions(); + descriptions.push('Получает меньше урона на 1'); + return descriptions; + } } +class Gatling extends Creature { + constructor(name = 'Гатлинг', maxPower = 6) { + super(name, maxPower); + } + + attack(gameContext, continuation) { + const taskQueue = new TaskQueue(); + + const {currentPlayer, oppositePlayer, position, updateView} = gameContext; + taskQueue.push(onDone => this.view.showAttack(onDone)); + oppositePlayer.table.forEach(card => { + taskQueue.push(onDone => { + if (card) { + this.dealDamageToCreature(2, card, gameContext, onDone); + } + }); + }); + taskQueue.continueWith(continuation); + }; + + getDescriptions() { + let descriptions = super.getDescriptions(); + descriptions.push('Наносит 2 урона всем противникам'); + return descriptions; + } +} + +class Lad extends Dog { + static getInGameCount() { return this.inGameCount || 0; } + static setInGameCount(value) { this.inGameCount = value; } + static getBonus() { return this.getInGameCount() * (this.getInGameCount() + 1) / 2; } + constructor(name = 'Браток', maxPower = 2) { + super(name, maxPower); + } + + doAfterComingIntoPlay(gameContext, continuation) { + Lad.setInGameCount(Lad.getInGameCount() + 1); + continuation(); + } + + doBeforeRemoving(continuation) { + Lad.setInGameCount(Lad.getInGameCount() - 1); + continuation(); + } + + modifyDealedDamageToCreature(value, toCard, gameContext, continuation) { + continuation(value + Lad.getBonus()); + } + + modifyTakenDamage(value, fromCard, gameContext, continuation) { + continuation(value - Lad.getBonus()); + } + + getDescriptions() { + let descriptions = super.getDescriptions(); + if (Lad.prototype.hasOwnProperty("modifyDealedDamageToCreature") && Lad.prototype.hasOwnProperty("modifyTakenDamage")) { + descriptions.push("Чем их больше, тем они сильнее"); + } + return descriptions; + } +} + +class Rogue extends Creature { + static properties = [ + 'modifyDealedDamageToCreature', + 'modifyDealedDamageToPlayer', + 'modifyTakenDamage', + ]; + + constructor(name = 'Изгой', maxPower = 2) { + super(name, maxPower); + } + + doBeforeAttack(gameContext, continuation) { + const {currentPlayer, oppositePlayer, position, updateView} = gameContext; + const oppositeCard = oppositePlayer.table[position]; + const obj = Object.getPrototypeOf(oppositeCard); + + Rogue.properties.forEach(property => { + if (obj.hasOwnProperty(property)) { + this[property] = obj[property]; + delete obj[property]; + } + }); + continuation(); + updateView(); + } + + getDescriptions() { + let descriptions = super.getDescriptions(); + descriptions.push('Крадет способности'); + return descriptions; + } +} + +class Brewer extends Duck { + constructor(name = "Пивовар", maxPower = 2) { + super(name, maxPower); + } + + doBeforeAttack(gameContext, continuation) { + let taskQueue = new TaskQueue(); + const cards = gameContext.currentPlayer.table.concat(gameContext.oppositePlayer.table); + + for(let card of cards.filter(card => isDuck(card))) { + card.view.signalHeal(); + card.maxPower += 1; + card.currentPower += 2; + card.updateView(); + } + + taskQueue.continueWith(continuation); + + } + + getDescriptions() { + let descriptions = super.getDescriptions(); + descriptions.push('Раздает живительное пиво'); + return descriptions; + } +} + +class PseudoDuck extends Dog { + constructor() { + super("Псевдоутка", 3); + } + swims() {} + quacks() {} +} + +class Nemo extends Creature { + constructor(name = 'Немо', maxPower = 4) { + super(name, maxPower); + } + + modifyDealedDamageToCreature(value, toCard, gameContext, continuation) { + Object.setPrototypeOf(this, Object.getPrototypeOf(toCard)); + this.doBeforeAttack(gameContext, continuation); + gameContext.updateView(); + } + + getDescriptions() { + let descriptions = super.getDescriptions(); + descriptions.push('The one without a name without an honest heart as compass'); + return descriptions; + } +} -// Колода Шерифа, нижнего игрока. const seriffStartDeck = [ - new Card('Мирный житель', 2), - new Card('Мирный житель', 2), - new Card('Мирный житель', 2), + new Nemo(), ]; - -// Колода Бандита, верхнего игрока. const banditStartDeck = [ - new Card('Бандит', 3), + new Brewer(), + new Brewer(), ]; - -// Создание игры. const game = new Game(seriffStartDeck, banditStartDeck); -// Глобальный объект, позволяющий управлять скоростью всех анимаций. -SpeedRate.set(1); +SpeedRate.set(3); -// Запуск игры. game.play(false, (winner) => { alert('Победил ' + winner.name); });